Merge branch 'master' into careminster
commit
258588d5b5
|
@ -0,0 +1,814 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Reflection;
|
||||
using log4net;
|
||||
using OMV = OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public sealed class BSCharacter : BSPhysObject
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private static readonly string LogHeader = "[BULLETS CHAR]";
|
||||
|
||||
// private bool _stopped;
|
||||
private OMV.Vector3 _size;
|
||||
private bool _grabbed;
|
||||
private bool _selected;
|
||||
private OMV.Vector3 _position;
|
||||
private float _mass;
|
||||
private float _avatarDensity;
|
||||
private float _avatarVolume;
|
||||
private OMV.Vector3 _force;
|
||||
private OMV.Vector3 _velocity;
|
||||
private OMV.Vector3 _torque;
|
||||
private float _collisionScore;
|
||||
private OMV.Vector3 _acceleration;
|
||||
private OMV.Quaternion _orientation;
|
||||
private int _physicsActorType;
|
||||
private bool _isPhysical;
|
||||
private bool _flying;
|
||||
private bool _setAlwaysRun;
|
||||
private bool _throttleUpdates;
|
||||
private bool _isColliding;
|
||||
private bool _collidingObj;
|
||||
private bool _floatOnWater;
|
||||
private OMV.Vector3 _rotationalVelocity;
|
||||
private bool _kinematic;
|
||||
private float _buoyancy;
|
||||
|
||||
// The friction and velocity of the avatar is modified depending on whether walking or not.
|
||||
private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar
|
||||
private float _currentFriction; // the friction currently being used (changed by setVelocity).
|
||||
|
||||
private BSVMotor _velocityMotor;
|
||||
|
||||
private OMV.Vector3 _PIDTarget;
|
||||
private bool _usePID;
|
||||
private float _PIDTau;
|
||||
private bool _useHoverPID;
|
||||
private float _PIDHoverHeight;
|
||||
private PIDHoverType _PIDHoverType;
|
||||
private float _PIDHoverTao;
|
||||
|
||||
public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying)
|
||||
: base(parent_scene, localID, avName, "BSCharacter")
|
||||
{
|
||||
_physicsActorType = (int)ActorTypes.Agent;
|
||||
_position = pos;
|
||||
|
||||
// Old versions of ScenePresence passed only the height. If width and/or depth are zero,
|
||||
// replace with the default values.
|
||||
_size = size;
|
||||
if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
|
||||
if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
|
||||
|
||||
// A motor to control the acceleration and deceleration of the avatar movement.
|
||||
// _velocityMotor = new BSVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
|
||||
// _velocityMotor = new BSPIDVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
|
||||
// Infinite decay and timescale values so motor only changes current to target values.
|
||||
_velocityMotor = new BSVMotor("BSCharacter.Velocity",
|
||||
0.2f, // time scale
|
||||
BSMotor.Infinite, // decay time scale
|
||||
BSMotor.InfiniteVector, // friction timescale
|
||||
1f // efficiency
|
||||
);
|
||||
_velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages.
|
||||
|
||||
_flying = isFlying;
|
||||
_orientation = OMV.Quaternion.Identity;
|
||||
_velocity = OMV.Vector3.Zero;
|
||||
_appliedVelocity = OMV.Vector3.Zero;
|
||||
_buoyancy = ComputeBuoyancyFromFlying(isFlying);
|
||||
_currentFriction = BSParam.AvatarStandingFriction;
|
||||
_avatarDensity = BSParam.AvatarDensity;
|
||||
|
||||
// The dimensions of the avatar capsule are kept in the scale.
|
||||
// Physics creates a unit capsule which is scaled by the physics engine.
|
||||
ComputeAvatarScale(_size);
|
||||
// set _avatarVolume and _mass based on capsule size, _density and Scale
|
||||
ComputeAvatarVolumeAndMass();
|
||||
DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
|
||||
LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
|
||||
|
||||
// do actual creation in taint time
|
||||
PhysicsScene.TaintedObject("BSCharacter.create", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.create,taint", LocalID);
|
||||
// New body and shape into PhysBody and PhysShape
|
||||
PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this);
|
||||
|
||||
SetPhysicalProperties();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// called when this character is being destroyed and the resources should be released
|
||||
public override void Destroy()
|
||||
{
|
||||
base.Destroy();
|
||||
|
||||
DetailLog("{0},BSCharacter.Destroy", LocalID);
|
||||
PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
|
||||
{
|
||||
PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
|
||||
PhysBody.Clear();
|
||||
PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
|
||||
PhysShape.Clear();
|
||||
});
|
||||
}
|
||||
|
||||
private void SetPhysicalProperties()
|
||||
{
|
||||
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
|
||||
|
||||
ZeroMotion(true);
|
||||
ForcePosition = _position;
|
||||
// Set the velocity and compute the proper friction
|
||||
ForceVelocity = _velocity;
|
||||
// Setting the current and target in the motor will cause it to start computing any deceleration.
|
||||
_velocityMotor.Reset();
|
||||
_velocityMotor.SetCurrent(_velocity);
|
||||
_velocityMotor.SetTarget(_velocity);
|
||||
_velocityMotor.Enabled = false;
|
||||
|
||||
// This will enable or disable the flying buoyancy of the avatar.
|
||||
// Needs to be reset especially when an avatar is recreated after crossing a region boundry.
|
||||
Flying = _flying;
|
||||
|
||||
BulletSimAPI.SetRestitution2(PhysBody.ptr, BSParam.AvatarRestitution);
|
||||
BulletSimAPI.SetMargin2(PhysShape.ptr, PhysicsScene.Params.collisionMargin);
|
||||
BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
|
||||
BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold);
|
||||
if (BSParam.CcdMotionThreshold > 0f)
|
||||
{
|
||||
BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold);
|
||||
BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
|
||||
}
|
||||
|
||||
UpdatePhysicalMassProperties(RawMass);
|
||||
|
||||
// Make so capsule does not fall over
|
||||
BulletSimAPI.SetAngularFactorV2(PhysBody.ptr, OMV.Vector3.Zero);
|
||||
|
||||
BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT);
|
||||
|
||||
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr, _position, _orientation);
|
||||
|
||||
// BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
|
||||
BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_DEACTIVATION);
|
||||
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
|
||||
|
||||
// Do this after the object has been added to the world
|
||||
PhysBody.collisionType = CollisionType.Avatar;
|
||||
PhysBody.ApplyCollisionMask();
|
||||
}
|
||||
|
||||
public override void RequestPhysicsterseUpdate()
|
||||
{
|
||||
base.RequestPhysicsterseUpdate();
|
||||
}
|
||||
// No one calls this method so I don't know what it could possibly mean
|
||||
public override bool Stopped { get { return false; } }
|
||||
|
||||
public override OMV.Vector3 Size {
|
||||
get
|
||||
{
|
||||
// Avatar capsule size is kept in the scale parameter.
|
||||
return _size;
|
||||
}
|
||||
|
||||
set {
|
||||
// When an avatar's size is set, only the height is changed.
|
||||
_size = value;
|
||||
// Old versions of ScenePresence passed only the height. If width and/or depth are zero,
|
||||
// replace with the default values.
|
||||
if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
|
||||
if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
|
||||
|
||||
ComputeAvatarScale(_size);
|
||||
ComputeAvatarVolumeAndMass();
|
||||
DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
|
||||
LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
|
||||
|
||||
PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape)
|
||||
{
|
||||
BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
|
||||
UpdatePhysicalMassProperties(RawMass);
|
||||
// Make sure this change appears as a property update event
|
||||
BulletSimAPI.PushUpdate2(PhysBody.ptr);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override PrimitiveBaseShape Shape
|
||||
{
|
||||
set { BaseShape = value; }
|
||||
}
|
||||
// I want the physics engine to make an avatar capsule
|
||||
public override BSPhysicsShapeType PreferredPhysicalShape
|
||||
{
|
||||
get {return BSPhysicsShapeType.SHAPE_CAPSULE; }
|
||||
}
|
||||
|
||||
public override bool Grabbed {
|
||||
set { _grabbed = value; }
|
||||
}
|
||||
public override bool Selected {
|
||||
set { _selected = value; }
|
||||
}
|
||||
public override void CrossingFailure() { return; }
|
||||
public override void link(PhysicsActor obj) { return; }
|
||||
public override void delink() { return; }
|
||||
|
||||
// Set motion values to zero.
|
||||
// Do it to the properties so the values get set in the physics engine.
|
||||
// Push the setting of the values to the viewer.
|
||||
// Called at taint time!
|
||||
public override void ZeroMotion(bool inTaintTime)
|
||||
{
|
||||
_velocity = OMV.Vector3.Zero;
|
||||
_velocityMotor.Zero();
|
||||
_acceleration = OMV.Vector3.Zero;
|
||||
_rotationalVelocity = OMV.Vector3.Zero;
|
||||
|
||||
// Zero some other properties directly into the physics engine
|
||||
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.ClearAllForces2(PhysBody.ptr);
|
||||
});
|
||||
}
|
||||
public override void ZeroAngularMotion(bool inTaintTime)
|
||||
{
|
||||
_rotationalVelocity = OMV.Vector3.Zero;
|
||||
|
||||
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
{
|
||||
BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
|
||||
BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
|
||||
// The next also get rid of applied linear force but the linear velocity is untouched.
|
||||
BulletSimAPI.ClearForces2(PhysBody.ptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public override void LockAngularMotion(OMV.Vector3 axis) { return; }
|
||||
|
||||
public override OMV.Vector3 RawPosition
|
||||
{
|
||||
get { return _position; }
|
||||
set { _position = value; }
|
||||
}
|
||||
public override OMV.Vector3 Position {
|
||||
get {
|
||||
// Don't refetch the position because this function is called a zillion times
|
||||
// _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID);
|
||||
return _position;
|
||||
}
|
||||
set {
|
||||
_position = value;
|
||||
PositionSanityCheck();
|
||||
|
||||
PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||
});
|
||||
}
|
||||
}
|
||||
public override OMV.Vector3 ForcePosition {
|
||||
get {
|
||||
_position = BulletSimAPI.GetPosition2(PhysBody.ptr);
|
||||
return _position;
|
||||
}
|
||||
set {
|
||||
_position = value;
|
||||
PositionSanityCheck();
|
||||
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check that the current position is sane and, if not, modify the position to make it so.
|
||||
// Check for being below terrain or on water.
|
||||
// Returns 'true' of the position was made sane by some action.
|
||||
private bool PositionSanityCheck()
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
// TODO: check for out of bounds
|
||||
if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
|
||||
{
|
||||
// The character is out of the known/simulated area.
|
||||
// Upper levels of code will handle the transition to other areas so, for
|
||||
// the time, we just ignore the position.
|
||||
return ret;
|
||||
}
|
||||
|
||||
// If below the ground, move the avatar up
|
||||
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
|
||||
if (Position.Z < terrainHeight)
|
||||
{
|
||||
DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
|
||||
_position.Z = terrainHeight + 2.0f;
|
||||
ret = true;
|
||||
}
|
||||
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
|
||||
{
|
||||
float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
|
||||
if (Position.Z < waterHeight)
|
||||
{
|
||||
_position.Z = waterHeight;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// A version of the sanity check that also makes sure a new position value is
|
||||
// pushed back to the physics engine. This routine would be used by anyone
|
||||
// who is not already pushing the value.
|
||||
private bool PositionSanityCheck(bool inTaintTime)
|
||||
{
|
||||
bool ret = false;
|
||||
if (PositionSanityCheck())
|
||||
{
|
||||
// The new position value must be pushed into the physics engine but we can't
|
||||
// just assign to "Position" because of potential call loops.
|
||||
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||
});
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override float Mass { get { return _mass; } }
|
||||
|
||||
// used when we only want this prim's mass and not the linkset thing
|
||||
public override float RawMass {
|
||||
get {return _mass; }
|
||||
}
|
||||
public override void UpdatePhysicalMassProperties(float physMass)
|
||||
{
|
||||
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
|
||||
BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, localInertia);
|
||||
}
|
||||
|
||||
public override OMV.Vector3 Force {
|
||||
get { return _force; }
|
||||
set {
|
||||
_force = value;
|
||||
// m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force);
|
||||
PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public bool TouchingGround()
|
||||
{
|
||||
bool ret = BulletSimAPI.RayCastGround(PhysicsScene.World.ptr,_position,_size.Z * 0.55f, PhysBody.ptr);
|
||||
return ret;
|
||||
}
|
||||
// Avatars don't do vehicles
|
||||
public override int VehicleType { get { return (int)Vehicle.TYPE_NONE; } set { return; } }
|
||||
public override void VehicleFloatParam(int param, float value) { }
|
||||
public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
|
||||
public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
|
||||
public override void VehicleFlags(int param, bool remove) { }
|
||||
|
||||
// Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
|
||||
public override void SetVolumeDetect(int param) { return; }
|
||||
|
||||
public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
|
||||
public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
|
||||
|
||||
// Sets the target in the motor. This starts the changing of the avatar's velocity.
|
||||
public override OMV.Vector3 TargetVelocity
|
||||
{
|
||||
get
|
||||
{
|
||||
return _velocityMotor.TargetValue;
|
||||
}
|
||||
set
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value);
|
||||
|
||||
if (!_flying)
|
||||
if ((value.Z >= 0.0001f) || (value.Z <= -0.0001f) || _velocity.Z < -0.0001f)
|
||||
if (!TouchingGround())
|
||||
value.Z = _velocity.Z;
|
||||
if (_setAlwaysRun)
|
||||
value *= 1.3f;
|
||||
|
||||
OMV.Vector3 targetVel = value;
|
||||
|
||||
PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate()
|
||||
{
|
||||
|
||||
_velocityMotor.Reset();
|
||||
_velocityMotor.SetTarget(targetVel);
|
||||
_velocityMotor.SetCurrent(_velocity);
|
||||
_velocityMotor.Enabled = true;
|
||||
|
||||
// Make sure a property update happens next step so the motor gets incorporated.
|
||||
BulletSimAPI.PushUpdate2(PhysBody.ptr);
|
||||
});
|
||||
}
|
||||
}
|
||||
// Directly setting velocity means this is what the user really wants now.
|
||||
public override OMV.Vector3 Velocity {
|
||||
get { return _velocity; }
|
||||
set {
|
||||
_velocity = value;
|
||||
// m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity);
|
||||
PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
|
||||
{
|
||||
_velocityMotor.Reset();
|
||||
_velocityMotor.SetCurrent(_velocity);
|
||||
_velocityMotor.SetTarget(_velocity);
|
||||
// Even though the motor is initialized, it's not used and the velocity goes straight into the avatar.
|
||||
_velocityMotor.Enabled = false;
|
||||
|
||||
DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity);
|
||||
ForceVelocity = _velocity;
|
||||
});
|
||||
}
|
||||
}
|
||||
public override OMV.Vector3 ForceVelocity {
|
||||
get { return _velocity; }
|
||||
set {
|
||||
PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity");
|
||||
|
||||
_velocity = value;
|
||||
// Depending on whether the avatar is moving or not, change the friction
|
||||
// to keep the avatar from slipping around
|
||||
if (_velocity.Length() == 0)
|
||||
{
|
||||
if (_currentFriction != BSParam.AvatarStandingFriction)
|
||||
{
|
||||
_currentFriction = BSParam.AvatarStandingFriction;
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentFriction != BSParam.AvatarFriction)
|
||||
{
|
||||
_currentFriction = BSParam.AvatarFriction;
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
|
||||
}
|
||||
}
|
||||
// Remember the set velocity so we can suppress the reduction by friction, ...
|
||||
_appliedVelocity = value;
|
||||
|
||||
BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
|
||||
BulletSimAPI.Activate2(PhysBody.ptr, true);
|
||||
}
|
||||
}
|
||||
public override OMV.Vector3 Torque {
|
||||
get { return _torque; }
|
||||
set { _torque = value;
|
||||
}
|
||||
}
|
||||
public override float CollisionScore {
|
||||
get { return _collisionScore; }
|
||||
set { _collisionScore = value;
|
||||
}
|
||||
}
|
||||
public override OMV.Vector3 Acceleration {
|
||||
get { return _acceleration; }
|
||||
set { _acceleration = value; }
|
||||
}
|
||||
public override OMV.Quaternion RawOrientation
|
||||
{
|
||||
get { return _orientation; }
|
||||
set { _orientation = value; }
|
||||
}
|
||||
public override OMV.Quaternion Orientation {
|
||||
get { return _orientation; }
|
||||
set {
|
||||
_orientation = value;
|
||||
// m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
|
||||
PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
{
|
||||
// _position = BulletSimAPI.GetPosition2(BSBody.ptr);
|
||||
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// Go directly to Bullet to get/set the value.
|
||||
public override OMV.Quaternion ForceOrientation
|
||||
{
|
||||
get
|
||||
{
|
||||
_orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
|
||||
return _orientation;
|
||||
}
|
||||
set
|
||||
{
|
||||
_orientation = value;
|
||||
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||
}
|
||||
}
|
||||
public override int PhysicsActorType {
|
||||
get { return _physicsActorType; }
|
||||
set { _physicsActorType = value;
|
||||
}
|
||||
}
|
||||
public override bool IsPhysical {
|
||||
get { return _isPhysical; }
|
||||
set { _isPhysical = value;
|
||||
}
|
||||
}
|
||||
public override bool IsSolid {
|
||||
get { return true; }
|
||||
}
|
||||
public override bool IsStatic {
|
||||
get { return false; }
|
||||
}
|
||||
public override bool Flying {
|
||||
get { return _flying; }
|
||||
set {
|
||||
_flying = value;
|
||||
|
||||
// simulate flying by changing the effect of gravity
|
||||
Buoyancy = ComputeBuoyancyFromFlying(_flying);
|
||||
}
|
||||
}
|
||||
// Flying is implimented by changing the avatar's buoyancy.
|
||||
// Would this be done better with a vehicle type?
|
||||
private float ComputeBuoyancyFromFlying(bool ifFlying) {
|
||||
return ifFlying ? 1f : 0f;
|
||||
}
|
||||
public override bool
|
||||
SetAlwaysRun {
|
||||
get { return _setAlwaysRun; }
|
||||
set { _setAlwaysRun = value; }
|
||||
}
|
||||
public override bool ThrottleUpdates {
|
||||
get { return _throttleUpdates; }
|
||||
set { _throttleUpdates = value; }
|
||||
}
|
||||
public override bool IsColliding {
|
||||
get { return (CollidingStep == PhysicsScene.SimulationStep); }
|
||||
set { _isColliding = value; }
|
||||
}
|
||||
public override bool CollidingGround {
|
||||
get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
|
||||
set { CollidingGround = value; }
|
||||
}
|
||||
public override bool CollidingObj {
|
||||
get { return _collidingObj; }
|
||||
set { _collidingObj = value; }
|
||||
}
|
||||
public override bool FloatOnWater {
|
||||
set {
|
||||
_floatOnWater = value;
|
||||
PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
{
|
||||
if (_floatOnWater)
|
||||
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
|
||||
else
|
||||
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
public override OMV.Vector3 RotationalVelocity {
|
||||
get { return _rotationalVelocity; }
|
||||
set { _rotationalVelocity = value; }
|
||||
}
|
||||
public override OMV.Vector3 ForceRotationalVelocity {
|
||||
get { return _rotationalVelocity; }
|
||||
set { _rotationalVelocity = value; }
|
||||
}
|
||||
public override bool Kinematic {
|
||||
get { return _kinematic; }
|
||||
set { _kinematic = value; }
|
||||
}
|
||||
// neg=fall quickly, 0=1g, 1=0g, pos=float up
|
||||
public override float Buoyancy {
|
||||
get { return _buoyancy; }
|
||||
set { _buoyancy = value;
|
||||
PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
|
||||
ForceBuoyancy = _buoyancy;
|
||||
});
|
||||
}
|
||||
}
|
||||
public override float ForceBuoyancy {
|
||||
get { return _buoyancy; }
|
||||
set {
|
||||
PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy");
|
||||
|
||||
_buoyancy = value;
|
||||
DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
|
||||
// Buoyancy is faked by changing the gravity applied to the object
|
||||
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
|
||||
}
|
||||
}
|
||||
|
||||
// Used for MoveTo
|
||||
public override OMV.Vector3 PIDTarget {
|
||||
set { _PIDTarget = value; }
|
||||
}
|
||||
public override bool PIDActive {
|
||||
set { _usePID = value; }
|
||||
}
|
||||
public override float PIDTau {
|
||||
set { _PIDTau = value; }
|
||||
}
|
||||
|
||||
// Used for llSetHoverHeight and maybe vehicle height
|
||||
// Hover Height will override MoveTo target's Z
|
||||
public override bool PIDHoverActive {
|
||||
set { _useHoverPID = value; }
|
||||
}
|
||||
public override float PIDHoverHeight {
|
||||
set { _PIDHoverHeight = value; }
|
||||
}
|
||||
public override PIDHoverType PIDHoverType {
|
||||
set { _PIDHoverType = value; }
|
||||
}
|
||||
public override float PIDHoverTau {
|
||||
set { _PIDHoverTao = value; }
|
||||
}
|
||||
|
||||
// For RotLookAt
|
||||
public override OMV.Quaternion APIDTarget { set { return; } }
|
||||
public override bool APIDActive { set { return; } }
|
||||
public override float APIDStrength { set { return; } }
|
||||
public override float APIDDamping { set { return; } }
|
||||
|
||||
public override void AddForce(OMV.Vector3 force, bool pushforce) {
|
||||
if (force.IsFinite())
|
||||
{
|
||||
_force.X += force.X;
|
||||
_force.Y += force.Y;
|
||||
_force.Z += force.Z;
|
||||
// m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force);
|
||||
PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
|
||||
{
|
||||
DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.ErrorFormat("{0}: Got a NaN force applied to a Character", LogHeader);
|
||||
}
|
||||
//m_lastUpdateSent = false;
|
||||
}
|
||||
|
||||
public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
|
||||
}
|
||||
public override void SetMomentum(OMV.Vector3 momentum) {
|
||||
}
|
||||
|
||||
private void ComputeAvatarScale(OMV.Vector3 size)
|
||||
{
|
||||
OMV.Vector3 newScale = size;
|
||||
// newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
|
||||
// newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
|
||||
|
||||
// From the total height, remove the capsule half spheres that are at each end
|
||||
// The 1.15f came from ODE. Not sure what this factors in.
|
||||
// newScale.Z = (size.Z * 1.15f) - (newScale.X + newScale.Y);
|
||||
|
||||
// The total scale height is the central cylindar plus the caps on the two ends.
|
||||
newScale.Z = size.Z + (Math.Min(size.X, size.Y) * 2f);
|
||||
|
||||
// Convert diameters to radii and height to half height -- the way Bullet expects it.
|
||||
Scale = newScale / 2f;
|
||||
}
|
||||
|
||||
// set _avatarVolume and _mass based on capsule size, _density and Scale
|
||||
private void ComputeAvatarVolumeAndMass()
|
||||
{
|
||||
_avatarVolume = (float)(
|
||||
Math.PI
|
||||
* Scale.X
|
||||
* Scale.Y // the area of capsule cylinder
|
||||
* Scale.Z // times height of capsule cylinder
|
||||
+ 1.33333333f
|
||||
* Math.PI
|
||||
* Scale.X
|
||||
* Math.Min(Scale.X, Scale.Y)
|
||||
* Scale.Y // plus the volume of the capsule end caps
|
||||
);
|
||||
_mass = _avatarDensity * _avatarVolume;
|
||||
}
|
||||
|
||||
// The physics engine says that properties have updated. Update same and inform
|
||||
// the world that things have changed.
|
||||
public override void UpdateProperties(EntityProperties entprop)
|
||||
{
|
||||
_position = entprop.Position;
|
||||
_orientation = entprop.Rotation;
|
||||
_velocity = entprop.Velocity;
|
||||
_acceleration = entprop.Acceleration;
|
||||
_rotationalVelocity = entprop.RotationalVelocity;
|
||||
|
||||
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
|
||||
PositionSanityCheck(true);
|
||||
|
||||
if (_velocityMotor.Enabled)
|
||||
{
|
||||
// TODO: Decide if the step parameters should be changed depending on the avatar's
|
||||
// state (flying, colliding, ...).
|
||||
|
||||
OMV.Vector3 stepVelocity = _velocityMotor.Step(PhysicsScene.LastTimeStep);
|
||||
|
||||
// If falling, we keep the world's downward vector no matter what the other axis specify.
|
||||
if (!Flying && !IsColliding)
|
||||
{
|
||||
stepVelocity.Z = entprop.Velocity.Z;
|
||||
DetailLog("{0},BSCharacter.UpdateProperties,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity);
|
||||
}
|
||||
|
||||
// If the user has said stop and we've stopped applying velocity correction,
|
||||
// the motor can be turned off. Set the velocity to zero so the zero motion is sent to the viewer.
|
||||
if (_velocityMotor.TargetValue.ApproxEquals(OMV.Vector3.Zero, 0.01f) && _velocityMotor.ErrorIsZero)
|
||||
{
|
||||
stepVelocity = OMV.Vector3.Zero;
|
||||
_velocityMotor.Enabled = false;
|
||||
DetailLog("{0},BSCharacter.UpdateProperties,taint,disableVelocityMotor,m={1}", LocalID, _velocityMotor);
|
||||
}
|
||||
|
||||
_velocity = stepVelocity;
|
||||
entprop.Velocity = _velocity;
|
||||
BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
|
||||
}
|
||||
|
||||
// remember the current and last set values
|
||||
LastEntityProperties = CurrentEntityProperties;
|
||||
CurrentEntityProperties = entprop;
|
||||
|
||||
// Tell the linkset about value changes
|
||||
Linkset.UpdateProperties(this, true);
|
||||
|
||||
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
|
||||
// base.RequestPhysicsterseUpdate();
|
||||
|
||||
DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
|
||||
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
public abstract class BSConstraint : IDisposable
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM CONSTRAINT]";
|
||||
|
||||
protected BulletSim m_world;
|
||||
protected BulletBody m_body1;
|
||||
protected BulletBody m_body2;
|
||||
protected BulletConstraint m_constraint;
|
||||
protected bool m_enabled = false;
|
||||
|
||||
public BulletBody Body1 { get { return m_body1; } }
|
||||
public BulletBody Body2 { get { return m_body2; } }
|
||||
public BulletConstraint Constraint { get { return m_constraint; } }
|
||||
public abstract ConstraintType Type { get; }
|
||||
public bool IsEnabled { get { return m_enabled; } }
|
||||
|
||||
public BSConstraint()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (m_enabled)
|
||||
{
|
||||
m_enabled = false;
|
||||
if (m_constraint.HasPhysicalConstraint)
|
||||
{
|
||||
bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
|
||||
m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}",
|
||||
BSScene.DetailLogZero,
|
||||
m_body1.ID, m_body1.ptr.ToString(),
|
||||
m_body2.ID, m_body2.ptr.ToString(),
|
||||
success);
|
||||
m_constraint.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool SetLinearLimits(Vector3 low, Vector3 high)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.SetLinearLimits2(m_constraint.ptr, low, high);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public virtual bool SetAngularLimits(Vector3 low, Vector3 high)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.SetAngularLimits2(m_constraint.ptr, low, high);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public virtual bool SetSolverIterations(float cnt)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.ptr, cnt);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public virtual bool CalculateTransforms()
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
// Recompute the internal transforms
|
||||
BulletSimAPI.CalculateTransforms2(m_constraint.ptr);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Reset this constraint making sure it has all its internal structures
|
||||
// recomputed and is enabled and ready to go.
|
||||
public virtual bool RecomputeConstraintVariables(float mass)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
ret = CalculateTransforms();
|
||||
if (ret)
|
||||
{
|
||||
// Setting an object's mass to zero (making it static like when it's selected)
|
||||
// automatically disables the constraints.
|
||||
// If the link is enabled, be sure to set the constraint itself to enabled.
|
||||
BulletSimAPI.SetConstraintEnable2(m_constraint.ptr, BSParam.NumericBool(true));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_world.physicsScene.Logger.ErrorFormat("{0} CalculateTransforms failed. A={1}, B={2}", LogHeader, Body1.ID, Body2.ID);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
public sealed class BSConstraint6Dof : BSConstraint
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]";
|
||||
|
||||
public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } }
|
||||
|
||||
// Create a btGeneric6DofConstraint
|
||||
public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 frame1, Quaternion frame1rot,
|
||||
Vector3 frame2, Quaternion frame2rot,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||
{
|
||||
m_world = world;
|
||||
m_body1 = obj1;
|
||||
m_body2 = obj2;
|
||||
m_constraint = new BulletConstraint(
|
||||
BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||
frame1, frame1rot,
|
||||
frame2, frame2rot,
|
||||
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||
m_enabled = true;
|
||||
world.physicsScene.DetailLog("{0},BS6DofConstraint,createFrame,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||
BSScene.DetailLogZero, world.worldID,
|
||||
obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||
}
|
||||
|
||||
public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 joinPoint,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||
{
|
||||
m_world = world;
|
||||
m_body1 = obj1;
|
||||
m_body2 = obj2;
|
||||
if (!obj1.HasPhysicalBody || !obj2.HasPhysicalBody)
|
||||
{
|
||||
world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||
BSScene.DetailLogZero, world.worldID,
|
||||
obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||
world.physicsScene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||
LogHeader, world.worldID, obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||
m_enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_constraint = new BulletConstraint(
|
||||
BulletSimAPI.Create6DofConstraintToPoint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||
joinPoint,
|
||||
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||
world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}",
|
||||
BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString(),
|
||||
obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||
if (!m_constraint.HasPhysicalConstraint)
|
||||
{
|
||||
world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}",
|
||||
LogHeader, obj1.ID, obj2.ID);
|
||||
m_enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
BulletSimAPI.SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool SetCFMAndERP(float cfm, float erp)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
{
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL);
|
||||
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool UseFrameOffset(bool useOffset)
|
||||
{
|
||||
bool ret = false;
|
||||
float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.UseFrameOffset2(m_constraint.ptr, onOff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool TranslationalLimitMotor(bool enable, float targetVelocity, float maxMotorForce)
|
||||
{
|
||||
bool ret = false;
|
||||
float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
|
||||
if (m_enabled)
|
||||
{
|
||||
ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce);
|
||||
m_world.physicsScene.DetailLog("{0},BS6DOFConstraint,TransLimitMotor,enable={1},vel={2},maxForce={3}",
|
||||
BSScene.DetailLogZero, enable, targetVelocity, maxMotorForce);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public bool SetBreakingImpulseThreshold(float threshold)
|
||||
{
|
||||
bool ret = false;
|
||||
if (m_enabled)
|
||||
ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.ptr, threshold);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
using log4net;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
public sealed class BSConstraintCollection : IDisposable
|
||||
{
|
||||
// private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
// private static readonly string LogHeader = "[CONSTRAINT COLLECTION]";
|
||||
|
||||
delegate bool ConstraintAction(BSConstraint constrain);
|
||||
|
||||
private List<BSConstraint> m_constraints;
|
||||
private BulletSim m_world;
|
||||
|
||||
public BSConstraintCollection(BulletSim world)
|
||||
{
|
||||
m_world = world;
|
||||
m_constraints = new List<BSConstraint>();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.Clear();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (m_constraints)
|
||||
{
|
||||
foreach (BSConstraint cons in m_constraints)
|
||||
{
|
||||
cons.Dispose();
|
||||
}
|
||||
m_constraints.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public bool AddConstraint(BSConstraint cons)
|
||||
{
|
||||
lock (m_constraints)
|
||||
{
|
||||
// There is only one constraint between any bodies. Remove any old just to make sure.
|
||||
RemoveAndDestroyConstraint(cons.Body1, cons.Body2);
|
||||
|
||||
m_constraints.Add(cons);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the constraint between two bodies. There can be only one.
|
||||
// Return 'true' if a constraint was found.
|
||||
public bool TryGetConstraint(BulletBody body1, BulletBody body2, out BSConstraint returnConstraint)
|
||||
{
|
||||
bool found = false;
|
||||
BSConstraint foundConstraint = null;
|
||||
|
||||
uint lookingID1 = body1.ID;
|
||||
uint lookingID2 = body2.ID;
|
||||
lock (m_constraints)
|
||||
{
|
||||
foreach (BSConstraint constrain in m_constraints)
|
||||
{
|
||||
if ((constrain.Body1.ID == lookingID1 && constrain.Body2.ID == lookingID2)
|
||||
|| (constrain.Body1.ID == lookingID2 && constrain.Body2.ID == lookingID1))
|
||||
{
|
||||
foundConstraint = constrain;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
returnConstraint = foundConstraint;
|
||||
return found;
|
||||
}
|
||||
|
||||
// Remove any constraint between the passed bodies.
|
||||
// Presumed there is only one such constraint possible.
|
||||
// Return 'true' if a constraint was found and destroyed.
|
||||
public bool RemoveAndDestroyConstraint(BulletBody body1, BulletBody body2)
|
||||
{
|
||||
bool ret = false;
|
||||
lock (m_constraints)
|
||||
{
|
||||
BSConstraint constrain;
|
||||
if (this.TryGetConstraint(body1, body2, out constrain))
|
||||
{
|
||||
// remove the constraint from our collection
|
||||
RemoveAndDestroyConstraint(constrain);
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The constraint MUST exist in the collection
|
||||
public bool RemoveAndDestroyConstraint(BSConstraint constrain)
|
||||
{
|
||||
lock (m_constraints)
|
||||
{
|
||||
// remove the constraint from our collection
|
||||
m_constraints.Remove(constrain);
|
||||
}
|
||||
// tell the engine that all its structures need to be freed
|
||||
constrain.Dispose();
|
||||
// we destroyed something
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove all constraints that reference the passed body.
|
||||
// Return 'true' if any constraints were destroyed.
|
||||
public bool RemoveAndDestroyConstraint(BulletBody body1)
|
||||
{
|
||||
List<BSConstraint> toRemove = new List<BSConstraint>();
|
||||
uint lookingID = body1.ID;
|
||||
lock (m_constraints)
|
||||
{
|
||||
foreach (BSConstraint constrain in m_constraints)
|
||||
{
|
||||
if (constrain.Body1.ID == lookingID || constrain.Body2.ID == lookingID)
|
||||
{
|
||||
toRemove.Add(constrain);
|
||||
}
|
||||
}
|
||||
foreach (BSConstraint constrain in toRemove)
|
||||
{
|
||||
m_constraints.Remove(constrain);
|
||||
constrain.Dispose();
|
||||
}
|
||||
}
|
||||
return (toRemove.Count > 0);
|
||||
}
|
||||
|
||||
public bool RecalculateAllConstraints()
|
||||
{
|
||||
bool ret = false;
|
||||
lock (m_constraints)
|
||||
{
|
||||
foreach (BSConstraint constrain in m_constraints)
|
||||
{
|
||||
constrain.CalculateTransforms();
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
public sealed class BSConstraintHinge : BSConstraint
|
||||
{
|
||||
public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } }
|
||||
|
||||
public BSConstraintHinge(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 pivotInA, Vector3 pivotInB,
|
||||
Vector3 axisInA, Vector3 axisInB,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||
{
|
||||
m_world = world;
|
||||
m_body1 = obj1;
|
||||
m_body2 = obj2;
|
||||
m_constraint = new BulletConstraint(
|
||||
BulletSimAPI.CreateHingeConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||
pivotInA, pivotInB,
|
||||
axisInA, axisInB,
|
||||
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||
m_enabled = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
|
||||
using OMV = OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
// A BSPrim can get individual information about its linkedness attached
|
||||
// to it through an instance of a subclass of LinksetInfo.
|
||||
// Each type of linkset will define the information needed for its type.
|
||||
public abstract class BSLinksetInfo
|
||||
{
|
||||
public virtual void Clear() { }
|
||||
}
|
||||
|
||||
public abstract class BSLinkset
|
||||
{
|
||||
// private static string LogHeader = "[BULLETSIM LINKSET]";
|
||||
|
||||
public enum LinksetImplementation
|
||||
{
|
||||
Constraint = 0, // linkset tied together with constraints
|
||||
Compound = 1, // linkset tied together as a compound object
|
||||
Manual = 2 // linkset tied together manually (code moves all the pieces)
|
||||
}
|
||||
// Create the correct type of linkset for this child
|
||||
public static BSLinkset Factory(BSScene physScene, BSPhysObject parent)
|
||||
{
|
||||
BSLinkset ret = null;
|
||||
|
||||
switch ((int)BSParam.LinksetImplementation)
|
||||
{
|
||||
case (int)LinksetImplementation.Constraint:
|
||||
ret = new BSLinksetConstraints(physScene, parent);
|
||||
break;
|
||||
case (int)LinksetImplementation.Compound:
|
||||
ret = new BSLinksetCompound(physScene, parent);
|
||||
break;
|
||||
case (int)LinksetImplementation.Manual:
|
||||
// ret = new BSLinksetManual(physScene, parent);
|
||||
break;
|
||||
default:
|
||||
ret = new BSLinksetCompound(physScene, parent);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public BSPhysObject LinksetRoot { get; protected set; }
|
||||
|
||||
public BSScene PhysicsScene { get; private set; }
|
||||
|
||||
static int m_nextLinksetID = 1;
|
||||
public int LinksetID { get; private set; }
|
||||
|
||||
// The children under the root in this linkset.
|
||||
protected HashSet<BSPhysObject> m_children;
|
||||
|
||||
// We lock the diddling of linkset classes to prevent any badness.
|
||||
// This locks the modification of the instances of this class. Changes
|
||||
// to the physical representation is done via the tainting mechenism.
|
||||
protected object m_linksetActivityLock = new Object();
|
||||
|
||||
// Some linksets have a preferred physical shape.
|
||||
// Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected.
|
||||
public virtual BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
|
||||
{
|
||||
return BSPhysicsShapeType.SHAPE_UNKNOWN;
|
||||
}
|
||||
|
||||
// We keep the prim's mass in the linkset structure since it could be dependent on other prims
|
||||
protected float m_mass;
|
||||
public float LinksetMass
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_mass;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool LinksetIsColliding { get { return false; } }
|
||||
|
||||
public OMV.Vector3 CenterOfMass
|
||||
{
|
||||
get { return ComputeLinksetCenterOfMass(); }
|
||||
}
|
||||
|
||||
public OMV.Vector3 GeometricCenter
|
||||
{
|
||||
get { return ComputeLinksetGeometricCenter(); }
|
||||
}
|
||||
|
||||
protected BSLinkset(BSScene scene, BSPhysObject parent)
|
||||
{
|
||||
// A simple linkset of one (no children)
|
||||
LinksetID = m_nextLinksetID++;
|
||||
// We create LOTS of linksets.
|
||||
if (m_nextLinksetID <= 0)
|
||||
m_nextLinksetID = 1;
|
||||
PhysicsScene = scene;
|
||||
LinksetRoot = parent;
|
||||
m_children = new HashSet<BSPhysObject>();
|
||||
m_mass = parent.RawMass;
|
||||
Rebuilding = false;
|
||||
}
|
||||
|
||||
// Link to a linkset where the child knows the parent.
|
||||
// Parent changing should not happen so do some sanity checking.
|
||||
// We return the parent's linkset so the child can track its membership.
|
||||
// Called at runtime.
|
||||
public BSLinkset AddMeToLinkset(BSPhysObject child)
|
||||
{
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
// Don't add the root to its own linkset
|
||||
if (!IsRoot(child))
|
||||
AddChildToLinkset(child);
|
||||
m_mass = ComputeLinksetMass();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// Remove a child from a linkset.
|
||||
// Returns a new linkset for the child which is a linkset of one (just the
|
||||
// orphened child).
|
||||
// Called at runtime.
|
||||
public BSLinkset RemoveMeFromLinkset(BSPhysObject child)
|
||||
{
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
if (IsRoot(child))
|
||||
{
|
||||
// Cannot remove the root from a linkset.
|
||||
return this;
|
||||
}
|
||||
RemoveChildFromLinkset(child);
|
||||
m_mass = ComputeLinksetMass();
|
||||
}
|
||||
|
||||
// The child is down to a linkset of just itself
|
||||
return BSLinkset.Factory(PhysicsScene, child);
|
||||
}
|
||||
|
||||
// Return 'true' if the passed object is the root object of this linkset
|
||||
public bool IsRoot(BSPhysObject requestor)
|
||||
{
|
||||
return (requestor.LocalID == LinksetRoot.LocalID);
|
||||
}
|
||||
|
||||
public int NumberOfChildren { get { return m_children.Count; } }
|
||||
|
||||
// Return 'true' if this linkset has any children (more than the root member)
|
||||
public bool HasAnyChildren { get { return (m_children.Count > 0); } }
|
||||
|
||||
// Return 'true' if this child is in this linkset
|
||||
public bool HasChild(BSPhysObject child)
|
||||
{
|
||||
bool ret = false;
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
ret = m_children.Contains(child);
|
||||
/* Safer version but the above should work
|
||||
foreach (BSPhysObject bp in m_children)
|
||||
{
|
||||
if (child.LocalID == bp.LocalID)
|
||||
{
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Perform an action on each member of the linkset including root prim.
|
||||
// Depends on the action on whether this should be done at taint time.
|
||||
public delegate bool ForEachMemberAction(BSPhysObject obj);
|
||||
public virtual bool ForEachMember(ForEachMemberAction action)
|
||||
{
|
||||
bool ret = false;
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
action(LinksetRoot);
|
||||
foreach (BSPhysObject po in m_children)
|
||||
{
|
||||
if (action(po))
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// I am the root of a linkset and a new child is being added
|
||||
// Called while LinkActivity is locked.
|
||||
protected abstract void AddChildToLinkset(BSPhysObject child);
|
||||
|
||||
// I am the root of a linkset and one of my children is being removed.
|
||||
// Safe to call even if the child is not really in my linkset.
|
||||
protected abstract void RemoveChildFromLinkset(BSPhysObject child);
|
||||
|
||||
// When physical properties are changed the linkset needs to recalculate
|
||||
// its internal properties.
|
||||
// May be called at runtime or taint-time.
|
||||
public abstract void Refresh(BSPhysObject requestor);
|
||||
|
||||
// Flag denoting the linkset is in the process of being rebuilt.
|
||||
// Used to know not the schedule a rebuild in the middle of a rebuild.
|
||||
protected bool Rebuilding { get; set; }
|
||||
|
||||
// The object is going dynamic (physical). Do any setup necessary
|
||||
// for a dynamic linkset.
|
||||
// Only the state of the passed object can be modified. The rest of the linkset
|
||||
// has not yet been fully constructed.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// Called at taint-time!
|
||||
public abstract bool MakeDynamic(BSPhysObject child);
|
||||
|
||||
// The object is going static (non-physical). Do any setup necessary
|
||||
// for a static linkset.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// Called at taint-time!
|
||||
public abstract bool MakeStatic(BSPhysObject child);
|
||||
|
||||
// Called when a parameter update comes from the physics engine for any object
|
||||
// of the linkset is received.
|
||||
// Passed flag is update came from physics engine (true) or the user (false).
|
||||
// Called at taint-time!!
|
||||
public abstract void UpdateProperties(BSPhysObject physObject, bool physicalUpdate);
|
||||
|
||||
// Routine used when rebuilding the body of the root of the linkset
|
||||
// Destroy all the constraints have have been made to root.
|
||||
// This is called when the root body is changing.
|
||||
// Returns 'true' of something was actually removed and would need restoring
|
||||
// Called at taint-time!!
|
||||
public abstract bool RemoveBodyDependencies(BSPrim child);
|
||||
|
||||
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||
// this routine will restore the removed constraints.
|
||||
// Called at taint-time!!
|
||||
public abstract void RestoreBodyDependencies(BSPrim child);
|
||||
|
||||
// ================================================================
|
||||
protected virtual float ComputeLinksetMass()
|
||||
{
|
||||
float mass = LinksetRoot.RawMass;
|
||||
if (HasAnyChildren)
|
||||
{
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
foreach (BSPhysObject bp in m_children)
|
||||
{
|
||||
mass += bp.RawMass;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mass;
|
||||
}
|
||||
|
||||
protected virtual OMV.Vector3 ComputeLinksetCenterOfMass()
|
||||
{
|
||||
OMV.Vector3 com;
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
com = LinksetRoot.Position * LinksetRoot.RawMass;
|
||||
float totalMass = LinksetRoot.RawMass;
|
||||
|
||||
foreach (BSPhysObject bp in m_children)
|
||||
{
|
||||
com += bp.Position * bp.RawMass;
|
||||
totalMass += bp.RawMass;
|
||||
}
|
||||
if (totalMass != 0f)
|
||||
com /= totalMass;
|
||||
}
|
||||
|
||||
return com;
|
||||
}
|
||||
|
||||
protected virtual OMV.Vector3 ComputeLinksetGeometricCenter()
|
||||
{
|
||||
OMV.Vector3 com;
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
com = LinksetRoot.Position;
|
||||
|
||||
foreach (BSPhysObject bp in m_children)
|
||||
{
|
||||
com += bp.Position * bp.RawMass;
|
||||
}
|
||||
com /= (m_children.Count + 1);
|
||||
}
|
||||
|
||||
return com;
|
||||
}
|
||||
|
||||
// Invoke the detailed logger and output something if it's enabled.
|
||||
protected void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
if (PhysicsScene.PhysicsLogging.Enabled)
|
||||
PhysicsScene.DetailLog(msg, args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,396 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
|
||||
using OpenSim.Framework;
|
||||
|
||||
using OMV = OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
// When a child is linked, the relationship position of the child to the parent
|
||||
// is remembered so the child's world position can be recomputed when it is
|
||||
// removed from the linkset.
|
||||
sealed class BSLinksetCompoundInfo : BSLinksetInfo
|
||||
{
|
||||
public OMV.Vector3 OffsetPos;
|
||||
public OMV.Quaternion OffsetRot;
|
||||
public BSLinksetCompoundInfo(OMV.Vector3 p, OMV.Quaternion r)
|
||||
{
|
||||
OffsetPos = p;
|
||||
OffsetRot = r;
|
||||
}
|
||||
public override void Clear()
|
||||
{
|
||||
OffsetPos = OMV.Vector3.Zero;
|
||||
OffsetRot = OMV.Quaternion.Identity;
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.Append("<p=");
|
||||
buff.Append(OffsetPos.ToString());
|
||||
buff.Append(",r=");
|
||||
buff.Append(OffsetRot.ToString());
|
||||
buff.Append(">");
|
||||
return buff.ToString();
|
||||
}
|
||||
};
|
||||
|
||||
public sealed class BSLinksetCompound : BSLinkset
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
|
||||
|
||||
public BSLinksetCompound(BSScene scene, BSPhysObject parent) : base(scene, parent)
|
||||
{
|
||||
}
|
||||
|
||||
// For compound implimented linksets, if there are children, use compound shape for the root.
|
||||
public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
|
||||
{
|
||||
// Returning 'unknown' means we don't have a preference.
|
||||
BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
|
||||
if (IsRoot(requestor) && HasAnyChildren)
|
||||
{
|
||||
ret = BSPhysicsShapeType.SHAPE_COMPOUND;
|
||||
}
|
||||
// DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// When physical properties are changed the linkset needs to recalculate
|
||||
// its internal properties.
|
||||
public override void Refresh(BSPhysObject requestor)
|
||||
{
|
||||
// Something changed so do the rebuilding thing
|
||||
// ScheduleRebuild();
|
||||
}
|
||||
|
||||
// Schedule a refresh to happen after all the other taint processing.
|
||||
private void ScheduleRebuild(BSPhysObject requestor)
|
||||
{
|
||||
DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,rebuilding={1}",
|
||||
requestor.LocalID, Rebuilding);
|
||||
// When rebuilding, it is possible to set properties that would normally require a rebuild.
|
||||
// If already rebuilding, don't request another rebuild.
|
||||
if (!Rebuilding)
|
||||
{
|
||||
PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", LinksetRoot.LocalID, delegate()
|
||||
{
|
||||
if (HasAnyChildren)
|
||||
RecomputeLinksetCompound();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// The object is going dynamic (physical). Do any setup necessary
|
||||
// for a dynamic linkset.
|
||||
// Only the state of the passed object can be modified. The rest of the linkset
|
||||
// has not yet been fully constructed.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// Called at taint-time!
|
||||
public override bool MakeDynamic(BSPhysObject child)
|
||||
{
|
||||
bool ret = false;
|
||||
DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
|
||||
if (IsRoot(child))
|
||||
{
|
||||
// The root is going dynamic. Make sure mass is properly set.
|
||||
m_mass = ComputeLinksetMass();
|
||||
ScheduleRebuild(LinksetRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The origional prims are removed from the world as the shape of the root compound
|
||||
// shape takes over.
|
||||
BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
|
||||
BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
|
||||
// We don't want collisions from the old linkset children.
|
||||
BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||
|
||||
child.PhysBody.collisionType = CollisionType.LinksetChild;
|
||||
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The object is going static (non-physical). Do any setup necessary for a static linkset.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// This doesn't normally happen -- OpenSim removes the objects from the physical
|
||||
// world if it is a static linkset.
|
||||
// Called at taint-time!
|
||||
public override bool MakeStatic(BSPhysObject child)
|
||||
{
|
||||
bool ret = false;
|
||||
DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
|
||||
if (IsRoot(child))
|
||||
{
|
||||
ScheduleRebuild(LinksetRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The non-physical children can come back to life.
|
||||
BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
|
||||
|
||||
child.PhysBody.collisionType = CollisionType.LinksetChild;
|
||||
|
||||
// Don't force activation so setting of DISABLE_SIMULATION can stay if used.
|
||||
BulletSimAPI.Activate2(child.PhysBody.ptr, false);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override void UpdateProperties(BSPhysObject updated, bool physicalUpdate)
|
||||
{
|
||||
// The user moving a child around requires the rebuilding of the linkset compound shape
|
||||
// One problem is this happens when a border is crossed -- the simulator implementation
|
||||
// is to store the position into the group which causes the move of the object
|
||||
// but it also means all the child positions get updated.
|
||||
// What would cause an unnecessary rebuild so we make sure the linkset is in a
|
||||
// region before bothering to do a rebuild.
|
||||
if (!IsRoot(updated)
|
||||
&& !physicalUpdate
|
||||
&& PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
|
||||
{
|
||||
updated.LinksetInfo = null;
|
||||
ScheduleRebuild(updated);
|
||||
}
|
||||
}
|
||||
|
||||
// Routine called when rebuilding the body of some member of the linkset.
|
||||
// Since we don't keep in world relationships, do nothing unless it's a child changing.
|
||||
// Returns 'true' of something was actually removed and would need restoring
|
||||
// Called at taint-time!!
|
||||
public override bool RemoveBodyDependencies(BSPrim child)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
|
||||
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), IsRoot(child));
|
||||
|
||||
if (!IsRoot(child))
|
||||
{
|
||||
// Because it is a convenient time, recompute child world position and rotation based on
|
||||
// its position in the linkset.
|
||||
RecomputeChildWorldPosition(child, true);
|
||||
}
|
||||
|
||||
// Cannot schedule a refresh/rebuild here because this routine is called when
|
||||
// the linkset is being rebuilt.
|
||||
// InternalRefresh(LinksetRoot);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||
// this routine will restore the removed constraints.
|
||||
// Called at taint-time!!
|
||||
public override void RestoreBodyDependencies(BSPrim child)
|
||||
{
|
||||
}
|
||||
|
||||
// When the linkset is built, the child shape is added to the compound shape relative to the
|
||||
// root shape. The linkset then moves around but this does not move the actual child
|
||||
// prim. The child prim's location must be recomputed based on the location of the root shape.
|
||||
private void RecomputeChildWorldPosition(BSPhysObject child, bool inTaintTime)
|
||||
{
|
||||
BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo;
|
||||
if (lci != null)
|
||||
{
|
||||
if (inTaintTime)
|
||||
{
|
||||
OMV.Vector3 oldPos = child.RawPosition;
|
||||
child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetPos;
|
||||
child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot;
|
||||
DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}",
|
||||
child.LocalID, oldPos, lci, child.RawPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TaintedObject is not used here so the raw position is set now and not at taint-time.
|
||||
child.Position = LinksetRoot.RawPosition + lci.OffsetPos;
|
||||
child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This happens when children have been added to the linkset but the linkset
|
||||
// has not been constructed yet. So like, at taint time, adding children to a linkset
|
||||
// and then changing properties of the children (makePhysical, for instance)
|
||||
// but the post-print action of actually rebuilding the linkset has not yet happened.
|
||||
// PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}",
|
||||
// LogHeader, child.LocalID);
|
||||
DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
|
||||
// Add a new child to the linkset.
|
||||
// Called while LinkActivity is locked.
|
||||
protected override void AddChildToLinkset(BSPhysObject child)
|
||||
{
|
||||
if (!HasChild(child))
|
||||
{
|
||||
m_children.Add(child);
|
||||
|
||||
DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
|
||||
|
||||
// Rebuild the compound shape with the new child shape included
|
||||
ScheduleRebuild(child);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the specified child from the linkset.
|
||||
// Safe to call even if the child is not really in the linkset.
|
||||
protected override void RemoveChildFromLinkset(BSPhysObject child)
|
||||
{
|
||||
if (m_children.Remove(child))
|
||||
{
|
||||
DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
|
||||
child.LocalID,
|
||||
LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(),
|
||||
child.LocalID, child.PhysBody.ptr.ToString());
|
||||
|
||||
// Cause the child's body to be rebuilt and thus restored to normal operation
|
||||
RecomputeChildWorldPosition(child, false);
|
||||
child.ForceBodyShapeRebuild(false);
|
||||
|
||||
if (!HasAnyChildren)
|
||||
{
|
||||
// The linkset is now empty. The root needs rebuilding.
|
||||
LinksetRoot.ForceBodyShapeRebuild(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rebuild the compound shape with the child removed
|
||||
ScheduleRebuild(child);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Called before the simulation step to make sure the compound based linkset
|
||||
// is all initialized.
|
||||
// Constraint linksets are rebuilt every time.
|
||||
// Note that this works for rebuilding just the root after a linkset is taken apart.
|
||||
// Called at taint time!!
|
||||
private void RecomputeLinksetCompound()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Suppress rebuilding while rebuilding
|
||||
Rebuilding = true;
|
||||
|
||||
// Cause the root shape to be rebuilt as a compound object with just the root in it
|
||||
LinksetRoot.ForceBodyShapeRebuild(true);
|
||||
|
||||
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
|
||||
LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
|
||||
|
||||
// Add a shape for each of the other children in the linkset
|
||||
ForEachMember(delegate(BSPhysObject cPrim)
|
||||
{
|
||||
if (!IsRoot(cPrim))
|
||||
{
|
||||
// Compute the displacement of the child from the root of the linkset.
|
||||
// This info is saved in the child prim so the relationship does not
|
||||
// change over time and the new child position can be computed
|
||||
// when the linkset is being disassembled (the linkset may have moved).
|
||||
BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
|
||||
if (lci == null)
|
||||
{
|
||||
// Each child position and rotation is given relative to the root.
|
||||
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
|
||||
OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
|
||||
OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
|
||||
|
||||
// Save relative position for recomputing child's world position after moving linkset.
|
||||
lci = new BSLinksetCompoundInfo(displacementPos, displacementRot);
|
||||
cPrim.LinksetInfo = lci;
|
||||
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
|
||||
}
|
||||
|
||||
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
|
||||
LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot);
|
||||
|
||||
if (cPrim.PhysShape.isNativeShape)
|
||||
{
|
||||
// A native shape is turning into a hull collision shape because native
|
||||
// shapes are not shared so we have to hullify it so it will be tracked
|
||||
// and freed at the correct time. This also solves the scaling problem
|
||||
// (native shapes scaled but hull/meshes are assumed to not be).
|
||||
// TODO: decide of the native shape can just be used in the compound shape.
|
||||
// Use call to CreateGeomNonSpecial().
|
||||
BulletShape saveShape = cPrim.PhysShape;
|
||||
cPrim.PhysShape.Clear(); // Don't let the create free the child's shape
|
||||
// PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null);
|
||||
PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
|
||||
BulletShape newShape = cPrim.PhysShape;
|
||||
cPrim.PhysShape = saveShape;
|
||||
BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, lci.OffsetPos, lci.OffsetRot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For the shared shapes (meshes and hulls), just use the shape in the child.
|
||||
// The reference count added here will be decremented when the compound shape
|
||||
// is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced).
|
||||
if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
|
||||
{
|
||||
PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
|
||||
LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
|
||||
}
|
||||
BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, lci.OffsetPos, lci.OffsetRot);
|
||||
}
|
||||
}
|
||||
return false; // 'false' says to move onto the next child in the list
|
||||
});
|
||||
|
||||
// With all of the linkset packed into the root prim, it has the mass of everyone.
|
||||
float linksetMass = LinksetMass;
|
||||
LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Rebuilding = false;
|
||||
}
|
||||
|
||||
BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr);
|
||||
|
||||
// DEBUG: see of inter-linkset collisions are causing problems for constraint linksets.
|
||||
// BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
|
||||
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
|
||||
using OMV = OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public sealed class BSLinksetConstraints : BSLinkset
|
||||
{
|
||||
// private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]";
|
||||
|
||||
public BSLinksetConstraints(BSScene scene, BSPhysObject parent) : base(scene, parent)
|
||||
{
|
||||
}
|
||||
|
||||
// When physical properties are changed the linkset needs to recalculate
|
||||
// its internal properties.
|
||||
// This is queued in the 'post taint' queue so the
|
||||
// refresh will happen once after all the other taints are applied.
|
||||
public override void Refresh(BSPhysObject requestor)
|
||||
{
|
||||
// Queue to happen after all the other taint processing
|
||||
PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate()
|
||||
{
|
||||
if (HasAnyChildren && IsRoot(requestor))
|
||||
RecomputeLinksetConstraints();
|
||||
});
|
||||
}
|
||||
|
||||
// The object is going dynamic (physical). Do any setup necessary
|
||||
// for a dynamic linkset.
|
||||
// Only the state of the passed object can be modified. The rest of the linkset
|
||||
// has not yet been fully constructed.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// Called at taint-time!
|
||||
public override bool MakeDynamic(BSPhysObject child)
|
||||
{
|
||||
// What is done for each object in BSPrim is what we want.
|
||||
return false;
|
||||
}
|
||||
|
||||
// The object is going static (non-physical). Do any setup necessary for a static linkset.
|
||||
// Return 'true' if any properties updated on the passed object.
|
||||
// This doesn't normally happen -- OpenSim removes the objects from the physical
|
||||
// world if it is a static linkset.
|
||||
// Called at taint-time!
|
||||
public override bool MakeStatic(BSPhysObject child)
|
||||
{
|
||||
// What is done for each object in BSPrim is what we want.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Called at taint-time!!
|
||||
public override void UpdateProperties(BSPhysObject updated, bool inTaintTime)
|
||||
{
|
||||
// Nothing to do for constraints on property updates
|
||||
}
|
||||
|
||||
// Routine called when rebuilding the body of some member of the linkset.
|
||||
// Destroy all the constraints have have been made to root and set
|
||||
// up to rebuild the constraints before the next simulation step.
|
||||
// Returns 'true' of something was actually removed and would need restoring
|
||||
// Called at taint-time!!
|
||||
public override bool RemoveBodyDependencies(BSPrim child)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
DetailLog("{0},BSLinksetConstraint.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}",
|
||||
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString());
|
||||
|
||||
lock (m_linksetActivityLock)
|
||||
{
|
||||
// Just undo all the constraints for this linkset. Rebuild at the end of the step.
|
||||
ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
|
||||
// Cause the constraints, et al to be rebuilt before the next simulation step.
|
||||
Refresh(LinksetRoot);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||
// this routine will restore the removed constraints.
|
||||
// Called at taint-time!!
|
||||
public override void RestoreBodyDependencies(BSPrim child)
|
||||
{
|
||||
// The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints.
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
|
||||
// Add a new child to the linkset.
|
||||
// Called while LinkActivity is locked.
|
||||
protected override void AddChildToLinkset(BSPhysObject child)
|
||||
{
|
||||
if (!HasChild(child))
|
||||
{
|
||||
m_children.Add(child);
|
||||
|
||||
DetailLog("{0},BSLinksetConstraints.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
|
||||
|
||||
// Cause constraints and assorted properties to be recomputed before the next simulation step.
|
||||
Refresh(LinksetRoot);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the specified child from the linkset.
|
||||
// Safe to call even if the child is not really in my linkset.
|
||||
protected override void RemoveChildFromLinkset(BSPhysObject child)
|
||||
{
|
||||
if (m_children.Remove(child))
|
||||
{
|
||||
BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
|
||||
BSPhysObject childx = child;
|
||||
|
||||
DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
|
||||
childx.LocalID,
|
||||
rootx.LocalID, rootx.PhysBody.ptr.ToString(),
|
||||
childx.LocalID, childx.PhysBody.ptr.ToString());
|
||||
|
||||
PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate()
|
||||
{
|
||||
PhysicallyUnlinkAChildFromRoot(rootx, childx);
|
||||
});
|
||||
// See that the linkset parameters are recomputed at the end of the taint time.
|
||||
Refresh(LinksetRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-fatal occurance.
|
||||
// PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a constraint between me (root of linkset) and the passed prim (the child).
|
||||
// Called at taint time!
|
||||
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||
{
|
||||
// Don't build the constraint when asked. Put it off until just before the simulation step.
|
||||
Refresh(rootPrim);
|
||||
}
|
||||
|
||||
private BSConstraint BuildConstraint(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||
{
|
||||
// Zero motion for children so they don't interpolate
|
||||
childPrim.ZeroMotion(true);
|
||||
|
||||
// Relative position normalized to the root prim
|
||||
// Essentually a vector pointing from center of rootPrim to center of childPrim
|
||||
OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position;
|
||||
|
||||
// real world coordinate of midpoint between the two objects
|
||||
OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
|
||||
|
||||
DetailLog("{0},BSLinksetConstraint.BuildConstraint,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
|
||||
rootPrim.LocalID,
|
||||
rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
|
||||
childPrim.LocalID, childPrim.PhysBody.ptr.ToString(),
|
||||
rootPrim.Position, childPrim.Position, midPoint);
|
||||
|
||||
// create a constraint that allows no freedom of movement between the two objects
|
||||
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
|
||||
|
||||
BSConstraint6Dof constrain = new BSConstraint6Dof(
|
||||
PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true );
|
||||
// PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true );
|
||||
|
||||
/* NOTE: below is an attempt to build constraint with full frame computation, etc.
|
||||
* Using the midpoint is easier since it lets the Bullet code manipulate the transforms
|
||||
* of the objects.
|
||||
* Code left for future programmers.
|
||||
// ==================================================================================
|
||||
// relative position normalized to the root prim
|
||||
OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation);
|
||||
OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation;
|
||||
|
||||
// relative rotation of the child to the parent
|
||||
OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation;
|
||||
OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation);
|
||||
|
||||
DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
|
||||
BS6DofConstraint constrain = new BS6DofConstraint(
|
||||
PhysicsScene.World, rootPrim.Body, childPrim.Body,
|
||||
OMV.Vector3.Zero,
|
||||
OMV.Quaternion.Inverse(rootPrim.Orientation),
|
||||
OMV.Vector3.Zero,
|
||||
OMV.Quaternion.Inverse(childPrim.Orientation),
|
||||
true,
|
||||
true
|
||||
);
|
||||
// ==================================================================================
|
||||
*/
|
||||
|
||||
PhysicsScene.Constraints.AddConstraint(constrain);
|
||||
|
||||
// zero linear and angular limits makes the objects unable to move in relation to each other
|
||||
constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
|
||||
constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
|
||||
|
||||
// tweek the constraint to increase stability
|
||||
constrain.UseFrameOffset(BSParam.BoolNumeric(BSParam.LinkConstraintUseFrameOffset));
|
||||
constrain.TranslationalLimitMotor(BSParam.BoolNumeric(BSParam.LinkConstraintEnableTransMotor),
|
||||
BSParam.LinkConstraintTransMotorMaxVel,
|
||||
BSParam.LinkConstraintTransMotorMaxForce);
|
||||
constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP);
|
||||
if (BSParam.LinkConstraintSolverIterations != 0f)
|
||||
{
|
||||
constrain.SetSolverIterations(BSParam.LinkConstraintSolverIterations);
|
||||
}
|
||||
return constrain;
|
||||
}
|
||||
|
||||
// Remove linkage between the linkset root and a particular child
|
||||
// The root and child bodies are passed in because we need to remove the constraint between
|
||||
// the bodies that were present at unlink time.
|
||||
// Called at taint time!
|
||||
private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||
{
|
||||
bool ret = false;
|
||||
DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
|
||||
rootPrim.LocalID,
|
||||
rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
|
||||
childPrim.LocalID, childPrim.PhysBody.ptr.ToString());
|
||||
|
||||
// Find the constraint for this link and get rid of it from the overall collection and from my list
|
||||
if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody))
|
||||
{
|
||||
// Make the child refresh its location
|
||||
BulletSimAPI.PushUpdate2(childPrim.PhysBody.ptr);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Remove linkage between myself and any possible children I might have.
|
||||
// Returns 'true' of any constraints were destroyed.
|
||||
// Called at taint time!
|
||||
private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
|
||||
{
|
||||
DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
|
||||
|
||||
return PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody);
|
||||
}
|
||||
|
||||
// Call each of the constraints that make up this linkset and recompute the
|
||||
// various transforms and variables. Create constraints of not created yet.
|
||||
// Called before the simulation step to make sure the constraint based linkset
|
||||
// is all initialized.
|
||||
// Called at taint time!!
|
||||
private void RecomputeLinksetConstraints()
|
||||
{
|
||||
float linksetMass = LinksetMass;
|
||||
LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
|
||||
|
||||
// DEBUG: see of inter-linkset collisions are causing problems
|
||||
// BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
|
||||
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
|
||||
DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}",
|
||||
LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), linksetMass);
|
||||
|
||||
foreach (BSPhysObject child in m_children)
|
||||
{
|
||||
// A child in the linkset physically shows the mass of the whole linkset.
|
||||
// This allows Bullet to apply enough force on the child to move the whole linkset.
|
||||
// (Also do the mass stuff before recomputing the constraint so mass is not zero.)
|
||||
child.UpdatePhysicalMassProperties(linksetMass);
|
||||
|
||||
BSConstraint constrain;
|
||||
if (!PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain))
|
||||
{
|
||||
// If constraint doesn't exist yet, create it.
|
||||
constrain = BuildConstraint(LinksetRoot, child);
|
||||
}
|
||||
constrain.RecomputeConstraintVariables(linksetMass);
|
||||
|
||||
// DEBUG: see of inter-linkset collisions are causing problems
|
||||
// BulletSimAPI.SetCollisionFilterMask2(child.BSBody.ptr,
|
||||
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
|
||||
|
||||
// BulletSimAPI.DumpConstraint2(PhysicsScene.World.ptr, constrain.Constraint.ptr); // DEBUG DEBUG
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
using System.Reflection;
|
||||
using Nini.Config;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
public struct MaterialAttributes
|
||||
{
|
||||
// Material type values that correspond with definitions for LSL
|
||||
public enum Material : int
|
||||
{
|
||||
Stone = 0,
|
||||
Metal,
|
||||
Glass,
|
||||
Wood,
|
||||
Flesh,
|
||||
Plastic,
|
||||
Rubber,
|
||||
Light,
|
||||
// Hereafter are BulletSim additions
|
||||
Avatar,
|
||||
NumberOfTypes // the count of types in the enum.
|
||||
}
|
||||
|
||||
// Names must be in the order of the above enum.
|
||||
// These names must coorespond to the lower case field names in the MaterialAttributes
|
||||
// structure as reflection is used to select the field to put the value in.
|
||||
public static readonly string[] MaterialAttribs = { "Density", "Friction", "Restitution"};
|
||||
|
||||
public MaterialAttributes(string t, float d, float f, float r)
|
||||
{
|
||||
type = t;
|
||||
density = d;
|
||||
friction = f;
|
||||
restitution = r;
|
||||
}
|
||||
public string type;
|
||||
public float density;
|
||||
public float friction;
|
||||
public float restitution;
|
||||
}
|
||||
|
||||
public static class BSMaterials
|
||||
{
|
||||
// Attributes for each material type
|
||||
private static readonly MaterialAttributes[] Attributes;
|
||||
|
||||
// Map of material name to material type code
|
||||
public static readonly Dictionary<string, MaterialAttributes.Material> MaterialMap;
|
||||
|
||||
static BSMaterials()
|
||||
{
|
||||
// Attribute sets for both the non-physical and physical instances of materials.
|
||||
Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2];
|
||||
|
||||
// Map of name to type code.
|
||||
MaterialMap = new Dictionary<string, MaterialAttributes.Material>();
|
||||
MaterialMap.Add("Stone", MaterialAttributes.Material.Stone);
|
||||
MaterialMap.Add("Metal", MaterialAttributes.Material.Metal);
|
||||
MaterialMap.Add("Glass", MaterialAttributes.Material.Glass);
|
||||
MaterialMap.Add("Wood", MaterialAttributes.Material.Wood);
|
||||
MaterialMap.Add("Flesh", MaterialAttributes.Material.Flesh);
|
||||
MaterialMap.Add("Plastic", MaterialAttributes.Material.Plastic);
|
||||
MaterialMap.Add("Rubber", MaterialAttributes.Material.Rubber);
|
||||
MaterialMap.Add("Light", MaterialAttributes.Material.Light);
|
||||
MaterialMap.Add("Avatar", MaterialAttributes.Material.Avatar);
|
||||
}
|
||||
|
||||
// This is where all the default material attributes are defined.
|
||||
public static void InitializeFromDefaults(ConfigurationParameters parms)
|
||||
{
|
||||
// Values from http://wiki.secondlife.com/wiki/PRIM_MATERIAL
|
||||
float dDensity = parms.defaultDensity;
|
||||
float dFriction = parms.defaultFriction;
|
||||
float dRestitution = parms.defaultRestitution;
|
||||
Attributes[(int)MaterialAttributes.Material.Stone] =
|
||||
new MaterialAttributes("stone",dDensity, 0.8f, 0.4f);
|
||||
Attributes[(int)MaterialAttributes.Material.Metal] =
|
||||
new MaterialAttributes("metal",dDensity, 0.3f, 0.4f);
|
||||
Attributes[(int)MaterialAttributes.Material.Glass] =
|
||||
new MaterialAttributes("glass",dDensity, 0.2f, 0.7f);
|
||||
Attributes[(int)MaterialAttributes.Material.Wood] =
|
||||
new MaterialAttributes("wood",dDensity, 0.6f, 0.5f);
|
||||
Attributes[(int)MaterialAttributes.Material.Flesh] =
|
||||
new MaterialAttributes("flesh",dDensity, 0.9f, 0.3f);
|
||||
Attributes[(int)MaterialAttributes.Material.Plastic] =
|
||||
new MaterialAttributes("plastic",dDensity, 0.4f, 0.7f);
|
||||
Attributes[(int)MaterialAttributes.Material.Rubber] =
|
||||
new MaterialAttributes("rubber",dDensity, 0.9f, 0.9f);
|
||||
Attributes[(int)MaterialAttributes.Material.Light] =
|
||||
new MaterialAttributes("light",dDensity, dFriction, dRestitution);
|
||||
Attributes[(int)MaterialAttributes.Material.Avatar] =
|
||||
new MaterialAttributes("avatar",60f, 0.2f, 0f);
|
||||
|
||||
Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("stonePhysical",dDensity, 0.8f, 0.4f);
|
||||
Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("metalPhysical",dDensity, 0.8f, 0.4f);
|
||||
Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("glassPhysical",dDensity, 0.8f, 0.7f);
|
||||
Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("woodPhysical",dDensity, 0.8f, 0.5f);
|
||||
Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("fleshPhysical",dDensity, 0.8f, 0.3f);
|
||||
Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("plasticPhysical",dDensity, 0.8f, 0.7f);
|
||||
Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("rubberPhysical",dDensity, 0.8f, 0.9f);
|
||||
Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("lightPhysical",dDensity, dFriction, dRestitution);
|
||||
Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("avatarPhysical",60f, 0.2f, 0f);
|
||||
}
|
||||
|
||||
// Under the [BulletSim] section, one can change the individual material
|
||||
// attribute values. The format of the configuration parameter is:
|
||||
// <materialName><Attribute>["Physical"] = floatValue
|
||||
// For instance:
|
||||
// [BulletSim]
|
||||
// StoneFriction = 0.2
|
||||
// FleshRestitutionPhysical = 0.8
|
||||
// Materials can have different parameters for their static and
|
||||
// physical instantiations. When setting the non-physical value,
|
||||
// both values are changed. Setting the physical value only changes
|
||||
// the physical value.
|
||||
public static void InitializefromParameters(IConfig pConfig)
|
||||
{
|
||||
foreach (KeyValuePair<string, MaterialAttributes.Material> kvp in MaterialMap)
|
||||
{
|
||||
string matName = kvp.Key;
|
||||
foreach (string attribName in MaterialAttributes.MaterialAttribs)
|
||||
{
|
||||
string paramName = matName + attribName;
|
||||
if (pConfig.Contains(paramName))
|
||||
{
|
||||
float paramValue = pConfig.GetFloat(paramName);
|
||||
SetAttributeValue((int)kvp.Value, attribName, paramValue);
|
||||
// set the physical value also
|
||||
SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
|
||||
}
|
||||
paramName += "Physical";
|
||||
if (pConfig.Contains(paramName))
|
||||
{
|
||||
float paramValue = pConfig.GetFloat(paramName);
|
||||
SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use reflection to set the value in the attribute structure.
|
||||
private static void SetAttributeValue(int matType, string attribName, float val)
|
||||
{
|
||||
MaterialAttributes thisAttrib = Attributes[matType];
|
||||
FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower());
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
fieldInfo.SetValue(thisAttrib, val);
|
||||
Attributes[matType] = thisAttrib;
|
||||
}
|
||||
}
|
||||
|
||||
// Given a material type, return a structure of attributes.
|
||||
public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical)
|
||||
{
|
||||
int ind = (int)type;
|
||||
if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes;
|
||||
return Attributes[ind];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
* 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.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public abstract class BSMotor
|
||||
{
|
||||
// Timescales and other things can be turned off by setting them to 'infinite'.
|
||||
public const float Infinite = 12345.6f;
|
||||
public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite);
|
||||
|
||||
public BSMotor(string useName)
|
||||
{
|
||||
UseName = useName;
|
||||
PhysicsScene = null;
|
||||
Enabled = true;
|
||||
}
|
||||
public virtual bool Enabled { get; set; }
|
||||
public virtual void Reset() { }
|
||||
public virtual void Zero() { }
|
||||
public virtual void GenerateTestOutput(float timeStep) { }
|
||||
|
||||
// A name passed at motor creation for easily identifyable debugging messages.
|
||||
public string UseName { get; private set; }
|
||||
|
||||
// Used only for outputting debug information. Might not be set so check for null.
|
||||
public BSScene PhysicsScene { get; set; }
|
||||
protected void MDetailLog(string msg, params Object[] parms)
|
||||
{
|
||||
if (PhysicsScene != null)
|
||||
{
|
||||
if (PhysicsScene.VehicleLoggingEnabled)
|
||||
{
|
||||
PhysicsScene.DetailLog(msg, parms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Motor which moves CurrentValue to TargetValue over TimeScale seconds.
|
||||
// The TargetValue decays in TargetValueDecayTimeScale and
|
||||
// the CurrentValue will be held back by FrictionTimeScale.
|
||||
// This motor will "zero itself" over time in that the targetValue will
|
||||
// decay to zero and the currentValue will follow it to that zero.
|
||||
// The overall effect is for the returned correction value to go from large
|
||||
// values (the total difference between current and target minus friction)
|
||||
// to small and eventually zero values.
|
||||
// TimeScale and TargetDelayTimeScale may be 'infinite' which means no decay.
|
||||
|
||||
// For instance, if something is moving at speed X and the desired speed is Y,
|
||||
// CurrentValue is X and TargetValue is Y. As the motor is stepped, new
|
||||
// values of CurrentValue are returned that approach the TargetValue.
|
||||
// The feature of decaying TargetValue is so vehicles will eventually
|
||||
// come to a stop rather than run forever. This can be disabled by
|
||||
// setting TargetValueDecayTimescale to 'infinite'.
|
||||
// The change from CurrentValue to TargetValue is linear over TimeScale seconds.
|
||||
public class BSVMotor : BSMotor
|
||||
{
|
||||
// public Vector3 FrameOfReference { get; set; }
|
||||
// public Vector3 Offset { get; set; }
|
||||
|
||||
public virtual float TimeScale { get; set; }
|
||||
public virtual float TargetValueDecayTimeScale { get; set; }
|
||||
public virtual Vector3 FrictionTimescale { get; set; }
|
||||
public virtual float Efficiency { get; set; }
|
||||
|
||||
public virtual float ErrorZeroThreshold { get; set; }
|
||||
|
||||
public virtual Vector3 TargetValue { get; protected set; }
|
||||
public virtual Vector3 CurrentValue { get; protected set; }
|
||||
public virtual Vector3 LastError { get; protected set; }
|
||||
|
||||
public virtual bool ErrorIsZero
|
||||
{ get {
|
||||
return (LastError == Vector3.Zero || LastError.LengthSquared() <= ErrorZeroThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
public BSVMotor(string useName)
|
||||
: base(useName)
|
||||
{
|
||||
TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
|
||||
Efficiency = 1f;
|
||||
FrictionTimescale = BSMotor.InfiniteVector;
|
||||
CurrentValue = TargetValue = Vector3.Zero;
|
||||
ErrorZeroThreshold = 0.001f;
|
||||
}
|
||||
public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency)
|
||||
: this(useName)
|
||||
{
|
||||
TimeScale = timeScale;
|
||||
TargetValueDecayTimeScale = decayTimeScale;
|
||||
FrictionTimescale = frictionTimeScale;
|
||||
Efficiency = efficiency;
|
||||
CurrentValue = TargetValue = Vector3.Zero;
|
||||
}
|
||||
public void SetCurrent(Vector3 current)
|
||||
{
|
||||
CurrentValue = current;
|
||||
}
|
||||
public void SetTarget(Vector3 target)
|
||||
{
|
||||
TargetValue = target;
|
||||
}
|
||||
public override void Zero()
|
||||
{
|
||||
base.Zero();
|
||||
CurrentValue = TargetValue = Vector3.Zero;
|
||||
}
|
||||
|
||||
// Compute the next step and return the new current value
|
||||
public virtual Vector3 Step(float timeStep)
|
||||
{
|
||||
if (!Enabled) return TargetValue;
|
||||
|
||||
Vector3 origTarget = TargetValue; // DEBUG
|
||||
Vector3 origCurrVal = CurrentValue; // DEBUG
|
||||
|
||||
Vector3 correction = Vector3.Zero;
|
||||
Vector3 error = TargetValue - CurrentValue;
|
||||
if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
|
||||
{
|
||||
correction = Step(timeStep, error);
|
||||
|
||||
CurrentValue += correction;
|
||||
|
||||
// The desired value reduces to zero which also reduces the difference with current.
|
||||
// If the decay time is infinite, don't decay at all.
|
||||
float decayFactor = 0f;
|
||||
if (TargetValueDecayTimeScale != BSMotor.Infinite)
|
||||
{
|
||||
decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
|
||||
TargetValue *= (1f - decayFactor);
|
||||
}
|
||||
|
||||
// The amount we can correct the error is reduced by the friction
|
||||
Vector3 frictionFactor = Vector3.Zero;
|
||||
if (FrictionTimescale != BSMotor.InfiniteVector)
|
||||
{
|
||||
// frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
|
||||
// Individual friction components can be 'infinite' so compute each separately.
|
||||
frictionFactor.X = (FrictionTimescale.X == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.X);
|
||||
frictionFactor.Y = (FrictionTimescale.Y == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Y);
|
||||
frictionFactor.Z = (FrictionTimescale.Z == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Z);
|
||||
frictionFactor *= timeStep;
|
||||
CurrentValue *= (Vector3.One - frictionFactor);
|
||||
}
|
||||
|
||||
MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}",
|
||||
BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
|
||||
timeStep, error, correction);
|
||||
MDetailLog("{0}, BSVMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},frictTS={4},frictFact={5},tgt={6},curr={7}",
|
||||
BSScene.DetailLogZero, UseName,
|
||||
TargetValueDecayTimeScale, decayFactor, FrictionTimescale, frictionFactor,
|
||||
TargetValue, CurrentValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Difference between what we have and target is small. Motor is done.
|
||||
CurrentValue = TargetValue;
|
||||
MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
|
||||
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
|
||||
}
|
||||
|
||||
return CurrentValue;
|
||||
}
|
||||
public virtual Vector3 Step(float timeStep, Vector3 error)
|
||||
{
|
||||
if (!Enabled) return Vector3.Zero;
|
||||
|
||||
LastError = error;
|
||||
Vector3 returnCorrection = Vector3.Zero;
|
||||
if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
|
||||
{
|
||||
// correction = error / secondsItShouldTakeToCorrect
|
||||
Vector3 correctionAmount;
|
||||
if (TimeScale == 0f || TimeScale == BSMotor.Infinite)
|
||||
correctionAmount = error * timeStep;
|
||||
else
|
||||
correctionAmount = error / TimeScale * timeStep;
|
||||
|
||||
returnCorrection = correctionAmount;
|
||||
MDetailLog("{0}, BSVMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}",
|
||||
BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount);
|
||||
}
|
||||
return returnCorrection;
|
||||
}
|
||||
|
||||
// The user sets all the parameters and calls this which outputs values until error is zero.
|
||||
public override void GenerateTestOutput(float timeStep)
|
||||
{
|
||||
// maximum number of outputs to generate.
|
||||
int maxOutput = 50;
|
||||
MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName);
|
||||
MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},frictTS={4},eff={5},curr={6},tgt={7}",
|
||||
BSScene.DetailLogZero, UseName,
|
||||
TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency,
|
||||
CurrentValue, TargetValue);
|
||||
|
||||
LastError = BSMotor.InfiniteVector;
|
||||
while (maxOutput-- > 0 && !LastError.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
|
||||
{
|
||||
Vector3 lastStep = Step(timeStep);
|
||||
MDetailLog("{0},BSVMotor.Test,{1},cur={2},tgt={3},lastError={4},lastStep={5}",
|
||||
BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep);
|
||||
}
|
||||
MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>",
|
||||
UseName, CurrentValue, TargetValue, TargetValueDecayTimeScale, FrictionTimescale);
|
||||
}
|
||||
}
|
||||
|
||||
public class BSFMotor : BSMotor
|
||||
{
|
||||
public float TimeScale { get; set; }
|
||||
public float DecayTimeScale { get; set; }
|
||||
public float Friction { get; set; }
|
||||
public float Efficiency { get; set; }
|
||||
|
||||
public float Target { get; private set; }
|
||||
public float CurrentValue { get; private set; }
|
||||
|
||||
public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency)
|
||||
: base(useName)
|
||||
{
|
||||
}
|
||||
public void SetCurrent(float target)
|
||||
{
|
||||
}
|
||||
public void SetTarget(float target)
|
||||
{
|
||||
}
|
||||
public virtual float Step(float timeStep)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Proportional, Integral, Derivitive Motor
|
||||
// Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors.
|
||||
public class BSPIDVMotor : BSVMotor
|
||||
{
|
||||
// Larger makes more overshoot, smaller means converge quicker. Range of 0.1 to 10.
|
||||
public Vector3 proportionFactor { get; set; }
|
||||
public Vector3 integralFactor { get; set; }
|
||||
public Vector3 derivFactor { get; set; }
|
||||
|
||||
// Arbritrary factor range.
|
||||
// EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct.
|
||||
public float EfficiencyHigh = 0.4f;
|
||||
public float EfficiencyLow = 4.0f;
|
||||
|
||||
// Running integration of the error
|
||||
Vector3 RunningIntegration { get; set; }
|
||||
|
||||
public BSPIDVMotor(string useName)
|
||||
: base(useName)
|
||||
{
|
||||
proportionFactor = new Vector3(1.00f, 1.00f, 1.00f);
|
||||
integralFactor = new Vector3(1.00f, 1.00f, 1.00f);
|
||||
derivFactor = new Vector3(1.00f, 1.00f, 1.00f);
|
||||
RunningIntegration = Vector3.Zero;
|
||||
LastError = Vector3.Zero;
|
||||
}
|
||||
|
||||
public override void Zero()
|
||||
{
|
||||
base.Zero();
|
||||
}
|
||||
|
||||
public override float Efficiency
|
||||
{
|
||||
get { return base.Efficiency; }
|
||||
set
|
||||
{
|
||||
base.Efficiency = Util.Clamp(value, 0f, 1f);
|
||||
// Compute factors based on efficiency.
|
||||
// If efficiency is high (1f), use a factor value that moves the error value to zero with little overshoot.
|
||||
// If efficiency is low (0f), use a factor value that overcorrects.
|
||||
// TODO: might want to vary contribution of different factor depending on efficiency.
|
||||
float factor = ((1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow) / 3f;
|
||||
// float factor = (1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow;
|
||||
proportionFactor = new Vector3(factor, factor, factor);
|
||||
integralFactor = new Vector3(factor, factor, factor);
|
||||
derivFactor = new Vector3(factor, factor, factor);
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore Current and Target Values and just advance the PID computation on this error.
|
||||
public override Vector3 Step(float timeStep, Vector3 error)
|
||||
{
|
||||
if (!Enabled) return Vector3.Zero;
|
||||
|
||||
// Add up the error so we can integrate over the accumulated errors
|
||||
RunningIntegration += error * timeStep;
|
||||
|
||||
// A simple derivitive is the rate of change from the last error.
|
||||
Vector3 derivFactor = (error - LastError) * timeStep;
|
||||
LastError = error;
|
||||
|
||||
// Correction = -(proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError)
|
||||
Vector3 ret = -(
|
||||
error * proportionFactor
|
||||
+ RunningIntegration * integralFactor
|
||||
+ derivFactor * derivFactor
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,559 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
using OpenMetaverse;
|
||||
using Nini.Config;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public static class BSParam
|
||||
{
|
||||
// Level of Detail values kept as float because that's what the Meshmerizer wants
|
||||
public static float MeshLOD { get; private set; }
|
||||
public static float MeshMegaPrimLOD { get; private set; }
|
||||
public static float MeshMegaPrimThreshold { get; private set; }
|
||||
public static float SculptLOD { get; private set; }
|
||||
|
||||
public static float MinimumObjectMass { get; private set; }
|
||||
public static float MaximumObjectMass { get; private set; }
|
||||
|
||||
public static float LinearDamping { get; private set; }
|
||||
public static float AngularDamping { get; private set; }
|
||||
public static float DeactivationTime { get; private set; }
|
||||
public static float LinearSleepingThreshold { get; private set; }
|
||||
public static float AngularSleepingThreshold { get; private set; }
|
||||
public static float CcdMotionThreshold { get; private set; }
|
||||
public static float CcdSweptSphereRadius { get; private set; }
|
||||
public static float ContactProcessingThreshold { get; private set; }
|
||||
|
||||
public static bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
|
||||
public static bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
|
||||
public static bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects
|
||||
|
||||
public static float TerrainImplementation { get; private set; }
|
||||
public static float TerrainFriction { get; private set; }
|
||||
public static float TerrainHitFraction { get; private set; }
|
||||
public static float TerrainRestitution { get; private set; }
|
||||
public static float TerrainCollisionMargin { get; private set; }
|
||||
|
||||
// Avatar parameters
|
||||
public static float AvatarFriction { get; private set; }
|
||||
public static float AvatarStandingFriction { get; private set; }
|
||||
public static float AvatarDensity { get; private set; }
|
||||
public static float AvatarRestitution { get; private set; }
|
||||
public static float AvatarCapsuleWidth { get; private set; }
|
||||
public static float AvatarCapsuleDepth { get; private set; }
|
||||
public static float AvatarCapsuleHeight { get; private set; }
|
||||
public static float AvatarContactProcessingThreshold { get; private set; }
|
||||
|
||||
public static float VehicleAngularDamping { get; private set; }
|
||||
|
||||
public static float LinksetImplementation { get; private set; }
|
||||
public static float LinkConstraintUseFrameOffset { get; private set; }
|
||||
public static float LinkConstraintEnableTransMotor { get; private set; }
|
||||
public static float LinkConstraintTransMotorMaxVel { get; private set; }
|
||||
public static float LinkConstraintTransMotorMaxForce { get; private set; }
|
||||
public static float LinkConstraintERP { get; private set; }
|
||||
public static float LinkConstraintCFM { get; private set; }
|
||||
public static float LinkConstraintSolverIterations { get; private set; }
|
||||
|
||||
public static float PID_D { get; private set; } // derivative
|
||||
public static float PID_P { get; private set; } // proportional
|
||||
|
||||
public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
|
||||
public delegate float ParamGet(BSScene scene);
|
||||
public delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
|
||||
public delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
|
||||
|
||||
public struct ParameterDefn
|
||||
{
|
||||
public string name; // string name of the parameter
|
||||
public string desc; // a short description of what the parameter means
|
||||
public float defaultValue; // default value if not specified anywhere else
|
||||
public ParamUser userParam; // get the value from the configuration file
|
||||
public ParamGet getter; // return the current value stored for this parameter
|
||||
public ParamSet setter; // set the current value for this parameter
|
||||
public SetOnObject onObject; // set the value on an object in the physical domain
|
||||
public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
|
||||
{
|
||||
name = n;
|
||||
desc = d;
|
||||
defaultValue = v;
|
||||
userParam = u;
|
||||
getter = g;
|
||||
setter = s;
|
||||
onObject = null;
|
||||
}
|
||||
public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
|
||||
{
|
||||
name = n;
|
||||
desc = d;
|
||||
defaultValue = v;
|
||||
userParam = u;
|
||||
getter = g;
|
||||
setter = s;
|
||||
onObject = o;
|
||||
}
|
||||
}
|
||||
|
||||
// List of all of the externally visible parameters.
|
||||
// For each parameter, this table maps a text name to getter and setters.
|
||||
// To add a new externally referencable/settable parameter, add the paramter storage
|
||||
// location somewhere in the program and make an entry in this table with the
|
||||
// getters and setters.
|
||||
// It is easiest to find an existing definition and copy it.
|
||||
// Parameter values are floats. Booleans are converted to a floating value.
|
||||
//
|
||||
// A ParameterDefn() takes the following parameters:
|
||||
// -- the text name of the parameter. This is used for console input and ini file.
|
||||
// -- a short text description of the parameter. This shows up in the console listing.
|
||||
// -- a default value (float)
|
||||
// -- a delegate for fetching the parameter from the ini file.
|
||||
// Should handle fetching the right type from the ini file and converting it.
|
||||
// -- a delegate for getting the value as a float
|
||||
// -- a delegate for setting the value from a float
|
||||
// -- an optional delegate to update the value in the world. Most often used to
|
||||
// push the new value to an in-world object.
|
||||
//
|
||||
// The single letter parameters for the delegates are:
|
||||
// s = BSScene
|
||||
// o = BSPhysObject
|
||||
// p = string parameter name
|
||||
// l = localID of referenced object
|
||||
// v = value (float)
|
||||
// cf = parameter configuration class (for fetching values from ini file)
|
||||
private static ParameterDefn[] ParameterDefinitions =
|
||||
{
|
||||
new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
|
||||
ConfigurationParameters.numericTrue,
|
||||
(s,cf,p,v) => { ShouldMeshSculptedPrim = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
|
||||
(s) => { return BSParam.NumericBool(ShouldMeshSculptedPrim); },
|
||||
(s,p,l,v) => { ShouldMeshSculptedPrim = BSParam.BoolNumeric(v); } ),
|
||||
new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
|
||||
ConfigurationParameters.numericFalse,
|
||||
(s,cf,p,v) => { ShouldForceSimplePrimMeshing = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
|
||||
(s) => { return BSParam.NumericBool(ShouldForceSimplePrimMeshing); },
|
||||
(s,p,l,v) => { ShouldForceSimplePrimMeshing = BSParam.BoolNumeric(v); } ),
|
||||
new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects",
|
||||
ConfigurationParameters.numericTrue,
|
||||
(s,cf,p,v) => { ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
|
||||
(s) => { return BSParam.NumericBool(ShouldUseHullsForPhysicalObjects); },
|
||||
(s,p,l,v) => { ShouldUseHullsForPhysicalObjects = BSParam.BoolNumeric(v); } ),
|
||||
|
||||
new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
|
||||
8f,
|
||||
(s,cf,p,v) => { MeshLOD = (float)cf.GetInt(p, (int)v); },
|
||||
(s) => { return MeshLOD; },
|
||||
(s,p,l,v) => { MeshLOD = v; } ),
|
||||
new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters",
|
||||
16f,
|
||||
(s,cf,p,v) => { MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); },
|
||||
(s) => { return MeshMegaPrimLOD; },
|
||||
(s,p,l,v) => { MeshMegaPrimLOD = v; } ),
|
||||
new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD",
|
||||
10f,
|
||||
(s,cf,p,v) => { MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); },
|
||||
(s) => { return MeshMegaPrimThreshold; },
|
||||
(s,p,l,v) => { MeshMegaPrimThreshold = v; } ),
|
||||
new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)",
|
||||
32f,
|
||||
(s,cf,p,v) => { SculptLOD = (float)cf.GetInt(p, (int)v); },
|
||||
(s) => { return SculptLOD; },
|
||||
(s,p,l,v) => { SculptLOD = v; } ),
|
||||
|
||||
new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps",
|
||||
10f,
|
||||
(s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_maxSubSteps; },
|
||||
(s,p,l,v) => { s.m_maxSubSteps = (int)v; } ),
|
||||
new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)",
|
||||
1f / 60f,
|
||||
(s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)s.m_fixedTimeStep; },
|
||||
(s,p,l,v) => { s.m_fixedTimeStep = v; } ),
|
||||
new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame",
|
||||
2048f,
|
||||
(s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_maxCollisionsPerFrame; },
|
||||
(s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ),
|
||||
new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame",
|
||||
8000f,
|
||||
(s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_maxUpdatesPerFrame; },
|
||||
(s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
|
||||
new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
|
||||
500f,
|
||||
(s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.m_taintsToProcessPerStep; },
|
||||
(s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
|
||||
new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)",
|
||||
0.0001f,
|
||||
(s,cf,p,v) => { MinimumObjectMass = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)MinimumObjectMass; },
|
||||
(s,p,l,v) => { MinimumObjectMass = v; } ),
|
||||
new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
|
||||
10000.01f,
|
||||
(s,cf,p,v) => { MaximumObjectMass = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)MaximumObjectMass; },
|
||||
(s,p,l,v) => { MaximumObjectMass = v; } ),
|
||||
|
||||
new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
|
||||
2200f,
|
||||
(s,cf,p,v) => { PID_D = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)PID_D; },
|
||||
(s,p,l,v) => { PID_D = v; } ),
|
||||
new ParameterDefn("PID_P", "Parameteric factor for motion smoothing",
|
||||
900f,
|
||||
(s,cf,p,v) => { PID_P = cf.GetFloat(p, v); },
|
||||
(s) => { return (float)PID_P; },
|
||||
(s,p,l,v) => { PID_P = v; } ),
|
||||
|
||||
new ParameterDefn("DefaultFriction", "Friction factor used on new objects",
|
||||
0.2f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].defaultFriction = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].defaultFriction; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].defaultFriction = v; } ),
|
||||
new ParameterDefn("DefaultDensity", "Density for new objects" ,
|
||||
10.000006836f, // Aluminum g/cm3
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].defaultDensity = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].defaultDensity; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].defaultDensity = v; } ),
|
||||
new ParameterDefn("DefaultRestitution", "Bouncyness of an object" ,
|
||||
0f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].defaultRestitution = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].defaultRestitution; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].defaultRestitution = v; } ),
|
||||
new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)",
|
||||
0.04f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].collisionMargin = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].collisionMargin; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].collisionMargin = v; } ),
|
||||
new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)",
|
||||
-9.80665f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].gravity = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].gravity; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{s.UnmanagedParams[0].gravity=x;}, p, PhysParameterEntry.APPLY_TO_NONE, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
|
||||
|
||||
|
||||
new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
|
||||
0f,
|
||||
(s,cf,p,v) => { LinearDamping = cf.GetFloat(p, v); },
|
||||
(s) => { return LinearDamping; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearDamping=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, AngularDamping); } ),
|
||||
new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
|
||||
0f,
|
||||
(s,cf,p,v) => { AngularDamping = cf.GetFloat(p, v); },
|
||||
(s) => { return AngularDamping; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularDamping=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, LinearDamping, v); } ),
|
||||
new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
|
||||
0.2f,
|
||||
(s,cf,p,v) => { DeactivationTime = cf.GetFloat(p, v); },
|
||||
(s) => { return DeactivationTime; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{DeactivationTime=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ),
|
||||
new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
|
||||
0.8f,
|
||||
(s,cf,p,v) => { LinearSleepingThreshold = cf.GetFloat(p, v); },
|
||||
(s) => { return LinearSleepingThreshold; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearSleepingThreshold=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
|
||||
new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
|
||||
1.0f,
|
||||
(s,cf,p,v) => { AngularSleepingThreshold = cf.GetFloat(p, v); },
|
||||
(s) => { return AngularSleepingThreshold; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularSleepingThreshold=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
|
||||
new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
|
||||
0f, // set to zero to disable
|
||||
(s,cf,p,v) => { CcdMotionThreshold = cf.GetFloat(p, v); },
|
||||
(s) => { return CcdMotionThreshold; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdMotionThreshold=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ),
|
||||
new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
|
||||
0f,
|
||||
(s,cf,p,v) => { CcdSweptSphereRadius = cf.GetFloat(p, v); },
|
||||
(s) => { return CcdSweptSphereRadius; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdSweptSphereRadius=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ),
|
||||
new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
|
||||
0.1f,
|
||||
(s,cf,p,v) => { ContactProcessingThreshold = cf.GetFloat(p, v); },
|
||||
(s) => { return ContactProcessingThreshold; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{ContactProcessingThreshold=x;}, p, l, v); },
|
||||
(s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
|
||||
|
||||
new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)",
|
||||
(float)BSTerrainPhys.TerrainImplementation.Heightmap,
|
||||
(s,cf,p,v) => { TerrainImplementation = cf.GetFloat(p,v); },
|
||||
(s) => { return TerrainImplementation; },
|
||||
(s,p,l,v) => { TerrainImplementation = v; } ),
|
||||
new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
|
||||
0.3f,
|
||||
(s,cf,p,v) => { TerrainFriction = cf.GetFloat(p, v); },
|
||||
(s) => { return TerrainFriction; },
|
||||
(s,p,l,v) => { TerrainFriction = v; /* TODO: set on real terrain */} ),
|
||||
new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
|
||||
0.8f,
|
||||
(s,cf,p,v) => { TerrainHitFraction = cf.GetFloat(p, v); },
|
||||
(s) => { return TerrainHitFraction; },
|
||||
(s,p,l,v) => { TerrainHitFraction = v; /* TODO: set on real terrain */ } ),
|
||||
new ParameterDefn("TerrainRestitution", "Bouncyness" ,
|
||||
0f,
|
||||
(s,cf,p,v) => { TerrainRestitution = cf.GetFloat(p, v); },
|
||||
(s) => { return TerrainRestitution; },
|
||||
(s,p,l,v) => { TerrainRestitution = v; /* TODO: set on real terrain */ } ),
|
||||
new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" ,
|
||||
0.04f,
|
||||
(s,cf,p,v) => { TerrainCollisionMargin = cf.GetFloat(p, v); },
|
||||
(s) => { return TerrainCollisionMargin; },
|
||||
(s,p,l,v) => { TerrainCollisionMargin = v; /* TODO: set on real terrain */ } ),
|
||||
|
||||
new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
|
||||
0.2f,
|
||||
(s,cf,p,v) => { AvatarFriction = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarFriction; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarFriction=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
|
||||
10.0f,
|
||||
(s,cf,p,v) => { AvatarStandingFriction = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarStandingFriction; },
|
||||
(s,p,l,v) => { AvatarStandingFriction = v; } ),
|
||||
new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
|
||||
60f,
|
||||
(s,cf,p,v) => { AvatarDensity = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarDensity; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarDensity=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.",
|
||||
0f,
|
||||
(s,cf,p,v) => { AvatarRestitution = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarRestitution; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarRestitution=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule",
|
||||
0.6f,
|
||||
(s,cf,p,v) => { AvatarCapsuleWidth = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarCapsuleWidth; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleWidth=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule",
|
||||
0.45f,
|
||||
(s,cf,p,v) => { AvatarCapsuleDepth = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarCapsuleDepth; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleDepth=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
|
||||
1.5f,
|
||||
(s,cf,p,v) => { AvatarCapsuleHeight = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarCapsuleHeight; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleHeight=x;}, p, l, v); } ),
|
||||
new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions",
|
||||
0.1f,
|
||||
(s,cf,p,v) => { AvatarContactProcessingThreshold = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarContactProcessingThreshold; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarContactProcessingThreshold=x;}, p, l, v); } ),
|
||||
|
||||
new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)",
|
||||
0.95f,
|
||||
(s,cf,p,v) => { VehicleAngularDamping = cf.GetFloat(p, v); },
|
||||
(s) => { return VehicleAngularDamping; },
|
||||
(s,p,l,v) => { VehicleAngularDamping = v; } ),
|
||||
|
||||
new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
|
||||
0f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].maxPersistantManifoldPoolSize; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = v; } ),
|
||||
new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)",
|
||||
0f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = v; } ),
|
||||
new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count",
|
||||
ConfigurationParameters.numericFalse,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = v; } ),
|
||||
new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step",
|
||||
ConfigurationParameters.numericFalse,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return s.UnmanagedParams[0].shouldForceUpdateAllAabbs; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = v; } ),
|
||||
new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction",
|
||||
ConfigurationParameters.numericTrue,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return s.UnmanagedParams[0].shouldRandomizeSolverOrder; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = v; } ),
|
||||
new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands",
|
||||
ConfigurationParameters.numericTrue,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return s.UnmanagedParams[0].shouldSplitSimulationIslands; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = v; } ),
|
||||
new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching",
|
||||
ConfigurationParameters.numericFalse,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return s.UnmanagedParams[0].shouldEnableFrictionCaching; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = v; } ),
|
||||
new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)",
|
||||
0f, // zero says use Bullet default
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].numberOfSolverIterations = cf.GetFloat(p, v); },
|
||||
(s) => { return s.UnmanagedParams[0].numberOfSolverIterations; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].numberOfSolverIterations = v; } ),
|
||||
|
||||
new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)",
|
||||
(float)BSLinkset.LinksetImplementation.Compound,
|
||||
(s,cf,p,v) => { LinksetImplementation = cf.GetFloat(p,v); },
|
||||
(s) => { return LinksetImplementation; },
|
||||
(s,p,l,v) => { LinksetImplementation = v; } ),
|
||||
new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.",
|
||||
ConfigurationParameters.numericFalse,
|
||||
(s,cf,p,v) => { LinkConstraintUseFrameOffset = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return LinkConstraintUseFrameOffset; },
|
||||
(s,p,l,v) => { LinkConstraintUseFrameOffset = v; } ),
|
||||
new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints",
|
||||
ConfigurationParameters.numericTrue,
|
||||
(s,cf,p,v) => { LinkConstraintEnableTransMotor = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||
(s) => { return LinkConstraintEnableTransMotor; },
|
||||
(s,p,l,v) => { LinkConstraintEnableTransMotor = v; } ),
|
||||
new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints",
|
||||
5.0f,
|
||||
(s,cf,p,v) => { LinkConstraintTransMotorMaxVel = cf.GetFloat(p, v); },
|
||||
(s) => { return LinkConstraintTransMotorMaxVel; },
|
||||
(s,p,l,v) => { LinkConstraintTransMotorMaxVel = v; } ),
|
||||
new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints",
|
||||
0.1f,
|
||||
(s,cf,p,v) => { LinkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
|
||||
(s) => { return LinkConstraintTransMotorMaxForce; },
|
||||
(s,p,l,v) => { LinkConstraintTransMotorMaxForce = v; } ),
|
||||
new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
|
||||
0.1f,
|
||||
(s,cf,p,v) => { LinkConstraintCFM = cf.GetFloat(p, v); },
|
||||
(s) => { return LinkConstraintCFM; },
|
||||
(s,p,l,v) => { LinkConstraintCFM = v; } ),
|
||||
new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
|
||||
0.1f,
|
||||
(s,cf,p,v) => { LinkConstraintERP = cf.GetFloat(p, v); },
|
||||
(s) => { return LinkConstraintERP; },
|
||||
(s,p,l,v) => { LinkConstraintERP = v; } ),
|
||||
new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
|
||||
40,
|
||||
(s,cf,p,v) => { LinkConstraintSolverIterations = cf.GetFloat(p, v); },
|
||||
(s) => { return LinkConstraintSolverIterations; },
|
||||
(s,p,l,v) => { LinkConstraintSolverIterations = v; } ),
|
||||
|
||||
new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)",
|
||||
0f,
|
||||
(s,cf,p,v) => { s.UnmanagedParams[0].physicsLoggingFrames = cf.GetInt(p, (int)v); },
|
||||
(s) => { return (float)s.UnmanagedParams[0].physicsLoggingFrames; },
|
||||
(s,p,l,v) => { s.UnmanagedParams[0].physicsLoggingFrames = (int)v; } ),
|
||||
};
|
||||
|
||||
// Convert a boolean to our numeric true and false values
|
||||
public static float NumericBool(bool b)
|
||||
{
|
||||
return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse);
|
||||
}
|
||||
|
||||
// Convert numeric true and false values to a boolean
|
||||
public static bool BoolNumeric(float b)
|
||||
{
|
||||
return (b == ConfigurationParameters.numericTrue ? true : false);
|
||||
}
|
||||
|
||||
// Search through the parameter definitions and return the matching
|
||||
// ParameterDefn structure.
|
||||
// Case does not matter as names are compared after converting to lower case.
|
||||
// Returns 'false' if the parameter is not found.
|
||||
internal static bool TryGetParameter(string paramName, out ParameterDefn defn)
|
||||
{
|
||||
bool ret = false;
|
||||
ParameterDefn foundDefn = new ParameterDefn();
|
||||
string pName = paramName.ToLower();
|
||||
|
||||
foreach (ParameterDefn parm in ParameterDefinitions)
|
||||
{
|
||||
if (pName == parm.name.ToLower())
|
||||
{
|
||||
foundDefn = parm;
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
defn = foundDefn;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Pass through the settable parameters and set the default values
|
||||
internal static void SetParameterDefaultValues(BSScene physicsScene)
|
||||
{
|
||||
foreach (ParameterDefn parm in ParameterDefinitions)
|
||||
{
|
||||
parm.setter(physicsScene, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Get user set values out of the ini file.
|
||||
internal static void SetParameterConfigurationValues(BSScene physicsScene, IConfig cfg)
|
||||
{
|
||||
foreach (ParameterDefn parm in ParameterDefinitions)
|
||||
{
|
||||
parm.userParam(physicsScene, cfg, parm.name, parm.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
internal static PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
|
||||
|
||||
// This creates an array in the correct format for returning the list of
|
||||
// parameters. This is used by the 'list' option of the 'physics' command.
|
||||
internal static void BuildParameterTable()
|
||||
{
|
||||
if (SettableParameters.Length < ParameterDefinitions.Length)
|
||||
{
|
||||
List<PhysParameterEntry> entries = new List<PhysParameterEntry>();
|
||||
for (int ii = 0; ii < ParameterDefinitions.Length; ii++)
|
||||
{
|
||||
ParameterDefn pd = ParameterDefinitions[ii];
|
||||
entries.Add(new PhysParameterEntry(pd.name, pd.desc));
|
||||
}
|
||||
|
||||
// make the list in alphabetical order for estetic reasons
|
||||
entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2)
|
||||
{
|
||||
return ppe1.name.CompareTo(ppe2.name);
|
||||
});
|
||||
|
||||
SettableParameters = entries.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
|
||||
using OMV = OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
/*
|
||||
* Class to wrap all objects.
|
||||
* The rest of BulletSim doesn't need to keep checking for avatars or prims
|
||||
* unless the difference is significant.
|
||||
*
|
||||
* Variables in the physicsl objects are in three forms:
|
||||
* VariableName: used by the simulator and performs taint operations, etc
|
||||
* RawVariableName: direct reference to the BulletSim storage for the variable value
|
||||
* ForceVariableName: direct reference (store and fetch) to the value in the physics engine.
|
||||
* The last two (and certainly the last one) should be referenced only in taint-time.
|
||||
*/
|
||||
|
||||
/*
|
||||
* As of 20121221, the following are the call sequences (going down) for different script physical functions:
|
||||
* llApplyImpulse llApplyRotImpulse llSetTorque llSetForce
|
||||
* SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce
|
||||
* SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse
|
||||
* PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v
|
||||
* BS.ApplyCentralForce BS.ApplyTorque
|
||||
*/
|
||||
|
||||
public abstract class BSPhysObject : PhysicsActor
|
||||
{
|
||||
protected BSPhysObject()
|
||||
{
|
||||
}
|
||||
protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName)
|
||||
{
|
||||
PhysicsScene = parentScene;
|
||||
LocalID = localID;
|
||||
PhysObjectName = name;
|
||||
TypeName = typeName;
|
||||
|
||||
Linkset = BSLinkset.Factory(PhysicsScene, this);
|
||||
LastAssetBuildFailed = false;
|
||||
|
||||
// Default material type
|
||||
Material = MaterialAttributes.Material.Wood;
|
||||
|
||||
CollisionCollection = new CollisionEventUpdate();
|
||||
SubscribedEventsMs = 0;
|
||||
CollidingStep = 0;
|
||||
CollidingGroundStep = 0;
|
||||
}
|
||||
|
||||
// Tell the object to clean up.
|
||||
public virtual void Destroy()
|
||||
{
|
||||
UnRegisterAllPreStepActions();
|
||||
}
|
||||
|
||||
public BSScene PhysicsScene { get; protected set; }
|
||||
// public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor
|
||||
public string PhysObjectName { get; protected set; }
|
||||
public string TypeName { get; protected set; }
|
||||
|
||||
public BSLinkset Linkset { get; set; }
|
||||
public BSLinksetInfo LinksetInfo { get; set; }
|
||||
|
||||
// Return the object mass without calculating it or having side effects
|
||||
public abstract float RawMass { get; }
|
||||
// Set the raw mass but also update physical mass properties (inertia, ...)
|
||||
public abstract void UpdatePhysicalMassProperties(float mass);
|
||||
|
||||
// The last value calculated for the prim's inertia
|
||||
public OMV.Vector3 Inertia { get; set; }
|
||||
|
||||
// Reference to the physical body (btCollisionObject) of this object
|
||||
public BulletBody PhysBody;
|
||||
// Reference to the physical shape (btCollisionShape) of this object
|
||||
public BulletShape PhysShape;
|
||||
|
||||
// 'true' if the mesh's underlying asset failed to build.
|
||||
// This will keep us from looping after the first time the build failed.
|
||||
public bool LastAssetBuildFailed { get; set; }
|
||||
|
||||
// The objects base shape information. Null if not a prim type shape.
|
||||
public PrimitiveBaseShape BaseShape { get; protected set; }
|
||||
// Some types of objects have preferred physical representations.
|
||||
// Returns SHAPE_UNKNOWN if there is no preference.
|
||||
public virtual BSPhysicsShapeType PreferredPhysicalShape
|
||||
{
|
||||
get { return BSPhysicsShapeType.SHAPE_UNKNOWN; }
|
||||
}
|
||||
|
||||
// When the physical properties are updated, an EntityProperty holds the update values.
|
||||
// Keep the current and last EntityProperties to enable computation of differences
|
||||
// between the current update and the previous values.
|
||||
public EntityProperties CurrentEntityProperties { get; set; }
|
||||
public EntityProperties LastEntityProperties { get; set; }
|
||||
|
||||
public virtual OMV.Vector3 Scale { get; set; }
|
||||
public abstract bool IsSolid { get; }
|
||||
public abstract bool IsStatic { get; }
|
||||
|
||||
// Materialness
|
||||
public MaterialAttributes.Material Material { get; private set; }
|
||||
public override void SetMaterial(int material)
|
||||
{
|
||||
Material = (MaterialAttributes.Material)material;
|
||||
}
|
||||
|
||||
// Stop all physical motion.
|
||||
public abstract void ZeroMotion(bool inTaintTime);
|
||||
public abstract void ZeroAngularMotion(bool inTaintTime);
|
||||
|
||||
// Step the vehicle simulation for this object. A NOOP if the vehicle was not configured.
|
||||
public virtual void StepVehicle(float timeStep) { }
|
||||
|
||||
// Update the physical location and motion of the object. Called with data from Bullet.
|
||||
public abstract void UpdateProperties(EntityProperties entprop);
|
||||
|
||||
public abstract OMV.Vector3 RawPosition { get; set; }
|
||||
public abstract OMV.Vector3 ForcePosition { get; set; }
|
||||
|
||||
public abstract OMV.Quaternion RawOrientation { get; set; }
|
||||
public abstract OMV.Quaternion ForceOrientation { get; set; }
|
||||
|
||||
// The system is telling us the velocity it wants to move at.
|
||||
// protected OMV.Vector3 m_targetVelocity; // use the definition in PhysicsActor
|
||||
public override OMV.Vector3 TargetVelocity
|
||||
{
|
||||
get { return m_targetVelocity; }
|
||||
set
|
||||
{
|
||||
m_targetVelocity = value;
|
||||
Velocity = value;
|
||||
}
|
||||
}
|
||||
public abstract OMV.Vector3 ForceVelocity { get; set; }
|
||||
|
||||
public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
|
||||
|
||||
public abstract float ForceBuoyancy { get; set; }
|
||||
|
||||
public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
|
||||
|
||||
#region Collisions
|
||||
|
||||
// Requested number of milliseconds between collision events. Zero means disabled.
|
||||
protected int SubscribedEventsMs { get; set; }
|
||||
// Given subscription, the time that a collision may be passed up
|
||||
protected int NextCollisionOkTime { get; set; }
|
||||
// The simulation step that last had a collision
|
||||
protected long CollidingStep { get; set; }
|
||||
// The simulation step that last had a collision with the ground
|
||||
protected long CollidingGroundStep { get; set; }
|
||||
// The collision flags we think are set in Bullet
|
||||
protected CollisionFlags CurrentCollisionFlags { get; set; }
|
||||
|
||||
// The collisions that have been collected this tick
|
||||
protected CollisionEventUpdate CollisionCollection;
|
||||
|
||||
// The simulation step is telling this object about a collision.
|
||||
// Return 'true' if a collision was processed and should be sent up.
|
||||
// Called at taint time from within the Step() function
|
||||
public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
|
||||
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
// The following lines make IsColliding() and IsCollidingGround() work
|
||||
CollidingStep = PhysicsScene.SimulationStep;
|
||||
if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID)
|
||||
{
|
||||
CollidingGroundStep = PhysicsScene.SimulationStep;
|
||||
}
|
||||
|
||||
// prims in the same linkset cannot collide with each other
|
||||
if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
// if someone has subscribed for collision events....
|
||||
if (SubscribedEvents()) {
|
||||
CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
|
||||
DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
|
||||
LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
|
||||
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Send the collected collisions into the simulator.
|
||||
// Called at taint time from within the Step() function thus no locking problems
|
||||
// with CollisionCollection and ObjectsWithNoMoreCollisions.
|
||||
// Return 'true' if there were some actual collisions passed up
|
||||
public virtual bool SendCollisions()
|
||||
{
|
||||
bool ret = true;
|
||||
// If the 'no collision' call, force it to happen right now so quick collision_end
|
||||
bool force = (CollisionCollection.Count == 0);
|
||||
|
||||
// throttle the collisions to the number of milliseconds specified in the subscription
|
||||
if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
|
||||
{
|
||||
NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs;
|
||||
|
||||
// We are called if we previously had collisions. If there are no collisions
|
||||
// this time, send up one last empty event so OpenSim can sense collision end.
|
||||
if (CollisionCollection.Count == 0)
|
||||
{
|
||||
// If I have no collisions this time, remove me from the list of objects with collisions.
|
||||
ret = false;
|
||||
}
|
||||
|
||||
// DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
|
||||
base.SendCollisionUpdate(CollisionCollection);
|
||||
|
||||
// The CollisionCollection instance is passed around in the simulator.
|
||||
// Make sure we don't have a handle to that one and that a new one is used for next time.
|
||||
// This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
|
||||
// a race condition is created for the other users of this instance.
|
||||
CollisionCollection = new CollisionEventUpdate();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Subscribe for collision events.
|
||||
// Parameter is the millisecond rate the caller wishes collision events to occur.
|
||||
public override void SubscribeEvents(int ms) {
|
||||
// DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms);
|
||||
SubscribedEventsMs = ms;
|
||||
if (ms > 0)
|
||||
{
|
||||
// make sure first collision happens
|
||||
NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
|
||||
|
||||
PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
|
||||
{
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subscribing for zero or less is the same as unsubscribing
|
||||
UnSubscribeEvents();
|
||||
}
|
||||
}
|
||||
public override void UnSubscribeEvents() {
|
||||
// DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
|
||||
SubscribedEventsMs = 0;
|
||||
PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
|
||||
{
|
||||
// Make sure there is a body there because sometimes destruction happens in an un-ideal order.
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||
});
|
||||
}
|
||||
// Return 'true' if the simulator wants collision events
|
||||
public override bool SubscribedEvents() {
|
||||
return (SubscribedEventsMs > 0);
|
||||
}
|
||||
|
||||
#endregion // Collisions
|
||||
|
||||
#region Per Simulation Step actions
|
||||
// There are some actions that must be performed for a physical object before each simulation step.
|
||||
// These actions are optional so, rather than scanning all the physical objects and asking them
|
||||
// if they have anything to do, a physical object registers for an event call before the step is performed.
|
||||
// This bookkeeping makes it easy to add, remove and clean up after all these registrations.
|
||||
private Dictionary<string, BSScene.PreStepAction> RegisteredActions = new Dictionary<string, BSScene.PreStepAction>();
|
||||
protected void RegisterPreStepAction(string op, uint id, BSScene.PreStepAction actn)
|
||||
{
|
||||
string identifier = op + "-" + id.ToString();
|
||||
RegisteredActions[identifier] = actn;
|
||||
PhysicsScene.BeforeStep += actn;
|
||||
DetailLog("{0},BSPhysObject.RegisterPreStepAction,id={1}", LocalID, identifier);
|
||||
}
|
||||
|
||||
// Unregister a pre step action. Safe to call if the action has not been registered.
|
||||
protected void UnRegisterPreStepAction(string op, uint id)
|
||||
{
|
||||
string identifier = op + "-" + id.ToString();
|
||||
bool removed = false;
|
||||
if (RegisteredActions.ContainsKey(identifier))
|
||||
{
|
||||
PhysicsScene.BeforeStep -= RegisteredActions[identifier];
|
||||
RegisteredActions.Remove(identifier);
|
||||
removed = true;
|
||||
}
|
||||
DetailLog("{0},BSPhysObject.UnRegisterPreStepAction,id={1},removed={2}", LocalID, identifier, removed);
|
||||
}
|
||||
|
||||
protected void UnRegisterAllPreStepActions()
|
||||
{
|
||||
foreach (KeyValuePair<string, BSScene.PreStepAction> kvp in RegisteredActions)
|
||||
{
|
||||
PhysicsScene.BeforeStep -= kvp.Value;
|
||||
}
|
||||
RegisteredActions.Clear();
|
||||
DetailLog("{0},BSPhysObject.UnRegisterAllPreStepActions,", LocalID);
|
||||
}
|
||||
|
||||
|
||||
#endregion // Per Simulation Step actions
|
||||
|
||||
// High performance detailed logging routine used by the physical objects.
|
||||
protected void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
if (PhysicsScene.PhysicsLogging.Enabled)
|
||||
PhysicsScene.DetailLog(msg, args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 OpenSim.Framework;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim.
|
||||
/// This module interfaces to an unmanaged C++ library which makes the
|
||||
/// actual calls into the Bullet physics engine.
|
||||
/// The unmanaged library is found in opensim-libs::trunk/unmanaged/BulletSim/.
|
||||
/// The unmanaged library is compiled and linked statically with Bullet
|
||||
/// to create BulletSim.dll and libBulletSim.so (for both 32 and 64 bit).
|
||||
/// </summary>
|
||||
public class BSPlugin : IPhysicsPlugin
|
||||
{
|
||||
//private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private BSScene _mScene;
|
||||
|
||||
public BSPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Init()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public PhysicsScene GetScene(String sceneIdentifier)
|
||||
{
|
||||
if (_mScene == null)
|
||||
{
|
||||
|
||||
// If not Windows, loading is performed by the
|
||||
// Mono loader as specified in
|
||||
// "bin/Physics/OpenSim.Region.Physics.BulletSNPlugin.dll.config".
|
||||
|
||||
_mScene = new BSScene(sceneIdentifier);
|
||||
}
|
||||
return (_mScene);
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return ("BulletSimN");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,954 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework;
|
||||
using OpenSim.Region.CoreModules;
|
||||
using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
using Nini.Config;
|
||||
using log4net;
|
||||
using OpenMetaverse;
|
||||
|
||||
// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
|
||||
// Based on material, set density and friction
|
||||
// More efficient memory usage when passing hull information from BSPrim to BulletSim
|
||||
// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
|
||||
// Implement LockAngularMotion
|
||||
// Add PID movement operations. What does ScenePresence.MoveToTarget do?
|
||||
// Check terrain size. 128 or 127?
|
||||
// Raycast
|
||||
//
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public sealed class BSScene : PhysicsScene, IPhysicsParameters
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private static readonly string LogHeader = "[BULLETS SCENE]";
|
||||
|
||||
// The name of the region we're working for.
|
||||
public string RegionName { get; private set; }
|
||||
|
||||
public string BulletSimVersion = "?";
|
||||
|
||||
public Dictionary<uint, BSPhysObject> PhysObjects;
|
||||
public BSShapeCollection Shapes;
|
||||
|
||||
// Keeping track of the objects with collisions so we can report begin and end of a collision
|
||||
public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();
|
||||
public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
|
||||
// Keep track of all the avatars so we can send them a collision event
|
||||
// every tick so OpenSim will update its animation.
|
||||
private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
|
||||
|
||||
// let my minuions use my logger
|
||||
public ILog Logger { get { return m_log; } }
|
||||
|
||||
public IMesher mesher;
|
||||
public uint WorldID { get; private set; }
|
||||
public BulletSim World { get; private set; }
|
||||
|
||||
// All the constraints that have been allocated in this instance.
|
||||
public BSConstraintCollection Constraints { get; private set; }
|
||||
|
||||
// Simulation parameters
|
||||
internal int m_maxSubSteps;
|
||||
internal float m_fixedTimeStep;
|
||||
internal long m_simulationStep = 0;
|
||||
public long SimulationStep { get { return m_simulationStep; } }
|
||||
internal int m_taintsToProcessPerStep;
|
||||
internal float LastTimeStep { get; private set; }
|
||||
|
||||
// Physical objects can register for prestep or poststep events
|
||||
public delegate void PreStepAction(float timeStep);
|
||||
public delegate void PostStepAction(float timeStep);
|
||||
public event PreStepAction BeforeStep;
|
||||
public event PreStepAction AfterStep;
|
||||
|
||||
// A value of the time now so all the collision and update routines do not have to get their own
|
||||
// Set to 'now' just before all the prims and actors are called for collisions and updates
|
||||
public int SimulationNowTime { get; private set; }
|
||||
|
||||
// True if initialized and ready to do simulation steps
|
||||
private bool m_initialized = false;
|
||||
|
||||
// Flag which is true when processing taints.
|
||||
// Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
|
||||
public bool InTaintTime { get; private set; }
|
||||
|
||||
// Pinned memory used to pass step information between managed and unmanaged
|
||||
internal int m_maxCollisionsPerFrame;
|
||||
private List<BulletXNA.CollisionDesc> m_collisionArray;
|
||||
//private GCHandle m_collisionArrayPinnedHandle;
|
||||
|
||||
internal int m_maxUpdatesPerFrame;
|
||||
private List<BulletXNA.EntityProperties> m_updateArray;
|
||||
//private GCHandle m_updateArrayPinnedHandle;
|
||||
|
||||
|
||||
public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
|
||||
public const uint GROUNDPLANE_ID = 1;
|
||||
public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
|
||||
|
||||
public float SimpleWaterLevel { get; set; }
|
||||
public BSTerrainManager TerrainManager { get; private set; }
|
||||
|
||||
public ConfigurationParameters Params
|
||||
{
|
||||
get { return UnmanagedParams[0]; }
|
||||
}
|
||||
public Vector3 DefaultGravity
|
||||
{
|
||||
get { return new Vector3(0f, 0f, Params.gravity); }
|
||||
}
|
||||
// Just the Z value of the gravity
|
||||
public float DefaultGravityZ
|
||||
{
|
||||
get { return Params.gravity; }
|
||||
}
|
||||
|
||||
// When functions in the unmanaged code must be called, it is only
|
||||
// done at a known time just before the simulation step. The taint
|
||||
// system saves all these function calls and executes them in
|
||||
// order before the simulation.
|
||||
public delegate void TaintCallback();
|
||||
private struct TaintCallbackEntry
|
||||
{
|
||||
public String ident;
|
||||
public TaintCallback callback;
|
||||
public TaintCallbackEntry(string i, TaintCallback c)
|
||||
{
|
||||
ident = i;
|
||||
callback = c;
|
||||
}
|
||||
}
|
||||
private Object _taintLock = new Object(); // lock for using the next object
|
||||
private List<TaintCallbackEntry> _taintOperations;
|
||||
private Dictionary<string, TaintCallbackEntry> _postTaintOperations;
|
||||
private List<TaintCallbackEntry> _postStepOperations;
|
||||
|
||||
// A pointer to an instance if this structure is passed to the C++ code
|
||||
// Used to pass basic configuration values to the unmanaged code.
|
||||
internal ConfigurationParameters[] UnmanagedParams;
|
||||
//GCHandle m_paramsHandle;
|
||||
|
||||
// Handle to the callback used by the unmanaged code to call into the managed code.
|
||||
// Used for debug logging.
|
||||
// Need to store the handle in a persistant variable so it won't be freed.
|
||||
private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle;
|
||||
|
||||
// Sometimes you just have to log everything.
|
||||
public Logging.LogWriter PhysicsLogging;
|
||||
private bool m_physicsLoggingEnabled;
|
||||
private string m_physicsLoggingDir;
|
||||
private string m_physicsLoggingPrefix;
|
||||
private int m_physicsLoggingFileMinutes;
|
||||
private bool m_physicsLoggingDoFlush;
|
||||
// 'true' of the vehicle code is to log lots of details
|
||||
public bool VehicleLoggingEnabled { get; private set; }
|
||||
public bool VehiclePhysicalLoggingEnabled { get; private set; }
|
||||
|
||||
#region Construction and Initialization
|
||||
public BSScene(string identifier)
|
||||
{
|
||||
m_initialized = false;
|
||||
// we are passed the name of the region we're working for.
|
||||
RegionName = identifier;
|
||||
}
|
||||
|
||||
public override void Initialise(IMesher meshmerizer, IConfigSource config)
|
||||
{
|
||||
mesher = meshmerizer;
|
||||
_taintOperations = new List<TaintCallbackEntry>();
|
||||
_postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
|
||||
_postStepOperations = new List<TaintCallbackEntry>();
|
||||
PhysObjects = new Dictionary<uint, BSPhysObject>();
|
||||
Shapes = new BSShapeCollection(this);
|
||||
|
||||
// Allocate pinned memory to pass parameters.
|
||||
UnmanagedParams = new ConfigurationParameters[1];
|
||||
//m_paramsHandle = GCHandle.Alloc(UnmanagedParams, GCHandleType.Pinned);
|
||||
|
||||
// Set default values for physics parameters plus any overrides from the ini file
|
||||
GetInitialParameterValues(config);
|
||||
|
||||
// allocate more pinned memory close to the above in an attempt to get the memory all together
|
||||
m_collisionArray = new List<BulletXNA.CollisionDesc>();
|
||||
//m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned);
|
||||
m_updateArray = new List<BulletXNA.EntityProperties>();
|
||||
//m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
|
||||
|
||||
// Enable very detailed logging.
|
||||
// By creating an empty logger when not logging, the log message invocation code
|
||||
// can be left in and every call doesn't have to check for null.
|
||||
if (m_physicsLoggingEnabled)
|
||||
{
|
||||
PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
|
||||
PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsLogging = new Logging.LogWriter();
|
||||
}
|
||||
|
||||
// If Debug logging level, enable logging from the unmanaged code
|
||||
m_DebugLogCallbackHandle = null;
|
||||
if (m_log.IsDebugEnabled || PhysicsLogging.Enabled)
|
||||
{
|
||||
m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader);
|
||||
if (PhysicsLogging.Enabled)
|
||||
// The handle is saved in a variable to make sure it doesn't get freed after this call
|
||||
m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog);
|
||||
else
|
||||
m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger);
|
||||
}
|
||||
|
||||
// Get the version of the DLL
|
||||
// TODO: this doesn't work yet. Something wrong with marshaling the returned string.
|
||||
// BulletSimVersion = BulletSimAPI.GetVersion();
|
||||
// m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
|
||||
|
||||
// The bounding box for the simulated world. The origin is 0,0,0 unless we're
|
||||
// a child in a mega-region.
|
||||
// Bullet actually doesn't care about the extents of the simulated
|
||||
// area. It tracks active objects no matter where they are.
|
||||
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
|
||||
|
||||
// m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
|
||||
|
||||
World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, UnmanagedParams,
|
||||
m_maxCollisionsPerFrame, ref m_collisionArray,
|
||||
m_maxUpdatesPerFrame,ref m_updateArray,
|
||||
m_DebugLogCallbackHandle));
|
||||
|
||||
Constraints = new BSConstraintCollection(World);
|
||||
|
||||
TerrainManager = new BSTerrainManager(this);
|
||||
TerrainManager.CreateInitialGroundPlaneAndTerrain();
|
||||
|
||||
m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
|
||||
|
||||
InTaintTime = false;
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
// All default parameter values are set here. There should be no values set in the
|
||||
// variable definitions.
|
||||
private void GetInitialParameterValues(IConfigSource config)
|
||||
{
|
||||
ConfigurationParameters parms = new ConfigurationParameters();
|
||||
UnmanagedParams[0] = parms;
|
||||
|
||||
BSParam.SetParameterDefaultValues(this);
|
||||
|
||||
if (config != null)
|
||||
{
|
||||
// If there are specifications in the ini file, use those values
|
||||
IConfig pConfig = config.Configs["BulletSim"];
|
||||
if (pConfig != null)
|
||||
{
|
||||
BSParam.SetParameterConfigurationValues(this, pConfig);
|
||||
|
||||
// Very detailed logging for physics debugging
|
||||
m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
|
||||
m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
|
||||
m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
|
||||
m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
|
||||
m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
|
||||
// Very detailed logging for vehicle debugging
|
||||
VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
|
||||
VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
|
||||
|
||||
// Do any replacements in the parameters
|
||||
m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
|
||||
}
|
||||
|
||||
// The material characteristics.
|
||||
BSMaterials.InitializeFromDefaults(Params);
|
||||
if (pConfig != null)
|
||||
{
|
||||
// Let the user add new and interesting material property values.
|
||||
BSMaterials.InitializefromParameters(pConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A helper function that handles a true/false parameter and returns the proper float number encoding
|
||||
float ParamBoolean(IConfig config, string parmName, float deflt)
|
||||
{
|
||||
float ret = deflt;
|
||||
if (config.Contains(parmName))
|
||||
{
|
||||
ret = ConfigurationParameters.numericFalse;
|
||||
if (config.GetBoolean(parmName, false))
|
||||
{
|
||||
ret = ConfigurationParameters.numericTrue;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Called directly from unmanaged code so don't do much
|
||||
private void BulletLogger(string msg)
|
||||
{
|
||||
m_log.Debug("[BULLETS UNMANAGED]:" + msg);
|
||||
}
|
||||
|
||||
// Called directly from unmanaged code so don't do much
|
||||
private void BulletLoggerPhysLog(string msg)
|
||||
{
|
||||
DetailLog("[BULLETS UNMANAGED]:" + msg);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
// m_log.DebugFormat("{0}: Dispose()", LogHeader);
|
||||
|
||||
// make sure no stepping happens while we're deleting stuff
|
||||
m_initialized = false;
|
||||
|
||||
foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
|
||||
{
|
||||
kvp.Value.Destroy();
|
||||
}
|
||||
PhysObjects.Clear();
|
||||
|
||||
// Now that the prims are all cleaned up, there should be no constraints left
|
||||
if (Constraints != null)
|
||||
{
|
||||
Constraints.Dispose();
|
||||
Constraints = null;
|
||||
}
|
||||
|
||||
if (Shapes != null)
|
||||
{
|
||||
Shapes.Dispose();
|
||||
Shapes = null;
|
||||
}
|
||||
|
||||
if (TerrainManager != null)
|
||||
{
|
||||
TerrainManager.ReleaseGroundPlaneAndTerrain();
|
||||
TerrainManager.Dispose();
|
||||
TerrainManager = null;
|
||||
}
|
||||
|
||||
// Anything left in the unmanaged code should be cleaned out
|
||||
BulletSimAPI.Shutdown2(World.ptr);
|
||||
|
||||
// Not logging any more
|
||||
PhysicsLogging.Close();
|
||||
}
|
||||
#endregion // Construction and Initialization
|
||||
|
||||
#region Prim and Avatar addition and removal
|
||||
|
||||
public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
|
||||
{
|
||||
m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
|
||||
return null;
|
||||
}
|
||||
|
||||
public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
|
||||
{
|
||||
// m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
|
||||
|
||||
if (!m_initialized) return null;
|
||||
|
||||
BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
|
||||
lock (PhysObjects) PhysObjects.Add(localID, actor);
|
||||
|
||||
// TODO: Remove kludge someday.
|
||||
// We must generate a collision for avatars whether they collide or not.
|
||||
// This is required by OpenSim to update avatar animations, etc.
|
||||
lock (m_avatars) m_avatars.Add(actor);
|
||||
|
||||
return actor;
|
||||
}
|
||||
|
||||
public override void RemoveAvatar(PhysicsActor actor)
|
||||
{
|
||||
// m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
|
||||
|
||||
if (!m_initialized) return;
|
||||
|
||||
BSCharacter bsactor = actor as BSCharacter;
|
||||
if (bsactor != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (PhysObjects) PhysObjects.Remove(actor.LocalID);
|
||||
// Remove kludge someday
|
||||
lock (m_avatars) m_avatars.Remove(bsactor);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e);
|
||||
}
|
||||
bsactor.Destroy();
|
||||
// bsactor.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemovePrim(PhysicsActor prim)
|
||||
{
|
||||
if (!m_initialized) return;
|
||||
|
||||
BSPrim bsprim = prim as BSPrim;
|
||||
if (bsprim != null)
|
||||
{
|
||||
DetailLog("{0},RemovePrim,call", bsprim.LocalID);
|
||||
// m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID);
|
||||
try
|
||||
{
|
||||
lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e);
|
||||
}
|
||||
bsprim.Destroy();
|
||||
// bsprim.dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader);
|
||||
}
|
||||
}
|
||||
|
||||
public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
|
||||
Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
|
||||
{
|
||||
// m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
|
||||
|
||||
if (!m_initialized) return null;
|
||||
|
||||
DetailLog("{0},AddPrimShape,call", localID);
|
||||
|
||||
BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical);
|
||||
lock (PhysObjects) PhysObjects.Add(localID, prim);
|
||||
return prim;
|
||||
}
|
||||
|
||||
// This is a call from the simulator saying that some physical property has been updated.
|
||||
// The BulletSim driver senses the changing of relevant properties so this taint
|
||||
// information call is not needed.
|
||||
public override void AddPhysicsActorTaint(PhysicsActor prim) { }
|
||||
|
||||
#endregion // Prim and Avatar addition and removal
|
||||
|
||||
#region Simulation
|
||||
// Simulate one timestep
|
||||
public override float Simulate(float timeStep)
|
||||
{
|
||||
// prevent simulation until we've been initialized
|
||||
if (!m_initialized) return 5.0f;
|
||||
|
||||
LastTimeStep = timeStep;
|
||||
|
||||
int updatedEntityCount = 0;
|
||||
//Object updatedEntitiesPtr;
|
||||
int collidersCount = 0;
|
||||
//Object collidersPtr;
|
||||
|
||||
int beforeTime = 0;
|
||||
int simTime = 0;
|
||||
|
||||
// update the prim states while we know the physics engine is not busy
|
||||
int numTaints = _taintOperations.Count;
|
||||
|
||||
InTaintTime = true; // Only used for debugging so locking is not necessary.
|
||||
|
||||
ProcessTaints();
|
||||
|
||||
// Some of the physical objects requre individual, pre-step calls
|
||||
TriggerPreStepEvent(timeStep);
|
||||
|
||||
// the prestep actions might have added taints
|
||||
ProcessTaints();
|
||||
|
||||
InTaintTime = false; // Only used for debugging so locking is not necessary.
|
||||
|
||||
// step the physical world one interval
|
||||
m_simulationStep++;
|
||||
int numSubSteps = 0;
|
||||
|
||||
try
|
||||
{
|
||||
//if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG
|
||||
if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
|
||||
|
||||
numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
|
||||
out updatedEntityCount, out m_updateArray, out collidersCount, out m_collisionArray);
|
||||
|
||||
if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
|
||||
DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
|
||||
DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
|
||||
updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
|
||||
LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
|
||||
DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
|
||||
DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
|
||||
updatedEntityCount = 0;
|
||||
collidersCount = 0;
|
||||
}
|
||||
|
||||
// Don't have to use the pointers passed back since we know it is the same pinned memory we passed in.
|
||||
|
||||
// Get a value for 'now' so all the collision and update routines don't have to get their own.
|
||||
SimulationNowTime = Util.EnvironmentTickCount();
|
||||
|
||||
// If there were collisions, process them by sending the event to the prim.
|
||||
// Collisions must be processed before updates.
|
||||
if (collidersCount > 0)
|
||||
{
|
||||
for (int ii = 0; ii < collidersCount; ii++)
|
||||
{
|
||||
uint cA = m_collisionArray[ii].aID;
|
||||
uint cB = m_collisionArray[ii].bID;
|
||||
Vector3 point = new Vector3(m_collisionArray[ii].point.X, m_collisionArray[ii].point.Y,
|
||||
m_collisionArray[ii].point.Z);
|
||||
Vector3 normal = new Vector3(m_collisionArray[ii].normal.X, m_collisionArray[ii].normal.Y,
|
||||
m_collisionArray[ii].normal.Z);
|
||||
SendCollision(cA, cB, point, normal, 0.01f);
|
||||
SendCollision(cB, cA, point, -normal, 0.01f);
|
||||
}
|
||||
}
|
||||
|
||||
// The above SendCollision's batch up the collisions on the objects.
|
||||
// Now push the collisions into the simulator.
|
||||
if (ObjectsWithCollisions.Count > 0)
|
||||
{
|
||||
foreach (BSPhysObject bsp in ObjectsWithCollisions)
|
||||
if (!bsp.SendCollisions())
|
||||
{
|
||||
// If the object is done colliding, see that it's removed from the colliding list
|
||||
ObjectsWithNoMoreCollisions.Add(bsp);
|
||||
}
|
||||
}
|
||||
|
||||
// This is a kludge to get avatar movement updates.
|
||||
// The simulator expects collisions for avatars even if there are have been no collisions.
|
||||
// The event updates avatar animations and stuff.
|
||||
// If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
|
||||
foreach (BSPhysObject bsp in m_avatars)
|
||||
if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
|
||||
bsp.SendCollisions();
|
||||
|
||||
// Objects that are done colliding are removed from the ObjectsWithCollisions list.
|
||||
// Not done above because it is inside an iteration of ObjectWithCollisions.
|
||||
// This complex collision processing is required to create an empty collision
|
||||
// event call after all collisions have happened on an object. This enables
|
||||
// the simulator to generate the 'collision end' event.
|
||||
if (ObjectsWithNoMoreCollisions.Count > 0)
|
||||
{
|
||||
foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
|
||||
ObjectsWithCollisions.Remove(po);
|
||||
ObjectsWithNoMoreCollisions.Clear();
|
||||
}
|
||||
// Done with collisions.
|
||||
|
||||
// If any of the objects had updated properties, tell the object it has been changed by the physics engine
|
||||
if (updatedEntityCount > 0)
|
||||
{
|
||||
for (int ii = 0; ii < updatedEntityCount; ii++)
|
||||
{
|
||||
|
||||
BulletXNA.EntityProperties entprop = m_updateArray[ii];
|
||||
BSPhysObject pobj;
|
||||
if (PhysObjects.TryGetValue(entprop.ID, out pobj))
|
||||
{
|
||||
EntityProperties prop = new EntityProperties()
|
||||
{
|
||||
Acceleration = new Vector3(entprop.Acceleration.X, entprop.Acceleration.Y, entprop.Acceleration.Z),
|
||||
ID = entprop.ID,
|
||||
Position = new Vector3(entprop.Position.X,entprop.Position.Y,entprop.Position.Z),
|
||||
Rotation = new Quaternion(entprop.Rotation.X,entprop.Rotation.Y,entprop.Rotation.Z,entprop.Rotation.W),
|
||||
RotationalVelocity = new Vector3(entprop.AngularVelocity.X,entprop.AngularVelocity.Y,entprop.AngularVelocity.Z),
|
||||
Velocity = new Vector3(entprop.Velocity.X,entprop.Velocity.Y,entprop.Velocity.Z)
|
||||
};
|
||||
//m_log.Debug(pobj.Name + ":" + prop.ToString() + "\n");
|
||||
pobj.UpdateProperties(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TriggerPostStepEvent(timeStep);
|
||||
|
||||
// The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
|
||||
// Only enable this in a limited test world with few objects.
|
||||
// BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
|
||||
|
||||
// The physics engine returns the number of milliseconds it simulated this call.
|
||||
// These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
|
||||
// Multiply by 55 to give a nominal frame rate of 55.
|
||||
return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f;
|
||||
}
|
||||
|
||||
// Something has collided
|
||||
private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
|
||||
{
|
||||
if (localID <= TerrainManager.HighestTerrainID)
|
||||
{
|
||||
return; // don't send collisions to the terrain
|
||||
}
|
||||
|
||||
BSPhysObject collider;
|
||||
if (!PhysObjects.TryGetValue(localID, out collider))
|
||||
{
|
||||
// If the object that is colliding cannot be found, just ignore the collision.
|
||||
DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
|
||||
return;
|
||||
}
|
||||
|
||||
// The terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
|
||||
BSPhysObject collidee = null;
|
||||
PhysObjects.TryGetValue(collidingWith, out collidee);
|
||||
|
||||
// DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
|
||||
|
||||
if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
|
||||
{
|
||||
// If a collision was posted, remember to send it to the simulator
|
||||
ObjectsWithCollisions.Add(collider);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion // Simulation
|
||||
|
||||
public override void GetResults() { }
|
||||
|
||||
#region Terrain
|
||||
|
||||
public override void SetTerrain(float[] heightMap) {
|
||||
TerrainManager.SetTerrain(heightMap);
|
||||
}
|
||||
|
||||
public override void SetWaterLevel(float baseheight)
|
||||
{
|
||||
SimpleWaterLevel = baseheight;
|
||||
}
|
||||
|
||||
public override void DeleteTerrain()
|
||||
{
|
||||
// m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
|
||||
}
|
||||
|
||||
// Although no one seems to check this, I do support combining.
|
||||
public override bool SupportsCombining()
|
||||
{
|
||||
return TerrainManager.SupportsCombining();
|
||||
}
|
||||
// This call says I am a child to region zero in a mega-region. 'pScene' is that
|
||||
// of region zero, 'offset' is my offset from regions zero's origin, and
|
||||
// 'extents' is the largest XY that is handled in my region.
|
||||
public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
|
||||
{
|
||||
TerrainManager.Combine(pScene, offset, extents);
|
||||
}
|
||||
|
||||
// Unhook all the combining that I know about.
|
||||
public override void UnCombine(PhysicsScene pScene)
|
||||
{
|
||||
TerrainManager.UnCombine(pScene);
|
||||
}
|
||||
|
||||
#endregion // Terrain
|
||||
|
||||
public override Dictionary<uint, float> GetTopColliders()
|
||||
{
|
||||
return new Dictionary<uint, float>();
|
||||
}
|
||||
|
||||
public override bool IsThreaded { get { return false; } }
|
||||
|
||||
#region Taints
|
||||
// The simulation execution order is:
|
||||
// Simulate()
|
||||
// DoOneTimeTaints
|
||||
// TriggerPreStepEvent
|
||||
// DoOneTimeTaints
|
||||
// Step()
|
||||
// ProcessAndForwardCollisions
|
||||
// ProcessAndForwardPropertyUpdates
|
||||
// TriggerPostStepEvent
|
||||
|
||||
// Calls to the PhysicsActors can't directly call into the physics engine
|
||||
// because it might be busy. We delay changes to a known time.
|
||||
// We rely on C#'s closure to save and restore the context for the delegate.
|
||||
public void TaintedObject(String ident, TaintCallback callback)
|
||||
{
|
||||
if (!m_initialized) return;
|
||||
|
||||
lock (_taintLock)
|
||||
{
|
||||
_taintOperations.Add(new TaintCallbackEntry(ident, callback));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Sometimes a potentially tainted operation can be used in and out of taint time.
|
||||
// This routine executes the command immediately if in taint-time otherwise it is queued.
|
||||
public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback)
|
||||
{
|
||||
if (inTaintTime)
|
||||
callback();
|
||||
else
|
||||
TaintedObject(ident, callback);
|
||||
}
|
||||
|
||||
private void TriggerPreStepEvent(float timeStep)
|
||||
{
|
||||
PreStepAction actions = BeforeStep;
|
||||
if (actions != null)
|
||||
actions(timeStep);
|
||||
|
||||
}
|
||||
|
||||
private void TriggerPostStepEvent(float timeStep)
|
||||
{
|
||||
PreStepAction actions = AfterStep;
|
||||
if (actions != null)
|
||||
actions(timeStep);
|
||||
|
||||
}
|
||||
|
||||
// When someone tries to change a property on a BSPrim or BSCharacter, the object queues
|
||||
// a callback into itself to do the actual property change. That callback is called
|
||||
// here just before the physics engine is called to step the simulation.
|
||||
public void ProcessTaints()
|
||||
{
|
||||
ProcessRegularTaints();
|
||||
ProcessPostTaintTaints();
|
||||
}
|
||||
|
||||
private void ProcessRegularTaints()
|
||||
{
|
||||
if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process
|
||||
{
|
||||
// swizzle a new list into the list location so we can process what's there
|
||||
List<TaintCallbackEntry> oldList;
|
||||
lock (_taintLock)
|
||||
{
|
||||
oldList = _taintOperations;
|
||||
_taintOperations = new List<TaintCallbackEntry>();
|
||||
}
|
||||
|
||||
foreach (TaintCallbackEntry tcbe in oldList)
|
||||
{
|
||||
try
|
||||
{
|
||||
DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
|
||||
tcbe.callback();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
|
||||
}
|
||||
}
|
||||
oldList.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule an update to happen after all the regular taints are processed.
|
||||
// Note that new requests for the same operation ("ident") for the same object ("ID")
|
||||
// will replace any previous operation by the same object.
|
||||
public void PostTaintObject(String ident, uint ID, TaintCallback callback)
|
||||
{
|
||||
string uniqueIdent = ident + "-" + ID.ToString();
|
||||
lock (_taintLock)
|
||||
{
|
||||
_postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Taints that happen after the normal taint processing but before the simulation step.
|
||||
private void ProcessPostTaintTaints()
|
||||
{
|
||||
if (_postTaintOperations.Count > 0)
|
||||
{
|
||||
Dictionary<string, TaintCallbackEntry> oldList;
|
||||
lock (_taintLock)
|
||||
{
|
||||
oldList = _postTaintOperations;
|
||||
_postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
|
||||
{
|
||||
try
|
||||
{
|
||||
DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
|
||||
kvp.Value.callback();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
|
||||
}
|
||||
}
|
||||
oldList.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Only used for debugging. Does not change state of anything so locking is not necessary.
|
||||
public bool AssertInTaintTime(string whereFrom)
|
||||
{
|
||||
if (!InTaintTime)
|
||||
{
|
||||
DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
|
||||
m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
|
||||
Util.PrintCallStack(); // Prints the stack into the DEBUG log file.
|
||||
}
|
||||
return InTaintTime;
|
||||
}
|
||||
|
||||
#endregion // Taints
|
||||
|
||||
#region INI and command line parameter processing
|
||||
|
||||
#region IPhysicsParameters
|
||||
// Get the list of parameters this physics engine supports
|
||||
public PhysParameterEntry[] GetParameterList()
|
||||
{
|
||||
BSParam.BuildParameterTable();
|
||||
return BSParam.SettableParameters;
|
||||
}
|
||||
|
||||
// Set parameter on a specific or all instances.
|
||||
// Return 'false' if not able to set the parameter.
|
||||
// Setting the value in the m_params block will change the value the physics engine
|
||||
// will use the next time since it's pinned and shared memory.
|
||||
// Some of the values require calling into the physics engine to get the new
|
||||
// value activated ('terrainFriction' for instance).
|
||||
public bool SetPhysicsParameter(string parm, float val, uint localID)
|
||||
{
|
||||
bool ret = false;
|
||||
BSParam.ParameterDefn theParam;
|
||||
if (BSParam.TryGetParameter(parm, out theParam))
|
||||
{
|
||||
theParam.setter(this, parm, localID, val);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// update all the localIDs specified
|
||||
// If the local ID is APPLY_TO_NONE, just change the default value
|
||||
// If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
|
||||
// If the localID is a specific object, apply the parameter change to only that object
|
||||
internal delegate void AssignVal(float x);
|
||||
internal void UpdateParameterObject(AssignVal setDefault, string parm, uint localID, float val)
|
||||
{
|
||||
List<uint> objectIDs = new List<uint>();
|
||||
switch (localID)
|
||||
{
|
||||
case PhysParameterEntry.APPLY_TO_NONE:
|
||||
setDefault(val); // setting only the default value
|
||||
// This will cause a call into the physical world if some operation is specified (SetOnObject).
|
||||
objectIDs.Add(TERRAIN_ID);
|
||||
TaintedUpdateParameter(parm, objectIDs, val);
|
||||
break;
|
||||
case PhysParameterEntry.APPLY_TO_ALL:
|
||||
setDefault(val); // setting ALL also sets the default value
|
||||
lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
|
||||
TaintedUpdateParameter(parm, objectIDs, val);
|
||||
break;
|
||||
default:
|
||||
// setting only one localID
|
||||
objectIDs.Add(localID);
|
||||
TaintedUpdateParameter(parm, objectIDs, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// schedule the actual updating of the paramter to when the phys engine is not busy
|
||||
private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
|
||||
{
|
||||
float xval = val;
|
||||
List<uint> xlIDs = lIDs;
|
||||
string xparm = parm;
|
||||
TaintedObject("BSScene.UpdateParameterSet", delegate() {
|
||||
BSParam.ParameterDefn thisParam;
|
||||
if (BSParam.TryGetParameter(xparm, out thisParam))
|
||||
{
|
||||
if (thisParam.onObject != null)
|
||||
{
|
||||
foreach (uint lID in xlIDs)
|
||||
{
|
||||
BSPhysObject theObject = null;
|
||||
PhysObjects.TryGetValue(lID, out theObject);
|
||||
thisParam.onObject(this, theObject, xval);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get parameter.
|
||||
// Return 'false' if not able to get the parameter.
|
||||
public bool GetPhysicsParameter(string parm, out float value)
|
||||
{
|
||||
float val = 0f;
|
||||
bool ret = false;
|
||||
BSParam.ParameterDefn theParam;
|
||||
if (BSParam.TryGetParameter(parm, out theParam))
|
||||
{
|
||||
val = theParam.getter(this);
|
||||
ret = true;
|
||||
}
|
||||
value = val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endregion IPhysicsParameters
|
||||
|
||||
#endregion Runtime settable parameters
|
||||
|
||||
// Invoke the detailed logger and output something if it's enabled.
|
||||
public void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
PhysicsLogging.Write(msg, args);
|
||||
// Add the Flush() if debugging crashes. Gets all the messages written out.
|
||||
if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
|
||||
}
|
||||
// Used to fill in the LocalID when there isn't one. It's the correct number of characters.
|
||||
public const string DetailLogZero = "0000000000";
|
||||
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public abstract class BSShape
|
||||
{
|
||||
public Object ptr { get; set; }
|
||||
public BSPhysicsShapeType type { get; set; }
|
||||
public System.UInt64 key { get; set; }
|
||||
public int referenceCount { get; set; }
|
||||
public DateTime lastReferenced { get; set; }
|
||||
|
||||
public BSShape()
|
||||
{
|
||||
ptr = null;
|
||||
type = BSPhysicsShapeType.SHAPE_UNKNOWN;
|
||||
key = 0;
|
||||
referenceCount = 0;
|
||||
lastReferenced = DateTime.Now;
|
||||
}
|
||||
|
||||
// Get a reference to a physical shape. Create if it doesn't exist
|
||||
public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
|
||||
{
|
||||
BSShape ret = null;
|
||||
|
||||
if (prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
|
||||
{
|
||||
// an avatar capsule is close to a native shape (it is not shared)
|
||||
ret = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE,
|
||||
FixedShapeKey.KEY_CAPSULE);
|
||||
physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret);
|
||||
}
|
||||
|
||||
// Compound shapes are handled special as they are rebuilt from scratch.
|
||||
// This isn't too great a hardship since most of the child shapes will already been created.
|
||||
if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
|
||||
{
|
||||
// Getting a reference to a compound shape gets you the compound shape with the root prim shape added
|
||||
ret = BSShapeCompound.GetReference(prim);
|
||||
physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret);
|
||||
}
|
||||
|
||||
if (ret == null)
|
||||
ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim);
|
||||
|
||||
return ret;
|
||||
}
|
||||
public static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public static BSShape GetShapeReferenceNonNative(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Release the use of a physical shape.
|
||||
public abstract void Dereference(BSScene physicsScene);
|
||||
|
||||
// All shapes have a static call to get a reference to the physical shape
|
||||
// protected abstract static BSShape GetReference();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.Append("<p=");
|
||||
buff.Append(ptr.ToString());
|
||||
buff.Append(",s=");
|
||||
buff.Append(type.ToString());
|
||||
buff.Append(",k=");
|
||||
buff.Append(key.ToString("X"));
|
||||
buff.Append(",c=");
|
||||
buff.Append(referenceCount.ToString());
|
||||
buff.Append(">");
|
||||
return buff.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class BSShapeNull : BSShape
|
||||
{
|
||||
public BSShapeNull() : base()
|
||||
{
|
||||
}
|
||||
public static BSShape GetReference() { return new BSShapeNull(); }
|
||||
public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ }
|
||||
}
|
||||
|
||||
public class BSShapeNative : BSShape
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM SHAPE NATIVE]";
|
||||
public BSShapeNative() : base()
|
||||
{
|
||||
}
|
||||
public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
|
||||
BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
|
||||
{
|
||||
// Native shapes are not shared and are always built anew.
|
||||
return new BSShapeNative(physicsScene, prim, shapeType, shapeKey);
|
||||
}
|
||||
|
||||
private BSShapeNative(BSScene physicsScene, BSPhysObject prim,
|
||||
BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
|
||||
{
|
||||
ShapeData nativeShapeData = new ShapeData();
|
||||
nativeShapeData.Type = shapeType;
|
||||
nativeShapeData.ID = prim.LocalID;
|
||||
nativeShapeData.Scale = prim.Scale;
|
||||
nativeShapeData.Size = prim.Scale;
|
||||
nativeShapeData.MeshKey = (ulong)shapeKey;
|
||||
nativeShapeData.HullKey = (ulong)shapeKey;
|
||||
|
||||
|
||||
if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
|
||||
{
|
||||
ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale);
|
||||
physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = BulletSimAPI.BuildNativeShape2(physicsScene.World.ptr, nativeShapeData);
|
||||
}
|
||||
if (ptr == null)
|
||||
{
|
||||
physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
|
||||
LogHeader, prim.LocalID, shapeType);
|
||||
}
|
||||
type = shapeType;
|
||||
key = (UInt64)shapeKey;
|
||||
}
|
||||
// Make this reference to the physical shape go away since native shapes are not shared.
|
||||
public override void Dereference(BSScene physicsScene)
|
||||
{
|
||||
// Native shapes are not tracked and are released immediately
|
||||
physicsScene.DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this);
|
||||
BulletSimAPI.DeleteCollisionShape2(physicsScene.World.ptr, ptr);
|
||||
ptr = null;
|
||||
// Garbage collection will free up this instance.
|
||||
}
|
||||
}
|
||||
|
||||
public class BSShapeMesh : BSShape
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM SHAPE MESH]";
|
||||
private static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
|
||||
|
||||
public BSShapeMesh() : base()
|
||||
{
|
||||
}
|
||||
public static BSShape GetReference() { return new BSShapeNull(); }
|
||||
public override void Dereference(BSScene physicsScene) { }
|
||||
}
|
||||
|
||||
public class BSShapeHull : BSShape
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM SHAPE HULL]";
|
||||
private static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>();
|
||||
|
||||
public BSShapeHull() : base()
|
||||
{
|
||||
}
|
||||
public static BSShape GetReference() { return new BSShapeNull(); }
|
||||
public override void Dereference(BSScene physicsScene) { }
|
||||
}
|
||||
|
||||
public class BSShapeCompound : BSShape
|
||||
{
|
||||
private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]";
|
||||
public BSShapeCompound() : base()
|
||||
{
|
||||
}
|
||||
public static BSShape GetReference(BSPhysObject prim)
|
||||
{
|
||||
return new BSShapeNull();
|
||||
}
|
||||
public override void Dereference(BSScene physicsScene) { }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework;
|
||||
using OpenSim.Region.CoreModules;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
using Nini.Config;
|
||||
using log4net;
|
||||
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public sealed class BSTerrainHeightmap : BSTerrainPhys
|
||||
{
|
||||
static string LogHeader = "[BULLETSIM TERRAIN HEIGHTMAP]";
|
||||
|
||||
BulletHeightMapInfo m_mapInfo = null;
|
||||
|
||||
// Constructor to build a default, flat heightmap terrain.
|
||||
public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
|
||||
: base(physicsScene, regionBase, id)
|
||||
{
|
||||
Vector3 minTerrainCoords = new Vector3(0f, 0f, BSTerrainManager.HEIGHT_INITIALIZATION - BSTerrainManager.HEIGHT_EQUAL_FUDGE);
|
||||
Vector3 maxTerrainCoords = new Vector3(regionSize.X, regionSize.Y, BSTerrainManager.HEIGHT_INITIALIZATION);
|
||||
int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y;
|
||||
float[] initialMap = new float[totalHeights];
|
||||
for (int ii = 0; ii < totalHeights; ii++)
|
||||
{
|
||||
initialMap[ii] = BSTerrainManager.HEIGHT_INITIALIZATION;
|
||||
}
|
||||
m_mapInfo = new BulletHeightMapInfo(id, initialMap, null);
|
||||
m_mapInfo.minCoords = minTerrainCoords;
|
||||
m_mapInfo.maxCoords = maxTerrainCoords;
|
||||
m_mapInfo.terrainRegionBase = TerrainBase;
|
||||
// Don't have to free any previous since we just got here.
|
||||
BuildHeightmapTerrain();
|
||||
}
|
||||
|
||||
// This minCoords and maxCoords passed in give the size of the terrain (min and max Z
|
||||
// are the high and low points of the heightmap).
|
||||
public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
|
||||
Vector3 minCoords, Vector3 maxCoords)
|
||||
: base(physicsScene, regionBase, id)
|
||||
{
|
||||
m_mapInfo = new BulletHeightMapInfo(id, initialMap, null);
|
||||
m_mapInfo.minCoords = minCoords;
|
||||
m_mapInfo.maxCoords = maxCoords;
|
||||
m_mapInfo.minZ = minCoords.Z;
|
||||
m_mapInfo.maxZ = maxCoords.Z;
|
||||
m_mapInfo.terrainRegionBase = TerrainBase;
|
||||
|
||||
// Don't have to free any previous since we just got here.
|
||||
BuildHeightmapTerrain();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
ReleaseHeightMapTerrain();
|
||||
}
|
||||
|
||||
// Using the information in m_mapInfo, create the physical representation of the heightmap.
|
||||
private void BuildHeightmapTerrain()
|
||||
{
|
||||
m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID,
|
||||
m_mapInfo.minCoords, m_mapInfo.maxCoords,
|
||||
m_mapInfo.heightMap, BSParam.TerrainCollisionMargin);
|
||||
|
||||
// Create the terrain shape from the mapInfo
|
||||
m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr),
|
||||
BSPhysicsShapeType.SHAPE_TERRAIN);
|
||||
|
||||
// The terrain object initial position is at the center of the object
|
||||
Vector3 centerPos;
|
||||
centerPos.X = m_mapInfo.minCoords.X + (m_mapInfo.sizeX / 2f);
|
||||
centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f);
|
||||
centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f + 0.5f);
|
||||
|
||||
m_mapInfo.terrainBody = new BulletBody(m_mapInfo.ID,
|
||||
BulletSimAPI.CreateBodyWithDefaultMotionState2(m_mapInfo.terrainShape.ptr,
|
||||
m_mapInfo.ID, centerPos, Quaternion.Identity));
|
||||
|
||||
// Set current terrain attributes
|
||||
BulletSimAPI.SetFriction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainFriction);
|
||||
BulletSimAPI.SetHitFraction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainHitFraction);
|
||||
BulletSimAPI.SetRestitution2(m_mapInfo.terrainBody.ptr, BSParam.TerrainRestitution);
|
||||
BulletSimAPI.SetCollisionFlags2(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
|
||||
|
||||
// Return the new terrain to the world of physical objects
|
||||
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr, centerPos, Quaternion.Identity);
|
||||
|
||||
// redo its bounding box now that it is in the world
|
||||
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
|
||||
|
||||
m_mapInfo.terrainBody.collisionType = CollisionType.Terrain;
|
||||
m_mapInfo.terrainBody.ApplyCollisionMask();
|
||||
|
||||
// Make it so the terrain will not move or be considered for movement.
|
||||
BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is information in m_mapInfo pointing to physical structures, release same.
|
||||
private void ReleaseHeightMapTerrain()
|
||||
{
|
||||
if (m_mapInfo != null)
|
||||
{
|
||||
if (m_mapInfo.terrainBody.HasPhysicalBody)
|
||||
{
|
||||
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
|
||||
// Frees both the body and the shape.
|
||||
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
|
||||
BulletSimAPI.ReleaseHeightMapInfo2(m_mapInfo.Ptr);
|
||||
}
|
||||
}
|
||||
m_mapInfo = null;
|
||||
}
|
||||
|
||||
// The passed position is relative to the base of the region.
|
||||
public override float GetTerrainHeightAtXYZ(Vector3 pos)
|
||||
{
|
||||
float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||
|
||||
int mapIndex = (int)pos.Y * (int)m_mapInfo.sizeY + (int)pos.X;
|
||||
try
|
||||
{
|
||||
ret = m_mapInfo.heightMap[mapIndex];
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
|
||||
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
|
||||
LogHeader, m_mapInfo.terrainRegionBase, pos);
|
||||
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The passed position is relative to the base of the region.
|
||||
public override float GetWaterLevelAtXYZ(Vector3 pos)
|
||||
{
|
||||
return PhysicsScene.SimpleWaterLevel;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework;
|
||||
using OpenSim.Region.CoreModules;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
using Nini.Config;
|
||||
using log4net;
|
||||
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
|
||||
// The physical implementation of the terrain is wrapped in this class.
|
||||
public abstract class BSTerrainPhys : IDisposable
|
||||
{
|
||||
public enum TerrainImplementation
|
||||
{
|
||||
Heightmap = 0,
|
||||
Mesh = 1
|
||||
}
|
||||
|
||||
public BSScene PhysicsScene { get; private set; }
|
||||
// Base of the region in world coordinates. Coordinates inside the region are relative to this.
|
||||
public Vector3 TerrainBase { get; private set; }
|
||||
public uint ID { get; private set; }
|
||||
|
||||
public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id)
|
||||
{
|
||||
PhysicsScene = physicsScene;
|
||||
TerrainBase = regionBase;
|
||||
ID = id;
|
||||
}
|
||||
public abstract void Dispose();
|
||||
public abstract float GetTerrainHeightAtXYZ(Vector3 pos);
|
||||
public abstract float GetWaterLevelAtXYZ(Vector3 pos);
|
||||
}
|
||||
|
||||
// ==========================================================================================
|
||||
public sealed class BSTerrainManager : IDisposable
|
||||
{
|
||||
static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
|
||||
|
||||
// These height values are fractional so the odd values will be
|
||||
// noticable when debugging.
|
||||
public const float HEIGHT_INITIALIZATION = 24.987f;
|
||||
public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f;
|
||||
public const float HEIGHT_GETHEIGHT_RET = 24.765f;
|
||||
public const float WATER_HEIGHT_GETHEIGHT_RET = 19.998f;
|
||||
|
||||
// If the min and max height are equal, we reduce the min by this
|
||||
// amount to make sure that a bounding box is built for the terrain.
|
||||
public const float HEIGHT_EQUAL_FUDGE = 0.2f;
|
||||
|
||||
// Until the whole simulator is changed to pass us the region size, we rely on constants.
|
||||
public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
|
||||
|
||||
// The scene that I am part of
|
||||
private BSScene PhysicsScene { get; set; }
|
||||
|
||||
// The ground plane created to keep thing from falling to infinity.
|
||||
private BulletBody m_groundPlane;
|
||||
|
||||
// If doing mega-regions, if we're region zero we will be managing multiple
|
||||
// region terrains since region zero does the physics for the whole mega-region.
|
||||
private Dictionary<Vector3, BSTerrainPhys> m_terrains;
|
||||
|
||||
// Flags used to know when to recalculate the height.
|
||||
private bool m_terrainModified = false;
|
||||
|
||||
// If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
|
||||
// This is incremented before assigning to new region so it is the last ID allocated.
|
||||
private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1;
|
||||
public uint HighestTerrainID { get {return m_terrainCount; } }
|
||||
|
||||
// If doing mega-regions, this holds our offset from region zero of
|
||||
// the mega-regions. "parentScene" points to the PhysicsScene of region zero.
|
||||
private Vector3 m_worldOffset;
|
||||
// If the parent region (region 0), this is the extent of the combined regions
|
||||
// relative to the origin of region zero
|
||||
private Vector3 m_worldMax;
|
||||
private PhysicsScene MegaRegionParentPhysicsScene { get; set; }
|
||||
|
||||
public BSTerrainManager(BSScene physicsScene)
|
||||
{
|
||||
PhysicsScene = physicsScene;
|
||||
m_terrains = new Dictionary<Vector3,BSTerrainPhys>();
|
||||
|
||||
// Assume one region of default size
|
||||
m_worldOffset = Vector3.Zero;
|
||||
m_worldMax = new Vector3(DefaultRegionSize);
|
||||
MegaRegionParentPhysicsScene = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ReleaseGroundPlaneAndTerrain();
|
||||
}
|
||||
|
||||
// Create the initial instance of terrain and the underlying ground plane.
|
||||
// This is called from the initialization routine so we presume it is
|
||||
// safe to call Bullet in real time. We hope no one is moving prims around yet.
|
||||
public void CreateInitialGroundPlaneAndTerrain()
|
||||
{
|
||||
// The ground plane is here to catch things that are trying to drop to negative infinity
|
||||
BulletShape groundPlaneShape = new BulletShape(
|
||||
BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f,
|
||||
BSParam.TerrainCollisionMargin),
|
||||
BSPhysicsShapeType.SHAPE_GROUNDPLANE);
|
||||
m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
|
||||
BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
|
||||
Vector3.Zero, Quaternion.Identity));
|
||||
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr);
|
||||
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr);
|
||||
// Ground plane does not move
|
||||
BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
|
||||
// Everything collides with the ground plane.
|
||||
m_groundPlane.collisionType = CollisionType.Groundplane;
|
||||
m_groundPlane.ApplyCollisionMask();
|
||||
|
||||
// Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
|
||||
BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
|
||||
m_terrains.Add(Vector3.Zero, initialTerrain);
|
||||
}
|
||||
|
||||
// Release all the terrain structures we might have allocated
|
||||
public void ReleaseGroundPlaneAndTerrain()
|
||||
{
|
||||
if (m_groundPlane.HasPhysicalBody)
|
||||
{
|
||||
if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr))
|
||||
{
|
||||
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr);
|
||||
}
|
||||
m_groundPlane.Clear();
|
||||
}
|
||||
|
||||
ReleaseTerrain();
|
||||
}
|
||||
|
||||
// Release all the terrain we have allocated
|
||||
public void ReleaseTerrain()
|
||||
{
|
||||
lock (m_terrains)
|
||||
{
|
||||
foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
|
||||
{
|
||||
kvp.Value.Dispose();
|
||||
}
|
||||
m_terrains.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// The simulator wants to set a new heightmap for the terrain.
|
||||
public void SetTerrain(float[] heightMap) {
|
||||
float[] localHeightMap = heightMap;
|
||||
// If there are multiple requests for changes to the same terrain between ticks,
|
||||
// only do that last one.
|
||||
PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate()
|
||||
{
|
||||
if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
|
||||
{
|
||||
// If a child of a mega-region, we shouldn't have any terrain allocated for us
|
||||
ReleaseGroundPlaneAndTerrain();
|
||||
// If doing the mega-prim stuff and we are the child of the zero region,
|
||||
// the terrain is added to our parent
|
||||
if (MegaRegionParentPhysicsScene is BSScene)
|
||||
{
|
||||
DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
|
||||
BSScene.DetailLogZero, m_worldOffset, m_worldMax);
|
||||
((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain(
|
||||
BSScene.CHILDTERRAIN_ID, localHeightMap,
|
||||
m_worldOffset, m_worldOffset + DefaultRegionSize, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If not doing the mega-prim thing, just change the terrain
|
||||
DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
|
||||
|
||||
UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap,
|
||||
m_worldOffset, m_worldOffset + DefaultRegionSize, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If called with no mapInfo for the terrain, this will create a new mapInfo and terrain
|
||||
// based on the passed information. The 'id' should be either the terrain id or
|
||||
// BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
|
||||
// The latter feature is for creating child terrains for mega-regions.
|
||||
// If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new
|
||||
// terrain shape is created and added to the body.
|
||||
// This call is most often used to update the heightMap and parameters of the terrain.
|
||||
// (The above does suggest that some simplification/refactoring is in order.)
|
||||
// Called during taint-time.
|
||||
private void UpdateTerrain(uint id, float[] heightMap,
|
||||
Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
|
||||
{
|
||||
DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
|
||||
BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
|
||||
|
||||
// Find high and low points of passed heightmap.
|
||||
// The min and max passed in is usually the area objects can be in (maximum
|
||||
// object height, for instance). The terrain wants the bounding box for the
|
||||
// terrain so replace passed min and max Z with the actual terrain min/max Z.
|
||||
float minZ = float.MaxValue;
|
||||
float maxZ = float.MinValue;
|
||||
foreach (float height in heightMap)
|
||||
{
|
||||
if (height < minZ) minZ = height;
|
||||
if (height > maxZ) maxZ = height;
|
||||
}
|
||||
if (minZ == maxZ)
|
||||
{
|
||||
// If min and max are the same, reduce min a little bit so a good bounding box is created.
|
||||
minZ -= BSTerrainManager.HEIGHT_EQUAL_FUDGE;
|
||||
}
|
||||
minCoords.Z = minZ;
|
||||
maxCoords.Z = maxZ;
|
||||
|
||||
Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
|
||||
|
||||
lock (m_terrains)
|
||||
{
|
||||
BSTerrainPhys terrainPhys;
|
||||
if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
|
||||
{
|
||||
// There is already a terrain in this spot. Free the old and build the new.
|
||||
DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
|
||||
BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
|
||||
|
||||
// Remove old terrain from the collection
|
||||
m_terrains.Remove(terrainRegionBase);
|
||||
// Release any physical memory it may be using.
|
||||
terrainPhys.Dispose();
|
||||
|
||||
if (MegaRegionParentPhysicsScene == null)
|
||||
{
|
||||
BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
|
||||
m_terrains.Add(terrainRegionBase, newTerrainPhys);
|
||||
|
||||
m_terrainModified = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's possible that Combine() was called after this code was queued.
|
||||
// If we are a child of combined regions, we don't create any terrain for us.
|
||||
DetailLog("{0},BSTerrainManager.UpdateTerrain:AmACombineChild,taint", BSScene.DetailLogZero);
|
||||
|
||||
// Get rid of any terrain that may have been allocated for us.
|
||||
ReleaseGroundPlaneAndTerrain();
|
||||
|
||||
// I hate doing this, but just bail
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't know about this terrain so either we are creating a new terrain or
|
||||
// our mega-prim child is giving us a new terrain to add to the phys world
|
||||
|
||||
// if this is a child terrain, calculate a unique terrain id
|
||||
uint newTerrainID = id;
|
||||
if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
|
||||
newTerrainID = ++m_terrainCount;
|
||||
|
||||
DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
|
||||
BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
|
||||
BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
|
||||
m_terrains.Add(terrainRegionBase, newTerrainPhys);
|
||||
|
||||
m_terrainModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: redo terrain implementation selection to allow other base types than heightMap.
|
||||
private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
|
||||
{
|
||||
PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
|
||||
LogHeader, PhysicsScene.RegionName, terrainRegionBase,
|
||||
(BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation);
|
||||
BSTerrainPhys newTerrainPhys = null;
|
||||
switch ((int)BSParam.TerrainImplementation)
|
||||
{
|
||||
case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
|
||||
newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id,
|
||||
heightMap, minCoords, maxCoords);
|
||||
break;
|
||||
case (int)BSTerrainPhys.TerrainImplementation.Mesh:
|
||||
newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id,
|
||||
heightMap, minCoords, maxCoords);
|
||||
break;
|
||||
default:
|
||||
PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}",
|
||||
LogHeader,
|
||||
(int)BSParam.TerrainImplementation,
|
||||
BSParam.TerrainImplementation,
|
||||
PhysicsScene.RegionName, terrainRegionBase);
|
||||
break;
|
||||
}
|
||||
return newTerrainPhys;
|
||||
}
|
||||
|
||||
// Return 'true' of this position is somewhere in known physical terrain space
|
||||
public bool IsWithinKnownTerrain(Vector3 pos)
|
||||
{
|
||||
Vector3 terrainBaseXYZ;
|
||||
BSTerrainPhys physTerrain;
|
||||
return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ);
|
||||
}
|
||||
|
||||
// Given an X and Y, find the height of the terrain.
|
||||
// Since we could be handling multiple terrains for a mega-region,
|
||||
// the base of the region is calcuated assuming all regions are
|
||||
// the same size and that is the default.
|
||||
// Once the heightMapInfo is found, we have all the information to
|
||||
// compute the offset into the array.
|
||||
private float lastHeightTX = 999999f;
|
||||
private float lastHeightTY = 999999f;
|
||||
private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
|
||||
public float GetTerrainHeightAtXYZ(Vector3 pos)
|
||||
{
|
||||
float tX = pos.X;
|
||||
float tY = pos.Y;
|
||||
// You'd be surprized at the number of times this routine is called
|
||||
// with the same parameters as last time.
|
||||
if (!m_terrainModified && (lastHeightTX == tX) && (lastHeightTY == tY))
|
||||
return lastHeight;
|
||||
m_terrainModified = false;
|
||||
|
||||
lastHeightTX = tX;
|
||||
lastHeightTY = tY;
|
||||
float ret = HEIGHT_GETHEIGHT_RET;
|
||||
|
||||
Vector3 terrainBaseXYZ;
|
||||
BSTerrainPhys physTerrain;
|
||||
if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
|
||||
{
|
||||
ret = physTerrain.GetTerrainHeightAtXYZ(pos - terrainBaseXYZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
|
||||
LogHeader, PhysicsScene.RegionName, tX, tY);
|
||||
DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}",
|
||||
BSScene.DetailLogZero, pos, terrainBaseXYZ);
|
||||
}
|
||||
lastHeight = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public float GetWaterLevelAtXYZ(Vector3 pos)
|
||||
{
|
||||
float ret = WATER_HEIGHT_GETHEIGHT_RET;
|
||||
|
||||
Vector3 terrainBaseXYZ;
|
||||
BSTerrainPhys physTerrain;
|
||||
if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
|
||||
{
|
||||
ret = physTerrain.GetWaterLevelAtXYZ(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}",
|
||||
LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Given an address, return 'true' of there is a description of that terrain and output
|
||||
// the descriptor class and the 'base' fo the addresses therein.
|
||||
private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase)
|
||||
{
|
||||
int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
|
||||
int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
|
||||
Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
|
||||
|
||||
BSTerrainPhys physTerrain = null;
|
||||
lock (m_terrains)
|
||||
{
|
||||
m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain);
|
||||
}
|
||||
outTerrainBase = terrainBaseXYZ;
|
||||
outPhysTerrain = physTerrain;
|
||||
return (physTerrain != null);
|
||||
}
|
||||
|
||||
// Although no one seems to check this, I do support combining.
|
||||
public bool SupportsCombining()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// This routine is called two ways:
|
||||
// One with 'offset' and 'pScene' zero and null but 'extents' giving the maximum
|
||||
// extent of the combined regions. This is to inform the parent of the size
|
||||
// of the combined regions.
|
||||
// and one with 'offset' as the offset of the child region to the base region,
|
||||
// 'pScene' pointing to the parent and 'extents' of zero. This informs the
|
||||
// child of its relative base and new parent.
|
||||
public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
|
||||
{
|
||||
m_worldOffset = offset;
|
||||
m_worldMax = extents;
|
||||
MegaRegionParentPhysicsScene = pScene;
|
||||
if (pScene != null)
|
||||
{
|
||||
// We are a child.
|
||||
// We want m_worldMax to be the highest coordinate of our piece of terrain.
|
||||
m_worldMax = offset + DefaultRegionSize;
|
||||
}
|
||||
DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}",
|
||||
BSScene.DetailLogZero, offset, extents, m_worldOffset, m_worldMax);
|
||||
}
|
||||
|
||||
// Unhook all the combining that I know about.
|
||||
public void UnCombine(PhysicsScene pScene)
|
||||
{
|
||||
// Just like ODE, we don't do anything yet.
|
||||
DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero);
|
||||
}
|
||||
|
||||
|
||||
private void DetailLog(string msg, params Object[] args)
|
||||
{
|
||||
PhysicsScene.PhysicsLogging.Write(msg, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework;
|
||||
using OpenSim.Region.CoreModules;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
|
||||
using Nini.Config;
|
||||
using log4net;
|
||||
|
||||
using OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
public sealed class BSTerrainMesh : BSTerrainPhys
|
||||
{
|
||||
static string LogHeader = "[BULLETSIM TERRAIN MESH]";
|
||||
|
||||
private float[] m_savedHeightMap;
|
||||
int m_sizeX;
|
||||
int m_sizeY;
|
||||
|
||||
BulletShape m_terrainShape;
|
||||
BulletBody m_terrainBody;
|
||||
|
||||
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
|
||||
: base(physicsScene, regionBase, id)
|
||||
{
|
||||
}
|
||||
|
||||
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id /* parameters for making mesh */)
|
||||
: base(physicsScene, regionBase, id)
|
||||
{
|
||||
}
|
||||
|
||||
// Create terrain mesh from a heightmap.
|
||||
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
|
||||
Vector3 minCoords, Vector3 maxCoords)
|
||||
: base(physicsScene, regionBase, id)
|
||||
{
|
||||
int indicesCount;
|
||||
int[] indices;
|
||||
int verticesCount;
|
||||
float[] vertices;
|
||||
|
||||
m_savedHeightMap = initialMap;
|
||||
|
||||
m_sizeX = (int)(maxCoords.X - minCoords.X);
|
||||
m_sizeY = (int)(maxCoords.Y - minCoords.Y);
|
||||
|
||||
if (!BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, initialMap,
|
||||
m_sizeX, m_sizeY,
|
||||
(float)m_sizeX, (float)m_sizeY,
|
||||
Vector3.Zero, 1.0f,
|
||||
out indicesCount, out indices, out verticesCount, out vertices))
|
||||
{
|
||||
// DISASTER!!
|
||||
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID);
|
||||
PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase);
|
||||
// Something is very messed up and a crash is in our future.
|
||||
return;
|
||||
}
|
||||
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,indices={1},indSz={2},vertices={3},vertSz={4}",
|
||||
ID, indicesCount, indices.Length, verticesCount, vertices.Length);
|
||||
|
||||
m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
|
||||
indicesCount, indices, verticesCount, vertices),
|
||||
BSPhysicsShapeType.SHAPE_MESH);
|
||||
if (!m_terrainShape.HasPhysicalShape)
|
||||
{
|
||||
// DISASTER!!
|
||||
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID);
|
||||
physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
|
||||
// Something is very messed up and a crash is in our future.
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 pos = regionBase;
|
||||
Quaternion rot = Quaternion.Identity;
|
||||
|
||||
m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot));
|
||||
if (!m_terrainBody.HasPhysicalBody)
|
||||
{
|
||||
// DISASTER!!
|
||||
physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
|
||||
// Something is very messed up and a crash is in our future.
|
||||
return;
|
||||
}
|
||||
|
||||
// Set current terrain attributes
|
||||
BulletSimAPI.SetFriction2(m_terrainBody.ptr, BSParam.TerrainFriction);
|
||||
BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, BSParam.TerrainHitFraction);
|
||||
BulletSimAPI.SetRestitution2(m_terrainBody.ptr, BSParam.TerrainRestitution);
|
||||
BulletSimAPI.SetCollisionFlags2(m_terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
|
||||
|
||||
// Static objects are not very massive.
|
||||
BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);
|
||||
|
||||
// Put the new terrain to the world of physical objects
|
||||
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr, pos, rot);
|
||||
|
||||
// Redo its bounding box now that it is in the world
|
||||
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);
|
||||
|
||||
m_terrainBody.collisionType = CollisionType.Terrain;
|
||||
m_terrainBody.ApplyCollisionMask();
|
||||
|
||||
// Make it so the terrain will not move or be considered for movement.
|
||||
BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (m_terrainBody.HasPhysicalBody)
|
||||
{
|
||||
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
|
||||
// Frees both the body and the shape.
|
||||
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_terrainBody.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetTerrainHeightAtXYZ(Vector3 pos)
|
||||
{
|
||||
// For the moment use the saved heightmap to get the terrain height.
|
||||
// TODO: raycast downward to find the true terrain below the position.
|
||||
float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||
|
||||
int mapIndex = (int)pos.Y * m_sizeY + (int)pos.X;
|
||||
try
|
||||
{
|
||||
ret = m_savedHeightMap[mapIndex];
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
|
||||
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
|
||||
LogHeader, TerrainBase, pos);
|
||||
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The passed position is relative to the base of the region.
|
||||
public override float GetWaterLevelAtXYZ(Vector3 pos)
|
||||
{
|
||||
return PhysicsScene.SimpleWaterLevel;
|
||||
}
|
||||
|
||||
// Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
|
||||
// Return 'true' if successfully created.
|
||||
public static bool ConvertHeightmapToMesh(
|
||||
BSScene physicsScene,
|
||||
float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
|
||||
float extentX, float extentY, // zero based range for output vertices
|
||||
Vector3 extentBase, // base to be added to all vertices
|
||||
float magnification, // number of vertices to create between heightMap coords
|
||||
out int indicesCountO, out int[] indicesO,
|
||||
out int verticesCountO, out float[] verticesO)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
int indicesCount = 0;
|
||||
int verticesCount = 0;
|
||||
int[] indices = new int[0];
|
||||
float[] vertices = new float[0];
|
||||
|
||||
// Simple mesh creation which assumes magnification == 1.
|
||||
// TODO: do a more general solution that scales, adds new vertices and smoothes the result.
|
||||
|
||||
// Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop
|
||||
// from zero to <= sizeX). The triangle indices are then generated as two triangles
|
||||
// per heightmap point. There are sizeX by sizeY of these squares. The extra row and
|
||||
// column of vertices are used to complete the triangles of the last row and column
|
||||
// of the heightmap.
|
||||
try
|
||||
{
|
||||
// One vertice per heightmap value plus the vertices off the top and bottom edge.
|
||||
int totalVertices = (sizeX + 1) * (sizeY + 1);
|
||||
vertices = new float[totalVertices * 3];
|
||||
int totalIndices = sizeX * sizeY * 6;
|
||||
indices = new int[totalIndices];
|
||||
|
||||
float magX = (float)sizeX / extentX;
|
||||
float magY = (float)sizeY / extentY;
|
||||
physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}",
|
||||
BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY);
|
||||
float minHeight = float.MaxValue;
|
||||
// Note that sizeX+1 vertices are created since there is land between this and the next region.
|
||||
for (int yy = 0; yy <= sizeY; yy++)
|
||||
{
|
||||
for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
|
||||
{
|
||||
int offset = yy * sizeX + xx;
|
||||
// Extend the height with the height from the last row or column
|
||||
if (yy == sizeY) offset -= sizeX;
|
||||
if (xx == sizeX) offset -= 1;
|
||||
float height = heightMap[offset];
|
||||
minHeight = Math.Min(minHeight, height);
|
||||
vertices[verticesCount + 0] = (float)xx * magX + extentBase.X;
|
||||
vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y;
|
||||
vertices[verticesCount + 2] = height + extentBase.Z;
|
||||
verticesCount += 3;
|
||||
}
|
||||
}
|
||||
verticesCount = verticesCount / 3;
|
||||
|
||||
for (int yy = 0; yy < sizeY; yy++)
|
||||
{
|
||||
for (int xx = 0; xx < sizeX; xx++)
|
||||
{
|
||||
int offset = yy * (sizeX + 1) + xx;
|
||||
// Each vertices is presumed to be the upper left corner of a box of two triangles
|
||||
indices[indicesCount + 0] = offset;
|
||||
indices[indicesCount + 1] = offset + 1;
|
||||
indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column
|
||||
indices[indicesCount + 3] = offset + 1;
|
||||
indices[indicesCount + 4] = offset + sizeX + 2;
|
||||
indices[indicesCount + 5] = offset + sizeX + 1;
|
||||
indicesCount += 6;
|
||||
}
|
||||
}
|
||||
|
||||
ret = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
|
||||
LogHeader, physicsScene.RegionName, extentBase, e);
|
||||
}
|
||||
|
||||
indicesCountO = indicesCount;
|
||||
indicesO = indices;
|
||||
verticesCountO = verticesCount;
|
||||
verticesO = vertices;
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* 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 copyrightD
|
||||
* 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 System.Text;
|
||||
using OMV = OpenMetaverse;
|
||||
|
||||
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||
{
|
||||
// Classes to allow some type checking for the API
|
||||
// These hold pointers to allocated objects in the unmanaged space.
|
||||
|
||||
// The physics engine controller class created at initialization
|
||||
public struct BulletSim
|
||||
{
|
||||
public BulletSim(uint worldId, BSScene bss, object xx)
|
||||
{
|
||||
ptr = xx;
|
||||
worldID = worldId;
|
||||
physicsScene = bss;
|
||||
}
|
||||
public object ptr;
|
||||
public uint worldID;
|
||||
// The scene is only in here so very low level routines have a handle to print debug/error messages
|
||||
public BSScene physicsScene;
|
||||
}
|
||||
|
||||
// An allocated Bullet btRigidBody
|
||||
public struct BulletBody
|
||||
{
|
||||
public BulletBody(uint id) : this(id, null)
|
||||
{
|
||||
}
|
||||
public BulletBody(uint id, object xx)
|
||||
{
|
||||
ID = id;
|
||||
ptr = xx;
|
||||
collisionType = CollisionType.Static;
|
||||
}
|
||||
public object ptr;
|
||||
public uint ID;
|
||||
public CollisionType collisionType;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ptr = null;
|
||||
}
|
||||
public bool HasPhysicalBody { get { return ptr != null; } }
|
||||
|
||||
// Apply the specificed collision mask into the physical world
|
||||
public void ApplyCollisionMask()
|
||||
{
|
||||
// Should assert the body has been added to the physical world.
|
||||
// (The collision masks are stored in the collision proxy cache which only exists for
|
||||
// a collision body that is in the world.)
|
||||
BulletSimAPI.SetCollisionGroupMask2(ptr,
|
||||
BulletSimData.CollisionTypeMasks[collisionType].group,
|
||||
BulletSimData.CollisionTypeMasks[collisionType].mask);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.Append("<id=");
|
||||
buff.Append(ID.ToString());
|
||||
buff.Append(",p=");
|
||||
buff.Append(ptr.ToString());
|
||||
buff.Append(",c=");
|
||||
buff.Append(collisionType);
|
||||
buff.Append(">");
|
||||
return buff.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public struct BulletShape
|
||||
{
|
||||
public BulletShape(object xx) : this(xx, BSPhysicsShapeType.SHAPE_UNKNOWN)
|
||||
{
|
||||
}
|
||||
public BulletShape(object xx, BSPhysicsShapeType typ)
|
||||
{
|
||||
ptr = xx;
|
||||
type = typ;
|
||||
shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
|
||||
isNativeShape = false;
|
||||
}
|
||||
public object ptr;
|
||||
public BSPhysicsShapeType type;
|
||||
public System.UInt64 shapeKey;
|
||||
public bool isNativeShape;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ptr = null;
|
||||
}
|
||||
public bool HasPhysicalShape { get { return ptr != null; } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder buff = new StringBuilder();
|
||||
buff.Append("<p=");
|
||||
buff.Append(ptr.ToString());
|
||||
buff.Append(",s=");
|
||||
buff.Append(type.ToString());
|
||||
buff.Append(",k=");
|
||||
buff.Append(shapeKey.ToString("X"));
|
||||
buff.Append(",n=");
|
||||
buff.Append(isNativeShape.ToString());
|
||||
buff.Append(">");
|
||||
return buff.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
// An allocated Bullet btConstraint
|
||||
public struct BulletConstraint
|
||||
{
|
||||
public BulletConstraint(object xx)
|
||||
{
|
||||
ptr = xx;
|
||||
}
|
||||
public object ptr;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ptr = null;
|
||||
}
|
||||
public bool HasPhysicalConstraint { get { return ptr != null; } }
|
||||
}
|
||||
|
||||
// An allocated HeightMapThing which holds various heightmap info.
|
||||
// Made a class rather than a struct so there would be only one
|
||||
// instance of this and C# will pass around pointers rather
|
||||
// than making copies.
|
||||
public class BulletHeightMapInfo
|
||||
{
|
||||
public BulletHeightMapInfo(uint id, float[] hm, object xx) {
|
||||
ID = id;
|
||||
Ptr = xx;
|
||||
heightMap = hm;
|
||||
terrainRegionBase = OMV.Vector3.Zero;
|
||||
minCoords = new OMV.Vector3(100f, 100f, 25f);
|
||||
maxCoords = new OMV.Vector3(101f, 101f, 26f);
|
||||
minZ = maxZ = 0f;
|
||||
sizeX = sizeY = 256f;
|
||||
}
|
||||
public uint ID;
|
||||
public object Ptr;
|
||||
public float[] heightMap;
|
||||
public OMV.Vector3 terrainRegionBase;
|
||||
public OMV.Vector3 minCoords;
|
||||
public OMV.Vector3 maxCoords;
|
||||
public float sizeX, sizeY;
|
||||
public float minZ, maxZ;
|
||||
public BulletShape terrainShape;
|
||||
public BulletBody terrainBody;
|
||||
|
||||
public float collisionMargin { get; set; }
|
||||
}
|
||||
|
||||
// The general class of collsion object.
|
||||
public enum CollisionType
|
||||
{
|
||||
Avatar,
|
||||
Groundplane,
|
||||
Terrain,
|
||||
Static,
|
||||
Dynamic,
|
||||
VolumeDetect,
|
||||
// Linkset, // A linkset should be either Static or Dynamic
|
||||
LinksetChild,
|
||||
Unknown
|
||||
};
|
||||
|
||||
// Hold specification of group and mask collision flags for a CollisionType
|
||||
public struct CollisionTypeFilterGroup
|
||||
{
|
||||
public CollisionTypeFilterGroup(CollisionType t, uint g, uint m)
|
||||
{
|
||||
type = t;
|
||||
group = g;
|
||||
mask = m;
|
||||
}
|
||||
public CollisionType type;
|
||||
public uint group;
|
||||
public uint mask;
|
||||
};
|
||||
|
||||
/* NOTE: old definitions kept for reference. Delete when things are working.
|
||||
// The collsion filters and masked are defined in one place -- don't want them scattered
|
||||
AvatarGroup = BCharacterGroup,
|
||||
AvatarMask = BAllGroup,
|
||||
ObjectGroup = BSolidGroup,
|
||||
ObjectMask = BAllGroup,
|
||||
StaticObjectGroup = BStaticGroup,
|
||||
StaticObjectMask = AvatarGroup | ObjectGroup, // static things don't interact with much
|
||||
LinksetGroup = BLinksetGroup,
|
||||
LinksetMask = BAllGroup,
|
||||
LinksetChildGroup = BLinksetChildGroup,
|
||||
LinksetChildMask = BNoneGroup, // Linkset children disappear from the world
|
||||
VolumeDetectGroup = BSensorTrigger,
|
||||
VolumeDetectMask = ~BSensorTrigger,
|
||||
TerrainGroup = BTerrainGroup,
|
||||
TerrainMask = BAllGroup & ~BStaticGroup, // static objects on the ground don't collide
|
||||
GroundPlaneGroup = BGroundPlaneGroup,
|
||||
GroundPlaneMask = BAllGroup
|
||||
*/
|
||||
|
||||
public static class BulletSimData
|
||||
{
|
||||
|
||||
// Map of collisionTypes to flags for collision groups and masks.
|
||||
// As mentioned above, don't use the CollisionFilterGroups definitions directly in the code
|
||||
// but, instead, use references to this dictionary. Finding and debugging
|
||||
// collision flag problems will be made easier.
|
||||
public static Dictionary<CollisionType, CollisionTypeFilterGroup> CollisionTypeMasks
|
||||
= new Dictionary<CollisionType, CollisionTypeFilterGroup>()
|
||||
{
|
||||
{ CollisionType.Avatar,
|
||||
new CollisionTypeFilterGroup(CollisionType.Avatar,
|
||||
(uint)CollisionFilterGroups.BCharacterGroup,
|
||||
(uint)CollisionFilterGroups.BAllGroup)
|
||||
},
|
||||
{ CollisionType.Groundplane,
|
||||
new CollisionTypeFilterGroup(CollisionType.Groundplane,
|
||||
(uint)CollisionFilterGroups.BGroundPlaneGroup,
|
||||
(uint)CollisionFilterGroups.BAllGroup)
|
||||
},
|
||||
{ CollisionType.Terrain,
|
||||
new CollisionTypeFilterGroup(CollisionType.Terrain,
|
||||
(uint)CollisionFilterGroups.BTerrainGroup,
|
||||
(uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup))
|
||||
},
|
||||
{ CollisionType.Static,
|
||||
new CollisionTypeFilterGroup(CollisionType.Static,
|
||||
(uint)CollisionFilterGroups.BStaticGroup,
|
||||
(uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup))
|
||||
},
|
||||
{ CollisionType.Dynamic,
|
||||
new CollisionTypeFilterGroup(CollisionType.Dynamic,
|
||||
(uint)CollisionFilterGroups.BSolidGroup,
|
||||
(uint)(CollisionFilterGroups.BAllGroup))
|
||||
},
|
||||
{ CollisionType.VolumeDetect,
|
||||
new CollisionTypeFilterGroup(CollisionType.VolumeDetect,
|
||||
(uint)CollisionFilterGroups.BSensorTrigger,
|
||||
(uint)(~CollisionFilterGroups.BSensorTrigger))
|
||||
},
|
||||
{ CollisionType.LinksetChild,
|
||||
new CollisionTypeFilterGroup(CollisionType.LinksetChild,
|
||||
(uint)CollisionFilterGroups.BTerrainGroup,
|
||||
(uint)(CollisionFilterGroups.BNoneGroup))
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -174,7 +174,7 @@ public sealed class BSCharacter : BSPhysObject
|
|||
BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
|
||||
}
|
||||
|
||||
UpdatePhysicalMassProperties(RawMass);
|
||||
UpdatePhysicalMassProperties(RawMass, false);
|
||||
|
||||
// Make so capsule does not fall over
|
||||
BulletSimAPI.SetAngularFactorV2(PhysBody.ptr, OMV.Vector3.Zero);
|
||||
|
@ -224,7 +224,7 @@ public sealed class BSCharacter : BSPhysObject
|
|||
if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape)
|
||||
{
|
||||
BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
|
||||
UpdatePhysicalMassProperties(RawMass);
|
||||
UpdatePhysicalMassProperties(RawMass, true);
|
||||
// Make sure this change appears as a property update event
|
||||
BulletSimAPI.PushUpdate2(PhysBody.ptr);
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ public sealed class BSCharacter : BSPhysObject
|
|||
public override float RawMass {
|
||||
get {return _mass; }
|
||||
}
|
||||
public override void UpdatePhysicalMassProperties(float physMass)
|
||||
public override void UpdatePhysicalMassProperties(float physMass, bool inWorld)
|
||||
{
|
||||
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
|
||||
BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, localInertia);
|
||||
|
@ -772,8 +772,9 @@ public sealed class BSCharacter : BSPhysObject
|
|||
// the motor can be turned off. Set the velocity to zero so the zero motion is sent to the viewer.
|
||||
if (_velocityMotor.TargetValue.ApproxEquals(OMV.Vector3.Zero, 0.01f) && _velocityMotor.ErrorIsZero)
|
||||
{
|
||||
stepVelocity = OMV.Vector3.Zero;
|
||||
_velocityMotor.Enabled = false;
|
||||
stepVelocity = OMV.Vector3.Zero;
|
||||
ZeroMotion(true);
|
||||
DetailLog("{0},BSCharacter.UpdateProperties,taint,disableVelocityMotor,m={1}", LocalID, _velocityMotor);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ public abstract class BSConstraint : IDisposable
|
|||
{
|
||||
private static string LogHeader = "[BULLETSIM CONSTRAINT]";
|
||||
|
||||
protected BulletSim m_world;
|
||||
protected BulletWorld m_world;
|
||||
protected BulletBody m_body1;
|
||||
protected BulletBody m_body2;
|
||||
protected BulletConstraint m_constraint;
|
||||
|
|
|
@ -39,7 +39,7 @@ public sealed class BSConstraint6Dof : BSConstraint
|
|||
public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } }
|
||||
|
||||
// Create a btGeneric6DofConstraint
|
||||
public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||
public BSConstraint6Dof(BulletWorld world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 frame1, Quaternion frame1rot,
|
||||
Vector3 frame2, Quaternion frame2rot,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||
|
@ -58,7 +58,7 @@ public sealed class BSConstraint6Dof : BSConstraint
|
|||
obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
|
||||
}
|
||||
|
||||
public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||
public BSConstraint6Dof(BulletWorld world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 joinPoint,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||
{
|
||||
|
|
|
@ -41,9 +41,9 @@ public sealed class BSConstraintCollection : IDisposable
|
|||
delegate bool ConstraintAction(BSConstraint constrain);
|
||||
|
||||
private List<BSConstraint> m_constraints;
|
||||
private BulletSim m_world;
|
||||
private BulletWorld m_world;
|
||||
|
||||
public BSConstraintCollection(BulletSim world)
|
||||
public BSConstraintCollection(BulletWorld world)
|
||||
{
|
||||
m_world = world;
|
||||
m_constraints = new List<BSConstraint>();
|
||||
|
|
|
@ -36,7 +36,7 @@ public sealed class BSConstraintHinge : BSConstraint
|
|||
{
|
||||
public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } }
|
||||
|
||||
public BSConstraintHinge(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||
public BSConstraintHinge(BulletWorld world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 pivotInA, Vector3 pivotInB,
|
||||
Vector3 axisInA, Vector3 axisInB,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||
|
|
|
@ -97,14 +97,7 @@ public abstract class BSLinkset
|
|||
}
|
||||
|
||||
// We keep the prim's mass in the linkset structure since it could be dependent on other prims
|
||||
protected float m_mass;
|
||||
public float LinksetMass
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_mass;
|
||||
}
|
||||
}
|
||||
public float LinksetMass { get; protected set; }
|
||||
|
||||
public virtual bool LinksetIsColliding { get { return false; } }
|
||||
|
||||
|
@ -128,7 +121,7 @@ public abstract class BSLinkset
|
|||
PhysicsScene = scene;
|
||||
LinksetRoot = parent;
|
||||
m_children = new HashSet<BSPhysObject>();
|
||||
m_mass = parent.RawMass;
|
||||
LinksetMass = parent.RawMass;
|
||||
Rebuilding = false;
|
||||
}
|
||||
|
||||
|
@ -143,7 +136,7 @@ public abstract class BSLinkset
|
|||
// Don't add the root to its own linkset
|
||||
if (!IsRoot(child))
|
||||
AddChildToLinkset(child);
|
||||
m_mass = ComputeLinksetMass();
|
||||
LinksetMass = ComputeLinksetMass();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -162,7 +155,7 @@ public abstract class BSLinkset
|
|||
return this;
|
||||
}
|
||||
RemoveChildFromLinkset(child);
|
||||
m_mass = ComputeLinksetMass();
|
||||
LinksetMass = ComputeLinksetMass();
|
||||
}
|
||||
|
||||
// The child is down to a linkset of just itself
|
||||
|
@ -230,7 +223,10 @@ public abstract class BSLinkset
|
|||
// When physical properties are changed the linkset needs to recalculate
|
||||
// its internal properties.
|
||||
// May be called at runtime or taint-time.
|
||||
public abstract void Refresh(BSPhysObject requestor);
|
||||
public virtual void Refresh(BSPhysObject requestor)
|
||||
{
|
||||
LinksetMass = ComputeLinksetMass();
|
||||
}
|
||||
|
||||
// Flag denoting the linkset is in the process of being rebuilt.
|
||||
// Used to know not the schedule a rebuild in the middle of a rebuild.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 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.
|
||||
* notice, this list of conditions and the following disclat simer.
|
||||
* * Redistributions in binary form must reproduce the above copyrightD
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
|
@ -89,6 +89,8 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
// its internal properties.
|
||||
public override void Refresh(BSPhysObject requestor)
|
||||
{
|
||||
base.Refresh(requestor);
|
||||
|
||||
// Something changed so do the rebuilding thing
|
||||
// ScheduleRebuild();
|
||||
}
|
||||
|
@ -96,13 +98,13 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
// Schedule a refresh to happen after all the other taint processing.
|
||||
private void ScheduleRebuild(BSPhysObject requestor)
|
||||
{
|
||||
DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,rebuilding={1}",
|
||||
DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1}",
|
||||
requestor.LocalID, Rebuilding);
|
||||
// When rebuilding, it is possible to set properties that would normally require a rebuild.
|
||||
// If already rebuilding, don't request another rebuild.
|
||||
if (!Rebuilding)
|
||||
{
|
||||
PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", LinksetRoot.LocalID, delegate()
|
||||
PhysicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate()
|
||||
{
|
||||
if (HasAnyChildren)
|
||||
RecomputeLinksetCompound();
|
||||
|
@ -123,7 +125,6 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
if (IsRoot(child))
|
||||
{
|
||||
// The root is going dynamic. Make sure mass is properly set.
|
||||
m_mass = ComputeLinksetMass();
|
||||
ScheduleRebuild(LinksetRoot);
|
||||
}
|
||||
else
|
||||
|
@ -377,8 +378,8 @@ public sealed class BSLinksetCompound : BSLinkset
|
|||
});
|
||||
|
||||
// With all of the linkset packed into the root prim, it has the mass of everyone.
|
||||
float linksetMass = LinksetMass;
|
||||
LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
|
||||
LinksetMass = LinksetMass;
|
||||
LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
@ -46,6 +46,8 @@ public sealed class BSLinksetConstraints : BSLinkset
|
|||
// refresh will happen once after all the other taints are applied.
|
||||
public override void Refresh(BSPhysObject requestor)
|
||||
{
|
||||
base.Refresh(requestor);
|
||||
|
||||
// Queue to happen after all the other taint processing
|
||||
PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate()
|
||||
{
|
||||
|
@ -279,7 +281,7 @@ public sealed class BSLinksetConstraints : BSLinkset
|
|||
private void RecomputeLinksetConstraints()
|
||||
{
|
||||
float linksetMass = LinksetMass;
|
||||
LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
|
||||
LinksetRoot.UpdatePhysicalMassProperties(linksetMass, true);
|
||||
|
||||
// DEBUG: see of inter-linkset collisions are causing problems
|
||||
// BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
|
||||
|
@ -292,7 +294,7 @@ public sealed class BSLinksetConstraints : BSLinkset
|
|||
// A child in the linkset physically shows the mass of the whole linkset.
|
||||
// This allows Bullet to apply enough force on the child to move the whole linkset.
|
||||
// (Also do the mass stuff before recomputing the constraint so mass is not zero.)
|
||||
child.UpdatePhysicalMassProperties(linksetMass);
|
||||
child.UpdatePhysicalMassProperties(linksetMass, true);
|
||||
|
||||
BSConstraint constrain;
|
||||
if (!PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain))
|
||||
|
|
|
@ -119,26 +119,26 @@ public static class BSMaterials
|
|||
Attributes[(int)MaterialAttributes.Material.Light] =
|
||||
new MaterialAttributes("light",dDensity, dFriction, dRestitution);
|
||||
Attributes[(int)MaterialAttributes.Material.Avatar] =
|
||||
new MaterialAttributes("avatar",60f, 0.2f, 0f);
|
||||
new MaterialAttributes("avatar",3.5f, 0.2f, 0f);
|
||||
|
||||
Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("stonePhysical",dDensity, 0.8f, 0.4f);
|
||||
Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("metalPhysical",dDensity, 0.8f, 0.4f);
|
||||
new MaterialAttributes("metalPhysical",dDensity, 0.3f, 0.4f);
|
||||
Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("glassPhysical",dDensity, 0.8f, 0.7f);
|
||||
new MaterialAttributes("glassPhysical",dDensity, 0.2f, 0.7f);
|
||||
Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("woodPhysical",dDensity, 0.8f, 0.5f);
|
||||
new MaterialAttributes("woodPhysical",dDensity, 0.6f, 0.5f);
|
||||
Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("fleshPhysical",dDensity, 0.8f, 0.3f);
|
||||
new MaterialAttributes("fleshPhysical",dDensity, 0.9f, 0.3f);
|
||||
Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("plasticPhysical",dDensity, 0.8f, 0.7f);
|
||||
new MaterialAttributes("plasticPhysical",dDensity, 0.4f, 0.7f);
|
||||
Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("rubberPhysical",dDensity, 0.8f, 0.9f);
|
||||
new MaterialAttributes("rubberPhysical",dDensity, 0.9f, 0.9f);
|
||||
Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("lightPhysical",dDensity, dFriction, dRestitution);
|
||||
Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||
new MaterialAttributes("avatarPhysical",60f, 0.2f, 0f);
|
||||
new MaterialAttributes("avatarPhysical",3.5f, 0.2f, 0f);
|
||||
}
|
||||
|
||||
// Under the [BulletSim] section, one can change the individual material
|
||||
|
|
|
@ -351,7 +351,7 @@ public static class BSParam
|
|||
(s) => { return AvatarStandingFriction; },
|
||||
(s,p,l,v) => { AvatarStandingFriction = v; } ),
|
||||
new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
|
||||
60f,
|
||||
3.5f,
|
||||
(s,cf,p,v) => { AvatarDensity = cf.GetFloat(p, v); },
|
||||
(s) => { return AvatarDensity; },
|
||||
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarDensity=x;}, p, l, v); } ),
|
||||
|
|
|
@ -96,7 +96,8 @@ public abstract class BSPhysObject : PhysicsActor
|
|||
// Return the object mass without calculating it or having side effects
|
||||
public abstract float RawMass { get; }
|
||||
// Set the raw mass but also update physical mass properties (inertia, ...)
|
||||
public abstract void UpdatePhysicalMassProperties(float mass);
|
||||
// 'inWorld' true if the object has already been added to the dynamic world.
|
||||
public abstract void UpdatePhysicalMassProperties(float mass, bool inWorld);
|
||||
|
||||
// The last value calculated for the prim's inertia
|
||||
public OMV.Vector3 Inertia { get; set; }
|
||||
|
|
|
@ -115,6 +115,8 @@ public sealed class BSPrim : BSPhysObject
|
|||
PhysBody = new BulletBody(LocalID);
|
||||
PhysShape = new BulletShape();
|
||||
|
||||
Linkset.Refresh(this);
|
||||
|
||||
DetailLog("{0},BSPrim.constructor,call", LocalID);
|
||||
// do the actual object creation at taint time
|
||||
PhysicsScene.TaintedObject("BSPrim.create", delegate()
|
||||
|
@ -384,13 +386,13 @@ public sealed class BSPrim : BSPhysObject
|
|||
}
|
||||
|
||||
// Return the effective mass of the object.
|
||||
// If there are multiple items in the linkset, add them together for the root
|
||||
// The definition of this call is to return the mass of the prim.
|
||||
// If the simulator cares about the mass of the linkset, it will sum it itself.
|
||||
public override float Mass
|
||||
{
|
||||
get
|
||||
{
|
||||
return Linkset.LinksetMass;
|
||||
// return _mass;
|
||||
return _mass;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -400,22 +402,41 @@ public sealed class BSPrim : BSPhysObject
|
|||
}
|
||||
// Set the physical mass to the passed mass.
|
||||
// Note that this does not change _mass!
|
||||
public override void UpdatePhysicalMassProperties(float physMass)
|
||||
public override void UpdatePhysicalMassProperties(float physMass, bool inWorld)
|
||||
{
|
||||
if (IsStatic)
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
{
|
||||
Inertia = OMV.Vector3.Zero;
|
||||
BulletSimAPI.SetMassProps2(PhysBody.ptr, 0f, Inertia);
|
||||
BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
Inertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
|
||||
BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, Inertia);
|
||||
BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
|
||||
// center of mass is at the zero of the object
|
||||
// DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(PhysBody.ptr, ForcePosition, ForceOrientation);
|
||||
DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2}", LocalID, physMass, Inertia);
|
||||
if (IsStatic)
|
||||
{
|
||||
Inertia = OMV.Vector3.Zero;
|
||||
BulletSimAPI.SetMassProps2(PhysBody.ptr, 0f, Inertia);
|
||||
BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inWorld)
|
||||
{
|
||||
// Changing interesting properties doesn't change proxy and collision cache
|
||||
// information. The Bullet solution is to re-add the object to the world
|
||||
// after parameters are changed.
|
||||
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
|
||||
}
|
||||
|
||||
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
|
||||
BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
|
||||
|
||||
Inertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
|
||||
BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, Inertia);
|
||||
BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
|
||||
// center of mass is at the zero of the object
|
||||
// DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(PhysBody.ptr, ForcePosition, ForceOrientation);
|
||||
DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2}", LocalID, physMass, Inertia);
|
||||
|
||||
if (inWorld)
|
||||
{
|
||||
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -714,7 +735,7 @@ public sealed class BSPrim : BSPhysObject
|
|||
Linkset.Refresh(this);
|
||||
|
||||
DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}",
|
||||
LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody, PhysShape);
|
||||
LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody, PhysShape);
|
||||
}
|
||||
|
||||
// "Making dynamic" means changing to and from static.
|
||||
|
@ -737,7 +758,7 @@ public sealed class BSPrim : BSPhysObject
|
|||
BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
|
||||
|
||||
// Mass is zero which disables a bunch of physics stuff in Bullet
|
||||
UpdatePhysicalMassProperties(0f);
|
||||
UpdatePhysicalMassProperties(0f, false);
|
||||
// Set collision detection parameters
|
||||
if (BSParam.CcdMotionThreshold > 0f)
|
||||
{
|
||||
|
@ -777,7 +798,7 @@ public sealed class BSPrim : BSPhysObject
|
|||
// DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation);
|
||||
|
||||
// A dynamic object has mass
|
||||
UpdatePhysicalMassProperties(RawMass);
|
||||
UpdatePhysicalMassProperties(RawMass, false);
|
||||
|
||||
// Set collision detection parameters
|
||||
if (BSParam.CcdMotionThreshold > 0f)
|
||||
|
@ -950,13 +971,9 @@ public sealed class BSPrim : BSPhysObject
|
|||
set {
|
||||
_buoyancy = value;
|
||||
// DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
|
||||
// Buoyancy is faked by changing the gravity applied to the object
|
||||
if (PhysBody.HasPhysicalBody)
|
||||
{
|
||||
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
|
||||
BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
|
||||
ActivateIfPhysical(false);
|
||||
}
|
||||
// Force the recalculation of the various inertia,etc variables in the object
|
||||
UpdatePhysicalMassProperties(_mass, true);
|
||||
ActivateIfPhysical(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
|
|||
|
||||
public IMesher mesher;
|
||||
public uint WorldID { get; private set; }
|
||||
public BulletSim World { get; private set; }
|
||||
public BulletWorld World { get; private set; }
|
||||
|
||||
// All the constraints that have been allocated in this instance.
|
||||
public BSConstraintCollection Constraints { get; private set; }
|
||||
|
@ -242,7 +242,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
|
|||
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
|
||||
|
||||
// m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
|
||||
World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
|
||||
World = new BulletWorld(0, this, BulletSimAPI.Initialize2(worldExtent, m_paramsHandle.AddrOfPinnedObject(),
|
||||
m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(),
|
||||
m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject(),
|
||||
m_DebugLogCallbackHandle));
|
||||
|
|
|
@ -98,7 +98,7 @@ public sealed class BSShapeCollection : IDisposable
|
|||
// higher level dependencies on the shape or body. Mostly used for LinkSets to
|
||||
// remove the physical constraints before the body is destroyed.
|
||||
// Called at taint-time!!
|
||||
public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim,
|
||||
public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim,
|
||||
ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
|
||||
{
|
||||
PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");
|
||||
|
@ -126,7 +126,7 @@ public sealed class BSShapeCollection : IDisposable
|
|||
return ret;
|
||||
}
|
||||
|
||||
public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim)
|
||||
public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim)
|
||||
{
|
||||
return GetBodyAndShape(forceRebuild, sim, prim, null, null);
|
||||
}
|
||||
|
@ -918,7 +918,7 @@ public sealed class BSShapeCollection : IDisposable
|
|||
// Updates prim.BSBody with the information about the new body if one is created.
|
||||
// Returns 'true' if an object was actually created.
|
||||
// Called at taint-time.
|
||||
private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape,
|
||||
private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, BulletShape shape,
|
||||
BodyDestructionCallback bodyCallback)
|
||||
{
|
||||
bool ret = false;
|
||||
|
|
|
@ -288,6 +288,398 @@ public enum ConstraintParamAxis : int
|
|||
AXIS_ALL
|
||||
};
|
||||
|
||||
public abstract class BulletSimAPITemplate
|
||||
{
|
||||
// Initialization and simulation
|
||||
public abstract BulletWorld Initialize2(Vector3 maxPosition, IntPtr parms,
|
||||
int maxCollisions, IntPtr collisionArray,
|
||||
int maxUpdates, IntPtr updateArray
|
||||
);
|
||||
|
||||
public abstract bool UpdateParameter2(BulletWorld world, uint localID, String parm, float value);
|
||||
|
||||
public abstract void SetHeightMap2(BulletWorld world, float[] heightmap);
|
||||
|
||||
public abstract void Shutdown2(BulletWorld sim);
|
||||
|
||||
public abstract int PhysicsStep2(BulletWorld world, float timeStep, int maxSubSteps, float fixedTimeStep,
|
||||
out int updatedEntityCount,
|
||||
out IntPtr updatedEntitiesPtr,
|
||||
out int collidersCount,
|
||||
out IntPtr collidersPtr);
|
||||
|
||||
public abstract bool PushUpdate2(BulletBody obj);
|
||||
|
||||
// =====================================================================================
|
||||
// Mesh, hull, shape and body creation helper routines
|
||||
public abstract BulletShape CreateMeshShape2(BulletWorld world,
|
||||
int indicesCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices,
|
||||
int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices );
|
||||
|
||||
public abstract BulletShape CreateHullShape2(BulletWorld world,
|
||||
int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls);
|
||||
|
||||
public abstract BulletShape BuildHullShapeFromMesh2(BulletWorld world, BulletShape meshShape);
|
||||
|
||||
public abstract BulletShape BuildNativeShape2(BulletWorld world, ShapeData shapeData);
|
||||
|
||||
public abstract bool IsNativeShape2(BulletShape shape);
|
||||
|
||||
public abstract void SetShapeCollisionMargin(BulletShape shape, float margin);
|
||||
|
||||
public abstract BulletShape BuildCapsuleShape2(BulletWorld world, float radius, float height, Vector3 scale);
|
||||
|
||||
public abstract BulletShape CreateCompoundShape2(BulletWorld sim, bool enableDynamicAabbTree);
|
||||
|
||||
public abstract int GetNumberOfCompoundChildren2(BulletShape cShape);
|
||||
|
||||
public abstract void AddChildShapeToCompoundShape2(BulletShape cShape, BulletShape addShape, Vector3 pos, Quaternion rot);
|
||||
|
||||
public abstract BulletShape GetChildShapeFromCompoundShapeIndex2(BulletShape cShape, int indx);
|
||||
|
||||
public abstract BulletShape RemoveChildShapeFromCompoundShapeIndex2(BulletShape cShape, int indx);
|
||||
|
||||
public abstract void RemoveChildShapeFromCompoundShape2(BulletShape cShape, BulletShape removeShape);
|
||||
|
||||
public abstract void RecalculateCompoundShapeLocalAabb2(BulletShape cShape);
|
||||
|
||||
public abstract BulletShape DuplicateCollisionShape2(BulletWorld sim, BulletShape srcShape, uint id);
|
||||
|
||||
public abstract BulletBody CreateBodyFromShapeAndInfo2(BulletWorld sim, BulletShape shape, uint id, IntPtr constructionInfo);
|
||||
|
||||
public abstract bool DeleteCollisionShape2(BulletWorld world, BulletShape shape);
|
||||
|
||||
public abstract int GetBodyType2(BulletBody obj);
|
||||
|
||||
public abstract BulletBody CreateBodyFromShape2(BulletWorld sim, BulletShape shape, uint id, Vector3 pos, Quaternion rot);
|
||||
|
||||
public abstract BulletBody CreateBodyWithDefaultMotionState2(BulletShape shape, uint id, Vector3 pos, Quaternion rot);
|
||||
|
||||
public abstract BulletBody CreateGhostFromShape2(BulletWorld sim, BulletShape shape, uint id, Vector3 pos, Quaternion rot);
|
||||
|
||||
public abstract IntPtr AllocateBodyInfo2(BulletBody obj);
|
||||
|
||||
public abstract void ReleaseBodyInfo2(IntPtr obj);
|
||||
|
||||
public abstract void DestroyObject2(BulletWorld sim, BulletBody obj);
|
||||
|
||||
// =====================================================================================
|
||||
// Terrain creation and helper routines
|
||||
public abstract IntPtr CreateHeightMapInfo2(BulletWorld sim, uint id, Vector3 minCoords, Vector3 maxCoords,
|
||||
[MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin);
|
||||
|
||||
public abstract IntPtr FillHeightMapInfo2(BulletWorld sim, IntPtr mapInfo, uint id, Vector3 minCoords, Vector3 maxCoords,
|
||||
[MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin);
|
||||
|
||||
public abstract bool ReleaseHeightMapInfo2(IntPtr heightMapInfo);
|
||||
|
||||
public abstract BulletBody CreateGroundPlaneShape2(uint id, float height, float collisionMargin);
|
||||
|
||||
public abstract BulletBody CreateTerrainShape2(IntPtr mapInfo);
|
||||
|
||||
// =====================================================================================
|
||||
// Constraint creation and helper routines
|
||||
public abstract BulletConstraint Create6DofConstraint2(BulletWorld world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 frame1loc, Quaternion frame1rot,
|
||||
Vector3 frame2loc, Quaternion frame2rot,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
|
||||
|
||||
public abstract BulletConstraint Create6DofConstraintToPoint2(BulletWorld world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 joinPoint,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
|
||||
|
||||
public abstract BulletConstraint CreateHingeConstraint2(BulletWorld world, BulletBody obj1, BulletBody obj2,
|
||||
Vector3 pivotinA, Vector3 pivotinB,
|
||||
Vector3 axisInA, Vector3 axisInB,
|
||||
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies);
|
||||
|
||||
public abstract void SetConstraintEnable2(BulletConstraint constrain, float numericTrueFalse);
|
||||
|
||||
public abstract void SetConstraintNumSolverIterations2(BulletConstraint constrain, float iterations);
|
||||
|
||||
public abstract bool SetFrames2(BulletConstraint constrain,
|
||||
Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot);
|
||||
|
||||
public abstract bool SetLinearLimits2(BulletConstraint constrain, Vector3 low, Vector3 hi);
|
||||
|
||||
public abstract bool SetAngularLimits2(BulletConstraint constrain, Vector3 low, Vector3 hi);
|
||||
|
||||
public abstract bool UseFrameOffset2(BulletConstraint constrain, float enable);
|
||||
|
||||
public abstract bool TranslationalLimitMotor2(BulletConstraint constrain, float enable, float targetVel, float maxMotorForce);
|
||||
|
||||
public abstract bool SetBreakingImpulseThreshold2(BulletConstraint constrain, float threshold);
|
||||
|
||||
public abstract bool CalculateTransforms2(BulletConstraint constrain);
|
||||
|
||||
public abstract bool SetConstraintParam2(BulletConstraint constrain, ConstraintParams paramIndex, float value, ConstraintParamAxis axis);
|
||||
|
||||
public abstract bool DestroyConstraint2(BulletWorld world, BulletConstraint constrain);
|
||||
|
||||
// =====================================================================================
|
||||
// btCollisionWorld entries
|
||||
public abstract void UpdateSingleAabb2(BulletWorld world, BulletBody obj);
|
||||
|
||||
public abstract void UpdateAabbs2(BulletWorld world);
|
||||
|
||||
public abstract bool GetForceUpdateAllAabbs2(BulletWorld world);
|
||||
|
||||
public abstract void SetForceUpdateAllAabbs2(BulletWorld world, bool force);
|
||||
|
||||
// =====================================================================================
|
||||
// btDynamicsWorld entries
|
||||
public abstract bool AddObjectToWorld2(BulletWorld world, BulletBody obj);
|
||||
|
||||
public abstract bool RemoveObjectFromWorld2(BulletWorld world, BulletBody obj);
|
||||
|
||||
public abstract bool AddConstraintToWorld2(BulletWorld world, BulletConstraint constrain, bool disableCollisionsBetweenLinkedObjects);
|
||||
|
||||
public abstract bool RemoveConstraintFromWorld2(BulletWorld world, BulletConstraint constrain);
|
||||
// =====================================================================================
|
||||
// btCollisionObject entries
|
||||
public abstract Vector3 GetAnisotripicFriction2(BulletConstraint constrain);
|
||||
|
||||
public abstract Vector3 SetAnisotripicFriction2(BulletConstraint constrain, Vector3 frict);
|
||||
|
||||
public abstract bool HasAnisotripicFriction2(BulletConstraint constrain);
|
||||
|
||||
public abstract void SetContactProcessingThreshold2(BulletBody obj, float val);
|
||||
|
||||
public abstract float GetContactProcessingThreshold2(BulletBody obj);
|
||||
|
||||
public abstract bool IsStaticObject2(BulletBody obj);
|
||||
|
||||
public abstract bool IsKinematicObject2(BulletBody obj);
|
||||
|
||||
public abstract bool IsStaticOrKinematicObject2(BulletBody obj);
|
||||
|
||||
public abstract bool HasContactResponse2(BulletBody obj);
|
||||
|
||||
public abstract void SetCollisionShape2(BulletWorld sim, BulletBody obj, BulletBody shape);
|
||||
|
||||
public abstract BulletShape GetCollisionShape2(BulletBody obj);
|
||||
|
||||
public abstract int GetActivationState2(BulletBody obj);
|
||||
|
||||
public abstract void SetActivationState2(BulletBody obj, int state);
|
||||
|
||||
public abstract void SetDeactivationTime2(BulletBody obj, float dtime);
|
||||
|
||||
public abstract float GetDeactivationTime2(BulletBody obj);
|
||||
|
||||
public abstract void ForceActivationState2(BulletBody obj, ActivationState state);
|
||||
|
||||
public abstract void Activate2(BulletBody obj, bool forceActivation);
|
||||
|
||||
public abstract bool IsActive2(BulletBody obj);
|
||||
|
||||
public abstract void SetRestitution2(BulletBody obj, float val);
|
||||
|
||||
public abstract float GetRestitution2(BulletBody obj);
|
||||
|
||||
public abstract void SetFriction2(BulletBody obj, float val);
|
||||
|
||||
public abstract float GetFriction2(BulletBody obj);
|
||||
|
||||
/* Haven't defined the type 'Transform'
|
||||
public abstract Transform GetWorldTransform2(BulletBody obj);
|
||||
|
||||
public abstract void setWorldTransform2(BulletBody obj, Transform trans);
|
||||
*/
|
||||
|
||||
public abstract Vector3 GetPosition2(BulletBody obj);
|
||||
|
||||
public abstract Quaternion GetOrientation2(BulletBody obj);
|
||||
|
||||
public abstract void SetTranslation2(BulletBody obj, Vector3 position, Quaternion rotation);
|
||||
|
||||
public abstract IntPtr GetBroadphaseHandle2(BulletBody obj);
|
||||
|
||||
public abstract void SetBroadphaseHandle2(BulletBody obj, IntPtr handle);
|
||||
|
||||
/*
|
||||
public abstract Transform GetInterpolationWorldTransform2(IntPtr obj);
|
||||
|
||||
public abstract void SetInterpolationWorldTransform2(IntPtr obj, Transform trans);
|
||||
*/
|
||||
|
||||
public abstract void SetInterpolationLinearVelocity2(BulletBody obj, Vector3 vel);
|
||||
|
||||
public abstract void SetInterpolationAngularVelocity2(BulletBody obj, Vector3 vel);
|
||||
|
||||
public abstract void SetInterpolationVelocity2(BulletBody obj, Vector3 linearVel, Vector3 angularVel);
|
||||
|
||||
public abstract float GetHitFraction2(BulletBody obj);
|
||||
|
||||
public abstract void SetHitFraction2(BulletBody obj, float val);
|
||||
|
||||
public abstract CollisionFlags GetCollisionFlags2(BulletBody obj);
|
||||
|
||||
public abstract CollisionFlags SetCollisionFlags2(BulletBody obj, CollisionFlags flags);
|
||||
|
||||
public abstract CollisionFlags AddToCollisionFlags2(BulletBody obj, CollisionFlags flags);
|
||||
|
||||
public abstract CollisionFlags RemoveFromCollisionFlags2(BulletBody obj, CollisionFlags flags);
|
||||
|
||||
public abstract float GetCcdMotionThreshold2(BulletBody obj);
|
||||
|
||||
public abstract void SetCcdMotionThreshold2(BulletBody obj, float val);
|
||||
|
||||
public abstract float GetCcdSweptSphereRadius2(BulletBody obj);
|
||||
|
||||
public abstract void SetCcdSweptSphereRadius2(BulletBody obj, float val);
|
||||
|
||||
public abstract IntPtr GetUserPointer2(BulletBody obj);
|
||||
|
||||
public abstract void SetUserPointer2(BulletBody obj, IntPtr val);
|
||||
|
||||
// =====================================================================================
|
||||
// btRigidBody entries
|
||||
public abstract void ApplyGravity2(BulletBody obj);
|
||||
|
||||
public abstract void SetGravity2(BulletBody obj, Vector3 val);
|
||||
|
||||
public abstract Vector3 GetGravity2(BulletBody obj);
|
||||
|
||||
public abstract void SetDamping2(BulletBody obj, float lin_damping, float ang_damping);
|
||||
|
||||
public abstract void SetLinearDamping2(BulletBody obj, float lin_damping);
|
||||
|
||||
public abstract void SetAngularDamping2(BulletBody obj, float ang_damping);
|
||||
|
||||
public abstract float GetLinearDamping2(BulletBody obj);
|
||||
|
||||
public abstract float GetAngularDamping2(BulletBody obj);
|
||||
|
||||
public abstract float GetLinearSleepingThreshold2(BulletBody obj);
|
||||
|
||||
|
||||
public abstract void ApplyDamping2(BulletBody obj, float timeStep);
|
||||
|
||||
public abstract void SetMassProps2(BulletBody obj, float mass, Vector3 inertia);
|
||||
|
||||
public abstract Vector3 GetLinearFactor2(BulletBody obj);
|
||||
|
||||
public abstract void SetLinearFactor2(BulletBody obj, Vector3 factor);
|
||||
|
||||
/*
|
||||
public abstract void SetCenterOfMassTransform2(BulletBody obj, Transform trans);
|
||||
*/
|
||||
|
||||
public abstract void SetCenterOfMassByPosRot2(BulletBody obj, Vector3 pos, Quaternion rot);
|
||||
|
||||
// Add a force to the object as if its mass is one.
|
||||
public abstract void ApplyCentralForce2(BulletBody obj, Vector3 force);
|
||||
|
||||
// Set the force being applied to the object as if its mass is one.
|
||||
public abstract void SetObjectForce2(BulletBody obj, Vector3 force);
|
||||
|
||||
public abstract Vector3 GetTotalForce2(BulletBody obj);
|
||||
|
||||
public abstract Vector3 GetTotalTorque2(BulletBody obj);
|
||||
|
||||
public abstract Vector3 GetInvInertiaDiagLocal2(BulletBody obj);
|
||||
|
||||
public abstract void SetInvInertiaDiagLocal2(BulletBody obj, Vector3 inert);
|
||||
|
||||
public abstract void SetSleepingThresholds2(BulletBody obj, float lin_threshold, float ang_threshold);
|
||||
|
||||
public abstract void ApplyTorque2(BulletBody obj, Vector3 torque);
|
||||
|
||||
// Apply force at the given point. Will add torque to the object.
|
||||
public abstract void ApplyForce2(BulletBody obj, Vector3 force, Vector3 pos);
|
||||
|
||||
// Apply impulse to the object. Same as "ApplycentralForce" but force scaled by object's mass.
|
||||
public abstract void ApplyCentralImpulse2(BulletBody obj, Vector3 imp);
|
||||
|
||||
// Apply impulse to the object's torque. Force is scaled by object's mass.
|
||||
public abstract void ApplyTorqueImpulse2(BulletBody obj, Vector3 imp);
|
||||
|
||||
// Apply impulse at the point given. For is scaled by object's mass and effects both linear and angular forces.
|
||||
public abstract void ApplyImpulse2(BulletBody obj, Vector3 imp, Vector3 pos);
|
||||
|
||||
public abstract void ClearForces2(BulletBody obj);
|
||||
|
||||
public abstract void ClearAllForces2(BulletBody obj);
|
||||
|
||||
public abstract void UpdateInertiaTensor2(BulletBody obj);
|
||||
|
||||
|
||||
/*
|
||||
public abstract Transform GetCenterOfMassTransform2(BulletBody obj);
|
||||
*/
|
||||
|
||||
public abstract Vector3 GetLinearVelocity2(BulletBody obj);
|
||||
|
||||
public abstract Vector3 GetAngularVelocity2(BulletBody obj);
|
||||
|
||||
public abstract void SetLinearVelocity2(BulletBody obj, Vector3 val);
|
||||
|
||||
public abstract void SetAngularVelocity2(BulletBody obj, Vector3 angularVelocity);
|
||||
|
||||
public abstract Vector3 GetVelocityInLocalPoint2(BulletBody obj, Vector3 pos);
|
||||
|
||||
public abstract void Translate2(BulletBody obj, Vector3 trans);
|
||||
|
||||
public abstract void UpdateDeactivation2(BulletBody obj, float timeStep);
|
||||
|
||||
public abstract bool WantsSleeping2(BulletBody obj);
|
||||
|
||||
public abstract void SetAngularFactor2(BulletBody obj, float factor);
|
||||
|
||||
public abstract void SetAngularFactorV2(BulletBody obj, Vector3 factor);
|
||||
|
||||
public abstract Vector3 GetAngularFactor2(BulletBody obj);
|
||||
|
||||
public abstract bool IsInWorld2(BulletBody obj);
|
||||
|
||||
public abstract void AddConstraintRef2(BulletBody obj, BulletConstraint constrain);
|
||||
|
||||
public abstract void RemoveConstraintRef2(BulletBody obj, BulletConstraint constrain);
|
||||
|
||||
public abstract BulletConstraint GetConstraintRef2(BulletBody obj, int index);
|
||||
|
||||
public abstract int GetNumConstraintRefs2(BulletBody obj);
|
||||
|
||||
public abstract bool SetCollisionGroupMask2(BulletBody body, uint filter, uint mask);
|
||||
|
||||
// =====================================================================================
|
||||
// btCollisionShape entries
|
||||
|
||||
public abstract float GetAngularMotionDisc2(BulletShape shape);
|
||||
|
||||
public abstract float GetContactBreakingThreshold2(BulletShape shape, float defaultFactor);
|
||||
|
||||
public abstract bool IsPolyhedral2(BulletShape shape);
|
||||
|
||||
public abstract bool IsConvex2d2(BulletShape shape);
|
||||
|
||||
public abstract bool IsConvex2(BulletShape shape);
|
||||
|
||||
public abstract bool IsNonMoving2(BulletShape shape);
|
||||
|
||||
public abstract bool IsConcave2(BulletShape shape);
|
||||
|
||||
public abstract bool IsCompound2(BulletShape shape);
|
||||
|
||||
public abstract bool IsSoftBody2(BulletShape shape);
|
||||
|
||||
public abstract bool IsInfinite2(BulletShape shape);
|
||||
|
||||
public abstract void SetLocalScaling2(BulletShape shape, Vector3 scale);
|
||||
|
||||
public abstract Vector3 GetLocalScaling2(BulletShape shape);
|
||||
|
||||
public abstract Vector3 CalculateLocalInertia2(BulletShape shape, float mass);
|
||||
|
||||
public abstract int GetShapeType2(BulletShape shape);
|
||||
|
||||
public abstract void SetMargin2(BulletShape shape, float val);
|
||||
|
||||
public abstract float GetMargin2(BulletShape shape);
|
||||
|
||||
};
|
||||
|
||||
// ===============================================================================
|
||||
static class BulletSimAPI {
|
||||
// ===============================================================================
|
||||
|
|
|
@ -35,9 +35,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
// These hold pointers to allocated objects in the unmanaged space.
|
||||
|
||||
// The physics engine controller class created at initialization
|
||||
public struct BulletSim
|
||||
public struct BulletWorld
|
||||
{
|
||||
public BulletSim(uint worldId, BSScene bss, IntPtr xx)
|
||||
public BulletWorld(uint worldId, BSScene bss, IntPtr xx)
|
||||
{
|
||||
ptr = xx;
|
||||
worldID = worldId;
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
CURRENT PRIORITIES
|
||||
=================================================
|
||||
Smooth avatar movement with motor
|
||||
Should motor update be all at taint-time?
|
||||
Smooth avatar movement with motor (DONE)
|
||||
Should motor update be all at taint-time? (Yes, DONE)
|
||||
Fix avatar slowly sliding when standing (zero motion when stopped)
|
||||
llApplyImpulse()
|
||||
Compare mass/movement in OS and SL. Calibrate actions.
|
||||
llSetBuoyancy()
|
||||
Boats float low in the water
|
||||
Enable vehicle border crossings (at least as poorly as ODE)
|
||||
Terrain skirts
|
||||
Avatar created in previous region and not new region when crossing border
|
||||
Vehicle recreated in new sim at small Z value (offset from root value?) (DONE)
|
||||
Add material densities to the material types.
|
||||
Vehicle movement on terrain smoothness
|
||||
Vehicle script tuning/debugging
|
||||
Avanti speed script
|
||||
|
@ -52,6 +58,8 @@ Incorporate inter-relationship of angular corrections. For instance, angularDefl
|
|||
|
||||
BULLETSIM TODO LIST:
|
||||
=================================================
|
||||
In SL, perfect spheres don't seem to have rolling friction. Add special case.
|
||||
Avatar density is WAY off. Compare and calibrate with what's in SL.
|
||||
Revisit CollisionMargin. Builders notice the 0.04 spacing between prims.
|
||||
Duplicating a physical prim causes old prim to jump away
|
||||
Dup a phys prim and the original become unselected and thus interacts w/ selected prim.
|
||||
|
@ -82,6 +90,9 @@ Linkset.Position and Linkset.Orientation requre rewrite to properly return
|
|||
Implement LockAngularMotion -- implements llSetStatus(ROTATE_AXIS_*, T/F)
|
||||
Should the different PID factors have non-equal contributions for different
|
||||
values of Efficiency?
|
||||
Selecting and deselecting physical objects causes CPU processing time to jump
|
||||
http://www.youtube.com/watch?v=Hjg57fWg8yI&hd=1
|
||||
put thousand physical objects, select and deselect same. CPU time will be large.
|
||||
|
||||
LINKSETS
|
||||
======================================================
|
||||
|
@ -135,6 +146,13 @@ Is there are more efficient method of implementing pre and post step actions?
|
|||
See http://www.codeproject.com/Articles/29922/Weak-Events-in-C
|
||||
|
||||
Physics Arena central pyramid: why is one side permiable?
|
||||
Enforce physical parameter min/max:
|
||||
Gravity: [-1, 28]
|
||||
Friction: [0, 255]
|
||||
Density: [1, 22587]
|
||||
Restitution [0, 1]
|
||||
http://wiki.secondlife.com/wiki/Physics_Material_Settings_test
|
||||
Avatar attachments have no mass? http://forums-archive.secondlife.com/54/f0/31796/1.html
|
||||
|
||||
INTERNAL IMPROVEMENT/CLEANUP
|
||||
=================================================
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* C# / XNA port of Bullet (c) 2011 Mark Neale <xexuxjy@hotmail.com>
|
||||
* http://code.google.com/p/bullet-xna/
|
||||
*
|
||||
* Bullet Continuous Collision Detection and Physics Library
|
||||
* Copyright (c) 2003-2008 Erwin Coumans http://www.bulletphysics.com/
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied warranty.
|
||||
* In no event will the authors be held liable for any damages arising from
|
||||
* the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
Binary file not shown.
Binary file not shown.
|
@ -206,12 +206,13 @@
|
|||
;; Choose one of the physics engines below
|
||||
;# {physics} {} {Select physics engine} {OpenDynamicsEngine BulletSim basicphysics POS} OpenDynamicsEngine
|
||||
;; OpenDynamicsEngine is by some distance the most developed physics engine
|
||||
;; BulletSim is incomplete and experimental but in active development
|
||||
;; BulletSim is incomplete and experimental but in active development. BulletSimN is a purely C# version of BulletSim.
|
||||
;; basicphysics effectively does not model physics at all, making all
|
||||
;; objects phantom
|
||||
;; objects phantom.
|
||||
;; Default is OpenDynamicsEngine
|
||||
; physics = OpenDynamicsEngine
|
||||
; physics = BulletSim
|
||||
; physics = BulletSimN
|
||||
; physics = basicphysics
|
||||
; physics = POS
|
||||
|
||||
|
|
34
prebuild.xml
34
prebuild.xml
|
@ -1879,6 +1879,40 @@
|
|||
</Files>
|
||||
</Project>
|
||||
|
||||
<Project frameworkVersion="v3_5" name="OpenSim.Region.Physics.BulletSNPlugin" path="OpenSim/Region/Physics/BulletSNPlugin" type="Library">
|
||||
<Configuration name="Debug">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/Physics/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
<Configuration name="Release">
|
||||
<Options>
|
||||
<OutputPath>../../../../bin/Physics/</OutputPath>
|
||||
</Options>
|
||||
</Configuration>
|
||||
|
||||
<ReferencePath>../../../../bin/</ReferencePath>
|
||||
<Reference name="System"/>
|
||||
<Reference name="System.Core"/>
|
||||
<Reference name="System.Xml"/>
|
||||
<Reference name="OpenMetaverseTypes" path="../../../../bin/"/>
|
||||
<Reference name="Nini.dll" path="../../../../bin/"/>
|
||||
<Reference name="OpenSim.Framework"/>
|
||||
<Reference name="OpenSim.Region.Framework"/>
|
||||
<Reference name="OpenSim.Region.CoreModules"/>
|
||||
<Reference name="OpenSim.Framework.Console"/>
|
||||
<Reference name="OpenSim.Region.Physics.Manager"/>
|
||||
<Reference name="OpenSim.Region.Physics.ConvexDecompositionDotNet"/>
|
||||
<Reference name="BulletXNA.dll" path="../../../../bin/"/>
|
||||
<Reference name="log4net.dll" path="../../../../bin/"/>
|
||||
|
||||
<Files>
|
||||
<Match pattern="*.cs" recurse="true">
|
||||
<Exclude name="Tests" pattern="Tests"/>
|
||||
</Match>
|
||||
</Files>
|
||||
</Project>
|
||||
|
||||
<!-- OpenSim app -->
|
||||
<Project frameworkVersion="v3_5" name="OpenSim" path="OpenSim/Region/Application" type="Exe">
|
||||
<Configuration name="Debug">
|
||||
|
|
Loading…
Reference in New Issue