Compare commits
15 Commits
master
...
stable0711
Author | SHA1 | Date |
---|---|---|
Jon Cundill | c0abc4d2e7 | |
Jon Cundill | 99bc21499b | |
Jon Cundill | 60cb5a018c | |
Jon Cundill | b02e89189f | |
Jon Cundill | e584d81e0c | |
Jon Cundill | 1c410fdb81 | |
Jon Cundill | f0cb3dee00 | |
Jon Cundill | 9083dbf23b | |
Jon Cundill | 9e84d1f957 | |
Jon Cundill | 502b66e7bb | |
Jon Cundill | c2ecfbaf7c | |
Jon Cundill | 9aa909ba74 | |
Jon Cundill | bf2061a391 | |
Jon Cundill | c1109c1810 | |
Jon Cundill | cdbac1c921 |
|
@ -1,21 +0,0 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
*.cs text diff=csharp eol=lf
|
||||
*.java text diff=java
|
||||
*.html text diff=html
|
||||
*.css text
|
||||
*.js text
|
||||
*.sql text
|
||||
|
||||
*.csproj text merge=union
|
||||
*.sln text merge=union eol=crlf
|
||||
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
|
||||
# absolute paths are ok, as are globs
|
||||
/**/postinst* text eol=lf
|
||||
|
||||
# paths that don't start with / are treated relative to the .gitattributes folder
|
||||
relative/path/*.txt text eol=lf
|
51
CHANGES.txt
51
CHANGES.txt
|
@ -1,8 +1,19 @@
|
|||
|
||||
08/07/2011 - Initial pre alpha release
|
||||
Basic Flocking algos implemented
|
||||
Border avoidance not so good
|
||||
No configuration capabilities
|
||||
23/07/2011
|
||||
added ability to script boid behaviour from lsl
|
||||
tidied up copyright notices
|
||||
further flow field work - still flying into objects, so still work to do here
|
||||
flocking simulation now doesn't start until invoked by start command
|
||||
flow field now recalculated on each start - so can edit scene and restart takes this into account
|
||||
|
||||
21/07/2011
|
||||
introduced branch based on stable 0.7.1.1 opensim code. This currently ahead of master
|
||||
added ability to change the flocking parameters at runtime through set commands
|
||||
improved the collision algorithms somewhat
|
||||
added orientation flock step that attempts to limit excessive vertical velocity vectors
|
||||
|
||||
13/07/2011
|
||||
added more documentation
|
||||
|
||||
09/07/2011
|
||||
Fixed Boid rotation, boid now orients with direction of travel
|
||||
|
@ -10,30 +21,10 @@
|
|||
added commands for setting size of flock, prim to use for boid
|
||||
added commands to start and stop the boids flocking
|
||||
|
||||
13/07/2011
|
||||
added more documentation
|
||||
|
||||
10/07/2014
|
||||
forked from https://github.com/jonc/osboids
|
||||
|
||||
17/07/2014
|
||||
refactored the code
|
||||
added some more config items to set the region border size (no go zone) and the max height
|
||||
added support to the module for varregions
|
||||
|
||||
24/12/2014
|
||||
renamed the module to OpenSimBirds, changed all references to [Bb]oids to [Bb]irds in code
|
||||
change the configuration to use own ini file and to setup per region configurations
|
||||
changed Util class to BirdsUtil to avoid clash with OpenSim.Util
|
||||
|
||||
Um.... I think I missed some change log here... many things were changed. Use the git log??
|
||||
|
||||
16/10/2015
|
||||
added a setting to determine whether the module is active on startup. This defaults to true,
|
||||
meaning that even if there is no config at all the module still loads and waits for commands
|
||||
from the console or in-world.
|
||||
08/07/2011 - Initial pre alpha release
|
||||
Basic Flocking algos implemented
|
||||
Border avoidance not so good
|
||||
No configuration capabilities
|
||||
|
||||
17/10/2015
|
||||
added basic security. It's now possible to specify a list of allowed UUIDs (or ESTATE_MANAGER or
|
||||
ESTATE_OWNER) of avatars that can send commands to the module from chat, or of object owners
|
||||
if the commands are from a script.
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
The following people have contributed to the development of the OpenSimBirds module
|
||||
The following people have contributed to the development of the osboids module
|
||||
|
||||
* Jon Cundill - initial implementation
|
||||
* Jak Daniels - refactor for opensim 0.8.x and varregions and per region configuration
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using log4net;
|
||||
using OpenMetaverse;
|
||||
using Utils = OpenSim.Framework.Util;
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
public class Boid
|
||||
{
|
||||
// private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
|
||||
private string m_id;
|
||||
private Vector3 m_size;
|
||||
|
||||
private Vector3 m_loc;
|
||||
private Vector3 m_vel;
|
||||
private Vector3 m_acc;
|
||||
private Random m_rndnums = new Random (Environment.TickCount);
|
||||
|
||||
private BoidBehaviour m_behaviour;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Flocking.Boid"/> class.
|
||||
/// </summary>
|
||||
/// <param name='l'>
|
||||
/// L. the initial position of this boid
|
||||
/// </param>
|
||||
/// <param name='ms'>
|
||||
/// Ms. max speed this boid can attain
|
||||
/// </param>
|
||||
/// <param name='mf'>
|
||||
/// Mf. max force / acceleration this boid can extert
|
||||
/// </param>
|
||||
public Boid (string id, Vector3 size, BoidBehaviour behaviour)
|
||||
{
|
||||
m_id = id;
|
||||
m_acc = Vector3.Zero;
|
||||
m_vel = new Vector3 (m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1));
|
||||
m_size = size;
|
||||
m_behaviour = behaviour;
|
||||
}
|
||||
|
||||
public Vector3 Location {
|
||||
get { return m_loc; }
|
||||
set { m_loc = value; }
|
||||
}
|
||||
|
||||
public Vector3 Velocity {
|
||||
get { return m_vel; }
|
||||
set { m_vel = value; }
|
||||
}
|
||||
|
||||
public Vector3 Size {
|
||||
get { return m_size; }
|
||||
}
|
||||
|
||||
public String Id {
|
||||
get { return m_id; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves our boid in the scene relative to the rest of the flock.
|
||||
/// </summary>
|
||||
/// <param name='boids'>
|
||||
/// Boids. all the other chaps in the scene
|
||||
/// </param>
|
||||
public void MoveInSceneRelativeToFlock (List<Boid> neighbours, FlowField field)
|
||||
{
|
||||
// we would like to stay with our mates
|
||||
Flock (neighbours);
|
||||
|
||||
// however, our first priority is to not hurt ourselves
|
||||
// so adjust where we would like to go to avoid hitting things
|
||||
AvoidObstacles (field);
|
||||
|
||||
// then we want to avoid any threats
|
||||
// this not implemented yet
|
||||
|
||||
|
||||
// ok so we worked our where we want to go, so ...
|
||||
UpdatePositionInScene ();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move within our local flock
|
||||
/// We accumulate a new acceleration each time based on three rules
|
||||
/// these are:
|
||||
/// our separation from our closest neighbours,
|
||||
/// our desire to keep travelling within the local flock,
|
||||
/// our desire to move towards the flock centre
|
||||
///
|
||||
/// </summary>
|
||||
void Flock (List<Boid> neighbours)
|
||||
{
|
||||
|
||||
// calc the force vectors on this boid
|
||||
Vector3 sep = Separate (neighbours); // Separation
|
||||
Vector3 ali = Align (neighbours); // Alignment
|
||||
Vector3 coh = Cohesion (neighbours); // Cohesion
|
||||
Vector3 ori = Orientation(); // its tricky to fly directly up or down
|
||||
|
||||
// Arbitrarily weight these forces
|
||||
sep *= m_behaviour.separationWeighting;
|
||||
ali *= m_behaviour.alignmentWeighting;
|
||||
coh *= m_behaviour.cohesionWeighting;
|
||||
|
||||
// Add the force vectors to the current acceleration of the boid
|
||||
m_acc += sep;
|
||||
m_acc += ali;
|
||||
m_acc += coh;
|
||||
//m_acc += ori;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Method to update our location within the scene.
|
||||
/// update our location in the world based on our
|
||||
/// current location, velocity and acceleration
|
||||
/// taking into account our max speed
|
||||
///
|
||||
/// </summary>
|
||||
void UpdatePositionInScene ()
|
||||
{
|
||||
// Update velocity
|
||||
m_vel += m_acc;
|
||||
// Limit speed
|
||||
m_vel = Util.Limit (m_vel, m_behaviour.maxSpeed);
|
||||
m_loc += m_vel;
|
||||
// Reset accelertion to 0 each cycle
|
||||
m_acc *= 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seek the specified target. Move into that flock
|
||||
/// Accelerate us towards where we want to go
|
||||
/// </summary>
|
||||
/// <param name='target'>
|
||||
/// Target. the position within the flock we would like to achieve
|
||||
/// </param>
|
||||
void Seek (Vector3 target)
|
||||
{
|
||||
m_acc += Steer (target, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Arrive the specified target. Slow us down, as we are almost there
|
||||
/// </summary>
|
||||
/// <param name='target'>
|
||||
/// Target. the flock we would like to think ourselves part of
|
||||
/// </param>
|
||||
void arrive (Vector3 target)
|
||||
{
|
||||
m_acc += Steer (target, true);
|
||||
}
|
||||
|
||||
/// A method that calculates a steering vector towards a target
|
||||
/// Takes a second argument, if true, it slows down as it approaches the target
|
||||
Vector3 Steer (Vector3 target, bool slowdown)
|
||||
{
|
||||
Vector3 steer = Vector3.Zero; // The steering vector
|
||||
Vector3 desired = target - m_loc; // A vector pointing from the location to the target
|
||||
float distance = desired.Length (); // Distance from the target is the magnitude of the vector
|
||||
// If the distance is greater than 0, calc steering (otherwise return zero vector)
|
||||
if (distance > 0) {
|
||||
// Normalize desired
|
||||
desired.Normalize ();
|
||||
// Two options for desired vector magnitude (1 -- based on distance, 2 -- maxspeed)
|
||||
if ((slowdown) && (distance < m_behaviour.lookaheadDistance )) {
|
||||
desired *= (m_behaviour.maxSpeed * (distance / m_behaviour.lookaheadDistance)); // This damping is somewhat arbitrary
|
||||
} else {
|
||||
desired *= m_behaviour.maxSpeed;
|
||||
}
|
||||
// Steering = Desired minus Velocity
|
||||
steer = desired - m_vel;
|
||||
//steer.limit(maxforce); // Limit to maximum steering force
|
||||
steer = Util.Limit (steer, m_behaviour.maxForce);
|
||||
}
|
||||
return steer;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// navigate away from whatever it is we are too close to
|
||||
/// </summary>
|
||||
void AvoidObstacles (FlowField field)
|
||||
{
|
||||
//look tolerance metres ahead
|
||||
//m_acc += field.AdjustVelocity( this, m_behaviour.lookaheadDistance );
|
||||
Vector3 normVel = Vector3.Normalize (m_vel);
|
||||
Vector3 inFront = m_loc + normVel * m_behaviour.LookaheadDistance;
|
||||
|
||||
Vector3 adjustedDestintation = field.FieldStrength (m_loc, m_size, inFront);
|
||||
Vector3 newVel = Vector3.Normalize (adjustedDestintation - m_loc) * Vector3.Mag (m_vel);
|
||||
|
||||
float mOrigVel = Vector3.Mag(m_vel);
|
||||
float mNewVel = Vector3.Mag(newVel);
|
||||
if( mNewVel != 0f && mNewVel > mOrigVel ) {
|
||||
newVel *= mOrigVel / mNewVel;
|
||||
}
|
||||
m_vel = newVel;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Separate ourselves from the specified boids.
|
||||
/// keeps us a respectable distance from our closest neighbours whilst still
|
||||
/// being part of our local flock
|
||||
/// </summary>
|
||||
/// <param name='neighbours'>
|
||||
/// Boids. all the boids in the scene
|
||||
/// </param>
|
||||
Vector3 Separate (List<Boid> neighbours)
|
||||
{
|
||||
// For every boid in the system, check if it's too close
|
||||
float desired = m_behaviour.desiredSeparation;
|
||||
//who are we too close to at the moment
|
||||
List<Boid> tooCloseNeighbours = neighbours.FindAll( delegate(Boid neighbour) {
|
||||
// Is the distance is less than the desired amount
|
||||
return Utils.DistanceLessThan(m_loc, neighbour.Location, desired); //GetDistanceTo (m_loc, neighbour.Location) < desired;
|
||||
});
|
||||
|
||||
// move a bit away from them
|
||||
Vector3 steer = Vector3.Zero;
|
||||
tooCloseNeighbours.ForEach( delegate(Boid neighbour) {
|
||||
// Calculate vector pointing away from neighbor
|
||||
Vector3 diff = m_loc - neighbour.Location;
|
||||
if( diff == Vector3.Zero ) {
|
||||
diff = new Vector3( (float)m_rndnums.NextDouble(), (float)m_rndnums.NextDouble(), (float)m_rndnums.NextDouble());
|
||||
}
|
||||
steer += Utils.GetNormalizedVector(diff) / (float)(Utils.GetDistanceTo (m_loc, neighbour.Location));
|
||||
});
|
||||
|
||||
if( steer.Length () > 0 ) {
|
||||
// Average -- divide by how many
|
||||
steer /= (float)tooCloseNeighbours.Count;
|
||||
// Implement Reynolds: Steering = Desired - Velocity
|
||||
steer.Normalize ();
|
||||
steer *= m_behaviour.maxSpeed;
|
||||
steer -= m_vel;
|
||||
//don't go too fast;
|
||||
steer = Util.Limit (steer, m_behaviour.maxForce);
|
||||
}
|
||||
return steer;
|
||||
}
|
||||
|
||||
Vector3 Orientation() {
|
||||
Vector3 retVal = Vector3.Zero;
|
||||
float biggestLevel = Math.Max( m_vel.X, m_vel.Y );
|
||||
if ( biggestLevel == 0f ) {
|
||||
// wobble off the vertical
|
||||
retVal.X += (float)(2*m_rndnums.NextDouble() - 1f);
|
||||
retVal.Y += (float)(2*m_rndnums.NextDouble() - 1f);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Align our boid within the flock.
|
||||
/// For every nearby boid in the system, calculate the average velocity
|
||||
/// and move us towards that - this keeps us moving with the flock.
|
||||
/// </summary>
|
||||
/// <param name='boids'>
|
||||
/// Boids. all the boids in the scene - we only really care about those in the neighbourdist
|
||||
/// </param>
|
||||
Vector3 Align (List<Boid> boids)
|
||||
{
|
||||
Vector3 steer = Vector3.Zero;
|
||||
|
||||
boids.ForEach( delegate( Boid other ) {
|
||||
steer += other.Velocity;
|
||||
});
|
||||
|
||||
int count = boids.Count;
|
||||
|
||||
if (count > 0) {
|
||||
steer /= (float)count;
|
||||
}
|
||||
|
||||
// As long as the vector is greater than 0
|
||||
if (steer.Length () > 0) {
|
||||
// Implement Reynolds: Steering = Desired - Velocity
|
||||
steer.Normalize ();
|
||||
steer *= m_behaviour.maxSpeed;
|
||||
steer -= m_vel;
|
||||
//steer.limit(maxforce);
|
||||
steer = Util.Limit (steer, m_behaviour.maxForce);
|
||||
|
||||
}
|
||||
return steer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MAintain the cohesion of our local flock
|
||||
/// For the average location (i.e. center) of all nearby boids, calculate our steering vector towards that location
|
||||
/// </summary>
|
||||
/// <param name='neighbours'>
|
||||
/// Boids. the boids in the scene
|
||||
/// </param>
|
||||
Vector3 Cohesion (List<Boid> neighbours)
|
||||
{
|
||||
|
||||
Vector3 sum = Vector3.Zero; // Start with empty vector to accumulate all locations
|
||||
|
||||
neighbours.ForEach( delegate(Boid other) {
|
||||
sum += other.Location; // Add location
|
||||
});
|
||||
|
||||
int count = neighbours.Count;
|
||||
if (count > 0) {
|
||||
sum /= (float)count;
|
||||
return Steer (sum, false); // Steer towards the location
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
public class BoidBehaviour
|
||||
{
|
||||
public float maxSpeed;
|
||||
public float maxForce;
|
||||
public float neighbourDistance;
|
||||
public float desiredSeparation;
|
||||
public float tolerance;
|
||||
public float separationWeighting;
|
||||
public float alignmentWeighting;
|
||||
public float cohesionWeighting;
|
||||
public float lookaheadDistance;
|
||||
private Dictionary<string, string> m_paramDescriptions = new Dictionary<string, string> ();
|
||||
|
||||
public float AlignmentWeighting {
|
||||
get {
|
||||
return this.alignmentWeighting;
|
||||
}
|
||||
set {
|
||||
alignmentWeighting = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float CohesionWeighting {
|
||||
get {
|
||||
return this.cohesionWeighting;
|
||||
}
|
||||
set {
|
||||
cohesionWeighting = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float DesiredSeparation {
|
||||
get {
|
||||
return this.desiredSeparation;
|
||||
}
|
||||
set {
|
||||
desiredSeparation = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float LookaheadDistance {
|
||||
get {
|
||||
return this.lookaheadDistance;
|
||||
}
|
||||
set {
|
||||
lookaheadDistance = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float MaxForce {
|
||||
get {
|
||||
return this.maxForce;
|
||||
}
|
||||
set {
|
||||
maxForce = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float MaxSpeed {
|
||||
get {
|
||||
return this.maxSpeed;
|
||||
}
|
||||
set {
|
||||
maxSpeed = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float NeighbourDistance {
|
||||
get {
|
||||
return this.neighbourDistance;
|
||||
}
|
||||
set {
|
||||
neighbourDistance = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float SeparationWeighting {
|
||||
get {
|
||||
return this.separationWeighting;
|
||||
}
|
||||
set {
|
||||
separationWeighting = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float Tolerance {
|
||||
get {
|
||||
return this.tolerance;
|
||||
}
|
||||
set {
|
||||
tolerance = value;
|
||||
}
|
||||
}
|
||||
public BoidBehaviour() {
|
||||
m_paramDescriptions.Add("max-speed", "max distance boid will travel per frame");
|
||||
m_paramDescriptions.Add("max-force", "max acceleration od decelleration boid can exert");
|
||||
m_paramDescriptions.Add("neighbour-distance", "boid will consider other boids within this distance as part of local flock");
|
||||
m_paramDescriptions.Add("desired-separation", "closest distance to other boids that our boid would like to get");
|
||||
m_paramDescriptions.Add("tolerance", "how close to the edges of objects or the scene should our boid get");
|
||||
m_paramDescriptions.Add("separation-weighting", "factor by which closeness to other boids should be favoured when updating position in flock");
|
||||
m_paramDescriptions.Add("alignment-weighting", "factor by which alignment with other boids should be favoured when updating position in flock");
|
||||
m_paramDescriptions.Add("cohesion-weighting", "factor by which keeping within the local flock should be favoured when updating position in flock");
|
||||
m_paramDescriptions.Add("lookahead-distance", "how far in front should the boid look for edges and boundaries");
|
||||
}
|
||||
|
||||
public bool IsValidParameter (string name)
|
||||
{
|
||||
return m_paramDescriptions.ContainsKey (name);
|
||||
}
|
||||
|
||||
public void SetParameter (string name, string newVal)
|
||||
{
|
||||
switch (name) {
|
||||
case "max-speed":
|
||||
maxSpeed = Convert.ToSingle(newVal);
|
||||
break;
|
||||
case "max-force":
|
||||
maxForce = Convert.ToSingle(newVal);
|
||||
break;
|
||||
case "neighbour-distance":
|
||||
neighbourDistance = Convert.ToSingle(newVal);
|
||||
break;
|
||||
case "desired-separation":
|
||||
desiredSeparation = Convert.ToSingle(newVal);
|
||||
break;
|
||||
case "tolerance":
|
||||
tolerance = Convert.ToSingle(newVal);
|
||||
break;
|
||||
case "separation-weighting":
|
||||
separationWeighting = Convert.ToSingle(newVal);
|
||||
break;
|
||||
case "alignment-weighting":
|
||||
alignmentWeighting = Convert.ToSingle(newVal);
|
||||
break;
|
||||
case "cohesion-weighting":
|
||||
cohesionWeighting = Convert.ToSingle(newVal);
|
||||
break;
|
||||
case "lookahead-distance":
|
||||
lookaheadDistance = Convert.ToSingle(newVal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetList ()
|
||||
{
|
||||
string retVal = Environment.NewLine;
|
||||
foreach (string name in m_paramDescriptions.Keys) {
|
||||
retVal += name + " - " + m_paramDescriptions [name] + Environment.NewLine;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
return string.Format (
|
||||
"alignment-weighting = {0}, " + Environment.NewLine +
|
||||
"cohesion-weighting = {1}, " + Environment.NewLine +
|
||||
"desired-separation = {2}, " + Environment.NewLine +
|
||||
"lookahead-distance = {3}, " + Environment.NewLine +
|
||||
"max-force = {4}, " + Environment.NewLine +
|
||||
"max-speed = {5}, " + Environment.NewLine +
|
||||
"neighbour-distance = {6}, " + Environment.NewLine +
|
||||
"separation-weighting = {7}, " + Environment.NewLine +
|
||||
"tolerance = {8}", AlignmentWeighting, CohesionWeighting, DesiredSeparation, LookaheadDistance, MaxForce, MaxSpeed, NeighbourDistance, SeparationWeighting, Tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using log4net;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
public abstract class FlockingCommand
|
||||
{
|
||||
protected static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
|
||||
protected FlockingController m_controller;
|
||||
protected string m_name;
|
||||
protected string m_args;
|
||||
protected string m_description;
|
||||
|
||||
public FlockingCommand( string name, string args, string description ) {
|
||||
m_name = name;
|
||||
m_args = args;
|
||||
m_description = description;
|
||||
}
|
||||
|
||||
public void Handle (string module, string[] args) {
|
||||
if( ShouldHandleCmd() ) {
|
||||
Invoke( args );
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldHandleCmd ()
|
||||
{
|
||||
return View.Scene.ConsoleScene () == View.Scene;
|
||||
}
|
||||
|
||||
public abstract void Invoke( string[] args );
|
||||
|
||||
public string Name {
|
||||
get{ return m_name;}
|
||||
}
|
||||
|
||||
public string Params {
|
||||
get{ return m_args; }
|
||||
}
|
||||
|
||||
public string Description {
|
||||
get{ return m_description;}
|
||||
}
|
||||
|
||||
public FlockingController Controller {
|
||||
get{ return m_controller; }
|
||||
set{ m_controller = value; }
|
||||
}
|
||||
|
||||
public FlockingModel Model {
|
||||
get { return Controller.Model; }
|
||||
}
|
||||
|
||||
public FlockingView View {
|
||||
get { return Controller.View; }
|
||||
}
|
||||
}
|
||||
|
||||
public class RoostCommand : FlockingCommand {
|
||||
|
||||
public RoostCommand() : base( "roost", "", "return all the boids to the start position and remove them from the scene") {}
|
||||
|
||||
public override void Invoke (string[] args) {
|
||||
if( Model.Active ) {
|
||||
Model.Goal = FlockGoal.Roost;
|
||||
} else {
|
||||
Controller.ShowResponse ("Flock is not active, roost has no effect", args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class StopCommand : FlockingCommand {
|
||||
|
||||
public StopCommand() : base("stop", "", "stop all Flocking"){}
|
||||
|
||||
public override void Invoke (string[] args) {
|
||||
m_log.Info ("stopping the flocking activity");
|
||||
Model.Active = false;
|
||||
View.Clear ();
|
||||
}
|
||||
}
|
||||
|
||||
public class SetPositionCommand : FlockingCommand {
|
||||
public SetPositionCommand() : base("position", "x,y,z", "location that the boids will start flocking from") {}
|
||||
|
||||
public override void Invoke (string[] args)
|
||||
{
|
||||
float x = Convert.ToSingle (args [1]);
|
||||
float y = Convert.ToSingle (args [2]);
|
||||
float z = Convert.ToSingle (args [3]);
|
||||
Vector3 startPos = new Vector3 (x, y, z);
|
||||
if (Model.ContainsPoint (startPos)) {
|
||||
Model.StartPosition = startPos;
|
||||
} else {
|
||||
Controller.ShowResponse (startPos + "is not within the flow field", args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SetParameterCommand : FlockingCommand {
|
||||
public SetParameterCommand() : base("set", "name, value", "change the flock behaviour properties"){}
|
||||
|
||||
public override void Invoke (string[] args)
|
||||
{
|
||||
string name = args [1];
|
||||
string newVal = args [2];
|
||||
|
||||
BoidBehaviour behaviour = Model.Behaviour;
|
||||
|
||||
if (behaviour.IsValidParameter (name)) {
|
||||
behaviour.SetParameter (name, newVal);
|
||||
} else {
|
||||
Controller.ShowResponse (name + "is not a valid flock parameter", args);
|
||||
Controller.ShowResponse ("valid parameters are: " + behaviour.GetList (), args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SetFrameRateCommand : FlockingCommand {
|
||||
public SetFrameRateCommand() : base("framerate", "num", "[debugging] only update boids every <num> frames") {}
|
||||
|
||||
public override void Invoke (string[] args)
|
||||
{
|
||||
int frameRate = Convert.ToInt32 (args [1]);
|
||||
Controller.FrameUpdateRate = frameRate;
|
||||
}
|
||||
}
|
||||
|
||||
public class SetBoundsCommand : FlockingCommand {
|
||||
public SetBoundsCommand() : base("bounds", "xMin,xMax,yMin,yMax,zMin,zMax", "Bounds of the 3D space that the flock will be confined to") {}
|
||||
|
||||
public override void Invoke (string[] args)
|
||||
{
|
||||
//TODO:
|
||||
}
|
||||
}
|
||||
|
||||
public class SetSizeCommand : FlockingCommand {
|
||||
public SetSizeCommand() : base("size", "num", "Adjust the size of the flock ") {}
|
||||
|
||||
public override void Invoke (string [] args)
|
||||
{
|
||||
lock (Controller.UI_SYNC) {
|
||||
int newSize = Convert.ToInt32 (args [1]);
|
||||
Model.Size = newSize;
|
||||
View.Clear ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SetPrimCommand : FlockingCommand {
|
||||
public SetPrimCommand() : base("prim", "name", "set the prim used for each boid to that passed in") {}
|
||||
|
||||
public override void Invoke (string[] args)
|
||||
{
|
||||
string primName = args [1];
|
||||
lock (Controller.UI_SYNC) {
|
||||
View.BoidPrim = primName;
|
||||
View.Clear ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ShowStatsCommand : FlockingCommand {
|
||||
public ShowStatsCommand() : base("stats", "", "show flocking stats") {}
|
||||
|
||||
public override void Invoke (string[] args)
|
||||
{
|
||||
string str = Model.ToString ();
|
||||
Controller.ShowResponse (str, args);
|
||||
}
|
||||
}
|
||||
|
||||
public class StartCommand : FlockingCommand {
|
||||
public StartCommand() : base("start", "", "Start Flocking") {}
|
||||
|
||||
public override void Invoke(string[] args) {
|
||||
if( Model.Active ) {
|
||||
Controller.ShowResponse("Already active, restarting", args);
|
||||
Model.Active = false;
|
||||
View.Clear();
|
||||
}
|
||||
|
||||
m_log.Info ("start the flocking capability");
|
||||
Model.Goal = FlockGoal.Flock;
|
||||
Controller.Start ();
|
||||
Model.Active = true;
|
||||
//m_module.FlockUpdate ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using log4net;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Framework.Console;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
public delegate void BoidCmdDelegate (string module,string[] args);
|
||||
|
||||
public class BoidCmdDefn
|
||||
{
|
||||
public string Help = "";
|
||||
public string Args = "";
|
||||
public int NumParams = 0;
|
||||
string m_name;
|
||||
|
||||
public BoidCmdDefn (string name, string args, string help)
|
||||
{
|
||||
Help = help;
|
||||
Args = args;
|
||||
m_name = name;
|
||||
|
||||
if (args.Trim ().Length > 0) {
|
||||
NumParams = args.Split (",".ToCharArray ()).Length;
|
||||
} else {
|
||||
NumParams = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetSyntax ()
|
||||
{
|
||||
return m_name + " " + Args + " (" + Help + ")";
|
||||
}
|
||||
}
|
||||
|
||||
public class FlockingController
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
|
||||
public object UI_SYNC = new object ();
|
||||
|
||||
private Scene m_scene;
|
||||
private FlockingModel m_model;
|
||||
private FlockingView m_view;
|
||||
private int m_chatChannel;
|
||||
private UUID m_owner;
|
||||
private Dictionary<string, BoidCmdDelegate> m_commandMap = new Dictionary<string, BoidCmdDelegate> ();
|
||||
private Dictionary<string, BoidCmdDefn> m_syntaxMap = new Dictionary<string, BoidCmdDefn> ();
|
||||
private uint m_frame = 0;
|
||||
private int m_frameUpdateRate = 1;
|
||||
private Vector3 m_startPos = new Vector3 (128f, 128f, 128f);
|
||||
private int m_minX = 0;
|
||||
private int m_maxX = 256;
|
||||
private int m_minY = 0;
|
||||
private int m_maxY = 256;
|
||||
private int m_minZ = 0;
|
||||
private int m_maxZ = 256;
|
||||
|
||||
|
||||
|
||||
public FlockingController (Scene scene, BoidBehaviour behaviour, int channel, string prim, int flockSize)
|
||||
{
|
||||
//make the view
|
||||
// who is the owner for the flock in this region
|
||||
UUID owner = scene.RegionInfo.EstateSettings.EstateOwner;
|
||||
m_view = new FlockingView (scene);
|
||||
m_view.PostInitialize (owner);
|
||||
m_view.BoidPrim = prim;
|
||||
|
||||
//make the model
|
||||
FlowField field = new FlowField( scene, m_minX, m_maxX, m_minY, m_maxY, m_minZ, m_maxZ);
|
||||
FlockingModel model = new FlockingModel(field, behaviour, m_startPos);
|
||||
Vector3 startPos = new Vector3(128f, 128f, 128f);//scene.GetSceneObjectPart (View.BoidPrim).ParentGroup.AbsolutePosition;
|
||||
model.StartPosition = startPos; // TODO: by default start from the prim
|
||||
|
||||
m_model = model;
|
||||
m_scene = scene;
|
||||
m_chatChannel = channel;
|
||||
|
||||
// who do we respond to in send messages
|
||||
m_owner = m_scene.RegionInfo.EstateSettings.EstateOwner;
|
||||
|
||||
// register our event handlers
|
||||
m_scene.EventManager.OnFrame += FlockUpdate; // plug in to the game loop
|
||||
m_scene.EventManager.OnChatFromClient += ProcessChatCommand; //listen for commands sent from the client
|
||||
IScriptModuleComms commsMod = m_scene.RequestModuleInterface<IScriptModuleComms>();
|
||||
commsMod.OnScriptCommand += ProcessScriptCommand; // listen to scripts
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
//ask the view how big the boid prim is
|
||||
Vector3 scale = View.GetBoidSize ();
|
||||
|
||||
FlowField field = new FlowField( m_scene, m_minX, m_maxX, m_minY, m_maxY, m_minZ, m_maxZ);
|
||||
// init model
|
||||
m_log.Info ("creating model");
|
||||
// Generate initial flock values
|
||||
m_model.BoidSize = scale;
|
||||
m_model.Initialise (field);
|
||||
m_log.Info ("done");
|
||||
|
||||
}
|
||||
|
||||
|
||||
public int FrameUpdateRate {
|
||||
get { return m_frameUpdateRate; }
|
||||
set { m_frameUpdateRate = value; }
|
||||
}
|
||||
|
||||
public FlockingModel Model {
|
||||
get { return m_model; }
|
||||
}
|
||||
|
||||
public FlockingView View {
|
||||
get { return m_view; }
|
||||
}
|
||||
|
||||
public void Deregister ()
|
||||
{
|
||||
m_scene.EventManager.OnChatFromClient -= ProcessChatCommand;
|
||||
IScriptModuleComms commsMod = m_scene.RequestModuleInterface<IScriptModuleComms>();
|
||||
commsMod.OnScriptCommand -= ProcessScriptCommand;
|
||||
m_scene.EventManager.OnFrame -= FlockUpdate;
|
||||
}
|
||||
|
||||
public void AddCommand (IRegionModuleBase module, FlockingCommand cmd)
|
||||
{
|
||||
cmd.Controller = this;
|
||||
string name = cmd.Name;
|
||||
string args = cmd.Params;
|
||||
string help = cmd.Description;
|
||||
CommandDelegate fn =cmd.Handle;
|
||||
|
||||
string argStr = "";
|
||||
if (args.Trim ().Length > 0) {
|
||||
argStr = " <" + args + "> ";
|
||||
}
|
||||
m_commandMap.Add (name, new BoidCmdDelegate (fn));
|
||||
m_syntaxMap.Add (name, new BoidCmdDefn (name, args, help));
|
||||
// register this command with the console
|
||||
m_scene.AddCommand (module, "flock-" + name, "flock-" + name + argStr, help, fn);
|
||||
}
|
||||
|
||||
|
||||
#region handlers
|
||||
|
||||
public void FlockUpdate ()
|
||||
{
|
||||
if (((m_frame++ % m_frameUpdateRate) != 0) || !m_model.Active) {
|
||||
return;
|
||||
}
|
||||
// work out where everyone has moved to
|
||||
// and tell the scene to render the new positions
|
||||
lock (UI_SYNC) {
|
||||
List<Boid > boids = m_model.UpdateFlockPos ();
|
||||
m_view.Render (boids);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void ProcessScriptCommand (UUID scriptId, string reqId, string module, string input, string key)
|
||||
{
|
||||
if (FlockingModule.NAME != module) {
|
||||
return;
|
||||
}
|
||||
|
||||
string[] tokens = (input+"|<script>").Split ( new char[] { '|' }, StringSplitOptions.None);
|
||||
|
||||
string command = tokens [0];
|
||||
m_log.Debug("Input was " + input + ", command is " + command);
|
||||
BoidCmdDefn defn = null;
|
||||
if (m_syntaxMap.TryGetValue (command, out defn)) {
|
||||
if (CorrectSignature (tokens, defn)) {
|
||||
|
||||
// we got the signature of the command right
|
||||
BoidCmdDelegate del = null;
|
||||
if (m_commandMap.TryGetValue (command, out del)) {
|
||||
m_log.Info("command ok - executing");
|
||||
del (module, tokens);
|
||||
} else {
|
||||
// we don't understand this command
|
||||
// shouldn't happen
|
||||
m_log.ErrorFormat ("Unable to invoke command {0}", command);
|
||||
}
|
||||
} else {
|
||||
m_log.Error(" signature wrong for " + command);
|
||||
}
|
||||
} else {
|
||||
m_log.Error("no command for " + command);
|
||||
}
|
||||
}
|
||||
|
||||
void RespondToScript (UUID scriptId, int msgNum, string response)
|
||||
{
|
||||
IScriptModuleComms commsMod = m_scene.RequestModuleInterface<IScriptModuleComms>();
|
||||
if( commsMod != null ) {
|
||||
commsMod.DispatchReply (scriptId, msgNum, response, "");
|
||||
} else {
|
||||
Console.WriteLine("No script comms");
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessChatCommand (Object x, OSChatMessage msg)
|
||||
{
|
||||
if (m_scene.ConsoleScene () != m_scene || msg.Channel != m_chatChannel)
|
||||
return; // not for us
|
||||
|
||||
// try and parse a valid cmd from this msg
|
||||
string cmd = msg.Message.ToLower ();
|
||||
|
||||
//stick ui in the args so we know to respond in world
|
||||
//bit of a hack - but lets us use CommandDelegate inWorld
|
||||
string[] args = (cmd + " <ui>").Split (" ".ToCharArray ());
|
||||
|
||||
BoidCmdDefn defn = null;
|
||||
if (m_syntaxMap.TryGetValue (args [0], out defn)) {
|
||||
if (CorrectSignature (args, defn)) {
|
||||
|
||||
// we got the signature of the command right
|
||||
BoidCmdDelegate del = null;
|
||||
if (m_commandMap.TryGetValue (args [0], out del)) {
|
||||
del (FlockingModule.NAME, args);
|
||||
} else {
|
||||
// we don't understand this command
|
||||
// shouldn't happen
|
||||
m_log.ErrorFormat ("Unable to invoke command {0}", args [0]);
|
||||
RespondToChat (msg, "Unable to invoke command " + args [0]);
|
||||
}
|
||||
|
||||
} else {
|
||||
// we recognise the command, but we got the call wrong
|
||||
RespondToChat (msg, "wrong syntax: " + defn.GetSyntax ());
|
||||
}
|
||||
} else {
|
||||
// this is not a command we recognise
|
||||
RespondToChat (msg, args [0] + " is not a valid command for osboids");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void RespondToChat (OSChatMessage msg, string message)
|
||||
{
|
||||
m_log.Debug ("sending response -> " + message);
|
||||
IClientAPI sender = msg.Sender;
|
||||
sender.SendChatMessage (message, (byte)ChatTypeEnum.Say, msg.Position, "osboids", msg.SenderUUID, (byte)ChatSourceType.Agent, (byte)ChatAudibleLevel.Fully);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
private bool CorrectSignature (string[] args, BoidCmdDefn defn)
|
||||
{
|
||||
// args contain cmd name at 0 and <ui> tagged in last pos
|
||||
return args.Length - 2 == defn.NumParams;
|
||||
}
|
||||
|
||||
public void ShowResponse (string response, string [] cmd)
|
||||
{
|
||||
bool inWorld = IsInWorldCmd (cmd);
|
||||
if (inWorld) {
|
||||
ScenePresence owner = m_scene.GetScenePresence(m_owner);
|
||||
SendMessage(owner, response);
|
||||
} else {
|
||||
MainConsole.Instance.Output (response);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsInWorldCmd (string [] args)
|
||||
{
|
||||
bool retVal = false;
|
||||
|
||||
if (args.Length > 0 && args [args.Length - 1].Equals ("<ui>")) {
|
||||
retVal = true;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
public void SendMessage (ScenePresence recipient, string message)
|
||||
{
|
||||
IClientAPI ownerAPI = recipient.ControllingClient;
|
||||
ownerAPI.SendChatMessage (message,
|
||||
(byte)ChatTypeEnum.Say,
|
||||
recipient.AbsolutePosition,
|
||||
"osboids",
|
||||
recipient.UUID,
|
||||
(byte)ChatSourceType.Agent,
|
||||
(byte)ChatAudibleLevel.Fully
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using Utils = OpenSim.Framework.Util;
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
public enum FlockGoal
|
||||
{
|
||||
Roost = 0x01,
|
||||
Perch = 0x02,
|
||||
Flock = 0x04,
|
||||
}
|
||||
|
||||
public class FlockingModel
|
||||
{
|
||||
private List<Boid> m_flock = new List<Boid>();
|
||||
private FlowField m_flowField;
|
||||
private BoidBehaviour m_behaviour;
|
||||
private int m_flockSize = 100;
|
||||
private Vector3 m_boidSize;
|
||||
private Vector3 m_startPos;
|
||||
private FlockGoal m_goal = FlockGoal.Flock;
|
||||
private bool m_active = false;
|
||||
private int m_minX = 0;
|
||||
private int m_maxX = 256;
|
||||
private int m_minY = 0;
|
||||
private int m_maxY = 256;
|
||||
private int m_minZ = 0;
|
||||
private int m_maxZ = 256;
|
||||
|
||||
|
||||
public int Size {
|
||||
get {return m_flockSize;}
|
||||
set {
|
||||
if( value < m_flock.Count ) {
|
||||
m_flock.RemoveRange( 0, m_flock.Count - value );
|
||||
} else while( value > m_flock.Count ) {
|
||||
AddBoid( "boid"+m_flock.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FlockGoal Goal {
|
||||
get { return m_goal; }
|
||||
set { m_goal = value; }
|
||||
}
|
||||
|
||||
public int MaxX {
|
||||
get {
|
||||
return this.m_maxX;
|
||||
}
|
||||
set {
|
||||
m_maxX = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxY {
|
||||
get {
|
||||
return this.m_maxY;
|
||||
}
|
||||
set {
|
||||
m_maxY = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxZ {
|
||||
get {
|
||||
return this.m_maxZ;
|
||||
}
|
||||
set {
|
||||
m_maxZ = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MinX {
|
||||
get {
|
||||
return this.m_minX;
|
||||
}
|
||||
set {
|
||||
m_minX = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MinY {
|
||||
get {
|
||||
return this.m_minY;
|
||||
}
|
||||
set {
|
||||
m_minY = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int MinZ {
|
||||
get {
|
||||
return this.m_minZ;
|
||||
}
|
||||
set {
|
||||
m_minZ = value;
|
||||
}
|
||||
}
|
||||
public bool Active {
|
||||
get { return m_active; }
|
||||
set { m_active = value; }
|
||||
}
|
||||
|
||||
public Vector3 StartPosition {
|
||||
get { return m_startPos; }
|
||||
set { m_startPos = value; }
|
||||
}
|
||||
|
||||
public BoidBehaviour Behaviour {
|
||||
get { return m_behaviour; }
|
||||
set { m_behaviour = value; }
|
||||
}
|
||||
|
||||
public Vector3 BoidSize {
|
||||
set { m_boidSize = value; }
|
||||
}
|
||||
|
||||
|
||||
public FlockingModel( FlowField field, BoidBehaviour behaviour, Vector3 startPos ) {
|
||||
m_flowField = field;
|
||||
m_behaviour = behaviour;
|
||||
m_startPos = startPos;
|
||||
}
|
||||
|
||||
void AddBoid (string name)
|
||||
{
|
||||
Boid boid = new Boid (name, m_boidSize, m_behaviour);
|
||||
double d1 = ( Utils.RandomClass.NextDouble() - 0.5 ) * 20;
|
||||
double d2 = ( Utils.RandomClass.NextDouble() - 0.5 ) * 20;
|
||||
double d3 = ( Utils.RandomClass.NextDouble() - 0.5 ) * 20;
|
||||
boid.Location = m_startPos + new Vector3( (float)d1, (float)d2, (float)d3 );
|
||||
boid.Velocity = Vector3.UnitX;
|
||||
m_flock.Add (boid);
|
||||
}
|
||||
|
||||
public bool ContainsPoint (Vector3 position)
|
||||
{
|
||||
return m_flowField.ContainsPoint( position );
|
||||
}
|
||||
|
||||
|
||||
public void Initialise (FlowField flowField)
|
||||
{
|
||||
m_flowField = flowField;
|
||||
for (int i = 0; i < m_flockSize; i++) {
|
||||
AddBoid ("boid"+i );
|
||||
//UpdateFlockPos();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Boid> GetNeighbours(Boid boid) {
|
||||
return m_flock.FindAll(delegate(Boid other)
|
||||
{
|
||||
return (boid != other) && (Utils.GetDistanceTo (boid.Location, other.Location) < m_behaviour.neighbourDistance);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public List<Boid> UpdateFlockPos ()
|
||||
{
|
||||
if( m_active ) {
|
||||
m_flock.ForEach( delegate(Boid boid) {
|
||||
boid.MoveInSceneRelativeToFlock(GetNeighbours(boid), m_flowField);
|
||||
} );
|
||||
}
|
||||
|
||||
return m_flock;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
string retVal = "Num Boids: " + m_flockSize + Environment.NewLine
|
||||
+ m_behaviour.ToString() + Environment.NewLine
|
||||
+ m_flowField.ToString();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Timers;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using System.IO;
|
||||
using Nini.Config;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Console;
|
||||
using OpenSim.Services.Interfaces;
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
public class FlockingModule : INonSharedRegionModule
|
||||
{
|
||||
|
||||
public static string NAME = "FlockingModule";
|
||||
|
||||
private bool m_enabled = false;
|
||||
private int m_chatChannel = 118;
|
||||
private string m_boidPrim = "boid-prim";
|
||||
private FlockingController m_controller;
|
||||
private BoidBehaviour m_behaviour;
|
||||
private int m_flockSize = 100;
|
||||
|
||||
#region IRegionModule Members
|
||||
|
||||
public void Initialise (IConfigSource source)
|
||||
{
|
||||
//check if we are in the ini files
|
||||
//if so get some default values out of them and pass them onto the controller
|
||||
IConfig config = source.Configs ["Boids"];
|
||||
if (config != null) {
|
||||
m_chatChannel = config.GetInt ("chat-channel", 118);
|
||||
m_boidPrim = config.GetString ("boid-prim", "boidPrim");
|
||||
m_flockSize = config.GetInt ("flock-size", 100);
|
||||
|
||||
m_behaviour = new BoidBehaviour ();
|
||||
m_behaviour.maxSpeed = config.GetFloat ("max-speed", 1f);
|
||||
m_behaviour.maxForce = config.GetFloat ("max-force", 0.25f);
|
||||
m_behaviour.neighbourDistance = config.GetFloat ("neighbour-dist", 25f);
|
||||
m_behaviour.desiredSeparation = config.GetFloat ("desired-separation", 20f);
|
||||
m_behaviour.tolerance = config.GetFloat ("tolerance", 5f);
|
||||
m_behaviour.separationWeighting = config.GetFloat ("separation-weighting", 1.5f);
|
||||
m_behaviour.alignmentWeighting = config.GetFloat ("alignment-weighting", 1f);
|
||||
m_behaviour.cohesionWeighting = config.GetFloat ("cohesion-weighting", 1f);
|
||||
m_behaviour.lookaheadDistance = config.GetFloat ("lookahead-dist", 100f);
|
||||
|
||||
// we're in the config - so turn on this module
|
||||
m_enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRegion (Scene scene)
|
||||
{
|
||||
}
|
||||
|
||||
public void RegionLoaded (Scene scene)
|
||||
{
|
||||
if (m_enabled) {
|
||||
//set up the boid module
|
||||
m_controller = new FlockingController (scene, m_behaviour, m_chatChannel, m_boidPrim, m_flockSize);
|
||||
RegisterCommand (new RoostCommand());
|
||||
RegisterCommand (new StopCommand());
|
||||
RegisterCommand (new StartCommand());
|
||||
RegisterCommand (new SetSizeCommand());
|
||||
RegisterCommand (new ShowStatsCommand());
|
||||
RegisterCommand (new SetPrimCommand());
|
||||
RegisterCommand (new SetPositionCommand());
|
||||
RegisterCommand (new SetBoundsCommand());
|
||||
RegisterCommand (new SetFrameRateCommand());
|
||||
RegisterCommand (new SetParameterCommand());
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterCommand (FlockingCommand cmd)
|
||||
{
|
||||
m_controller.AddCommand( this, cmd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void RemoveRegion (Scene scene)
|
||||
{
|
||||
if (m_enabled) {
|
||||
m_controller.Deregister ();
|
||||
}
|
||||
}
|
||||
|
||||
public string Name {
|
||||
get { return NAME; }
|
||||
}
|
||||
|
||||
public bool IsSharedModule {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IRegionModuleBase Members
|
||||
|
||||
public void Close ()
|
||||
{
|
||||
}
|
||||
|
||||
public Type ReplaceableInterface {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* https://github.com/JakDaniels/OpenSimBirds
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -27,27 +26,27 @@
|
|||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using log4net;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using log4net;
|
||||
using Utils = OpenSim.Framework.Util;
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
public class FlockingView
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private Scene m_scene;
|
||||
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
|
||||
|
||||
private Scene m_scene;
|
||||
private UUID m_owner;
|
||||
private String m_name;
|
||||
private String m_birdPrim;
|
||||
private String m_boidPrim;
|
||||
|
||||
private Dictionary<string, SceneObjectGroup> m_sogMap = new Dictionary<string, SceneObjectGroup> ();
|
||||
|
||||
public FlockingView (String moduleName, Scene scene)
|
||||
public FlockingView (Scene scene)
|
||||
{
|
||||
m_name = moduleName;
|
||||
m_scene = scene;
|
||||
m_scene = scene;
|
||||
}
|
||||
|
||||
public void PostInitialize (UUID owner)
|
||||
|
@ -55,78 +54,71 @@ namespace Flocking
|
|||
m_owner = owner;
|
||||
}
|
||||
|
||||
public String BirdPrim {
|
||||
get { return m_birdPrim; }
|
||||
set{ m_birdPrim = value;}
|
||||
public string BoidPrim {
|
||||
get{ return m_boidPrim; }
|
||||
set{ m_boidPrim = value; }
|
||||
}
|
||||
|
||||
public Scene Scene {
|
||||
get {return m_scene;}
|
||||
}
|
||||
|
||||
|
||||
public Vector3 GetBoidSize ()
|
||||
{
|
||||
float offsetHeight;
|
||||
return findByName(m_boidPrim).GetAxisAlignedBoundingBox( out offsetHeight );
|
||||
}
|
||||
|
||||
public void Clear ()
|
||||
{
|
||||
//trash everything we have
|
||||
foreach (string name in m_sogMap.Keys)
|
||||
{
|
||||
m_log.InfoFormat("[{0}]: Removing prim {1} from region {2}", m_name, name, m_scene.RegionInfo.RegionName);
|
||||
SceneObjectGroup sog = m_sogMap[name];
|
||||
m_scene.DeleteSceneObject(sog, false);
|
||||
}
|
||||
List<string> current = new List<string> (m_sogMap.Keys);
|
||||
current.ForEach (delegate(string name) {
|
||||
RemoveSOGFromScene(name);
|
||||
});
|
||||
m_sogMap.Clear();
|
||||
m_scene.ForceClientUpdate();
|
||||
}
|
||||
|
||||
public void Render(List<Bird> birds)
|
||||
public void Render (List<Boid> boids)
|
||||
{
|
||||
foreach (Bird bird in birds) {
|
||||
DrawBird (bird);
|
||||
}
|
||||
boids.ForEach(delegate( Boid boid ) {
|
||||
DrawBoid (boid);
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawBird (Bird bird)
|
||||
private void DrawBoid (Boid boid)
|
||||
{
|
||||
SceneObjectPart existing = m_scene.GetSceneObjectPart (bird.Id);
|
||||
|
||||
SceneObjectPart existing = m_scene.GetSceneObjectPart (boid.Id);
|
||||
|
||||
SceneObjectGroup sog;
|
||||
SceneObjectPart rootPart;
|
||||
|
||||
if (existing == null) {
|
||||
m_log.InfoFormat("[{0}]: Adding prim {1} in region {2}", m_name, bird.Id, m_scene.RegionInfo.RegionName);
|
||||
SceneObjectGroup group = findByName (m_birdPrim);
|
||||
sog = CopyPrim (group, bird.Id);
|
||||
rootPart = sog.RootPart;
|
||||
rootPart.AddFlag(PrimFlags.Temporary);
|
||||
rootPart.AddFlag(PrimFlags.Phantom);
|
||||
//set prim to phantom
|
||||
//sog.UpdatePrimFlags(rootPart.LocalId, false, false, true, false);
|
||||
m_sogMap [bird.Id] = sog;
|
||||
//m_log.Error( "didnt find " + boid.Id );
|
||||
SceneObjectGroup group = findByName (m_boidPrim);
|
||||
sog = CopyPrim (group, boid.Id);
|
||||
m_sogMap [boid.Id] = sog;
|
||||
m_scene.AddNewSceneObject (sog, false);
|
||||
// Fire script on_rez
|
||||
sog.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 1);
|
||||
rootPart.ParentGroup.ResumeScripts();
|
||||
rootPart.ScheduleFullUpdate();
|
||||
sog.DetachFromBackup();
|
||||
} else {
|
||||
sog = existing.ParentGroup;
|
||||
m_sogMap[bird.Id] = sog;
|
||||
//rootPart = sog.RootPart;
|
||||
//set prim to phantom
|
||||
//sog.UpdatePrimFlags(rootPart.LocalId, false, false, true, false);
|
||||
}
|
||||
|
||||
Quaternion rotation = CalcRotationToEndpoint (sog, sog.AbsolutePosition, bird.Location);
|
||||
sog.UpdateGroupRotationPR( bird.Location, rotation);
|
||||
Quaternion rotation = CalcRotationToEndpoint (sog, boid.Location);
|
||||
//sog.UpdateGroupRotationPR( boid.Location, rotation);
|
||||
sog.UpdateGroupPosition( boid.Location );
|
||||
sog.UpdateGroupRotationR( rotation );
|
||||
}
|
||||
|
||||
private static Quaternion CalcRotationToEndpoint (SceneObjectGroup copy, Vector3 sv, Vector3 ev)
|
||||
private static Quaternion CalcRotationToEndpoint (SceneObjectGroup sog, Vector3 ev)
|
||||
{
|
||||
//llSetRot(llRotBetween(<1,0,0>,llVecNorm(targetPosition - llGetPos())));
|
||||
// bird wil fly x forwards and Z up
|
||||
// boid wil fly x forwards and Z up
|
||||
Vector3 sv = sog.AbsolutePosition;
|
||||
|
||||
Vector3 currDirVec = Vector3.UnitX;
|
||||
Vector3 desiredDirVec = Vector3.Subtract (ev, sv);
|
||||
desiredDirVec.Normalize ();
|
||||
|
||||
Quaternion rot = Vector3.RotationBetween (currDirVec, desiredDirVec);
|
||||
return rot;
|
||||
|
||||
return Vector3.RotationBetween (currDirVec, desiredDirVec);
|
||||
}
|
||||
|
||||
private SceneObjectGroup CopyPrim (SceneObjectGroup prim, string name)
|
||||
|
@ -139,18 +131,22 @@ namespace Flocking
|
|||
|
||||
private SceneObjectGroup findByName (string name)
|
||||
{
|
||||
// SceneObjectGroup retVal = (SceneObjectGroup)m_scene.Entities.Find (delegate( EntityBase e ) {
|
||||
// return e.Name == name;
|
||||
//});
|
||||
|
||||
SceneObjectGroup retVal = null;
|
||||
foreach (EntityBase e in m_scene.GetEntities()) {
|
||||
if (e.Name == name) {
|
||||
retVal = (SceneObjectGroup)e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SceneObjectPart sop = m_scene.GetSceneObjectPart(name);
|
||||
|
||||
// can't find it so make a default one
|
||||
if (retVal == null) {
|
||||
m_log.InfoFormat("[{0}]: Prim named {1} was not found in region {2}. Making default wooden sphere.", m_name, name, m_scene.RegionInfo.RegionName);
|
||||
if (sop == null) {
|
||||
m_log.Error("no " + name);
|
||||
retVal = MakeDefaultPrim (name);
|
||||
} else {
|
||||
retVal = sop.ParentGroup;
|
||||
}
|
||||
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
@ -158,9 +154,9 @@ namespace Flocking
|
|||
private SceneObjectGroup MakeDefaultPrim (string name)
|
||||
{
|
||||
PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere ();
|
||||
shape.Scale = new Vector3 (0.5f, 0.5f, 0.5f);
|
||||
shape.Scale = new Vector3 (0.5f, 0.5f, 0.5f);
|
||||
|
||||
SceneObjectGroup prim = new SceneObjectGroup(m_owner, new Vector3((float)m_scene.RegionInfo.RegionSizeX / 2, (float)m_scene.RegionInfo.RegionSizeY / 2, 25f), shape);
|
||||
SceneObjectGroup prim = new SceneObjectGroup (m_owner, new Vector3 (128f, 128f, 25f), shape);
|
||||
prim.Name = name;
|
||||
prim.DetachFromBackup ();
|
||||
m_scene.AddNewSceneObject (prim, false);
|
||||
|
@ -168,6 +164,15 @@ namespace Flocking
|
|||
return prim;
|
||||
}
|
||||
|
||||
private void RemoveSOGFromScene(string sogName)
|
||||
{
|
||||
SceneObjectGroup sog = m_sogMap[sogName];
|
||||
m_scene.DeleteSceneObject(sog, false);
|
||||
//sog.SendGroupFullUpdate();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
public class FlowField
|
||||
{
|
||||
private const int BUFFER = 5;
|
||||
private Scene m_scene;
|
||||
private float m_startX;
|
||||
private float m_startY;
|
||||
private float m_startZ;
|
||||
private float m_endX;
|
||||
private float m_endY;
|
||||
private float m_endZ;
|
||||
private UUID TERRAIN = UUID.Random ();
|
||||
private UUID EDGE = UUID.Random ();
|
||||
private UUID[,,] m_field = new UUID[256, 256, 256]; // field of objects at this position
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Flocking.FlowField"/> class.
|
||||
/// </summary>
|
||||
/// <param name='scene'>
|
||||
/// Scene.
|
||||
/// </param>
|
||||
/// <param name='centre'>
|
||||
/// Centre.
|
||||
/// </param>
|
||||
/// <param name='width'>
|
||||
/// Width.
|
||||
/// </param>
|
||||
/// <param name='depth'>
|
||||
/// Depth.
|
||||
/// </param>
|
||||
/// <param name='height'>
|
||||
/// Height.
|
||||
/// </param>
|
||||
///
|
||||
public FlowField (Scene scene, int minX, int maxX, int minY, int maxY, int minZ, int maxZ)
|
||||
{
|
||||
m_scene = scene;
|
||||
|
||||
m_startX = Math.Max (BUFFER, minX);
|
||||
m_startY = Math.Max (BUFFER, minY);
|
||||
m_startZ = Math.Max (BUFFER, minZ);
|
||||
m_endX = Math.Min (Util.SCENE_SIZE - BUFFER, maxX);
|
||||
m_endY = Math.Min (Util.SCENE_SIZE - BUFFER, maxY);
|
||||
m_endZ = Math.Min (Util.SCENE_SIZE - BUFFER, maxZ);
|
||||
|
||||
// build the flow field over the given bounds
|
||||
Initialize ();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// build a flow field on the scene at the specified centre
|
||||
/// position in the scene and of extent given by width, depth and height.
|
||||
/// </summary>
|
||||
public void Initialize ()
|
||||
{
|
||||
|
||||
//fill in the boundaries
|
||||
for (int x = 0; x < 256; x++) {
|
||||
for (int y = 0; y < 256; y++) {
|
||||
m_field [x, y, 0] = EDGE;
|
||||
m_field [x, y, 255] = EDGE;
|
||||
}
|
||||
}
|
||||
for (int x = 0; x < 256; x++) {
|
||||
for (int z = 0; z < 256; z++) {
|
||||
m_field [x, 0, z] = EDGE;
|
||||
m_field [x, 255, z] = EDGE;
|
||||
}
|
||||
}
|
||||
for (int y = 0; y < 256; y++) {
|
||||
for (int z = 0; z < 256; z++) {
|
||||
m_field [0, y, z] = EDGE;
|
||||
m_field [255, y, z] = EDGE;
|
||||
}
|
||||
}
|
||||
|
||||
//fill in the terrain
|
||||
for (int x = 0; x < 256; x++) {
|
||||
for (int y = 0; y < 256; y++) {
|
||||
int zMax = Convert.ToInt32 (m_scene.GetGroundHeight (x, y));
|
||||
for (int z = 1; z < zMax; z++) {
|
||||
m_field [x, y, z] = TERRAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (SceneObjectGroup sog in m_scene.Entities.GetAllByType<SceneObjectGroup>()) {
|
||||
//todo: ignore phantom
|
||||
float fmaxX, fminX, fmaxY, fminY, fmaxZ, fminZ;
|
||||
int maxX, minX, maxY, minY, maxZ, minZ;
|
||||
sog.GetAxisAlignedBoundingBoxRaw (out fminX, out fmaxX, out fminY, out fmaxY, out fminZ, out fmaxZ);
|
||||
Vector3 pos = sog.AbsolutePosition;
|
||||
|
||||
minX = Convert.ToInt32 (fminX + pos.X);
|
||||
maxX = Convert.ToInt32 (fmaxX + pos.X);
|
||||
minY = Convert.ToInt32 (fminY + pos.Y);
|
||||
maxY = Convert.ToInt32 (fmaxX + pos.Y);
|
||||
minZ = Convert.ToInt32 (fminZ + pos.Z);
|
||||
maxZ = Convert.ToInt32 (fmaxZ + pos.Z);
|
||||
|
||||
for (int x = minX; x < maxX; x++) {
|
||||
for (int y = minY; y < maxY; y++) {
|
||||
for (int z = minZ; z < maxZ; z++) {
|
||||
if( inBounds(x,y,z) ) {
|
||||
m_field [x, y, z] = sog.UUID;
|
||||
} else {
|
||||
Console.WriteLine(sog.Name + " OOB at " + sog.AbsolutePosition + " -> " + x + " " + y + " " + z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsPoint (Vector3 p)
|
||||
{
|
||||
return p.X > m_startX &&
|
||||
p.X < m_endX &&
|
||||
p.Y > m_startY &&
|
||||
p.Y < m_endY &&
|
||||
p.Z > m_startZ &&
|
||||
p.Z < m_endZ;
|
||||
}
|
||||
|
||||
private bool inBounds (int x, int y, int z)
|
||||
{
|
||||
return x >= 0 && x < 256 && y >= 0 && y < 256 && z >= 0;
|
||||
}
|
||||
|
||||
#if false
|
||||
public Vector3 AdjustVelocity (Boid boid, float lookAheadDist)
|
||||
{
|
||||
Vector3 normVel = Vector3.Normalize (boid.Velocity);
|
||||
Vector3 loc = boid.Location;
|
||||
Vector3 inFront = loc + normVel * lookAheadDist;
|
||||
|
||||
Vector3 adjustedDestintation = FieldStrength (loc, boid.Size, inFront);
|
||||
Vector3 newVel = Vector3.Normalize (adjustedDestintation - loc) * Vector3.Mag (boid.Velocity);
|
||||
|
||||
float mOrigVel = Vector3.Mag(boid.Velocity);
|
||||
float mNewVel = Vector3.Mag(newVel);
|
||||
if( mNewVel != 0f && mNewVel > mOrigVel ) {
|
||||
newVel *= mOrigVel / mNewVel;
|
||||
}
|
||||
return newVel;
|
||||
}
|
||||
#endif
|
||||
|
||||
public Vector3 FieldStrength (Vector3 currentPos, Vector3 size, Vector3 targetPos)
|
||||
{
|
||||
float length = size.X/2;
|
||||
float width = size.Y/2;
|
||||
float height = size.Z/2;
|
||||
|
||||
//keep us in bounds
|
||||
targetPos.X = Math.Min( targetPos.X, m_endX - length );
|
||||
targetPos.X = Math.Max(targetPos.X, m_startX + length);
|
||||
targetPos.Y = Math.Min( targetPos.Y, m_endY - width );
|
||||
targetPos.Y = Math.Max(targetPos.Y, m_startY + width);
|
||||
targetPos.Z = Math.Min( targetPos.Z, m_endZ - height );
|
||||
targetPos.Z = Math.Max(targetPos.Z, m_startZ + height);
|
||||
|
||||
int count = 0;
|
||||
|
||||
//now get the field strength at the inbounds position
|
||||
UUID collider = LookUp (targetPos);
|
||||
while (collider != UUID.Zero && count < 100) {
|
||||
count++;
|
||||
if (collider == TERRAIN) {
|
||||
// ground height at currentPos and dest averaged
|
||||
float h1 = m_scene.GetGroundHeight (currentPos.X, currentPos.Y);
|
||||
float h2 = m_scene.GetGroundHeight (targetPos.X, targetPos.Y);
|
||||
float h = (h1 + h2) / 2;
|
||||
targetPos.Z = h + height;
|
||||
} else if (collider == EDGE) {
|
||||
//keep us in bounds
|
||||
targetPos.X = Math.Min( targetPos.X, m_endX - length );
|
||||
targetPos.X = Math.Max(targetPos.X, m_startX + length);
|
||||
targetPos.Y = Math.Min( targetPos.Y, m_endY - width );
|
||||
targetPos.Y = Math.Max(targetPos.Y, m_startY + width);
|
||||
targetPos.Z = Math.Min( targetPos.Z, m_endZ - height );
|
||||
targetPos.Z = Math.Max(targetPos.Z, m_startZ + height);
|
||||
} else {
|
||||
//we have hit a SOG
|
||||
SceneObjectGroup sog = m_scene.GetSceneObjectPart(collider).ParentGroup;
|
||||
if (sog == null) {
|
||||
Console.WriteLine (collider);
|
||||
} else {
|
||||
float sogMinX, sogMinY, sogMinZ, sogMaxX, sogMaxY, sogMaxZ;
|
||||
sog.GetAxisAlignedBoundingBoxRaw (out sogMinX, out sogMaxX, out sogMinY, out sogMaxY, out sogMinZ, out sogMaxZ);
|
||||
Vector3 pos = sog.AbsolutePosition;
|
||||
//keep us out of the sog
|
||||
// adjust up/down first if necessary
|
||||
// then turn left or right
|
||||
if (targetPos.Z > sogMinZ + pos.Z)
|
||||
targetPos.Z = (sogMinZ + pos.Z) - height;
|
||||
if (targetPos.Z < sogMaxZ + pos.Z)
|
||||
targetPos.Z = (sogMaxZ + pos.Z) + height;
|
||||
if (targetPos.X > sogMinX + pos.X)
|
||||
targetPos.X = (sogMinX + pos.X) - length;
|
||||
if (targetPos.Y > sogMinY + pos.Y)
|
||||
targetPos.Y = (sogMinY + pos.Y) - width;
|
||||
if (targetPos.X < sogMaxX + pos.X)
|
||||
targetPos.X = (sogMaxX + pos.X) + length;
|
||||
if (targetPos.Y < sogMaxY + pos.Y)
|
||||
targetPos.Y = (sogMaxY + pos.Y) + width;
|
||||
}
|
||||
}
|
||||
|
||||
// we what is at the new target position
|
||||
collider = LookUp (targetPos);
|
||||
}
|
||||
|
||||
return targetPos;
|
||||
}
|
||||
|
||||
public UUID LookUp (Vector3 loc)
|
||||
{
|
||||
return m_field [(int)loc.X, (int)loc.Y, (int)loc.Z];
|
||||
}
|
||||
|
||||
public override string ToString ()
|
||||
{
|
||||
return string.Format ("[FlowField]" + Environment.NewLine +
|
||||
"startX = {0}" + Environment.NewLine +
|
||||
"endX = {1}" + Environment.NewLine +
|
||||
"startY = {2}" + Environment.NewLine +
|
||||
"endY = {3}" + Environment.NewLine +
|
||||
"startZ = {4}" + Environment.NewLine +
|
||||
"endZ = {5}", m_startX, m_endX, m_startY, m_endY, m_startZ, m_endZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* https://github.com/JakDaniels/OpenSimBirds
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -27,7 +26,6 @@
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
|
@ -36,63 +34,50 @@ namespace Flocking
|
|||
public class FlowMap
|
||||
{
|
||||
private Scene m_scene;
|
||||
private uint regionX;
|
||||
private uint regionY;
|
||||
private uint regionZ;
|
||||
private float regionBorder;
|
||||
|
||||
private List<FlowEntry> m_flowMap = new List<FlowEntry>();
|
||||
|
||||
public FlowMap (Scene scene, int maxHeight, float borderSize)
|
||||
private float[,,] m_flowMap = new float[256,256,256];
|
||||
|
||||
public FlowMap (Scene scene)
|
||||
{
|
||||
m_scene = scene;
|
||||
regionX = m_scene.RegionInfo.RegionSizeX;
|
||||
regionY = m_scene.RegionInfo.RegionSizeY;
|
||||
regionZ = (uint)maxHeight;
|
||||
regionBorder = borderSize;
|
||||
}
|
||||
|
||||
public int LengthX {
|
||||
get {return (int)regionX;}
|
||||
get {return 256;}
|
||||
}
|
||||
public int LengthY {
|
||||
get {return (int)regionY;}
|
||||
get {return 256;}
|
||||
}
|
||||
public int LengthZ {
|
||||
get {return (int)regionZ;}
|
||||
get {return 256;}
|
||||
}
|
||||
public int Border {
|
||||
get {return (int)regionBorder;}
|
||||
}
|
||||
|
||||
public void Initialise() {
|
||||
//fill in the boundaries
|
||||
for( int x = 0; x < regionX; x++ ) {
|
||||
for( int y = 0; y < regionY; y++ ) {
|
||||
m_flowMap.Add(new FlowEntry(x, y, 0, 100));
|
||||
m_flowMap.Add(new FlowEntry(x, y, regionZ - 1, 100));
|
||||
for( int x = 0; x < 256; x++ ) {
|
||||
for( int y = 0; y < 256; y++ ) {
|
||||
m_flowMap[x,y,0] = 100f;
|
||||
m_flowMap[x,y,255] = 100f;
|
||||
}
|
||||
}
|
||||
for( int x = 0; x < regionX; x++ ) {
|
||||
for( int z = 0; z < regionZ; z++ ) {
|
||||
m_flowMap.Add(new FlowEntry(x, 0, z, 100));
|
||||
m_flowMap.Add(new FlowEntry(x, regionY - 1, z, 100));
|
||||
for( int x = 0; x < 256; x++ ) {
|
||||
for( int z = 0; z < 256; z++ ) {
|
||||
m_flowMap[x,0,z] = 100f;
|
||||
m_flowMap[x,255,z] = 100f;
|
||||
}
|
||||
}
|
||||
for( int y = 0; y < regionY; y++ ) {
|
||||
for( int z = 0; z < regionZ; z++ ) {
|
||||
m_flowMap.Add(new FlowEntry(0, y, z, 100));
|
||||
m_flowMap.Add(new FlowEntry(regionX - 1, y, z, 100));
|
||||
for( int y = 0; y < 256; y++ ) {
|
||||
for( int z = 0; z < 256; z++ ) {
|
||||
m_flowMap[0,y,z] = 100f;
|
||||
m_flowMap[255,y,z] = 100f;
|
||||
}
|
||||
}
|
||||
|
||||
//fill in the terrain
|
||||
for( int x = 0; x < regionX; x++ ) {
|
||||
for( int y = 0; y < regionY; y++ ) {
|
||||
for( int x = 0; x < 256; x++ ) {
|
||||
for( int y = 0; y < 256; y++ ) {
|
||||
int zMax = Convert.ToInt32(m_scene.GetGroundHeight( x, y ));
|
||||
for( int z = 1; z < zMax; z++ ) {
|
||||
if (x < regionX && y < regionY && z < zMax)
|
||||
m_flowMap.Add(new FlowEntry(x, y, z, 100));
|
||||
m_flowMap[x,y,z] = 100f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,10 +86,8 @@ namespace Flocking
|
|||
foreach( EntityBase entity in m_scene.GetEntities() ) {
|
||||
if( entity is SceneObjectGroup ) {
|
||||
SceneObjectGroup sog = (SceneObjectGroup)entity;
|
||||
|
||||
if (sog.IsPhantom)
|
||||
continue;
|
||||
|
||||
|
||||
//todo: ignore phantom
|
||||
float fmaxX, fminX, fmaxY, fminY, fmaxZ, fminZ;
|
||||
int maxX, minX, maxY, minY, maxZ, minZ;
|
||||
sog.GetAxisAlignedBoundingBoxRaw( out fminX, out fmaxX, out fminY, out fmaxY, out fminZ, out fmaxZ );
|
||||
|
@ -119,8 +102,7 @@ namespace Flocking
|
|||
for( int x = minX; x < maxX; x++ ) {
|
||||
for( int y = minY; y < maxY; y++ ) {
|
||||
for( int z = minZ; z < maxZ; z++ ) {
|
||||
if(x < maxX && y < maxY && z < maxX)
|
||||
m_flowMap.Add(new FlowEntry(x, y, z, 100));
|
||||
m_flowMap[x,y,z] = 100f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,12 +127,12 @@ namespace Flocking
|
|||
|
||||
public bool IsOutOfBounds(Vector3 targetPos) {
|
||||
bool retVal = false;
|
||||
if( targetPos.X < regionBorder ||
|
||||
targetPos.X > regionX - regionBorder ||
|
||||
targetPos.Y < regionBorder ||
|
||||
targetPos.Y > regionY - regionBorder ||
|
||||
targetPos.Z < regionBorder ||
|
||||
targetPos.Z > regionZ - regionBorder ) {
|
||||
if( targetPos.X < 5f ||
|
||||
targetPos.X > 250f ||
|
||||
targetPos.Y < 5f ||
|
||||
targetPos.Y > 250f ||
|
||||
targetPos.Z < 5f ||
|
||||
targetPos.Z > 250f ) {
|
||||
|
||||
retVal = true;
|
||||
}
|
||||
|
@ -187,20 +169,11 @@ namespace Flocking
|
|||
|
||||
public bool IsWithinObstacle( int x, int y, int z ) {
|
||||
bool retVal = false;
|
||||
|
||||
FlowEntry flow = m_flowMap.Find(X => X.X == x && X.Y == y && X.Z == z);
|
||||
|
||||
if (flow == null)
|
||||
return retVal;
|
||||
|
||||
if (x >= LengthX || y >= LengthY || z >= LengthZ)
|
||||
{
|
||||
if( x > LengthX || y > LengthY || z > LengthZ ) {
|
||||
retVal = true;
|
||||
}
|
||||
else if (x < 0 || y < 0 || z < 0)
|
||||
{
|
||||
} else if( x < 0 || y < 0 || z < 0 ) {
|
||||
retVal = true;
|
||||
} else if (flow.Value > 50) {
|
||||
} else if (m_flowMap[x,y,z] > 50f) {
|
||||
retVal = true;
|
||||
}
|
||||
return retVal;
|
|
@ -1,6 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* https://github.com/JakDaniels/OpenSimBirds
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -30,8 +29,11 @@ using OpenMetaverse;
|
|||
|
||||
namespace Flocking
|
||||
{
|
||||
public class BirdsUtil
|
||||
|
||||
public class Util
|
||||
{
|
||||
public const int SCENE_SIZE = 256;
|
||||
|
||||
public static Vector3 Limit (Vector3 initial, float maxLen)
|
||||
{
|
||||
float currLen = initial.Length ();
|
|
@ -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>
|
|
@ -1,373 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osbirds
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using log4net;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
public class Bird
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
|
||||
private string m_id;
|
||||
|
||||
private Vector3 m_loc;
|
||||
private Vector3 m_vel;
|
||||
private Vector3 m_acc;
|
||||
private Random m_rndnums = new Random (Environment.TickCount);
|
||||
|
||||
private FlockingModel m_model;
|
||||
private FlowMap m_flowMap;
|
||||
private int m_regionX;
|
||||
private int m_regionY;
|
||||
private int m_regionZ;
|
||||
private float m_regionBorder;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Flocking.Bird"/> class.
|
||||
/// </summary>
|
||||
/// <param name='l'>
|
||||
/// L. the initial position of this bird
|
||||
/// </param>
|
||||
/// <param name='ms'>
|
||||
/// Ms. max speed this bird can attain
|
||||
/// </param>
|
||||
/// <param name='mf'>
|
||||
/// Mf. max force / acceleration this bird can extert
|
||||
/// </param>
|
||||
public Bird (string id, FlockingModel model, FlowMap flowMap)
|
||||
{
|
||||
m_id = id;
|
||||
m_acc = Vector3.Zero;
|
||||
m_vel = new Vector3 (m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1), m_rndnums.Next (-1, 1));
|
||||
m_model = model;
|
||||
m_flowMap = flowMap;
|
||||
m_regionX = m_flowMap.LengthX;
|
||||
m_regionY = m_flowMap.LengthY;
|
||||
m_regionZ = m_flowMap.LengthZ;
|
||||
m_regionBorder = m_flowMap.Border;
|
||||
}
|
||||
|
||||
public Vector3 Location {
|
||||
get { return m_loc;}
|
||||
set { m_loc = value; }
|
||||
}
|
||||
|
||||
public Vector3 Velocity {
|
||||
get { return m_vel;}
|
||||
}
|
||||
|
||||
public String Id {
|
||||
get {return m_id;}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves our bird in the scene relative to the rest of the flock.
|
||||
/// </summary>
|
||||
/// <param name='birds'>
|
||||
/// Birds. all the other chaps in the scene
|
||||
/// </param>
|
||||
public void MoveInSceneRelativeToFlock (List<Bird> birds)
|
||||
{
|
||||
// we would like to stay with our mates
|
||||
Flock (birds);
|
||||
|
||||
// our first priority is to not hurt ourselves
|
||||
AvoidObstacles ();
|
||||
|
||||
// then we want to avoid any threats
|
||||
// this not implemented yet
|
||||
|
||||
|
||||
// ok so we worked our where we want to go, so ...
|
||||
UpdatePositionInScene ();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move within our flock
|
||||
///
|
||||
/// We accumulate a new acceleration each time based on three rules
|
||||
/// these are:
|
||||
/// our separation from our closest neighbours,
|
||||
/// our desire to keep travelling within the local flock,
|
||||
/// our desire to move towards the flock centre
|
||||
///
|
||||
/// </summary>
|
||||
void Flock (List<Bird> birds)
|
||||
{
|
||||
|
||||
// calc the force vectors on this bird
|
||||
Vector3 sep = Separate (birds); // Separation
|
||||
Vector3 ali = Align (birds); // Alignment
|
||||
Vector3 coh = Cohesion (birds); // Cohesion
|
||||
|
||||
// Arbitrarily weight these forces
|
||||
//TODO: expose these consts
|
||||
sep *= 1.5f; //.mult(1.5);
|
||||
//ali.mult(1.0);
|
||||
ali *= 1.0f;
|
||||
//coh.mult(1.0);
|
||||
coh *= 1.0f;
|
||||
|
||||
// Add the force vectors to the current acceleration of the bird
|
||||
//acc.add(sep);
|
||||
m_acc += sep;
|
||||
//acc.add(ali);
|
||||
m_acc += ali;
|
||||
//acc.add(coh);
|
||||
m_acc += coh;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Method to update our location within the scene.
|
||||
/// update our location in the world based on our
|
||||
/// current location, velocity and acceleration
|
||||
/// taking into account our max speed
|
||||
///
|
||||
/// </summary>
|
||||
void UpdatePositionInScene ()
|
||||
{
|
||||
// Update velocity
|
||||
//vel.add(acc);
|
||||
m_vel += m_acc;
|
||||
// Limit speed
|
||||
//m_vel.limit(maxspeed);
|
||||
m_vel = BirdsUtil.Limit (m_vel, m_model.MaxSpeed);
|
||||
m_loc += m_vel;
|
||||
// Reset accelertion to 0 each cycle
|
||||
m_acc *= 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seek the specified target. Move into that flock
|
||||
/// Accelerate us towards where we want to go
|
||||
/// </summary>
|
||||
/// <param name='target'>
|
||||
/// Target. the position within the flock we would like to achieve
|
||||
/// </param>
|
||||
void Seek (Vector3 target)
|
||||
{
|
||||
m_acc += Steer (target, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Arrive the specified target. Slow us down, as we are almost there
|
||||
/// </summary>
|
||||
/// <param name='target'>
|
||||
/// Target. the flock we would like to think ourselves part of
|
||||
/// </param>
|
||||
void arrive (Vector3 target)
|
||||
{
|
||||
m_acc += Steer (target, true);
|
||||
}
|
||||
|
||||
/// A method that calculates a steering vector towards a target
|
||||
/// Takes a second argument, if true, it slows down as it approaches the target
|
||||
Vector3 Steer (Vector3 target, bool slowdown)
|
||||
{
|
||||
Vector3 steer; // The steering vector
|
||||
Vector3 desired = Vector3.Subtract(target, m_loc); // A vector pointing from the location to the target
|
||||
float d = desired.Length (); // Distance from the target is the magnitude of the vector
|
||||
// If the distance is greater than 0, calc steering (otherwise return zero vector)
|
||||
if (d > 0) {
|
||||
// Normalize desired
|
||||
desired.Normalize ();
|
||||
// Two options for desired vector magnitude (1 -- based on distance, 2 -- maxspeed)
|
||||
if ((slowdown) && (d < 100.0f)) {
|
||||
desired *= (m_model.MaxSpeed * (d / 100.0f)); // This damping is somewhat arbitrary
|
||||
} else {
|
||||
desired *= m_model.MaxSpeed;
|
||||
}
|
||||
// Steering = Desired minus Velocity
|
||||
//steer = target.sub(desired,m_vel);
|
||||
steer = Vector3.Subtract (desired, m_vel);
|
||||
//steer.limit(maxforce); // Limit to maximum steering force
|
||||
steer = BirdsUtil.Limit (steer, m_model.MaxForce);
|
||||
} else {
|
||||
steer = Vector3.Zero;
|
||||
}
|
||||
return steer;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Borders this instance.
|
||||
/// if we get too close wrap us around
|
||||
/// CHANGE THIS to navigate away from whatever it is we are too close to
|
||||
/// </summary>
|
||||
void AvoidObstacles ()
|
||||
{
|
||||
//look tolerance metres ahead
|
||||
Vector3 normVel = Vector3.Normalize(m_vel);
|
||||
Vector3 inFront = m_loc + Vector3.Multiply(normVel, m_model.Tolerance);
|
||||
if( m_flowMap.WouldHitObstacle( m_loc, inFront ) ) {
|
||||
AdjustVelocityToAvoidObstacles ();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void AdjustVelocityToAvoidObstacles ()
|
||||
{
|
||||
for( int i = 1; i < 5; i++ ) {
|
||||
Vector3 normVel = Vector3.Normalize(m_vel);
|
||||
int xDelta = m_rndnums.Next (-i, i);
|
||||
int yDelta = m_rndnums.Next (-i, i);
|
||||
int zDelta = m_rndnums.Next (-i, i);
|
||||
normVel.X += xDelta;
|
||||
normVel.Y += yDelta;
|
||||
normVel.Z += zDelta;
|
||||
Vector3 inFront = m_loc + Vector3.Multiply(normVel, m_model.Tolerance);
|
||||
if( !m_flowMap.WouldHitObstacle( m_loc, inFront ) ) {
|
||||
m_vel.X += xDelta;
|
||||
m_vel.Y += yDelta;
|
||||
m_vel.Z += zDelta;
|
||||
//m_log.Info("avoided");
|
||||
return;
|
||||
}
|
||||
}
|
||||
//m_log.Info("didn't avoid");
|
||||
// try increaing our acceleration
|
||||
// or try decreasing our acceleration
|
||||
// or turn around - coz where we came from was OK
|
||||
if (m_loc.X < m_regionBorder || m_loc.X > m_regionX - m_regionBorder)
|
||||
m_vel.X = -m_vel.X;
|
||||
if (m_loc.Y < m_regionBorder || m_loc.Y > m_regionY - m_regionBorder)
|
||||
m_vel.Y = -m_vel.Y;
|
||||
if (m_loc.Z < 21 || m_loc.Z > m_regionZ )
|
||||
m_vel.Z = -m_vel.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Separate ourselves from the specified birds.
|
||||
/// keeps us a respectable distance from our closest neighbours whilst still
|
||||
/// being part of our local flock
|
||||
/// </summary>
|
||||
/// <param name='birds'>
|
||||
/// Birds. all the birds in the scene
|
||||
/// </param>
|
||||
Vector3 Separate (List<Bird> birds)
|
||||
{
|
||||
Vector3 steer = new Vector3 (0, 0, 0);
|
||||
int count = 0;
|
||||
// For every bird in the system, check if it's too close
|
||||
foreach (Bird other in birds) {
|
||||
float d = Vector3.Distance (m_loc, other.Location);
|
||||
// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
|
||||
if ((d > 0) && (d < m_model.DesiredSeparation)) {
|
||||
// Calculate vector pointing away from neighbor
|
||||
Vector3 diff = Vector3.Subtract (m_loc, other.Location);
|
||||
diff.Normalize ();
|
||||
diff = Vector3.Divide (diff, d);
|
||||
steer = Vector3.Add (steer, diff);
|
||||
count++; // Keep track of how many
|
||||
}
|
||||
}
|
||||
// Average -- divide by how many
|
||||
if (count > 0) {
|
||||
steer /= (float)count;
|
||||
}
|
||||
|
||||
// As long as the vector is greater than 0
|
||||
if (steer.Length () > 0) {
|
||||
// Implement Reynolds: Steering = Desired - Velocity
|
||||
steer.Normalize ();
|
||||
steer *= m_model.MaxSpeed;
|
||||
steer -= m_vel;
|
||||
//steer.limit(maxforce);
|
||||
steer = BirdsUtil.Limit (steer, m_model.MaxForce);
|
||||
}
|
||||
return steer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Align our bird within the flock.
|
||||
/// For every nearby bird in the system, calculate the average velocity
|
||||
/// and move us towards that - this keeps us moving with the flock.
|
||||
/// </summary>
|
||||
/// <param name='birds'>
|
||||
/// Birds. all the birds in the scene - we only really care about those in the neighbourdist
|
||||
/// </param>
|
||||
Vector3 Align (List<Bird> birds)
|
||||
{
|
||||
Vector3 steer = new Vector3 (0, 0, 0);
|
||||
int count = 0;
|
||||
foreach (Bird other in birds) {
|
||||
float d = Vector3.Distance (m_loc, other.Location);
|
||||
if ((d > 0) && (d < m_model.NeighbourDistance)) {
|
||||
steer += other.Velocity;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
steer /= (float)count;
|
||||
}
|
||||
|
||||
// As long as the vector is greater than 0
|
||||
if (steer.Length () > 0) {
|
||||
// Implement Reynolds: Steering = Desired - Velocity
|
||||
steer.Normalize ();
|
||||
steer *= m_model.MaxSpeed;
|
||||
steer -= m_vel;
|
||||
//steer.limit(maxforce);
|
||||
steer = BirdsUtil.Limit (steer, m_model.MaxForce);
|
||||
|
||||
}
|
||||
return steer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MAintain the cohesion of our local flock
|
||||
/// For the average location (i.e. center) of all nearby birds, calculate our steering vector towards that location
|
||||
/// </summary>
|
||||
/// <param name='birds'>
|
||||
/// Birds. the birds in the scene
|
||||
/// </param>
|
||||
Vector3 Cohesion (List<Bird> birds)
|
||||
{
|
||||
|
||||
Vector3 sum = Vector3.Zero; // Start with empty vector to accumulate all locations
|
||||
int count = 0;
|
||||
|
||||
foreach (Bird other in birds) {
|
||||
float d = Vector3.Distance (m_loc, other.Location);
|
||||
if ((d > 0) && (d < m_model.NeighbourDistance)) {
|
||||
sum += other.Location; // Add location
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
sum /= (float)count;
|
||||
return Steer (sum, false); // Steer towards the location
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* https://github.com/JakDaniels/OpenSimBirds
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
public class FlockingModel
|
||||
{
|
||||
private List<Bird> m_flock = new List<Bird>();
|
||||
private FlowMap m_flowMap;
|
||||
private float m_maxSpeed;
|
||||
private float m_maxForce;
|
||||
private float m_neighbourDistance;
|
||||
private float m_desiredSeparation;
|
||||
private float m_tolerance;
|
||||
private float m_border;
|
||||
private string m_name;
|
||||
|
||||
private Random m_rnd = new Random(Environment.TickCount);
|
||||
|
||||
public int Size {
|
||||
get {return m_flock.Count;}
|
||||
set {
|
||||
//if( value < m_flock.Count ) {
|
||||
// m_flock.RemoveRange( 0, m_flock.Count - value );
|
||||
//} else
|
||||
m_flock = new List<Bird>();
|
||||
while( value > m_flock.Count ) {
|
||||
AddBird(m_name + m_flock.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FlockingModel(string moduleName, float maxSpeed, float maxForce, float neighbourDistance, float desiredSeparation, float tolerance, float border) {
|
||||
m_name = moduleName;
|
||||
m_maxSpeed = maxSpeed;
|
||||
m_maxForce = maxForce;
|
||||
m_neighbourDistance = neighbourDistance;
|
||||
m_desiredSeparation = desiredSeparation;
|
||||
m_tolerance = tolerance;
|
||||
m_border = border;
|
||||
}
|
||||
|
||||
void AddBird (string name)
|
||||
{
|
||||
Bird Bird = new Bird (name, this, m_flowMap);
|
||||
|
||||
// find an initial random location for this Bird
|
||||
// somewhere not within an obstacle
|
||||
int xInit = m_rnd.Next(m_flowMap.LengthX);
|
||||
int yInit = m_rnd.Next(m_flowMap.LengthY);
|
||||
int zInit = m_rnd.Next(m_flowMap.LengthZ);
|
||||
|
||||
while( m_flowMap.IsWithinObstacle( xInit, yInit, zInit ) ){
|
||||
xInit = m_rnd.Next(m_flowMap.LengthX);
|
||||
yInit = m_rnd.Next(m_flowMap.LengthY);
|
||||
zInit = m_rnd.Next(m_flowMap.LengthZ);
|
||||
}
|
||||
|
||||
Bird.Location = new Vector3 (Convert.ToSingle(xInit), Convert.ToSingle(yInit), Convert.ToSingle(zInit));
|
||||
m_flock.Add (Bird);
|
||||
}
|
||||
|
||||
public float MaxSpeed {
|
||||
get {return m_maxSpeed;}
|
||||
set { m_maxSpeed = value; }
|
||||
}
|
||||
|
||||
public float MaxForce {
|
||||
get {return m_maxForce;}
|
||||
set { m_maxForce = value; }
|
||||
}
|
||||
|
||||
public float NeighbourDistance {
|
||||
get {return m_neighbourDistance;}
|
||||
set { m_neighbourDistance = value; }
|
||||
}
|
||||
|
||||
public float DesiredSeparation {
|
||||
get {return m_desiredSeparation;}
|
||||
set { m_desiredSeparation = value; }
|
||||
}
|
||||
|
||||
public float Tolerance {
|
||||
get {return m_tolerance;}
|
||||
set { m_tolerance = value; }
|
||||
}
|
||||
|
||||
|
||||
public void Initialise (int num, FlowMap flowMap)
|
||||
{
|
||||
m_flowMap = flowMap;
|
||||
for (int i = 0; i < num; i++) {
|
||||
AddBird (m_name + i );
|
||||
}
|
||||
}
|
||||
|
||||
public List<Bird> UpdateFlockPos ()
|
||||
{
|
||||
foreach (Bird b in m_flock) {
|
||||
b.MoveInSceneRelativeToFlock(m_flock); // Passing the entire list of Birds to each Bird individually
|
||||
}
|
||||
|
||||
return m_flock;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,648 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, https://github.com/jonc/osboids
|
||||
* https://github.com/JakDaniels/OpenSimBirds
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSimulator Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Timers;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using Nini.Config;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.ScriptEngine.Shared;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Console;
|
||||
using Mono.Addins;
|
||||
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
|
||||
|
||||
[assembly: Addin("OpenSimBirds", "0.2")]
|
||||
[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "OpenSimBirds")]
|
||||
public class FlockingModule : INonSharedRegionModule
|
||||
{
|
||||
#region Fields
|
||||
private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
|
||||
|
||||
public string m_name = "OpenSimBirds";
|
||||
private string m_regionConfigDir = "";
|
||||
private Scene m_scene;
|
||||
private ICommandConsole m_console;
|
||||
private FlockingModel m_model;
|
||||
private FlockingView m_view;
|
||||
private bool m_startup = true;
|
||||
private bool m_enabled = false;
|
||||
private bool m_ready = false;
|
||||
private uint m_frame = 0;
|
||||
private int m_frameUpdateRate = 1;
|
||||
private int m_chatChannel = 118;
|
||||
private string m_birdPrim;
|
||||
private int m_flockSize = 50;
|
||||
private int m_maxFlockSize = 100;
|
||||
private float m_maxSpeed;
|
||||
private float m_maxForce;
|
||||
private float m_neighbourDistance;
|
||||
private float m_desiredSeparation;
|
||||
private float m_tolerance;
|
||||
private float m_borderSize;
|
||||
private int m_maxHeight;
|
||||
private Vector3 m_shoutPos = new Vector3(128f, 128f, 30f);
|
||||
static object m_sync = new object();
|
||||
private List<UUID> m_allowedControllers = new List<UUID>();
|
||||
|
||||
public IConfigSource m_config;
|
||||
|
||||
private UUID m_owner;
|
||||
#endregion
|
||||
|
||||
#region IRegionModuleBase implementation
|
||||
|
||||
public string Name { get { return m_name; } }
|
||||
public Type ReplaceableInterface { get { return null; } }
|
||||
public bool IsSharedModule { get { return false; } }
|
||||
|
||||
public void Initialise (IConfigSource source)
|
||||
{
|
||||
m_config = source;
|
||||
}
|
||||
|
||||
public void AddRegion (Scene scene)
|
||||
{
|
||||
IConfig cnf;
|
||||
m_log.InfoFormat("[{0}]: Adding region {1} to this module", m_name, scene.RegionInfo.RegionName);
|
||||
|
||||
cnf = m_config.Configs["Startup"];
|
||||
m_regionConfigDir = cnf.GetString("regionload_regionsdir", Path.Combine(Util.configDir(), "bin/Regions/"));
|
||||
|
||||
cnf = m_config.Configs[scene.RegionInfo.RegionName];
|
||||
|
||||
if (cnf == null)
|
||||
{
|
||||
m_log.InfoFormat("[{0}]: No region section [{1}] found in addon-modules/{2}/config/*.ini configuration files.", m_name, scene.RegionInfo.RegionName, m_name);
|
||||
//string moduleConfigFile = Path.Combine(Util.configDir(),m_name + ".ini");
|
||||
string moduleConfigFile = Path.Combine(m_regionConfigDir, "Regions.ini");
|
||||
try
|
||||
{
|
||||
m_log.InfoFormat("[{0}]: Checking {1} for [{2}] section containing valid config keys", m_name, moduleConfigFile, scene.RegionInfo.RegionName);
|
||||
m_config = new IniConfigSource(moduleConfigFile);
|
||||
cnf = m_config.Configs[scene.RegionInfo.RegionName];
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
cnf = null;
|
||||
}
|
||||
|
||||
if (cnf == null)
|
||||
{
|
||||
m_log.InfoFormat("[{0}]: No region section [{1}] found in configuration {2}. Birds in this region are set to Disabled", m_name, scene.RegionInfo.RegionName, moduleConfigFile);
|
||||
m_enabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_startup = cnf.GetBoolean("BirdsModuleStartup", true);
|
||||
|
||||
if (m_startup)
|
||||
{
|
||||
m_scene = scene;
|
||||
m_enabled = cnf.GetBoolean("BirdsEnabled", false);
|
||||
m_chatChannel = cnf.GetInt("BirdsChatChannel", 118);
|
||||
m_birdPrim = cnf.GetString("BirdsPrim", "birdPrim");
|
||||
m_flockSize = cnf.GetInt("BirdsFlockSize", 20);
|
||||
m_maxFlockSize = cnf.GetInt("BirdsMaxFlockSize", 100);
|
||||
m_maxSpeed = cnf.GetFloat("BirdsMaxSpeed", 1.5f);
|
||||
m_maxForce = cnf.GetFloat("BirdsMaxForce", 0.2f);
|
||||
m_neighbourDistance = cnf.GetFloat("BirdsNeighbourDistance", 25f);
|
||||
m_desiredSeparation = cnf.GetFloat("BirdsDesiredSeparation", 10f);
|
||||
m_tolerance = cnf.GetFloat("BirdsTolerance", 5f);
|
||||
m_borderSize = cnf.GetFloat("BirdsRegionBorderSize", 5f);
|
||||
m_maxHeight = cnf.GetInt("BirdsMaxHeight", 75);
|
||||
m_frameUpdateRate = cnf.GetInt("BirdsUpdateEveryNFrames", 1);
|
||||
|
||||
string allowedControllers = cnf.GetString("BirdsAllowedControllers", UUID.Zero.ToString());
|
||||
if (allowedControllers != UUID.Zero.ToString())
|
||||
{
|
||||
string[] ac = allowedControllers.Split(new char[] { ',' });
|
||||
UUID acUUID;
|
||||
for (int i = 0; i < ac.Length; i++)
|
||||
{
|
||||
string value = ac[i].Trim();
|
||||
if (value == "ESTATE_OWNER")
|
||||
{
|
||||
UUID eoUUID = m_scene.RegionInfo.EstateSettings.EstateOwner;
|
||||
m_allowedControllers.Add(eoUUID);
|
||||
m_log.InfoFormat("[{0}] Added Estate Owner UUID: {1} to list of allowed users", m_name, eoUUID.ToString());
|
||||
continue;
|
||||
}
|
||||
if (value == "ESTATE_MANAGER")
|
||||
{
|
||||
foreach (UUID emUUID in m_scene.RegionInfo.EstateSettings.EstateManagers)
|
||||
{
|
||||
m_allowedControllers.Add(emUUID);
|
||||
m_log.InfoFormat("[{0}] Added Estate Manager UUID: {1} to list of allowed users", m_name, emUUID.ToString());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (UUID.TryParse(ac[i].Trim(), out acUUID))
|
||||
{
|
||||
m_allowedControllers.Add(acUUID);
|
||||
m_log.InfoFormat("[{0}] Added UUID: {1} to list of allowed users", m_name, acUUID.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.InfoFormat("[{0}] No command security was defined in the config. Any user may possibly configure this module from a script!", m_name);
|
||||
}
|
||||
|
||||
m_log.InfoFormat("[{0}] Module is {1} listening for commands on channel {2} with Flock Size {3}", m_name, m_enabled ? "enabled and" : "disabled, but still", m_chatChannel, m_flockSize);
|
||||
|
||||
m_console = MainConsole.Instance;
|
||||
//register commands with the scene
|
||||
RegisterCommands();
|
||||
|
||||
//register handlers
|
||||
m_scene.EventManager.OnFrame += FlockUpdate;
|
||||
m_scene.EventManager.OnChatFromClient += SimChatSent; //listen for commands sent from the client
|
||||
m_scene.EventManager.OnChatFromWorld += SimChatSent;
|
||||
m_scene.EventManager.OnPrimsLoaded += PrimsLoaded;
|
||||
|
||||
// init module
|
||||
m_model = new FlockingModel(m_name, m_maxSpeed, m_maxForce, m_neighbourDistance, m_desiredSeparation, m_tolerance, m_borderSize);
|
||||
m_view = new FlockingView(m_name, m_scene);
|
||||
m_view.BirdPrim = m_birdPrim;
|
||||
m_frame = 0;
|
||||
m_shoutPos = new Vector3(scene.RegionInfo.RegionSizeX / 2f, scene.RegionInfo.RegionSizeY / 2f, 30f);
|
||||
|
||||
FlockInitialise();
|
||||
|
||||
}
|
||||
else m_log.InfoFormat("[{0}] Module is disabled in Region {1}", m_name, scene.RegionInfo.RegionName);
|
||||
}
|
||||
|
||||
public void RegionLoaded (Scene scene)
|
||||
{
|
||||
if (m_startup)
|
||||
{
|
||||
// Mark Module Ready for duty
|
||||
m_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void PrimsLoaded(Scene scene)
|
||||
{
|
||||
m_scene = scene;
|
||||
ClearPersisted();
|
||||
}
|
||||
|
||||
public void RemoveRegion (Scene scene)
|
||||
{
|
||||
m_log.InfoFormat("[{0}]: Removing region {1} from this module", m_name, scene.RegionInfo.RegionName);
|
||||
if (m_startup) {
|
||||
m_view.Clear();
|
||||
scene.EventManager.OnFrame -= FlockUpdate;
|
||||
scene.EventManager.OnChatFromClient -= SimChatSent;
|
||||
scene.EventManager.OnChatFromWorld -= SimChatSent;
|
||||
scene.EventManager.OnPrimsLoaded -= PrimsLoaded;
|
||||
m_ready = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (m_startup)
|
||||
{
|
||||
m_scene.EventManager.OnFrame -= FlockUpdate;
|
||||
m_scene.EventManager.OnChatFromClient -= SimChatSent;
|
||||
m_scene.EventManager.OnChatFromWorld -= SimChatSent;
|
||||
m_scene.EventManager.OnPrimsLoaded -= PrimsLoaded;
|
||||
m_ready = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
public void FlockInitialise()
|
||||
{
|
||||
//make a flow map for this scene
|
||||
FlowMap flowMap = new FlowMap(m_scene, m_maxHeight, m_borderSize);
|
||||
flowMap.Initialise();
|
||||
|
||||
// Generate initial flock values
|
||||
m_model.Initialise(m_flockSize, flowMap);
|
||||
|
||||
// who is the owner for the flock in this region
|
||||
m_owner = m_scene.RegionInfo.EstateSettings.EstateOwner;
|
||||
m_view.PostInitialize(m_owner);
|
||||
}
|
||||
|
||||
public void ClearPersisted()
|
||||
{
|
||||
//really trash all possible birds that may have been persisted at last shutdown
|
||||
int i;
|
||||
for (i = m_flockSize; i < m_maxFlockSize; i++)
|
||||
{
|
||||
foreach (EntityBase e in m_scene.GetEntities())
|
||||
{
|
||||
if (e.Name == (string)(m_name + i))
|
||||
{
|
||||
m_log.InfoFormat("[{0}]: Removing old persisted prim {1} from region {2}", m_name, m_name + i, m_scene.RegionInfo.RegionName);
|
||||
m_scene.DeleteSceneObject((SceneObjectGroup)e, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_scene.ForceClientUpdate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EventHandlers
|
||||
|
||||
public void FlockUpdate ()
|
||||
{
|
||||
if (!m_ready || !m_enabled || ((m_frame++ % m_frameUpdateRate) != 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//m_log.InfoFormat("update my birds");
|
||||
|
||||
// work out where everyone has moved to
|
||||
// and tell the scene to render the new positions
|
||||
lock( m_sync ) {
|
||||
List<Bird > birds = m_model.UpdateFlockPos ();
|
||||
m_view.Render (birds);
|
||||
}
|
||||
}
|
||||
|
||||
protected void SimChatSent (Object x, OSChatMessage msg)
|
||||
{
|
||||
if (msg.Channel != m_chatChannel) return; // not for us
|
||||
if (m_allowedControllers.Count > 0)
|
||||
{
|
||||
bool reject = true;
|
||||
if (msg.SenderObject != null)
|
||||
{
|
||||
UUID ooUUID = ((SceneObjectPart)msg.SenderObject).OwnerID;
|
||||
//m_log.InfoFormat("[{0}]: Message from object {1} with OwnerID: {2}", m_name, msg.SenderUUID, ooUUID);
|
||||
if (m_allowedControllers.Contains(ooUUID)) reject = false;
|
||||
}
|
||||
if (m_allowedControllers.Contains(msg.SenderUUID)) reject = false;
|
||||
|
||||
if (reject) return; //not for us
|
||||
}
|
||||
|
||||
// try and parse a valid cmd from this msg
|
||||
string cmd = msg.Message; //.ToLower ();
|
||||
|
||||
//stick ui in the args so we know to respond in world
|
||||
//bit of a hack - but lets us use CommandDelegate inWorld
|
||||
string[] args = (cmd + " <ui>").Split (" ".ToCharArray ());
|
||||
|
||||
if (cmd.StartsWith ("stop")) {
|
||||
HandleStopCmd (m_name, args);
|
||||
} else if (cmd.StartsWith ("start")) {
|
||||
HandleStartCmd (m_name, args);
|
||||
} else if (cmd.StartsWith("enable")) {
|
||||
HandleEnableCmd(m_name, args);
|
||||
} else if (cmd.StartsWith("disable")) {
|
||||
HandleDisableCmd(m_name, args);
|
||||
} else if (cmd.StartsWith ("size")) {
|
||||
HandleSetSizeCmd (m_name, args);
|
||||
} else if (cmd.StartsWith ("stats")) {
|
||||
HandleShowStatsCmd (m_name, args);
|
||||
} else if (cmd.StartsWith ("prim")) {
|
||||
HandleSetPrimCmd (m_name, args);
|
||||
} else if (cmd.StartsWith("speed")) {
|
||||
HandleSetMaxSpeedCmd(m_name, args);
|
||||
} else if (cmd.StartsWith("force")) {
|
||||
HandleSetMaxForceCmd(m_name, args);
|
||||
} else if (cmd.StartsWith("distance")) {
|
||||
HandleSetNeighbourDistanceCmd(m_name, args);
|
||||
} else if (cmd.StartsWith("separation")) {
|
||||
HandleSetDesiredSeparationCmd(m_name, args);
|
||||
} else if (cmd.StartsWith("tolerance")) {
|
||||
HandleSetToleranceCmd(m_name, args);
|
||||
} else if (cmd.StartsWith ("framerate")) {
|
||||
HandleSetFrameRateCmd (m_name, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Command Handling
|
||||
|
||||
private void AddCommand (string cmd, string args, string help, CommandDelegate fn)
|
||||
{
|
||||
string argStr = "";
|
||||
if (args.Trim ().Length > 0) {
|
||||
argStr = " <" + args + "> ";
|
||||
}
|
||||
m_log.InfoFormat("[{0}]: Adding console command {1} - {2} to region {3}", m_name, "birds-" + cmd + argStr, help, m_scene.RegionInfo.RegionName);
|
||||
//m_scene.AddCommand (this, "birds-" + cmd, "birds-" + cmd + argStr, help, fn);
|
||||
m_console.Commands.AddCommand(m_name, false, "birds-" + cmd, "birds-" + cmd + argStr, help, fn);
|
||||
}
|
||||
|
||||
private void RegisterCommands ()
|
||||
{
|
||||
AddCommand ("stop", "", "Stop Birds Flocking", HandleStopCmd);
|
||||
AddCommand ("start", "", "Start Birds Flocking", HandleStartCmd);
|
||||
AddCommand ("enable", "", "Enable Birds Flocking", HandleEnableCmd);
|
||||
AddCommand ("disable", "", "Disable Birds Flocking", HandleDisableCmd);
|
||||
AddCommand ("size", "num", "Adjust the size of the flock ", HandleSetSizeCmd);
|
||||
AddCommand ("prim", "name", "Set the prim used for each bird to that passed in", HandleSetPrimCmd);
|
||||
AddCommand ("speed", "num", "Set the maximum velocity each bird may achieve", HandleSetMaxSpeedCmd);
|
||||
AddCommand("force", "num", "Set the maximum force each bird may accelerate", HandleSetMaxForceCmd);
|
||||
AddCommand("distance", "num", "Set the maximum distance that other birds are to be considered in the same flock as us", HandleSetNeighbourDistanceCmd);
|
||||
AddCommand("separation", "num", "How far away from other birds we would like to stay", HandleSetDesiredSeparationCmd);
|
||||
AddCommand("tolerance", "num", "How close to the edges of things can we get without being worried", HandleSetToleranceCmd);
|
||||
AddCommand("stats", "", "Show birds stats", HandleShowStatsCmd);
|
||||
AddCommand("framerate", "num", "[debugging] only update birds every <num> frames", HandleSetFrameRateCmd);
|
||||
}
|
||||
|
||||
private void RegisterScriptFunctions()
|
||||
{
|
||||
IScriptModuleComms comms = m_scene.RequestModuleInterface<IScriptModuleComms>();
|
||||
if (comms != null)
|
||||
{
|
||||
comms.RegisterScriptInvocation(this, "birdsGetStats");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private string birdsGetStats(UUID host, UUID script, string stat)
|
||||
{
|
||||
return ""; //currently a placeholder
|
||||
}
|
||||
|
||||
private bool ShouldHandleCmd ()
|
||||
{
|
||||
if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsInWorldCmd (ref string [] args)
|
||||
{
|
||||
if (args.Length > 0 && args [args.Length - 1].Equals ("<ui>")) {
|
||||
m_log.InfoFormat("[{0}]: Inworld command detected in region {1}", m_name, m_scene.RegionInfo.RegionName);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ShowResponse (string response, bool inWorld)
|
||||
{
|
||||
if (inWorld) {
|
||||
//IClientAPI ownerAPI = null;
|
||||
//if (m_scene.TryGetClient (m_owner, out ownerAPI)) {
|
||||
// ownerAPI.SendBlueBoxMessage (m_owner, "Birds", response);
|
||||
//}
|
||||
SendSimChat(response, m_chatChannel);
|
||||
} else {
|
||||
MainConsole.Instance.Output (response);
|
||||
}
|
||||
}
|
||||
|
||||
private void SendSimChat(string msg, int channel)
|
||||
{
|
||||
m_scene.SimChatBroadcast(Utils.StringToBytes(msg), ChatTypeEnum.Region, channel, m_shoutPos, m_name, UUID.Zero, false);
|
||||
}
|
||||
|
||||
public void HandleDisableCmd(string module, string[] args)
|
||||
{
|
||||
if (m_ready && ShouldHandleCmd ()) {
|
||||
m_log.InfoFormat("[{0}]: Bird flocking is disabled in region {1}.", m_name, m_scene.RegionInfo.RegionName);
|
||||
m_enabled = false;
|
||||
//m_ready = false;
|
||||
m_view.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleEnableCmd(string module, string[] args)
|
||||
{
|
||||
if (m_ready && ShouldHandleCmd())
|
||||
{
|
||||
m_log.InfoFormat("[{0}]: Bird flocking is enabled in region {1}.", m_name, m_scene.RegionInfo.RegionName);
|
||||
m_enabled = true;
|
||||
//m_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleStopCmd (string module, string[] args)
|
||||
{
|
||||
if (m_enabled && m_ready && ShouldHandleCmd())
|
||||
{
|
||||
m_log.InfoFormat("[{0}]: Bird flocking is stopped in region {1}.", m_name, m_scene.RegionInfo.RegionName);
|
||||
m_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleStartCmd(string module, string[] args)
|
||||
{
|
||||
if (!m_enabled && m_ready && ShouldHandleCmd())
|
||||
{
|
||||
m_log.InfoFormat("[{0}]: Bird flocking is started in region {1}.", m_name, m_scene.RegionInfo.RegionName);
|
||||
m_enabled = true;
|
||||
FlockUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void HandleSetFrameRateCmd (string module, string[] args)
|
||||
{
|
||||
if (m_ready && ShouldHandleCmd())
|
||||
{
|
||||
int frameRate = Convert.ToInt32( args[1] );
|
||||
m_frameUpdateRate = frameRate;
|
||||
m_log.InfoFormat("[{0}]: Bird updates set to every {1} frames in region {2}.", m_name, frameRate, m_scene.RegionInfo.RegionName);
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleSetSizeCmd (string module, string[] args)
|
||||
{
|
||||
if (m_ready && ShouldHandleCmd())
|
||||
{
|
||||
lock( m_sync ) {
|
||||
int newSize = Convert.ToInt32(args[1]);
|
||||
if (newSize > m_maxFlockSize) newSize = m_maxFlockSize;
|
||||
m_view.Clear();
|
||||
m_model.Size = newSize;
|
||||
m_log.InfoFormat("[{0}]: Bird flock size is set to {1} in region {2}.", m_name, newSize, m_scene.RegionInfo.RegionName);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleShowStatsCmd (string module, string[] args)
|
||||
{
|
||||
if (m_ready && ShouldHandleCmd())
|
||||
{
|
||||
bool inWorld = IsInWorldCmd (ref args);
|
||||
int i;
|
||||
int s=m_model.Size;
|
||||
UUID primUuid;
|
||||
List<ScenePresence> avatarsSitList = new List<ScenePresence>();
|
||||
string avUuids;
|
||||
if (inWorld)
|
||||
{
|
||||
m_log.InfoFormat("[{0}]: Sending bird statistics to region {1}.", m_name, m_scene.RegionInfo.RegionName);
|
||||
}
|
||||
|
||||
ShowResponse("birds-started = " + (m_enabled ? "True" : "False"), inWorld);
|
||||
ShowResponse("birds-enabled = " + (m_ready ? "True" : "False"), inWorld);
|
||||
ShowResponse("birds-prim = " + m_view.BirdPrim, inWorld);
|
||||
ShowResponse("birds-framerate = " + m_frameUpdateRate, inWorld);
|
||||
ShowResponse("birds-maxsize = " + m_maxFlockSize, inWorld);
|
||||
ShowResponse("birds-size = " + s, inWorld);
|
||||
ShowResponse("birds-speed = " + m_model.MaxSpeed, inWorld);
|
||||
ShowResponse("birds-force = " + m_model.MaxForce, inWorld);
|
||||
ShowResponse("birds-distance = " + m_model.NeighbourDistance, inWorld);
|
||||
ShowResponse("birds-separation = " + m_model.DesiredSeparation, inWorld);
|
||||
ShowResponse("birds-tolerance = " + m_model.Tolerance, inWorld);
|
||||
ShowResponse("birds-border = " + m_borderSize, inWorld);
|
||||
|
||||
for (i = 0; i < s; i++)
|
||||
{
|
||||
primUuid = UUID.Zero;
|
||||
avatarsSitList.Clear();
|
||||
avUuids = "";
|
||||
foreach (EntityBase e in m_scene.GetEntities())
|
||||
{
|
||||
if (e.Name == m_name + i)
|
||||
{
|
||||
SceneObjectGroup sog = (SceneObjectGroup)e;
|
||||
SceneObjectPart rootPart = sog.RootPart;
|
||||
primUuid = rootPart.UUID;
|
||||
avatarsSitList = sog.GetSittingAvatars();
|
||||
foreach (ScenePresence av in avatarsSitList)
|
||||
{
|
||||
avUuids += av.UUID.ToString() + " ";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ShowResponse("birds-prim" + i + " = " + m_name + i + " : " + primUuid.ToString() + " : " + avUuids.Trim(), inWorld);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleSetPrimCmd (string module, string[] args)
|
||||
{
|
||||
if (m_ready && ShouldHandleCmd())
|
||||
{
|
||||
string primName = args[1];
|
||||
lock(m_sync) {
|
||||
m_view.BirdPrim = primName;
|
||||
m_log.InfoFormat("[{0}]: Bird prim is set to {1} in region {2}.", m_name, primName, m_scene.RegionInfo.RegionName);
|
||||
m_view.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleSetMaxSpeedCmd(string module, string[] args)
|
||||
{
|
||||
if (m_ready && ShouldHandleCmd())
|
||||
{
|
||||
float maxSpeed = (float)Convert.ToDecimal(args[1]);
|
||||
lock (m_sync)
|
||||
{
|
||||
m_model.MaxSpeed = maxSpeed;
|
||||
m_log.InfoFormat("[{0}]: Birds maximum speed is set to {1} in region {2}.", m_name, maxSpeed, m_scene.RegionInfo.RegionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleSetMaxForceCmd(string module, string[] args)
|
||||
{
|
||||
if (m_ready && ShouldHandleCmd())
|
||||
{
|
||||
float maxForce = (float)Convert.ToDecimal(args[1]);
|
||||
lock (m_sync)
|
||||
{
|
||||
m_model.MaxForce = maxForce;
|
||||
m_log.InfoFormat("[{0}]: Birds maximum force is set to {1} in region {2}.", m_name, maxForce, m_scene.RegionInfo.RegionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleSetNeighbourDistanceCmd(string module, string[] args)
|
||||
{
|
||||
if (m_ready && ShouldHandleCmd())
|
||||
{
|
||||
float neighbourDistance = (float)Convert.ToDecimal(args[1]);
|
||||
lock (m_sync)
|
||||
{
|
||||
m_model.NeighbourDistance = neighbourDistance;
|
||||
m_log.InfoFormat("[{0}]: Birds neighbour distance is set to {1} in region {2}.", m_name, neighbourDistance, m_scene.RegionInfo.RegionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleSetDesiredSeparationCmd(string module, string[] args)
|
||||
{
|
||||
if (m_ready && ShouldHandleCmd())
|
||||
{
|
||||
float desiredSeparation = (float)Convert.ToDecimal(args[1]);
|
||||
lock (m_sync)
|
||||
{
|
||||
m_model.DesiredSeparation = desiredSeparation;
|
||||
m_log.InfoFormat("[{0}]: Birds desired separation is set to {1} in region {2}.", m_name, desiredSeparation, m_scene.RegionInfo.RegionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleSetToleranceCmd(string module, string[] args)
|
||||
{
|
||||
if (m_ready && ShouldHandleCmd())
|
||||
{
|
||||
float tolerance = (float)Convert.ToDecimal(args[1]);
|
||||
lock (m_sync)
|
||||
{
|
||||
m_model.Tolerance = tolerance;
|
||||
m_log.InfoFormat("[{0}]: Birds tolerance is set to {1} in region {2}.", m_name, tolerance, m_scene.RegionInfo.RegionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Flocking
|
||||
{
|
||||
class FlowEntry
|
||||
{
|
||||
public float X = 0;
|
||||
public float Y = 0;
|
||||
public float Z = 0;
|
||||
|
||||
public float Value = 0;
|
||||
|
||||
public FlowEntry(float x, float y, float z, float value)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Z = z;
|
||||
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
Region Module - ability to control flocks within an OpenSim scene
|
||||
|
||||
|
||||
This branch tracks the OpenSim 0.7.1.1 release
|
||||
|
||||
To build from source
|
||||
|
||||
Add osboids source tree under opensim/addon-modules
|
||||
|
||||
./runprebuild.sh against opensim root to build this module into the solution
|
||||
then xbuild, or build within Visual Studio / Monodevelop to produce the binaries
|
||||
|
||||
osboids has no external dependencies other than the dlls currently included in opensim.
|
||||
The project generates a single dll - Flocking.dll which is copied into opensim/bin as part of the build step
|
||||
|
||||
|
||||
Configuration
|
||||
|
||||
To become active, the module needs to be both referenced and enabled in the ini files. Otherwise it does nothing on startup
|
||||
|
||||
Entry is as follows:
|
||||
|
||||
|
||||
[Boids]
|
||||
enabled = true
|
||||
|
||||
|
||||
removing the Boids group or setting enabled = false will switch off the module
|
||||
|
||||
In addition various config parameters are available to control the flock dynamics
|
||||
|
||||
flock-size = 100 the number of Boids to flock
|
||||
|
||||
|
||||
By default the module will create a flock of plain wooden spheres, however this can be overridden
|
||||
|
||||
boid-prim = fish01 names the prim to use to represent each boid in the flock
|
||||
currently this prim needs to already exist in the scene - i.e. be rezzed in the region.
|
||||
Eventually this will be an inventory lookup
|
||||
|
||||
|
||||
Various runtime commands control the flocking module behaviour - described below. These can either be invoked
|
||||
from the Console or in world by directing them to a chat channel. To specify which channel to use:
|
||||
|
||||
chat-channel = 118 the chat channel to listen for boid commands on
|
||||
|
||||
|
||||
|
||||
Runtime Commands
|
||||
|
||||
The following commands, which can either be issued on the Console, or via a chat channel in-world, control the behaviour
|
||||
of the flock at runtime
|
||||
|
||||
flock-stop or /118 stop in chat - stop all flocking and remove boids from the region
|
||||
flock-start or /118 start - start the flocking simulation
|
||||
flock-size <num> or /118 size <num> - change the size of the flock
|
||||
flock-prim <name> or /118 prim <name> - change the boid prim to that passed in - must be rezzed in the scene
|
||||
flock-framerate <num> or /118 framerate - only update the flock positions every <num> frames - only really useful
|
||||
- for photography and debugging boid behaviour
|
||||
|
||||
Additionally various parameters of the flocking algorithms can be set at runtime, these are configured using the
|
||||
flock-set <name> <value> or /118 set <name> <value> commands
|
||||
|
||||
The following parameters can be set - current defaults are shown below
|
||||
|
||||
|
||||
max-speed = 3 how far each boid can travel per update
|
||||
max-force = 0.25 the maximum acceleration allowed to the current velocity of the boid
|
||||
neighbour-distance = 25 max distance for other boids to be considered in the same flock as us
|
||||
desired-separation = 20 how far away from other boids we would like to stay
|
||||
tolerance = 5 how close to the edges of objects or the scene should our boid get
|
||||
separation-weighting = 1.5 factor by which closeness to other boids should be favoured when updating position in flock
|
||||
alignment-weighting = 1 factor by which alignment with other boids should be favoured when updating position in flock
|
||||
cohesion-weighting = 1 factor by which keeping within the local flock should be favoured when updating position in flock
|
||||
lookahead-distance = 10 how far in front should the boid look for edges and boundaries
|
||||
|
||||
Boidprims
|
||||
|
||||
Any, currently rezzed in scene, object can be used as the boid prim. However fps is very much affected by the
|
||||
complexity of the entity to use. It is easier to throw a single prim (or sculpty) around the scene than it is to
|
||||
throw the constituent parts of a 200 linked prim dragon.
|
||||
|
||||
Tests show that a few hundred single prims can be flocked effectively - depending on system and network
|
||||
|
||||
I intend to allow inventory items and UUIDs to represent the boids - this is not written yet however.
|
||||
|
||||
|
||||
Prebuilt binaries etc.. proved in project root
|
||||
|
||||
|
||||
Status
|
||||
|
||||
probably made it to alpha by now ...
|
||||
|
||||
|
||||
Next Steps
|
||||
|
||||
I want to improve the ability of the boids to avoid obstacles within the scene. Current avoidance is pretty basic, and
|
||||
only functions correctly about fifty percent of the time. Need to improve this without increasing computational cost.
|
||||
|
||||
|
||||
|
||||
Licence: all files released under a BSD licence
|
196
README.md
196
README.md
|
@ -1,196 +0,0 @@
|
|||
INonSharedRegion Module - ability to control flocks of prims within an OpenSim scene.
|
||||
|
||||
To build from source:
|
||||
|
||||
Add OpenSimBirds source tree under opensim/addon-modules
|
||||
|
||||
e.g.
|
||||
|
||||
cd ~/opensim/addon-modules
|
||||
git clone https://github.com/JakDaniels/OpenSimBirds.git
|
||||
|
||||
./runprebuild.sh against opensim root to build this module into the solution
|
||||
then xbuild, or build within Visual Studio / Monodevelop to produce the binaries.
|
||||
|
||||
OpenSimBirds does not need a config in order to run, but without one it will not persist your bird flock settings from one
|
||||
region restart to another!
|
||||
|
||||
The settings go in an .ini file in bin/addon-modules/OpenSimBirds/config/ *or* in your existing Regions.ini
|
||||
|
||||
OpenSimBirds has no external dependencies other than the dlls currently included in opensim.
|
||||
The project generates a single dll - OpenSimBirds.Module.dll which is copied into opensim/bin as part of the build step
|
||||
|
||||
|
||||
Configuration:
|
||||
|
||||
If you have the module added to or compiled with your OpenSim build then it will run a instance of itself once per region.
|
||||
By default it will configure itself to some sensible defaults and will sit quietly in the background waiting for commands
|
||||
from the console, or from in-world. It will not enable itself (i.e. rez some birds) until commanded to do so, or
|
||||
configured to do so in the .ini file. It is possible to completely stop the module from doing anything (including
|
||||
listening for commands), but you must have a particular setting in the .ini file for that region (see below) - in other
|
||||
words, you must have a config for it!
|
||||
|
||||
To become active, the module needs to be enabled in the ini file or commanded to do so from inworld or the console.
|
||||
Otherwise it does nothing on startup except listen for commands. If you are running multiple regions on one simulator you
|
||||
can have different Birds settings per region in the configuration file, in the exact same way you can customize per Region
|
||||
setting in Regions.ini
|
||||
|
||||
The configuration for this module can be placed in one of two places:
|
||||
|
||||
Option 1:
|
||||
|
||||
bin/addon-modules/OpenSimBirds/config/OpenSimBirds.ini
|
||||
|
||||
This follows the similar format as a Regions.ini file, where you specify setting for
|
||||
each region using the [Region Name] section heading. There is an example .ini file
|
||||
provided which should be edited and copied to the correct place above.
|
||||
|
||||
Option 2:
|
||||
|
||||
bin/Regions/Regions.ini
|
||||
|
||||
Add config parameters to the existing Regions.ini file under the appropriate region name heading.
|
||||
The file is usually in the above location, but this could have been changed in OpenSim.ini via the
|
||||
'regionload_regionsdir' parameter in the [Startup] section of OpenSim.ini
|
||||
|
||||
|
||||
Here is an example config:
|
||||
|
||||
;; Set the Birds settings per named region
|
||||
|
||||
[Test Region 1]
|
||||
|
||||
BirdsModuleStartup = True ;this is the default and determines whether the module does anything
|
||||
BirdsEnabled = True ;set to false to disable the birds from appearing in this region
|
||||
BirdsFlockSize = 50 ;the number of birds to flock
|
||||
BirdsMaxFlockSize = 100 ;the maximum flock size that can be created (keeps things sane)
|
||||
BirdsMaxSpeed = 3 ;how far each bird can travel per update
|
||||
BirdsMaxForce = 0.25 ;the maximum acceleration allowed to the current velocity of the bird
|
||||
BirdsNeighbourDistance = 25 ;max distance for other birds to be considered in the same flock as us
|
||||
BirdsDesiredSeparation = 20 ;how far away from other birds we would like to stay
|
||||
BirdsTolerance = 5 ;how close to the edges of things can we get without being worried
|
||||
BirdsBorderSize = 5 ;how close to the edge of a region can we get?
|
||||
BirdsMaxHeight = 256 ;how high are we allowed to flock
|
||||
BirdsUpdateEveryNFrames = 1 ;update bird positions every N simulator frames
|
||||
BirdsPrim = SeaGull1 ;By default the module will create a flock of plain wooden spheres,
|
||||
;however this can be overridden to the name of an existing prim that
|
||||
;needs to already exist in the scene - i.e. be rezzed in the region.
|
||||
|
||||
;who is allowed to send commands via chat or script: list of UUIDs or ESTATE_OWNER or ESTATE_MANAGER
|
||||
;or everyone if not specified
|
||||
BirdsAllowedControllers = ESTATE_OWNER, ESTATE_MANAGER, 12345678-1234-1234-1234-123456789abc
|
||||
|
||||
|
||||
Various runtime commands control the flocking module behaviour - described below. These can either be invoked
|
||||
from the Console or in world by directing them to a chat channel either from the client's Local Chat or via a script.
|
||||
You can specify which channel to use in the .ini:
|
||||
|
||||
BirdsChatChannel = 118 ;the chat channel to listen for Bird commands on
|
||||
|
||||
|
||||
|
||||
Runtime Commands:
|
||||
|
||||
The following commands, which can be issued on the Console or via in-world chat or scripted chat on the BirdsChatChannel
|
||||
to control the birds at runtime:
|
||||
|
||||
birds-stop or /118 stop ;stop all birds flocking
|
||||
birds-start or /118 start ;start all birds flocking
|
||||
birds-enable or /118 enable ;enable the flocking simulation if disabled and rez new birds
|
||||
birds-disable or /118 disable ;stop all birds and remove them from the scene
|
||||
birds-prim <name> or /118 prim <name> ;change the bird prim to a prim already rezzed in the scene
|
||||
birds-stats or /118 stats ;show all the parameters and list the bird prim names and uuids
|
||||
birds-framerate <num> or /118 framerate <num> ;only update the flock positions every <num> frames
|
||||
;only really useful for photography and debugging bird
|
||||
;behaviour
|
||||
|
||||
|
||||
These commands are great for playing with the flock dynamics in real time:
|
||||
|
||||
birds-size <num> or /118 size <num> ;change the size of the flock
|
||||
birds-speed <num> or /118 speed <num> ;change the maximum velocity each bird may achieve
|
||||
birds-force <num> or /118 force <num> ;change the maximum force each bird may accelerate
|
||||
birds-distance <num> or /118 distance <num> ;change the maximum distance that other birds are to be considered in the same flock as us
|
||||
birds-separation <num> or /118 separation <num> ;sets how far away from other birds we would like to stay
|
||||
birds-tolerance <num> or /118 tolerance <num> ;sets how close to the edges of things can we get without being worried
|
||||
|
||||
Of course if distance is less than separation then the birds will never flock. The other way around and they will always
|
||||
eventually form one or more flocks.
|
||||
|
||||
Security:
|
||||
|
||||
By default anyone can send commands to the module from within a script or via the in-world chat on the 'BirdsChatChannel' channel.
|
||||
You should use a high negative value for channel if you want to allow script access, but not in-world chat. Further you can restrict
|
||||
which users are allowed to control the module using the 'BirdsAllowedControllers' setting. This is a comma separated list of user UUIDs,
|
||||
but it may also contain one of the pre-defined constants ESTATE_OWNER (evaluates to the UUID of the estate owner) and ESTATE_MANAGER
|
||||
(evaluates to a list of estate manager UUIDS).
|
||||
|
||||
* For commands sent from in-world chat, it is the UUID of the avatar sending the command that is checked against the list.
|
||||
* For commands sent from a script it is the UUID of the owner of the prim in which the script resides that is checked against the list.
|
||||
|
||||
Bird prims:
|
||||
|
||||
Any currently rezzed in-scene-object can be used as the bird prim. However fps is very much affected by the
|
||||
complexity of the entity to use. It is easier to throw a single prim (or sculpty) around the scene than it is to
|
||||
throw the constituent parts of a 200 linked prim dragon.
|
||||
|
||||
Tests show that <= 500 single prims can be flocked effectively - depending on system and network
|
||||
However maybe <= 300 simple linksets can perform as well.
|
||||
|
||||
Network Traffic:
|
||||
|
||||
I tested the amount of network traffic generated by bird updates. 20 birds (each with 4 linked prims) takes up about 300kbps
|
||||
in network position updates. 50 of the same birds generates about 750kbps traffic.
|
||||
Each bird uses roughly 15kbps of network traffic. This is all measured using an update framerate of 1, i.e. birds' position
|
||||
is updated every simulator frame.
|
||||
|
||||
Statistics:
|
||||
|
||||
The stats command in-world or via script returns data to BirdsChatChannel. The console command returns stats to the console.
|
||||
All the the modules parameters are returned including a list of the active bird prims currently rezzed in the region,
|
||||
and the UUIDs of those prims' root prim. Also included is a list of any avatar UUIDs that may be sitting on those prims. Here
|
||||
is an example output:
|
||||
|
||||
birds-started = False
|
||||
birds-enabled = True
|
||||
birds-prim = SeaGull1
|
||||
birds-framerate = 1
|
||||
birds-maxsize = 100
|
||||
birds-size = 20
|
||||
birds-speed = 1.5
|
||||
birds-force = 0.2
|
||||
birds-distance = 25
|
||||
birds-separation = 10
|
||||
birds-tolerance = 5
|
||||
birds-border = 5
|
||||
birds-prim0 = OpenSimBirds0 : 01abef79-7fb2-4c8d-831e-62ce1ce878f1 :
|
||||
birds-prim1 = OpenSimBirds1 : af85996d-af4d-4dda-bc89-721c51e09d0c :
|
||||
birds-prim2 = OpenSimBirds2 : ca766390-1877-4b19-a29e-4590cf40aece :
|
||||
birds-prim3 = OpenSimBirds3 : 6694bfa9-8e7f-4ac5-b336-ad13e5cfced2 :
|
||||
birds-prim4 = OpenSimBirds4 : 1c6b152d-dcca-4fef-8979-b7ccc8139e1e :
|
||||
birds-prim5 = OpenSimBirds5 : 08bba2cc-d427-4855-a7f0-57aa55109707 :
|
||||
birds-prim6 = OpenSimBirds6 : bbeb8b6d-28d8-41a9-b8ce-dab3173bd454 :
|
||||
birds-prim7 = OpenSimBirds7 : 45c73475-1f0f-487f-ac9f-87d30d0315e8 :
|
||||
birds-prim8 = OpenSimBirds8 : d5891cc8-c196-4b05-82ef-3c7d0f703963 :
|
||||
birds-prim9 = OpenSimBirds9 : 557b61e1-5fd6-4878-980e-e93cabcc078f :
|
||||
birds-prim10 = OpenSimBirds10 : 7ff2c02d-d73c-4e49-a4e9-84b652dc70a9 :
|
||||
birds-prim11 = OpenSimBirds11 : c2b0820c-ba20-4318-a0e8-ec6ad521f524 :
|
||||
birds-prim12 = OpenSimBirds12 : e8e87309-7a47-4983-89a1-4bb11d05a40c :
|
||||
birds-prim13 = OpenSimBirds13 : a351e0e3-ae99-48b8-877d-65156f437b33 :
|
||||
birds-prim14 = OpenSimBirds14 : 150f1c3b-e9d9-4cda-9e03-69fb5286e436 :
|
||||
birds-prim15 = OpenSimBirds15 : ebf63de1-d419-45d0-8eee-3db14295e401 :
|
||||
birds-prim16 = OpenSimBirds16 : faad97af-4ee6-425c-b221-99ef53650e93 :
|
||||
birds-prim17 = OpenSimBirds17 : d75ba544-bbc2-4f5a-9d7e-00e21ed6f191 :
|
||||
birds-prim18 = OpenSimBirds18 : b91e42cb-ae5b-4f03-bf6e-dc03d52858b7 : 19cc284d-11c6-4ee7-a8de-f69788d08434
|
||||
birds-prim19 = OpenSimBirds19 : 44aa3e14-56bc-43dd-afbd-7348c5dfe3a5 :
|
||||
|
||||
In the above example, there is one avatar sitting on bird-prim18. For more than one avatar the UUID list will be separated by spaces.
|
||||
|
||||
Please Note:
|
||||
|
||||
This module is currently only tested against opensim master.
|
||||
|
||||
Licence: all files released under a BSD licence
|
||||
If you have any question please contact Jak Daniels, jak@ateb.co.uk
|
||||
If you do fork or modify this project for the better, please let me know!! I would be interested to incorporate any enhancements you may make.
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
|
||||
TODO:
|
||||
|
||||
[done] Expose Settings to ini Files
|
||||
|
||||
[done] Write in world Chat controller to allow maintenance of flocking in world.
|
||||
|
||||
[done - but basic] Handle Borders Correctly
|
||||
|
||||
[done - but basic] Handle Collision Avoidance
|
||||
|
||||
Replace above with a proper flow field implementation
|
||||
|
||||
Handle Wander
|
||||
|
||||
Handle Perching
|
||||
|
||||
Handle Predator Avoidance
|
||||
|
||||
|
||||
Optimise it all...
|
||||
|
||||
Look at maintaining flocks across sim boundaries.
|
||||
|
||||
Look at exposing flocking as a service to scripts
|
||||
|
||||
|
Binary file not shown.
|
@ -1,22 +0,0 @@
|
|||
;; Set the Birds settings per named region
|
||||
|
||||
[Test Region 2]
|
||||
|
||||
BirdsModuleStartup = True ;this is the default and determines whether the module does anything
|
||||
BirdsEnabled = True ;set to false to disable the birds from appearing in this region
|
||||
BirdsChatChannel = 118 ;which channel do we listen on for in world commands
|
||||
BirdsFlockSize = 20 ;the number of birds to flock
|
||||
BirdsMaxSpeed = 1.5 ;how far each bird can travel per update
|
||||
BirdsMaxForce = 0.20 ;the maximum acceleration allowed to the current velocity of the bird
|
||||
BirdsNeighbourDistance = 25 ;max distance for other birds to be considered in the same flock as us
|
||||
BirdsDesiredSeparation = 10 ;how far away from other birds we would like to stay
|
||||
BirdsTolerance = 5 ;how close to the edges of things can we get without being worried
|
||||
BirdsBorderSize = 5 ;how close to the edge of a region can we get?
|
||||
BirdsMaxHeight = 75 ;how high are we allowed to flock
|
||||
BirdsPrim = SeaGull1 ;By default the module will create a flock of plain wooden spheres,
|
||||
;however this can be overridden to the name of an existing prim that
|
||||
;needs to already exist in the scene - i.e. be rezzed in the region.
|
||||
|
||||
;who is allowed to send commands via chat or script: list of UUIDs or ESTATE_OWNER or ESTATE_MANAGER
|
||||
;or everyone if not specified
|
||||
BirdsAllowedControllers = ESTATE_OWNER, ESTATE_MANAGER, 12345678-1234-1234-1234-123456789abc
|
66
prebuild.xml
66
prebuild.xml
|
@ -1,33 +1,37 @@
|
|||
<?xml version="1.0" ?>
|
||||
<Project frameworkVersion="v4_0" name="OpenSimBirds.Module" path="addon-modules/OpenSimBirds/Module" type="Library">
|
||||
<Configuration name="Debug">
|
||||
<Options>
|
||||
<OutputPath>../../../bin/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
<Configuration name="Release">
|
||||
<Options>
|
||||
<OutputPath>../../../bin/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!-- Core osboids Project -->
|
||||
|
||||
<ReferencePath>../../../bin/</ReferencePath>
|
||||
<Reference name="System"/>
|
||||
<Reference name="System.Net"/>
|
||||
<Reference name="OpenMetaverse"/>
|
||||
<Reference name="OpenMetaverseTypes"/>
|
||||
<Reference name="OpenMetaverse.StructuredData"/>
|
||||
<Reference name="OpenSim.Framework"/>
|
||||
<Reference name="OpenSim.Framework.Communications"/>
|
||||
<Reference name="OpenSim.Framework.Console"/>
|
||||
<Reference name="OpenSim.Region.Framework"/>
|
||||
<Reference name="OpenSim.Region.ScriptEngine.Shared"/>
|
||||
<Reference name="Nini"/>
|
||||
<Reference name="log4net"/>
|
||||
<Reference name="Mono.Addins"/>
|
||||
<Project frameworkVersion="v3_5" name="Flocking" path="addon-modules/osboids/Flocking/" type="Library">
|
||||
<Configuration name="Debug">
|
||||
<Options>
|
||||
<OutputPath>../../../bin/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
<Configuration name="Release">
|
||||
<Options>
|
||||
<OutputPath>../../../bin/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
|
||||
<ReferencePath>../../../bin/</ReferencePath>
|
||||
<Reference name="log4net" path="../../../bin/" />
|
||||
<Reference name="System"/>
|
||||
<Reference name="System.Drawing" />
|
||||
<Reference name="System.Xml"/>
|
||||
<Reference name="Nini" path="../../../bin/" />
|
||||
<Reference name="nunit.framework" path="../../../bin/" />
|
||||
<Reference name="OpenSim.Framework" path="../../../bin/" />
|
||||
<Reference name="OpenSim.Tests.Common" path="../../../bin/" />
|
||||
<Reference name="OpenSim.Framework.Communications" path="../../../bin/" />
|
||||
<Reference name="OpenSim.Region.Framework" path="../../../bin/" />
|
||||
<Reference name="OpenSim.Framework.Console" path="../../../bin/" />
|
||||
<Reference name="OpenSim.Services.Interfaces" path="../../../bin/" />
|
||||
<Reference name="OpenMetaverseTypes" path="../../../bin/" />
|
||||
<Reference name="OpenMetaverse" path="../../../bin/" />
|
||||
<Files>
|
||||
<Match pattern="*.cs" recurse="true"/>
|
||||
<Match pattern="*.addin.xml" path="Resources" buildAction="EmbeddedResource" recurse="true"/>
|
||||
|
||||
</Files>
|
||||
</Project>
|
||||
|
||||
<Files>
|
||||
<Match pattern="*.cs" recurse="true">
|
||||
</Match>
|
||||
</Files>
|
||||
</Project>
|
||||
|
|
Loading…
Reference in New Issue