* Dumping BulletSimNPlugin in favor of combining the API
parent
82b954b212
commit
6a75949323
|
@ -1,814 +0,0 @@
|
||||||
/*
|
|
||||||
* 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, false);
|
|
||||||
|
|
||||||
// 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, true);
|
|
||||||
// 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;
|
|
||||||
_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, bool inWorld)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
ZeroMotion(true);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,135 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 BulletWorld 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
/*
|
|
||||||
* 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(BulletWorld 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(BulletWorld 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,180 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 BulletWorld m_world;
|
|
||||||
|
|
||||||
public BSConstraintCollection(BulletWorld 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* 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(BulletWorld 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
|
@ -1,329 +0,0 @@
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
public float LinksetMass { get; protected set; }
|
|
||||||
|
|
||||||
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>();
|
|
||||||
LinksetMass = 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);
|
|
||||||
LinksetMass = 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);
|
|
||||||
LinksetMass = 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 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.
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,397 +0,0 @@
|
||||||
/*
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
base.Refresh(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.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.ScheduleRebuild", 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.
|
|
||||||
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.
|
|
||||||
LinksetMass = LinksetMass;
|
|
||||||
LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,316 +0,0 @@
|
||||||
/*
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
base.Refresh(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, true);
|
|
||||||
|
|
||||||
// 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, true);
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,200 +0,0 @@
|
||||||
/*
|
|
||||||
* 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",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.3f, 0.4f);
|
|
||||||
Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
|
|
||||||
new MaterialAttributes("glassPhysical",dDensity, 0.2f, 0.7f);
|
|
||||||
Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
|
|
||||||
new MaterialAttributes("woodPhysical",dDensity, 0.6f, 0.5f);
|
|
||||||
Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
|
|
||||||
new MaterialAttributes("fleshPhysical",dDensity, 0.9f, 0.3f);
|
|
||||||
Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
|
|
||||||
new MaterialAttributes("plasticPhysical",dDensity, 0.4f, 0.7f);
|
|
||||||
Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
|
|
||||||
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",3.5f, 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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,347 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,559 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.",
|
|
||||||
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); } ),
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,346 +0,0 @@
|
||||||
/*
|
|
||||||
* 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, ...)
|
|
||||||
// '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; }
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* 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
|
@ -1,957 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 BulletWorld 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 BulletXNA.CollisionDesc[] m_collisionArray;
|
|
||||||
//private GCHandle m_collisionArrayPinnedHandle;
|
|
||||||
|
|
||||||
internal int m_maxUpdatesPerFrame;
|
|
||||||
private 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 BulletXNA.CollisionDesc[0];
|
|
||||||
//m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned);
|
|
||||||
m_updateArray = new BulletXNA.EntityProperties[0];
|
|
||||||
//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 BulletWorld(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.
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// step the physical world one interval
|
|
||||||
m_simulationStep++;
|
|
||||||
int numSubSteps = 0;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
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(DetailLog);
|
|
||||||
}
|
|
||||||
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
|
@ -1,208 +0,0 @@
|
||||||
/*
|
|
||||||
* 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) { }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,175 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,461 +0,0 @@
|
||||||
/*
|
|
||||||
* 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, Vector3.Zero, Quaternion.Identity);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,267 +0,0 @@
|
||||||
/*
|
|
||||||
* 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
|
@ -1,280 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 BulletWorld
|
|
||||||
{
|
|
||||||
public BulletWorld(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))
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue