1132 lines
39 KiB
C#
1132 lines
39 KiB
C#
/*
|
|
* Copyright (c) Contributors, http://opensimulator.org/
|
|
* 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.Reflection;
|
|
using BulletDotNET;
|
|
using OpenMetaverse;
|
|
using OpenSim.Framework;
|
|
using OpenSim.Region.Physics.Manager;
|
|
using log4net;
|
|
|
|
namespace OpenSim.Region.Physics.BulletDotNETPlugin
|
|
{
|
|
public class BulletDotNETCharacter : PhysicsActor
|
|
{
|
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
public btRigidBody Body;
|
|
public btCollisionShape Shell;
|
|
public btVector3 tempVector1;
|
|
public btVector3 tempVector2;
|
|
public btVector3 tempVector3;
|
|
public btVector3 tempVector4;
|
|
|
|
public btVector3 tempVector5RayCast;
|
|
public btVector3 tempVector6RayCast;
|
|
public btVector3 tempVector7RayCast;
|
|
|
|
public btQuaternion tempQuat1;
|
|
public btTransform tempTrans1;
|
|
|
|
public ClosestNotMeRayResultCallback ClosestCastResult;
|
|
private btTransform m_bodyTransform;
|
|
private btVector3 m_bodyPosition;
|
|
private btVector3 m_CapsuleOrientationAxis;
|
|
private btQuaternion m_bodyOrientation;
|
|
private btDefaultMotionState m_bodyMotionState;
|
|
private btGeneric6DofConstraint m_aMotor;
|
|
private PhysicsVector m_movementComparision;
|
|
private PhysicsVector m_position;
|
|
private PhysicsVector m_zeroPosition;
|
|
private bool m_zeroFlag = false;
|
|
private bool m_lastUpdateSent = false;
|
|
private PhysicsVector m_velocity;
|
|
private PhysicsVector m_target_velocity;
|
|
private PhysicsVector m_acceleration;
|
|
private PhysicsVector m_rotationalVelocity;
|
|
private bool m_pidControllerActive = true;
|
|
public float PID_D = 80.0f;
|
|
public float PID_P = 90.0f;
|
|
public float CAPSULE_RADIUS = 0.37f;
|
|
public float CAPSULE_LENGTH = 2.140599f;
|
|
public float heightFudgeFactor = 0.52f;
|
|
public float walkDivisor = 1.3f;
|
|
public float runDivisor = 0.8f;
|
|
private float m_mass = 80f;
|
|
public float m_density = 60f;
|
|
private bool m_flying = false;
|
|
private bool m_iscolliding = false;
|
|
private bool m_iscollidingGround = false;
|
|
private bool m_wascolliding = false;
|
|
private bool m_wascollidingGround = false;
|
|
private bool m_iscollidingObj = false;
|
|
private bool m_alwaysRun = false;
|
|
private bool m_hackSentFall = false;
|
|
private bool m_hackSentFly = false;
|
|
public uint m_localID = 0;
|
|
public bool m_returnCollisions = false;
|
|
// taints and their non-tainted counterparts
|
|
public bool m_isPhysical = false; // the current physical status
|
|
public bool m_tainted_isPhysical = false; // set when the physical status is tainted (false=not existing in physics engine, true=existing)
|
|
private float m_tainted_CAPSULE_LENGTH; // set when the capsule length changes.
|
|
private bool m_taintRemove = false;
|
|
private bool m_taintedPosition = false;
|
|
private PhysicsVector m_taintedPosition_value;
|
|
private PhysicsVector m_taintedForce;
|
|
|
|
private float m_buoyancy = 0f;
|
|
|
|
// private CollisionLocker ode;
|
|
|
|
private string m_name = String.Empty;
|
|
|
|
private bool[] m_colliderarr = new bool[11];
|
|
private bool[] m_colliderGroundarr = new bool[11];
|
|
|
|
|
|
|
|
private BulletDotNETScene m_parent_scene;
|
|
|
|
public int m_eventsubscription = 0;
|
|
private CollisionEventUpdate CollisionEventsThisFrame = new CollisionEventUpdate();
|
|
|
|
public BulletDotNETCharacter(string avName, BulletDotNETScene parent_scene, PhysicsVector pos, PhysicsVector size, float pid_d, float pid_p, float capsule_radius, float tensor, float density, float height_fudge_factor, float walk_divisor, float rundivisor)
|
|
{
|
|
m_taintedForce = new PhysicsVector();
|
|
m_velocity = new PhysicsVector();
|
|
m_target_velocity = new PhysicsVector();
|
|
m_position = pos;
|
|
m_zeroPosition = new PhysicsVector(pos.X, pos.Y, pos.Z); // this is a class, not a struct. Must make new, or m_zeroPosition will == position regardless
|
|
m_acceleration = new PhysicsVector();
|
|
m_parent_scene = parent_scene;
|
|
PID_D = pid_d;
|
|
PID_P = pid_p;
|
|
CAPSULE_RADIUS = capsule_radius;
|
|
m_density = density;
|
|
heightFudgeFactor = height_fudge_factor;
|
|
walkDivisor = walk_divisor;
|
|
runDivisor = rundivisor;
|
|
|
|
for (int i = 0; i < 11; i++)
|
|
{
|
|
m_colliderarr[i] = false;
|
|
}
|
|
for (int i = 0; i < 11; i++)
|
|
{
|
|
m_colliderGroundarr[i] = false;
|
|
}
|
|
CAPSULE_LENGTH = (size.Z * 1.15f) - CAPSULE_RADIUS * 2.0f;
|
|
m_tainted_CAPSULE_LENGTH = CAPSULE_LENGTH;
|
|
m_isPhysical = false; // current status: no ODE information exists
|
|
m_tainted_isPhysical = true; // new tainted status: need to create ODE information
|
|
|
|
m_parent_scene.AddPhysicsActorTaint(this);
|
|
|
|
m_name = avName;
|
|
tempVector1 = new btVector3(0, 0, 0);
|
|
tempVector2 = new btVector3(0, 0, 0);
|
|
tempVector3 = new btVector3(0, 0, 0);
|
|
tempVector4 = new btVector3(0, 0, 0);
|
|
|
|
tempVector5RayCast = new btVector3(0, 0, 0);
|
|
tempVector6RayCast = new btVector3(0, 0, 0);
|
|
tempVector7RayCast = new btVector3(0, 0, 0);
|
|
|
|
tempQuat1 = new btQuaternion(0, 0, 0, 1);
|
|
tempTrans1 = new btTransform(tempQuat1, tempVector1);
|
|
m_movementComparision = new PhysicsVector(0, 0, 0);
|
|
m_CapsuleOrientationAxis = new btVector3(1, 0, 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// This creates the Avatar's physical Surrogate at the position supplied
|
|
/// </summary>
|
|
/// <param name="npositionX"></param>
|
|
/// <param name="npositionY"></param>
|
|
/// <param name="npositionZ"></param>
|
|
|
|
// WARNING: This MUST NOT be called outside of ProcessTaints, else we can have unsynchronized access
|
|
// to ODE internals. ProcessTaints is called from within thread-locked Simulate(), so it is the only
|
|
// place that is safe to call this routine AvatarGeomAndBodyCreation.
|
|
private void AvatarGeomAndBodyCreation(float npositionX, float npositionY, float npositionZ)
|
|
{
|
|
|
|
if (CAPSULE_LENGTH <= 0)
|
|
{
|
|
m_log.Warn("[PHYSICS]: The capsule size you specified in opensim.ini is invalid! Setting it to the smallest possible size!");
|
|
CAPSULE_LENGTH = 0.01f;
|
|
|
|
}
|
|
|
|
if (CAPSULE_RADIUS <= 0)
|
|
{
|
|
m_log.Warn("[PHYSICS]: The capsule size you specified in opensim.ini is invalid! Setting it to the smallest possible size!");
|
|
CAPSULE_RADIUS = 0.01f;
|
|
|
|
}
|
|
|
|
Shell = new btCapsuleShape(CAPSULE_RADIUS, CAPSULE_LENGTH);
|
|
|
|
if (m_bodyPosition == null)
|
|
m_bodyPosition = new btVector3(npositionX, npositionY, npositionZ);
|
|
|
|
m_bodyPosition.setValue(npositionX, npositionY, npositionZ);
|
|
|
|
if (m_bodyOrientation == null)
|
|
m_bodyOrientation = new btQuaternion(m_CapsuleOrientationAxis, (Utils.DEG_TO_RAD * 90));
|
|
|
|
if (m_bodyTransform == null)
|
|
m_bodyTransform = new btTransform(m_bodyOrientation, m_bodyPosition);
|
|
else
|
|
{
|
|
m_bodyTransform.Dispose();
|
|
m_bodyTransform = new btTransform(m_bodyOrientation, m_bodyPosition);
|
|
}
|
|
|
|
if (m_bodyMotionState == null)
|
|
m_bodyMotionState = new btDefaultMotionState(m_bodyTransform);
|
|
else
|
|
m_bodyMotionState.setWorldTransform(m_bodyTransform);
|
|
|
|
m_mass = Mass;
|
|
|
|
Body = new btRigidBody(m_mass, m_bodyMotionState, Shell);
|
|
Body.setUserPointer(new IntPtr((int)Body.Handle));
|
|
|
|
if (ClosestCastResult != null)
|
|
ClosestCastResult.Dispose();
|
|
ClosestCastResult = new ClosestNotMeRayResultCallback(Body);
|
|
|
|
m_parent_scene.AddRigidBody(Body);
|
|
Body.setActivationState(4);
|
|
if (m_aMotor != null)
|
|
{
|
|
if (m_aMotor.Handle != IntPtr.Zero)
|
|
{
|
|
m_parent_scene.getBulletWorld().removeConstraint(m_aMotor);
|
|
m_aMotor.Dispose();
|
|
}
|
|
m_aMotor = null;
|
|
}
|
|
|
|
m_aMotor = new btGeneric6DofConstraint(Body, m_parent_scene.TerrainBody,
|
|
m_parent_scene.TransZero,
|
|
m_parent_scene.TransZero, false);
|
|
m_aMotor.setAngularLowerLimit(m_parent_scene.VectorZero);
|
|
m_aMotor.setAngularUpperLimit(m_parent_scene.VectorZero);
|
|
|
|
|
|
}
|
|
public void Remove()
|
|
{
|
|
m_taintRemove = true;
|
|
}
|
|
public override bool Stopped
|
|
{
|
|
get { return m_zeroFlag; }
|
|
}
|
|
|
|
public override PhysicsVector Size
|
|
{
|
|
get { return new PhysicsVector(CAPSULE_RADIUS * 2, CAPSULE_RADIUS * 2, CAPSULE_LENGTH); }
|
|
set
|
|
{
|
|
m_pidControllerActive = true;
|
|
|
|
PhysicsVector SetSize = value;
|
|
m_tainted_CAPSULE_LENGTH = (SetSize.Z * 1.15f) - CAPSULE_RADIUS * 2.0f;
|
|
//m_log.Info("[SIZE]: " + CAPSULE_LENGTH.ToString());
|
|
|
|
Velocity = new PhysicsVector(0f, 0f, 0f);
|
|
|
|
m_parent_scene.AddPhysicsActorTaint(this);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// turn the PID controller on or off.
|
|
/// The PID Controller will turn on all by itself in many situations
|
|
/// </summary>
|
|
/// <param name="status"></param>
|
|
public void SetPidStatus(bool status)
|
|
{
|
|
m_pidControllerActive = status;
|
|
}
|
|
|
|
public override PrimitiveBaseShape Shape
|
|
{
|
|
set { return; }
|
|
}
|
|
|
|
public override uint LocalID
|
|
{
|
|
set { m_localID = value; }
|
|
}
|
|
|
|
public override bool Grabbed
|
|
{
|
|
set { return; }
|
|
}
|
|
|
|
public override bool Selected
|
|
{
|
|
set { return; }
|
|
}
|
|
|
|
|
|
public override void CrossingFailure()
|
|
{
|
|
|
|
}
|
|
|
|
public override void link(PhysicsActor obj)
|
|
{
|
|
|
|
}
|
|
|
|
public override void delink()
|
|
{
|
|
|
|
}
|
|
|
|
public override void LockAngularMotion(PhysicsVector axis)
|
|
{
|
|
|
|
}
|
|
|
|
public override PhysicsVector Position
|
|
{
|
|
get { return m_position; }
|
|
set
|
|
{
|
|
m_taintedPosition_value = value;
|
|
m_position = value;
|
|
m_taintedPosition = true;
|
|
}
|
|
}
|
|
|
|
public override float Mass
|
|
{
|
|
get
|
|
{
|
|
float AVvolume = (float)(Math.PI * Math.Pow(CAPSULE_RADIUS, 2) * CAPSULE_LENGTH);
|
|
return m_density * AVvolume;
|
|
}
|
|
}
|
|
|
|
public override PhysicsVector Force
|
|
{
|
|
get { return new PhysicsVector(m_target_velocity.X, m_target_velocity.Y, m_target_velocity.Z); }
|
|
set { return; }
|
|
}
|
|
|
|
public override int VehicleType
|
|
{
|
|
get { return 0; }
|
|
set { return; }
|
|
}
|
|
|
|
public override void VehicleFloatParam(int param, float value)
|
|
{
|
|
|
|
}
|
|
|
|
public override void VehicleVectorParam(int param, PhysicsVector value)
|
|
{
|
|
|
|
}
|
|
|
|
public override void VehicleRotationParam(int param, Quaternion rotation)
|
|
{
|
|
|
|
}
|
|
|
|
public override void SetVolumeDetect(int param)
|
|
{
|
|
|
|
}
|
|
|
|
public override PhysicsVector GeometricCenter
|
|
{
|
|
get { return PhysicsVector.Zero; }
|
|
}
|
|
|
|
public override PhysicsVector CenterOfMass
|
|
{
|
|
get { return PhysicsVector.Zero; }
|
|
}
|
|
|
|
public override PhysicsVector Velocity
|
|
{
|
|
get
|
|
{
|
|
// There's a problem with PhysicsVector.Zero! Don't Use it Here!
|
|
if (m_zeroFlag)
|
|
return new PhysicsVector(0f, 0f, 0f);
|
|
m_lastUpdateSent = false;
|
|
return m_velocity;
|
|
}
|
|
set
|
|
{
|
|
m_pidControllerActive = true;
|
|
m_target_velocity = value;
|
|
}
|
|
}
|
|
|
|
public override PhysicsVector Torque
|
|
{
|
|
get { return PhysicsVector.Zero; }
|
|
set { return; }
|
|
}
|
|
|
|
public override float CollisionScore
|
|
{
|
|
get { return 0f; }
|
|
set { }
|
|
}
|
|
|
|
public override PhysicsVector Acceleration
|
|
{
|
|
get { return m_acceleration; }
|
|
}
|
|
|
|
public override Quaternion Orientation
|
|
{
|
|
get { return Quaternion.Identity; }
|
|
set
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
public override int PhysicsActorType
|
|
{
|
|
get { return (int)ActorTypes.Agent; }
|
|
set { return; }
|
|
}
|
|
|
|
public override bool IsPhysical
|
|
{
|
|
get { return false; }
|
|
set { return; }
|
|
}
|
|
|
|
public override bool Flying
|
|
{
|
|
get { return m_flying; }
|
|
set { m_flying = value; }
|
|
}
|
|
|
|
public override bool SetAlwaysRun
|
|
{
|
|
get { return m_alwaysRun; }
|
|
set { m_alwaysRun = value; }
|
|
}
|
|
|
|
|
|
public override bool ThrottleUpdates
|
|
{
|
|
get { return false; }
|
|
set { return; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns if the avatar is colliding in general.
|
|
/// This includes the ground and objects and avatar.
|
|
/// </summary>
|
|
public override bool IsColliding
|
|
{
|
|
get { return m_iscolliding; }
|
|
set
|
|
{
|
|
int i;
|
|
int truecount = 0;
|
|
int falsecount = 0;
|
|
|
|
if (m_colliderarr.Length >= 10)
|
|
{
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
m_colliderarr[i] = m_colliderarr[i + 1];
|
|
}
|
|
}
|
|
m_colliderarr[10] = value;
|
|
|
|
for (i = 0; i < 11; i++)
|
|
{
|
|
if (m_colliderarr[i])
|
|
{
|
|
truecount++;
|
|
}
|
|
else
|
|
{
|
|
falsecount++;
|
|
}
|
|
}
|
|
|
|
// Equal truecounts and false counts means we're colliding with something.
|
|
m_log.DebugFormat("[PHYSICS]: TrueCount:{0}, FalseCount:{1}",truecount,falsecount);
|
|
if (falsecount > 1.2 * truecount)
|
|
{
|
|
m_iscolliding = false;
|
|
}
|
|
else
|
|
{
|
|
m_iscolliding = true;
|
|
}
|
|
if (m_wascolliding != m_iscolliding)
|
|
{
|
|
//base.SendCollisionUpdate(new CollisionEventUpdate());
|
|
}
|
|
m_wascolliding = m_iscolliding;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns if an avatar is colliding with the ground
|
|
/// </summary>
|
|
public override bool CollidingGround
|
|
{
|
|
get { return m_iscollidingGround; }
|
|
set
|
|
{
|
|
// Collisions against the ground are not really reliable
|
|
// So, to get a consistant value we have to average the current result over time
|
|
// Currently we use 1 second = 10 calls to this.
|
|
int i;
|
|
int truecount = 0;
|
|
int falsecount = 0;
|
|
|
|
if (m_colliderGroundarr.Length >= 10)
|
|
{
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
m_colliderGroundarr[i] = m_colliderGroundarr[i + 1];
|
|
}
|
|
}
|
|
m_colliderGroundarr[10] = value;
|
|
|
|
for (i = 0; i < 11; i++)
|
|
{
|
|
if (m_colliderGroundarr[i])
|
|
{
|
|
truecount++;
|
|
}
|
|
else
|
|
{
|
|
falsecount++;
|
|
}
|
|
}
|
|
|
|
// Equal truecounts and false counts means we're colliding with something.
|
|
|
|
if (falsecount > 1.2 * truecount)
|
|
{
|
|
m_iscollidingGround = false;
|
|
}
|
|
else
|
|
{
|
|
m_iscollidingGround = true;
|
|
}
|
|
if (m_wascollidingGround != m_iscollidingGround)
|
|
{
|
|
//base.SendCollisionUpdate(new CollisionEventUpdate());
|
|
}
|
|
m_wascollidingGround = m_iscollidingGround;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns if the avatar is colliding with an object
|
|
/// </summary>
|
|
public override bool CollidingObj
|
|
{
|
|
get { return m_iscollidingObj; }
|
|
set
|
|
{
|
|
m_iscollidingObj = value;
|
|
if (value)
|
|
m_pidControllerActive = false;
|
|
else
|
|
m_pidControllerActive = true;
|
|
}
|
|
}
|
|
|
|
|
|
public override bool FloatOnWater
|
|
{
|
|
set { return; }
|
|
}
|
|
|
|
public override PhysicsVector RotationalVelocity
|
|
{
|
|
get { return m_rotationalVelocity; }
|
|
set { m_rotationalVelocity = value; }
|
|
}
|
|
|
|
public override bool Kinematic
|
|
{
|
|
get { return false; }
|
|
set { }
|
|
}
|
|
|
|
public override float Buoyancy
|
|
{
|
|
get { return m_buoyancy; }
|
|
set { m_buoyancy = value; }
|
|
}
|
|
|
|
public override PhysicsVector PIDTarget { set { return; } }
|
|
public override bool PIDActive { set { return; } }
|
|
public override float PIDTau { set { return; } }
|
|
|
|
public override bool PIDHoverActive
|
|
{
|
|
set { return; }
|
|
}
|
|
|
|
public override float PIDHoverHeight
|
|
{
|
|
set { return; }
|
|
}
|
|
|
|
public override PIDHoverType PIDHoverType
|
|
{
|
|
set { return; }
|
|
}
|
|
|
|
public override float PIDHoverTau
|
|
{
|
|
set { return; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the force supplied to the Target Velocity
|
|
/// The PID controller takes this target velocity and tries to make it a reality
|
|
/// </summary>
|
|
/// <param name="force"></param>
|
|
/// <param name="pushforce">Is this a push by a script?</param>
|
|
public override void AddForce(PhysicsVector force, bool pushforce)
|
|
{
|
|
if (pushforce)
|
|
{
|
|
m_pidControllerActive = false;
|
|
force *= 100f;
|
|
doForce(force, false);
|
|
//System.Console.WriteLine("Push!");
|
|
//_target_velocity.X += force.X;
|
|
// _target_velocity.Y += force.Y;
|
|
//_target_velocity.Z += force.Z;
|
|
}
|
|
else
|
|
{
|
|
m_pidControllerActive = true;
|
|
m_target_velocity.X += force.X;
|
|
m_target_velocity.Y += force.Y;
|
|
m_target_velocity.Z += force.Z;
|
|
}
|
|
//m_lastUpdateSent = false;
|
|
}
|
|
|
|
public void doForce(PhysicsVector force, bool now)
|
|
{
|
|
|
|
tempVector3.setValue(force.X, force.Y, force.Z);
|
|
if (now)
|
|
{
|
|
Body.applyCentralForce(tempVector3);
|
|
}
|
|
else
|
|
{
|
|
m_taintedForce += force;
|
|
m_parent_scene.AddPhysicsActorTaint(this);
|
|
}
|
|
}
|
|
|
|
public void doImpulse(PhysicsVector force, bool now)
|
|
{
|
|
|
|
tempVector3.setValue(force.X, force.Y, force.Z);
|
|
if (now)
|
|
{
|
|
Body.applyCentralImpulse(tempVector3);
|
|
}
|
|
else
|
|
{
|
|
m_taintedForce += force;
|
|
m_parent_scene.AddPhysicsActorTaint(this);
|
|
}
|
|
}
|
|
|
|
public override void AddAngularForce(PhysicsVector force, bool pushforce)
|
|
{
|
|
|
|
}
|
|
|
|
public override void SetMomentum(PhysicsVector momentum)
|
|
{
|
|
|
|
}
|
|
|
|
public override void SubscribeEvents(int ms)
|
|
{
|
|
m_eventsubscription = ms;
|
|
m_parent_scene.addCollisionEventReporting(this);
|
|
}
|
|
|
|
public override void UnSubscribeEvents()
|
|
{
|
|
m_parent_scene.remCollisionEventReporting(this);
|
|
m_eventsubscription = 0;
|
|
}
|
|
|
|
public override bool SubscribedEvents()
|
|
{
|
|
if (m_eventsubscription > 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
internal void Dispose()
|
|
{
|
|
if (Body.isInWorld())
|
|
m_parent_scene.removeFromWorld(Body);
|
|
|
|
if (m_aMotor.Handle != IntPtr.Zero)
|
|
m_parent_scene.getBulletWorld().removeConstraint(m_aMotor);
|
|
|
|
m_aMotor.Dispose(); m_aMotor = null;
|
|
ClosestCastResult.Dispose(); ClosestCastResult = null;
|
|
Body.Dispose(); Body = null;
|
|
Shell.Dispose(); Shell = null;
|
|
tempQuat1.Dispose();
|
|
tempTrans1.Dispose();
|
|
tempVector1.Dispose();
|
|
tempVector2.Dispose();
|
|
tempVector3.Dispose();
|
|
tempVector4.Dispose();
|
|
tempVector5RayCast.Dispose();
|
|
tempVector6RayCast.Dispose();
|
|
|
|
}
|
|
|
|
public void ProcessTaints(float timestep)
|
|
{
|
|
|
|
if (m_tainted_isPhysical != m_isPhysical)
|
|
{
|
|
if (m_tainted_isPhysical)
|
|
{
|
|
// Create avatar capsule and related ODE data
|
|
if (!(Shell == null && Body == null))
|
|
{
|
|
m_log.Warn("[PHYSICS]: re-creating the following avatar ODE data, even though it already exists - "
|
|
+ (Shell != null ? "Shell " : "")
|
|
+ (Body != null ? "Body " : ""));
|
|
}
|
|
AvatarGeomAndBodyCreation(m_position.X, m_position.Y, m_position.Z);
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
// destroy avatar capsule and related ODE data
|
|
|
|
Dispose();
|
|
tempVector1 = new btVector3(0, 0, 0);
|
|
tempVector2 = new btVector3(0, 0, 0);
|
|
tempVector3 = new btVector3(0, 0, 0);
|
|
tempVector4 = new btVector3(0, 0, 0);
|
|
|
|
tempVector5RayCast = new btVector3(0, 0, 0);
|
|
tempVector6RayCast = new btVector3(0, 0, 0);
|
|
tempVector7RayCast = new btVector3(0, 0, 0);
|
|
|
|
tempQuat1 = new btQuaternion(0, 0, 0, 1);
|
|
tempTrans1 = new btTransform(tempQuat1, tempVector1);
|
|
m_movementComparision = new PhysicsVector(0, 0, 0);
|
|
m_CapsuleOrientationAxis = new btVector3(1, 0, 1);
|
|
}
|
|
|
|
m_isPhysical = m_tainted_isPhysical;
|
|
}
|
|
|
|
if (m_tainted_CAPSULE_LENGTH != CAPSULE_LENGTH)
|
|
{
|
|
if (Body != null)
|
|
{
|
|
|
|
m_pidControllerActive = true;
|
|
// no lock needed on _parent_scene.OdeLock because we are called from within the thread lock in OdePlugin's simulate()
|
|
//d.JointDestroy(Amotor);
|
|
float prevCapsule = CAPSULE_LENGTH;
|
|
CAPSULE_LENGTH = m_tainted_CAPSULE_LENGTH;
|
|
//m_log.Info("[SIZE]: " + CAPSULE_LENGTH.ToString());
|
|
Dispose();
|
|
|
|
tempVector1 = new btVector3(0, 0, 0);
|
|
tempVector2 = new btVector3(0, 0, 0);
|
|
tempVector3 = new btVector3(0, 0, 0);
|
|
tempVector4 = new btVector3(0, 0, 0);
|
|
|
|
tempVector5RayCast = new btVector3(0, 0, 0);
|
|
tempVector6RayCast = new btVector3(0, 0, 0);
|
|
tempVector7RayCast = new btVector3(0, 0, 0);
|
|
|
|
tempQuat1 = new btQuaternion(0, 0, 0, 1);
|
|
tempTrans1 = new btTransform(tempQuat1, tempVector1);
|
|
m_movementComparision = new PhysicsVector(0, 0, 0);
|
|
m_CapsuleOrientationAxis = new btVector3(1, 0, 1);
|
|
|
|
AvatarGeomAndBodyCreation(m_position.X, m_position.Y,
|
|
m_position.Z + (Math.Abs(CAPSULE_LENGTH - prevCapsule) * 2));
|
|
Velocity = new PhysicsVector(0f, 0f, 0f);
|
|
|
|
}
|
|
else
|
|
{
|
|
m_log.Warn("[PHYSICS]: trying to change capsule size, but the following ODE data is missing - "
|
|
+ (Shell == null ? "Shell " : "")
|
|
+ (Body == null ? "Body " : ""));
|
|
}
|
|
}
|
|
if (m_taintRemove)
|
|
{
|
|
Dispose();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called from Simulate
|
|
/// This is the avatar's movement control + PID Controller
|
|
/// </summary>
|
|
/// <param name="timeStep"></param>
|
|
public void Move(float timeStep)
|
|
{
|
|
// no lock; for now it's only called from within Simulate()
|
|
|
|
// If the PID Controller isn't active then we set our force
|
|
// calculating base velocity to the current position
|
|
if (Body == null)
|
|
return;
|
|
tempTrans1.Dispose();
|
|
tempTrans1 = Body.getInterpolationWorldTransform();
|
|
tempVector1.Dispose();
|
|
tempVector1 = tempTrans1.getOrigin();
|
|
tempVector2.Dispose();
|
|
tempVector2 = Body.getInterpolationLinearVelocity();
|
|
|
|
if (m_pidControllerActive == false)
|
|
{
|
|
m_zeroPosition.X = tempVector1.getX();
|
|
m_zeroPosition.Y = tempVector1.getY();
|
|
m_zeroPosition.Z = tempVector1.getZ();
|
|
}
|
|
//PidStatus = true;
|
|
|
|
PhysicsVector vec = new PhysicsVector();
|
|
|
|
PhysicsVector vel = new PhysicsVector(tempVector2.getX(), tempVector2.getY(), tempVector2.getZ());
|
|
|
|
float movementdivisor = 1f;
|
|
|
|
if (!m_alwaysRun)
|
|
{
|
|
movementdivisor = walkDivisor;
|
|
}
|
|
else
|
|
{
|
|
movementdivisor = runDivisor;
|
|
}
|
|
|
|
// if velocity is zero, use position control; otherwise, velocity control
|
|
if (m_target_velocity.X == 0.0f && m_target_velocity.Y == 0.0f && m_target_velocity.Z == 0.0f && m_iscolliding)
|
|
{
|
|
// keep track of where we stopped. No more slippin' & slidin'
|
|
if (!m_zeroFlag)
|
|
{
|
|
m_zeroFlag = true;
|
|
m_zeroPosition.X = tempVector1.getX();
|
|
m_zeroPosition.Y = tempVector1.getY();
|
|
m_zeroPosition.Z = tempVector1.getZ();
|
|
}
|
|
if (m_pidControllerActive)
|
|
{
|
|
// We only want to deactivate the PID Controller if we think we want to have our surrogate
|
|
// react to the physics scene by moving it's position.
|
|
// Avatar to Avatar collisions
|
|
// Prim to avatar collisions
|
|
|
|
PhysicsVector pos = new PhysicsVector(tempVector1.getX(), tempVector1.getY(), tempVector1.getZ());
|
|
vec.X = (m_target_velocity.X - vel.X) * (PID_D) + (m_zeroPosition.X - pos.X) * (PID_P * 2);
|
|
vec.Y = (m_target_velocity.Y - vel.Y) * (PID_D) + (m_zeroPosition.Y - pos.Y) * (PID_P * 2);
|
|
if (m_flying)
|
|
{
|
|
vec.Z = (m_target_velocity.Z - vel.Z) * (PID_D) + (m_zeroPosition.Z - pos.Z) * PID_P;
|
|
}
|
|
}
|
|
//PidStatus = true;
|
|
}
|
|
else
|
|
{
|
|
m_pidControllerActive = true;
|
|
m_zeroFlag = false;
|
|
if (m_iscolliding && !m_flying)
|
|
{
|
|
// We're standing on something
|
|
vec.X = ((m_target_velocity.X / movementdivisor) - vel.X) * (PID_D);
|
|
vec.Y = ((m_target_velocity.Y / movementdivisor) - vel.Y) * (PID_D);
|
|
}
|
|
else if (m_iscolliding && m_flying)
|
|
{
|
|
// We're flying and colliding with something
|
|
vec.X = ((m_target_velocity.X / movementdivisor) - vel.X) * (PID_D / 16);
|
|
vec.Y = ((m_target_velocity.Y / movementdivisor) - vel.Y) * (PID_D / 16);
|
|
}
|
|
else if (!m_iscolliding && m_flying)
|
|
{
|
|
// we're in mid air suspended
|
|
vec.X = ((m_target_velocity.X / movementdivisor) - vel.X) * (PID_D / 6);
|
|
vec.Y = ((m_target_velocity.Y / movementdivisor) - vel.Y) * (PID_D / 6);
|
|
}
|
|
|
|
if (m_iscolliding && !m_flying && m_target_velocity.Z > 0.0f)
|
|
{
|
|
// We're colliding with something and we're not flying but we're moving
|
|
// This means we're walking or running.
|
|
PhysicsVector pos = new PhysicsVector(tempVector1.getX(), tempVector1.getY(), tempVector1.getZ());
|
|
vec.Z = (m_target_velocity.Z - vel.Z) * PID_D + (m_zeroPosition.Z - pos.Z) * PID_P;
|
|
if (m_target_velocity.X > 0)
|
|
{
|
|
vec.X = ((m_target_velocity.X - vel.X) / 1.2f) * PID_D;
|
|
}
|
|
if (m_target_velocity.Y > 0)
|
|
{
|
|
vec.Y = ((m_target_velocity.Y - vel.Y) / 1.2f) * PID_D;
|
|
}
|
|
}
|
|
else if (!m_iscolliding && !m_flying)
|
|
{
|
|
// we're not colliding and we're not flying so that means we're falling!
|
|
// m_iscolliding includes collisions with the ground.
|
|
|
|
// d.Vector3 pos = d.BodyGetPosition(Body);
|
|
if (m_target_velocity.X > 0)
|
|
{
|
|
vec.X = ((m_target_velocity.X - vel.X) / 1.2f) * PID_D;
|
|
}
|
|
if (m_target_velocity.Y > 0)
|
|
{
|
|
vec.Y = ((m_target_velocity.Y - vel.Y) / 1.2f) * PID_D;
|
|
}
|
|
}
|
|
|
|
|
|
if (m_flying)
|
|
{
|
|
vec.Z = (m_target_velocity.Z - vel.Z) * (PID_D);
|
|
}
|
|
}
|
|
if (m_flying)
|
|
{
|
|
//vec.Z += ((-1 * m_parent_scene.gravityz) * m_mass);
|
|
}
|
|
if (Body != null && (((m_target_velocity.X > 0.2f || m_target_velocity.X < -0.2f) || (m_target_velocity.Y > 0.2f || m_target_velocity.Y < -0.2f))))
|
|
{
|
|
Body.setFriction(0.001f);
|
|
//m_log.DebugFormat("[PHYSICS]: Avatar force applied: {0}, Target:{1}", vec.ToString(), m_target_velocity.ToString());
|
|
}
|
|
|
|
if (Body != null)
|
|
{
|
|
int activationstate = Body.getActivationState();
|
|
if (activationstate == 0)
|
|
{
|
|
Body.forceActivationState(1);
|
|
}
|
|
|
|
|
|
}
|
|
doImpulse(vec, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the reported position and velocity. This essentially sends the data up to ScenePresence.
|
|
/// </summary>
|
|
public void UpdatePositionAndVelocity()
|
|
{
|
|
if (Body == null)
|
|
return;
|
|
//int val = Environment.TickCount;
|
|
CheckIfStandingOnObject();
|
|
//m_log.DebugFormat("time:{0}", Environment.TickCount - val);
|
|
|
|
//IsColliding = Body.checkCollideWith(m_parent_scene.TerrainBody);
|
|
|
|
tempTrans1.Dispose();
|
|
tempTrans1 = Body.getInterpolationWorldTransform();
|
|
tempVector1.Dispose();
|
|
tempVector1 = tempTrans1.getOrigin();
|
|
tempVector2.Dispose();
|
|
tempVector2 = Body.getInterpolationLinearVelocity();
|
|
|
|
// no lock; called from Simulate() -- if you call this from elsewhere, gotta lock or do Monitor.Enter/Exit!
|
|
PhysicsVector vec = new PhysicsVector(tempVector1.getX(),tempVector1.getY(),tempVector1.getZ());
|
|
|
|
// kluge to keep things in bounds. ODE lets dead avatars drift away (they should be removed!)
|
|
if (vec.X < 0.0f) vec.X = 0.0f;
|
|
if (vec.Y < 0.0f) vec.Y = 0.0f;
|
|
if (vec.X > (int)Constants.RegionSize - 0.2f) vec.X = (int)Constants.RegionSize - 0.2f;
|
|
if (vec.Y > (int)Constants.RegionSize - 0.2f) vec.Y = (int)Constants.RegionSize - 0.2f;
|
|
|
|
m_position.X = vec.X;
|
|
m_position.Y = vec.Y;
|
|
m_position.Z = vec.Z;
|
|
|
|
// Did we move last? = zeroflag
|
|
// This helps keep us from sliding all over
|
|
|
|
if (m_zeroFlag)
|
|
{
|
|
m_velocity.X = 0.0f;
|
|
m_velocity.Y = 0.0f;
|
|
m_velocity.Z = 0.0f;
|
|
|
|
// Did we send out the 'stopped' message?
|
|
if (!m_lastUpdateSent)
|
|
{
|
|
m_lastUpdateSent = true;
|
|
//base.RequestPhysicsterseUpdate();
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_lastUpdateSent = false;
|
|
vec = new PhysicsVector(tempVector2.getX(), tempVector2.getY(), tempVector2.getZ());
|
|
m_velocity.X = (vec.X);
|
|
m_velocity.Y = (vec.Y);
|
|
|
|
m_velocity.Z = (vec.Z);
|
|
|
|
if (m_velocity.Z < -6 && !m_hackSentFall)
|
|
{
|
|
m_hackSentFall = true;
|
|
m_pidControllerActive = false;
|
|
}
|
|
else if (m_flying && !m_hackSentFly)
|
|
{
|
|
//m_hackSentFly = true;
|
|
//base.SendCollisionUpdate(new CollisionEventUpdate());
|
|
}
|
|
else
|
|
{
|
|
m_hackSentFly = false;
|
|
m_hackSentFall = false;
|
|
}
|
|
}
|
|
if (Body != null)
|
|
{
|
|
if (Body.getFriction() < 0.9f)
|
|
Body.setFriction(0.9f);
|
|
}
|
|
//if (Body != null)
|
|
// Body.clearForces();
|
|
}
|
|
|
|
public void CheckIfStandingOnObject()
|
|
{
|
|
|
|
float capsuleHalfHeight = ((CAPSULE_LENGTH + 2*CAPSULE_RADIUS)*0.5f);
|
|
|
|
tempVector5RayCast.setValue(m_position.X, m_position.Y, m_position.Z);
|
|
tempVector6RayCast.setValue(m_position.X, m_position.Y, m_position.Z - 1 * capsuleHalfHeight * 1.1f);
|
|
|
|
|
|
ClosestCastResult.Dispose();
|
|
ClosestCastResult = new ClosestNotMeRayResultCallback(Body);
|
|
|
|
try
|
|
{
|
|
m_parent_scene.getBulletWorld().rayTest(tempVector5RayCast, tempVector6RayCast, ClosestCastResult);
|
|
}
|
|
catch (AccessViolationException)
|
|
{
|
|
m_log.Debug("BAD!");
|
|
}
|
|
if (ClosestCastResult.hasHit())
|
|
{
|
|
|
|
if (tempVector7RayCast != null)
|
|
tempVector7RayCast.Dispose();
|
|
|
|
//tempVector7RayCast = ClosestCastResult.getHitPointWorld();
|
|
|
|
/*if (tempVector7RayCast == null) // null == no result also
|
|
{
|
|
CollidingObj = false;
|
|
IsColliding = false;
|
|
CollidingGround = false;
|
|
|
|
return;
|
|
}
|
|
float zVal = tempVector7RayCast.getZ();
|
|
if (zVal != 0)
|
|
m_log.Debug("[PHYSICS]: HAAAA");
|
|
if (zVal < m_position.Z && zVal > ((CAPSULE_LENGTH + 2 * CAPSULE_RADIUS) *0.5f))
|
|
{
|
|
CollidingObj = true;
|
|
IsColliding = true;
|
|
}
|
|
else
|
|
{
|
|
CollidingObj = false;
|
|
IsColliding = false;
|
|
CollidingGround = false;
|
|
}*/
|
|
|
|
//height+2*radius = capsule full length
|
|
//CollidingObj = true;
|
|
//IsColliding = true;
|
|
m_iscolliding = true;
|
|
}
|
|
else
|
|
{
|
|
//CollidingObj = false;
|
|
//IsColliding = false;
|
|
//CollidingGround = false;
|
|
m_iscolliding = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|