* Initial commit of BulletSimN (BulletSNPlugin). Purely C# implementation of BulletSim. This is designed to be /as close as possible/ to the BulletSim plugin while still being entirely in the managed space to make keeping it up to date easy as possible (no thinking work). This implementation is /slower/ then the c++ version just because it's fully managed, so it's not appropriate for huge sims, but it will run small ones OK. At the moment, it supports all known features of BulletSim. Think of it like.. POS but everything works. To use this plugin, set the physics plugin to BulletSimN.
parent
50ee50bcd5
commit
92e4f9f412
|
@ -0,0 +1,814 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using log4net;
|
||||||
|
using OMV = OpenMetaverse;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Region.Physics.Manager;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
public sealed class BSCharacter : BSPhysObject
|
||||||
|
{
|
||||||
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
private static readonly string LogHeader = "[BULLETS CHAR]";
|
||||||
|
|
||||||
|
// private bool _stopped;
|
||||||
|
private OMV.Vector3 _size;
|
||||||
|
private bool _grabbed;
|
||||||
|
private bool _selected;
|
||||||
|
private OMV.Vector3 _position;
|
||||||
|
private float _mass;
|
||||||
|
private float _avatarDensity;
|
||||||
|
private float _avatarVolume;
|
||||||
|
private OMV.Vector3 _force;
|
||||||
|
private OMV.Vector3 _velocity;
|
||||||
|
private OMV.Vector3 _torque;
|
||||||
|
private float _collisionScore;
|
||||||
|
private OMV.Vector3 _acceleration;
|
||||||
|
private OMV.Quaternion _orientation;
|
||||||
|
private int _physicsActorType;
|
||||||
|
private bool _isPhysical;
|
||||||
|
private bool _flying;
|
||||||
|
private bool _setAlwaysRun;
|
||||||
|
private bool _throttleUpdates;
|
||||||
|
private bool _isColliding;
|
||||||
|
private bool _collidingObj;
|
||||||
|
private bool _floatOnWater;
|
||||||
|
private OMV.Vector3 _rotationalVelocity;
|
||||||
|
private bool _kinematic;
|
||||||
|
private float _buoyancy;
|
||||||
|
|
||||||
|
// The friction and velocity of the avatar is modified depending on whether walking or not.
|
||||||
|
private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar
|
||||||
|
private float _currentFriction; // the friction currently being used (changed by setVelocity).
|
||||||
|
|
||||||
|
private BSVMotor _velocityMotor;
|
||||||
|
|
||||||
|
private OMV.Vector3 _PIDTarget;
|
||||||
|
private bool _usePID;
|
||||||
|
private float _PIDTau;
|
||||||
|
private bool _useHoverPID;
|
||||||
|
private float _PIDHoverHeight;
|
||||||
|
private PIDHoverType _PIDHoverType;
|
||||||
|
private float _PIDHoverTao;
|
||||||
|
|
||||||
|
public BSCharacter(uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, bool isFlying)
|
||||||
|
: base(parent_scene, localID, avName, "BSCharacter")
|
||||||
|
{
|
||||||
|
_physicsActorType = (int)ActorTypes.Agent;
|
||||||
|
_position = pos;
|
||||||
|
|
||||||
|
// Old versions of ScenePresence passed only the height. If width and/or depth are zero,
|
||||||
|
// replace with the default values.
|
||||||
|
_size = size;
|
||||||
|
if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
|
||||||
|
if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
|
||||||
|
|
||||||
|
// A motor to control the acceleration and deceleration of the avatar movement.
|
||||||
|
// _velocityMotor = new BSVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
|
||||||
|
// _velocityMotor = new BSPIDVMotor("BSCharacter.Velocity", 3f, 5f, BSMotor.InfiniteVector, 1f);
|
||||||
|
// Infinite decay and timescale values so motor only changes current to target values.
|
||||||
|
_velocityMotor = new BSVMotor("BSCharacter.Velocity",
|
||||||
|
0.2f, // time scale
|
||||||
|
BSMotor.Infinite, // decay time scale
|
||||||
|
BSMotor.InfiniteVector, // friction timescale
|
||||||
|
1f // efficiency
|
||||||
|
);
|
||||||
|
_velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages.
|
||||||
|
|
||||||
|
_flying = isFlying;
|
||||||
|
_orientation = OMV.Quaternion.Identity;
|
||||||
|
_velocity = OMV.Vector3.Zero;
|
||||||
|
_appliedVelocity = OMV.Vector3.Zero;
|
||||||
|
_buoyancy = ComputeBuoyancyFromFlying(isFlying);
|
||||||
|
_currentFriction = BSParam.AvatarStandingFriction;
|
||||||
|
_avatarDensity = BSParam.AvatarDensity;
|
||||||
|
|
||||||
|
// The dimensions of the avatar capsule are kept in the scale.
|
||||||
|
// Physics creates a unit capsule which is scaled by the physics engine.
|
||||||
|
ComputeAvatarScale(_size);
|
||||||
|
// set _avatarVolume and _mass based on capsule size, _density and Scale
|
||||||
|
ComputeAvatarVolumeAndMass();
|
||||||
|
DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
|
||||||
|
LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
|
||||||
|
|
||||||
|
// do actual creation in taint time
|
||||||
|
PhysicsScene.TaintedObject("BSCharacter.create", delegate()
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSCharacter.create,taint", LocalID);
|
||||||
|
// New body and shape into PhysBody and PhysShape
|
||||||
|
PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this);
|
||||||
|
|
||||||
|
SetPhysicalProperties();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// called when this character is being destroyed and the resources should be released
|
||||||
|
public override void Destroy()
|
||||||
|
{
|
||||||
|
base.Destroy();
|
||||||
|
|
||||||
|
DetailLog("{0},BSCharacter.Destroy", LocalID);
|
||||||
|
PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
|
||||||
|
{
|
||||||
|
PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
|
||||||
|
PhysBody.Clear();
|
||||||
|
PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
|
||||||
|
PhysShape.Clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetPhysicalProperties()
|
||||||
|
{
|
||||||
|
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
|
||||||
|
|
||||||
|
ZeroMotion(true);
|
||||||
|
ForcePosition = _position;
|
||||||
|
// Set the velocity and compute the proper friction
|
||||||
|
ForceVelocity = _velocity;
|
||||||
|
// Setting the current and target in the motor will cause it to start computing any deceleration.
|
||||||
|
_velocityMotor.Reset();
|
||||||
|
_velocityMotor.SetCurrent(_velocity);
|
||||||
|
_velocityMotor.SetTarget(_velocity);
|
||||||
|
_velocityMotor.Enabled = false;
|
||||||
|
|
||||||
|
// This will enable or disable the flying buoyancy of the avatar.
|
||||||
|
// Needs to be reset especially when an avatar is recreated after crossing a region boundry.
|
||||||
|
Flying = _flying;
|
||||||
|
|
||||||
|
BulletSimAPI.SetRestitution2(PhysBody.ptr, BSParam.AvatarRestitution);
|
||||||
|
BulletSimAPI.SetMargin2(PhysShape.ptr, PhysicsScene.Params.collisionMargin);
|
||||||
|
BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
|
||||||
|
BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, BSParam.ContactProcessingThreshold);
|
||||||
|
if (BSParam.CcdMotionThreshold > 0f)
|
||||||
|
{
|
||||||
|
BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, BSParam.CcdMotionThreshold);
|
||||||
|
BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, BSParam.CcdSweptSphereRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdatePhysicalMassProperties(RawMass);
|
||||||
|
|
||||||
|
// Make so capsule does not fall over
|
||||||
|
BulletSimAPI.SetAngularFactorV2(PhysBody.ptr, OMV.Vector3.Zero);
|
||||||
|
|
||||||
|
BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT);
|
||||||
|
|
||||||
|
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr, _position, _orientation);
|
||||||
|
|
||||||
|
// BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG);
|
||||||
|
BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_DEACTIVATION);
|
||||||
|
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
|
||||||
|
|
||||||
|
// Do this after the object has been added to the world
|
||||||
|
PhysBody.collisionType = CollisionType.Avatar;
|
||||||
|
PhysBody.ApplyCollisionMask();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RequestPhysicsterseUpdate()
|
||||||
|
{
|
||||||
|
base.RequestPhysicsterseUpdate();
|
||||||
|
}
|
||||||
|
// No one calls this method so I don't know what it could possibly mean
|
||||||
|
public override bool Stopped { get { return false; } }
|
||||||
|
|
||||||
|
public override OMV.Vector3 Size {
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// Avatar capsule size is kept in the scale parameter.
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
// When an avatar's size is set, only the height is changed.
|
||||||
|
_size = value;
|
||||||
|
// Old versions of ScenePresence passed only the height. If width and/or depth are zero,
|
||||||
|
// replace with the default values.
|
||||||
|
if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
|
||||||
|
if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
|
||||||
|
|
||||||
|
ComputeAvatarScale(_size);
|
||||||
|
ComputeAvatarVolumeAndMass();
|
||||||
|
DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
|
||||||
|
LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
|
||||||
|
|
||||||
|
PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
|
||||||
|
{
|
||||||
|
if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape)
|
||||||
|
{
|
||||||
|
BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
|
||||||
|
UpdatePhysicalMassProperties(RawMass);
|
||||||
|
// Make sure this change appears as a property update event
|
||||||
|
BulletSimAPI.PushUpdate2(PhysBody.ptr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override PrimitiveBaseShape Shape
|
||||||
|
{
|
||||||
|
set { BaseShape = value; }
|
||||||
|
}
|
||||||
|
// I want the physics engine to make an avatar capsule
|
||||||
|
public override BSPhysicsShapeType PreferredPhysicalShape
|
||||||
|
{
|
||||||
|
get {return BSPhysicsShapeType.SHAPE_CAPSULE; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Grabbed {
|
||||||
|
set { _grabbed = value; }
|
||||||
|
}
|
||||||
|
public override bool Selected {
|
||||||
|
set { _selected = value; }
|
||||||
|
}
|
||||||
|
public override void CrossingFailure() { return; }
|
||||||
|
public override void link(PhysicsActor obj) { return; }
|
||||||
|
public override void delink() { return; }
|
||||||
|
|
||||||
|
// Set motion values to zero.
|
||||||
|
// Do it to the properties so the values get set in the physics engine.
|
||||||
|
// Push the setting of the values to the viewer.
|
||||||
|
// Called at taint time!
|
||||||
|
public override void ZeroMotion(bool inTaintTime)
|
||||||
|
{
|
||||||
|
_velocity = OMV.Vector3.Zero;
|
||||||
|
_velocityMotor.Zero();
|
||||||
|
_acceleration = OMV.Vector3.Zero;
|
||||||
|
_rotationalVelocity = OMV.Vector3.Zero;
|
||||||
|
|
||||||
|
// Zero some other properties directly into the physics engine
|
||||||
|
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
|
||||||
|
{
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
BulletSimAPI.ClearAllForces2(PhysBody.ptr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public override void ZeroAngularMotion(bool inTaintTime)
|
||||||
|
{
|
||||||
|
_rotationalVelocity = OMV.Vector3.Zero;
|
||||||
|
|
||||||
|
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
|
||||||
|
{
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
{
|
||||||
|
BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
|
||||||
|
BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
|
||||||
|
// The next also get rid of applied linear force but the linear velocity is untouched.
|
||||||
|
BulletSimAPI.ClearForces2(PhysBody.ptr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void LockAngularMotion(OMV.Vector3 axis) { return; }
|
||||||
|
|
||||||
|
public override OMV.Vector3 RawPosition
|
||||||
|
{
|
||||||
|
get { return _position; }
|
||||||
|
set { _position = value; }
|
||||||
|
}
|
||||||
|
public override OMV.Vector3 Position {
|
||||||
|
get {
|
||||||
|
// Don't refetch the position because this function is called a zillion times
|
||||||
|
// _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID);
|
||||||
|
return _position;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_position = value;
|
||||||
|
PositionSanityCheck();
|
||||||
|
|
||||||
|
PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override OMV.Vector3 ForcePosition {
|
||||||
|
get {
|
||||||
|
_position = BulletSimAPI.GetPosition2(PhysBody.ptr);
|
||||||
|
return _position;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
_position = value;
|
||||||
|
PositionSanityCheck();
|
||||||
|
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check that the current position is sane and, if not, modify the position to make it so.
|
||||||
|
// Check for being below terrain or on water.
|
||||||
|
// Returns 'true' of the position was made sane by some action.
|
||||||
|
private bool PositionSanityCheck()
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
// TODO: check for out of bounds
|
||||||
|
if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
|
||||||
|
{
|
||||||
|
// The character is out of the known/simulated area.
|
||||||
|
// Upper levels of code will handle the transition to other areas so, for
|
||||||
|
// the time, we just ignore the position.
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If below the ground, move the avatar up
|
||||||
|
float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
|
||||||
|
if (Position.Z < terrainHeight)
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
|
||||||
|
_position.Z = terrainHeight + 2.0f;
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
|
||||||
|
{
|
||||||
|
float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
|
||||||
|
if (Position.Z < waterHeight)
|
||||||
|
{
|
||||||
|
_position.Z = waterHeight;
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A version of the sanity check that also makes sure a new position value is
|
||||||
|
// pushed back to the physics engine. This routine would be used by anyone
|
||||||
|
// who is not already pushing the value.
|
||||||
|
private bool PositionSanityCheck(bool inTaintTime)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (PositionSanityCheck())
|
||||||
|
{
|
||||||
|
// The new position value must be pushed into the physics engine but we can't
|
||||||
|
// just assign to "Position" because of potential call loops.
|
||||||
|
PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate()
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||||
|
});
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float Mass { get { return _mass; } }
|
||||||
|
|
||||||
|
// used when we only want this prim's mass and not the linkset thing
|
||||||
|
public override float RawMass {
|
||||||
|
get {return _mass; }
|
||||||
|
}
|
||||||
|
public override void UpdatePhysicalMassProperties(float physMass)
|
||||||
|
{
|
||||||
|
OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
|
||||||
|
BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, localInertia);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override OMV.Vector3 Force {
|
||||||
|
get { return _force; }
|
||||||
|
set {
|
||||||
|
_force = value;
|
||||||
|
// m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force);
|
||||||
|
PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TouchingGround()
|
||||||
|
{
|
||||||
|
bool ret = BulletSimAPI.RayCastGround(PhysicsScene.World.ptr,_position,_size.Z * 0.55f, PhysBody.ptr);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
// Avatars don't do vehicles
|
||||||
|
public override int VehicleType { get { return (int)Vehicle.TYPE_NONE; } set { return; } }
|
||||||
|
public override void VehicleFloatParam(int param, float value) { }
|
||||||
|
public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
|
||||||
|
public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
|
||||||
|
public override void VehicleFlags(int param, bool remove) { }
|
||||||
|
|
||||||
|
// Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
|
||||||
|
public override void SetVolumeDetect(int param) { return; }
|
||||||
|
|
||||||
|
public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
|
||||||
|
public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
|
||||||
|
|
||||||
|
// Sets the target in the motor. This starts the changing of the avatar's velocity.
|
||||||
|
public override OMV.Vector3 TargetVelocity
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _velocityMotor.TargetValue;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value);
|
||||||
|
|
||||||
|
if (!_flying)
|
||||||
|
if ((value.Z >= 0.0001f) || (value.Z <= -0.0001f) || _velocity.Z < -0.0001f)
|
||||||
|
if (!TouchingGround())
|
||||||
|
value.Z = _velocity.Z;
|
||||||
|
if (_setAlwaysRun)
|
||||||
|
value *= 1.3f;
|
||||||
|
|
||||||
|
OMV.Vector3 targetVel = value;
|
||||||
|
|
||||||
|
PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate()
|
||||||
|
{
|
||||||
|
|
||||||
|
_velocityMotor.Reset();
|
||||||
|
_velocityMotor.SetTarget(targetVel);
|
||||||
|
_velocityMotor.SetCurrent(_velocity);
|
||||||
|
_velocityMotor.Enabled = true;
|
||||||
|
|
||||||
|
// Make sure a property update happens next step so the motor gets incorporated.
|
||||||
|
BulletSimAPI.PushUpdate2(PhysBody.ptr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Directly setting velocity means this is what the user really wants now.
|
||||||
|
public override OMV.Vector3 Velocity {
|
||||||
|
get { return _velocity; }
|
||||||
|
set {
|
||||||
|
_velocity = value;
|
||||||
|
// m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity);
|
||||||
|
PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate()
|
||||||
|
{
|
||||||
|
_velocityMotor.Reset();
|
||||||
|
_velocityMotor.SetCurrent(_velocity);
|
||||||
|
_velocityMotor.SetTarget(_velocity);
|
||||||
|
// Even though the motor is initialized, it's not used and the velocity goes straight into the avatar.
|
||||||
|
_velocityMotor.Enabled = false;
|
||||||
|
|
||||||
|
DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity);
|
||||||
|
ForceVelocity = _velocity;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override OMV.Vector3 ForceVelocity {
|
||||||
|
get { return _velocity; }
|
||||||
|
set {
|
||||||
|
PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity");
|
||||||
|
|
||||||
|
_velocity = value;
|
||||||
|
// Depending on whether the avatar is moving or not, change the friction
|
||||||
|
// to keep the avatar from slipping around
|
||||||
|
if (_velocity.Length() == 0)
|
||||||
|
{
|
||||||
|
if (_currentFriction != BSParam.AvatarStandingFriction)
|
||||||
|
{
|
||||||
|
_currentFriction = BSParam.AvatarStandingFriction;
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_currentFriction != BSParam.AvatarFriction)
|
||||||
|
{
|
||||||
|
_currentFriction = BSParam.AvatarFriction;
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remember the set velocity so we can suppress the reduction by friction, ...
|
||||||
|
_appliedVelocity = value;
|
||||||
|
|
||||||
|
BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
|
||||||
|
BulletSimAPI.Activate2(PhysBody.ptr, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override OMV.Vector3 Torque {
|
||||||
|
get { return _torque; }
|
||||||
|
set { _torque = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override float CollisionScore {
|
||||||
|
get { return _collisionScore; }
|
||||||
|
set { _collisionScore = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override OMV.Vector3 Acceleration {
|
||||||
|
get { return _acceleration; }
|
||||||
|
set { _acceleration = value; }
|
||||||
|
}
|
||||||
|
public override OMV.Quaternion RawOrientation
|
||||||
|
{
|
||||||
|
get { return _orientation; }
|
||||||
|
set { _orientation = value; }
|
||||||
|
}
|
||||||
|
public override OMV.Quaternion Orientation {
|
||||||
|
get { return _orientation; }
|
||||||
|
set {
|
||||||
|
_orientation = value;
|
||||||
|
// m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
|
||||||
|
PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
|
||||||
|
{
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
{
|
||||||
|
// _position = BulletSimAPI.GetPosition2(BSBody.ptr);
|
||||||
|
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Go directly to Bullet to get/set the value.
|
||||||
|
public override OMV.Quaternion ForceOrientation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
_orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
|
||||||
|
return _orientation;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_orientation = value;
|
||||||
|
BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override int PhysicsActorType {
|
||||||
|
get { return _physicsActorType; }
|
||||||
|
set { _physicsActorType = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override bool IsPhysical {
|
||||||
|
get { return _isPhysical; }
|
||||||
|
set { _isPhysical = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override bool IsSolid {
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
public override bool IsStatic {
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
public override bool Flying {
|
||||||
|
get { return _flying; }
|
||||||
|
set {
|
||||||
|
_flying = value;
|
||||||
|
|
||||||
|
// simulate flying by changing the effect of gravity
|
||||||
|
Buoyancy = ComputeBuoyancyFromFlying(_flying);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Flying is implimented by changing the avatar's buoyancy.
|
||||||
|
// Would this be done better with a vehicle type?
|
||||||
|
private float ComputeBuoyancyFromFlying(bool ifFlying) {
|
||||||
|
return ifFlying ? 1f : 0f;
|
||||||
|
}
|
||||||
|
public override bool
|
||||||
|
SetAlwaysRun {
|
||||||
|
get { return _setAlwaysRun; }
|
||||||
|
set { _setAlwaysRun = value; }
|
||||||
|
}
|
||||||
|
public override bool ThrottleUpdates {
|
||||||
|
get { return _throttleUpdates; }
|
||||||
|
set { _throttleUpdates = value; }
|
||||||
|
}
|
||||||
|
public override bool IsColliding {
|
||||||
|
get { return (CollidingStep == PhysicsScene.SimulationStep); }
|
||||||
|
set { _isColliding = value; }
|
||||||
|
}
|
||||||
|
public override bool CollidingGround {
|
||||||
|
get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
|
||||||
|
set { CollidingGround = value; }
|
||||||
|
}
|
||||||
|
public override bool CollidingObj {
|
||||||
|
get { return _collidingObj; }
|
||||||
|
set { _collidingObj = value; }
|
||||||
|
}
|
||||||
|
public override bool FloatOnWater {
|
||||||
|
set {
|
||||||
|
_floatOnWater = value;
|
||||||
|
PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
|
||||||
|
{
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
{
|
||||||
|
if (_floatOnWater)
|
||||||
|
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
|
||||||
|
else
|
||||||
|
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override OMV.Vector3 RotationalVelocity {
|
||||||
|
get { return _rotationalVelocity; }
|
||||||
|
set { _rotationalVelocity = value; }
|
||||||
|
}
|
||||||
|
public override OMV.Vector3 ForceRotationalVelocity {
|
||||||
|
get { return _rotationalVelocity; }
|
||||||
|
set { _rotationalVelocity = value; }
|
||||||
|
}
|
||||||
|
public override bool Kinematic {
|
||||||
|
get { return _kinematic; }
|
||||||
|
set { _kinematic = value; }
|
||||||
|
}
|
||||||
|
// neg=fall quickly, 0=1g, 1=0g, pos=float up
|
||||||
|
public override float Buoyancy {
|
||||||
|
get { return _buoyancy; }
|
||||||
|
set { _buoyancy = value;
|
||||||
|
PhysicsScene.TaintedObject("BSCharacter.setBuoyancy", delegate()
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
|
||||||
|
ForceBuoyancy = _buoyancy;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override float ForceBuoyancy {
|
||||||
|
get { return _buoyancy; }
|
||||||
|
set {
|
||||||
|
PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy");
|
||||||
|
|
||||||
|
_buoyancy = value;
|
||||||
|
DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
|
||||||
|
// Buoyancy is faked by changing the gravity applied to the object
|
||||||
|
float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for MoveTo
|
||||||
|
public override OMV.Vector3 PIDTarget {
|
||||||
|
set { _PIDTarget = value; }
|
||||||
|
}
|
||||||
|
public override bool PIDActive {
|
||||||
|
set { _usePID = value; }
|
||||||
|
}
|
||||||
|
public override float PIDTau {
|
||||||
|
set { _PIDTau = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used for llSetHoverHeight and maybe vehicle height
|
||||||
|
// Hover Height will override MoveTo target's Z
|
||||||
|
public override bool PIDHoverActive {
|
||||||
|
set { _useHoverPID = value; }
|
||||||
|
}
|
||||||
|
public override float PIDHoverHeight {
|
||||||
|
set { _PIDHoverHeight = value; }
|
||||||
|
}
|
||||||
|
public override PIDHoverType PIDHoverType {
|
||||||
|
set { _PIDHoverType = value; }
|
||||||
|
}
|
||||||
|
public override float PIDHoverTau {
|
||||||
|
set { _PIDHoverTao = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// For RotLookAt
|
||||||
|
public override OMV.Quaternion APIDTarget { set { return; } }
|
||||||
|
public override bool APIDActive { set { return; } }
|
||||||
|
public override float APIDStrength { set { return; } }
|
||||||
|
public override float APIDDamping { set { return; } }
|
||||||
|
|
||||||
|
public override void AddForce(OMV.Vector3 force, bool pushforce) {
|
||||||
|
if (force.IsFinite())
|
||||||
|
{
|
||||||
|
_force.X += force.X;
|
||||||
|
_force.Y += force.Y;
|
||||||
|
_force.Z += force.Z;
|
||||||
|
// m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force);
|
||||||
|
PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat("{0}: Got a NaN force applied to a Character", LogHeader);
|
||||||
|
}
|
||||||
|
//m_lastUpdateSent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
|
||||||
|
}
|
||||||
|
public override void SetMomentum(OMV.Vector3 momentum) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ComputeAvatarScale(OMV.Vector3 size)
|
||||||
|
{
|
||||||
|
OMV.Vector3 newScale = size;
|
||||||
|
// newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
|
||||||
|
// newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
|
||||||
|
|
||||||
|
// From the total height, remove the capsule half spheres that are at each end
|
||||||
|
// The 1.15f came from ODE. Not sure what this factors in.
|
||||||
|
// newScale.Z = (size.Z * 1.15f) - (newScale.X + newScale.Y);
|
||||||
|
|
||||||
|
// The total scale height is the central cylindar plus the caps on the two ends.
|
||||||
|
newScale.Z = size.Z + (Math.Min(size.X, size.Y) * 2f);
|
||||||
|
|
||||||
|
// Convert diameters to radii and height to half height -- the way Bullet expects it.
|
||||||
|
Scale = newScale / 2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set _avatarVolume and _mass based on capsule size, _density and Scale
|
||||||
|
private void ComputeAvatarVolumeAndMass()
|
||||||
|
{
|
||||||
|
_avatarVolume = (float)(
|
||||||
|
Math.PI
|
||||||
|
* Scale.X
|
||||||
|
* Scale.Y // the area of capsule cylinder
|
||||||
|
* Scale.Z // times height of capsule cylinder
|
||||||
|
+ 1.33333333f
|
||||||
|
* Math.PI
|
||||||
|
* Scale.X
|
||||||
|
* Math.Min(Scale.X, Scale.Y)
|
||||||
|
* Scale.Y // plus the volume of the capsule end caps
|
||||||
|
);
|
||||||
|
_mass = _avatarDensity * _avatarVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The physics engine says that properties have updated. Update same and inform
|
||||||
|
// the world that things have changed.
|
||||||
|
public override void UpdateProperties(EntityProperties entprop)
|
||||||
|
{
|
||||||
|
_position = entprop.Position;
|
||||||
|
_orientation = entprop.Rotation;
|
||||||
|
_velocity = entprop.Velocity;
|
||||||
|
_acceleration = entprop.Acceleration;
|
||||||
|
_rotationalVelocity = entprop.RotationalVelocity;
|
||||||
|
|
||||||
|
// Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
|
||||||
|
PositionSanityCheck(true);
|
||||||
|
|
||||||
|
if (_velocityMotor.Enabled)
|
||||||
|
{
|
||||||
|
// TODO: Decide if the step parameters should be changed depending on the avatar's
|
||||||
|
// state (flying, colliding, ...).
|
||||||
|
|
||||||
|
OMV.Vector3 stepVelocity = _velocityMotor.Step(PhysicsScene.LastTimeStep);
|
||||||
|
|
||||||
|
// If falling, we keep the world's downward vector no matter what the other axis specify.
|
||||||
|
if (!Flying && !IsColliding)
|
||||||
|
{
|
||||||
|
stepVelocity.Z = entprop.Velocity.Z;
|
||||||
|
DetailLog("{0},BSCharacter.UpdateProperties,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user has said stop and we've stopped applying velocity correction,
|
||||||
|
// the motor can be turned off. Set the velocity to zero so the zero motion is sent to the viewer.
|
||||||
|
if (_velocityMotor.TargetValue.ApproxEquals(OMV.Vector3.Zero, 0.01f) && _velocityMotor.ErrorIsZero)
|
||||||
|
{
|
||||||
|
stepVelocity = OMV.Vector3.Zero;
|
||||||
|
_velocityMotor.Enabled = false;
|
||||||
|
DetailLog("{0},BSCharacter.UpdateProperties,taint,disableVelocityMotor,m={1}", LocalID, _velocityMotor);
|
||||||
|
}
|
||||||
|
|
||||||
|
_velocity = stepVelocity;
|
||||||
|
entprop.Velocity = _velocity;
|
||||||
|
BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember the current and last set values
|
||||||
|
LastEntityProperties = CurrentEntityProperties;
|
||||||
|
CurrentEntityProperties = entprop;
|
||||||
|
|
||||||
|
// Tell the linkset about value changes
|
||||||
|
Linkset.UpdateProperties(this, true);
|
||||||
|
|
||||||
|
// Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
|
||||||
|
// base.RequestPhysicsterseUpdate();
|
||||||
|
|
||||||
|
DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
|
||||||
|
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
public abstract class BSConstraint : IDisposable
|
||||||
|
{
|
||||||
|
private static string LogHeader = "[BULLETSIM CONSTRAINT]";
|
||||||
|
|
||||||
|
protected BulletSim m_world;
|
||||||
|
protected BulletBody m_body1;
|
||||||
|
protected BulletBody m_body2;
|
||||||
|
protected BulletConstraint m_constraint;
|
||||||
|
protected bool m_enabled = false;
|
||||||
|
|
||||||
|
public BulletBody Body1 { get { return m_body1; } }
|
||||||
|
public BulletBody Body2 { get { return m_body2; } }
|
||||||
|
public BulletConstraint Constraint { get { return m_constraint; } }
|
||||||
|
public abstract ConstraintType Type { get; }
|
||||||
|
public bool IsEnabled { get { return m_enabled; } }
|
||||||
|
|
||||||
|
public BSConstraint()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
if (m_enabled)
|
||||||
|
{
|
||||||
|
m_enabled = false;
|
||||||
|
if (m_constraint.HasPhysicalConstraint)
|
||||||
|
{
|
||||||
|
bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
|
||||||
|
m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}",
|
||||||
|
BSScene.DetailLogZero,
|
||||||
|
m_body1.ID, m_body1.ptr.ToString(),
|
||||||
|
m_body2.ID, m_body2.ptr.ToString(),
|
||||||
|
success);
|
||||||
|
m_constraint.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool SetLinearLimits(Vector3 low, Vector3 high)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (m_enabled)
|
||||||
|
ret = BulletSimAPI.SetLinearLimits2(m_constraint.ptr, low, high);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool SetAngularLimits(Vector3 low, Vector3 high)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (m_enabled)
|
||||||
|
ret = BulletSimAPI.SetAngularLimits2(m_constraint.ptr, low, high);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool SetSolverIterations(float cnt)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (m_enabled)
|
||||||
|
{
|
||||||
|
BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.ptr, cnt);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool CalculateTransforms()
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (m_enabled)
|
||||||
|
{
|
||||||
|
// Recompute the internal transforms
|
||||||
|
BulletSimAPI.CalculateTransforms2(m_constraint.ptr);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset this constraint making sure it has all its internal structures
|
||||||
|
// recomputed and is enabled and ready to go.
|
||||||
|
public virtual bool RecomputeConstraintVariables(float mass)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (m_enabled)
|
||||||
|
{
|
||||||
|
ret = CalculateTransforms();
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
// Setting an object's mass to zero (making it static like when it's selected)
|
||||||
|
// automatically disables the constraints.
|
||||||
|
// If the link is enabled, be sure to set the constraint itself to enabled.
|
||||||
|
BulletSimAPI.SetConstraintEnable2(m_constraint.ptr, BSParam.NumericBool(true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_world.physicsScene.Logger.ErrorFormat("{0} CalculateTransforms failed. A={1}, B={2}", LogHeader, Body1.ID, Body2.ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
public sealed class BSConstraint6Dof : BSConstraint
|
||||||
|
{
|
||||||
|
private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]";
|
||||||
|
|
||||||
|
public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } }
|
||||||
|
|
||||||
|
// Create a btGeneric6DofConstraint
|
||||||
|
public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||||
|
Vector3 frame1, Quaternion frame1rot,
|
||||||
|
Vector3 frame2, Quaternion frame2rot,
|
||||||
|
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||||
|
{
|
||||||
|
m_world = world;
|
||||||
|
m_body1 = obj1;
|
||||||
|
m_body2 = obj2;
|
||||||
|
m_constraint = new BulletConstraint(
|
||||||
|
BulletSimAPI.Create6DofConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||||
|
frame1, frame1rot,
|
||||||
|
frame2, frame2rot,
|
||||||
|
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||||
|
m_enabled = true;
|
||||||
|
world.physicsScene.DetailLog("{0},BS6DofConstraint,createFrame,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||||
|
BSScene.DetailLogZero, world.worldID,
|
||||||
|
obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BSConstraint6Dof(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||||
|
Vector3 joinPoint,
|
||||||
|
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||||
|
{
|
||||||
|
m_world = world;
|
||||||
|
m_body1 = obj1;
|
||||||
|
m_body2 = obj2;
|
||||||
|
if (!obj1.HasPhysicalBody || !obj2.HasPhysicalBody)
|
||||||
|
{
|
||||||
|
world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||||
|
BSScene.DetailLogZero, world.worldID,
|
||||||
|
obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||||
|
world.physicsScene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
|
||||||
|
LogHeader, world.worldID, obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||||
|
m_enabled = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_constraint = new BulletConstraint(
|
||||||
|
BulletSimAPI.Create6DofConstraintToPoint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||||
|
joinPoint,
|
||||||
|
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||||
|
world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}",
|
||||||
|
BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString(),
|
||||||
|
obj1.ID, obj1.ptr.ToString(), obj2.ID, obj2.ptr.ToString());
|
||||||
|
if (!m_constraint.HasPhysicalConstraint)
|
||||||
|
{
|
||||||
|
world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}",
|
||||||
|
LogHeader, obj1.ID, obj2.ID);
|
||||||
|
m_enabled = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (m_enabled)
|
||||||
|
{
|
||||||
|
BulletSimAPI.SetFrames2(m_constraint.ptr, frameA, frameArot, frameB, frameBrot);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetCFMAndERP(float cfm, float erp)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (m_enabled)
|
||||||
|
{
|
||||||
|
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
|
||||||
|
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL);
|
||||||
|
BulletSimAPI.SetConstraintParam2(m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UseFrameOffset(bool useOffset)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
|
||||||
|
if (m_enabled)
|
||||||
|
ret = BulletSimAPI.UseFrameOffset2(m_constraint.ptr, onOff);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TranslationalLimitMotor(bool enable, float targetVelocity, float maxMotorForce)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse;
|
||||||
|
if (m_enabled)
|
||||||
|
{
|
||||||
|
ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.ptr, onOff, targetVelocity, maxMotorForce);
|
||||||
|
m_world.physicsScene.DetailLog("{0},BS6DOFConstraint,TransLimitMotor,enable={1},vel={2},maxForce={3}",
|
||||||
|
BSScene.DetailLogZero, enable, targetVelocity, maxMotorForce);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetBreakingImpulseThreshold(float threshold)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (m_enabled)
|
||||||
|
ret = BulletSimAPI.SetBreakingImpulseThreshold2(m_constraint.ptr, threshold);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using log4net;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
public sealed class BSConstraintCollection : IDisposable
|
||||||
|
{
|
||||||
|
// private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
// private static readonly string LogHeader = "[CONSTRAINT COLLECTION]";
|
||||||
|
|
||||||
|
delegate bool ConstraintAction(BSConstraint constrain);
|
||||||
|
|
||||||
|
private List<BSConstraint> m_constraints;
|
||||||
|
private BulletSim m_world;
|
||||||
|
|
||||||
|
public BSConstraintCollection(BulletSim world)
|
||||||
|
{
|
||||||
|
m_world = world;
|
||||||
|
m_constraints = new List<BSConstraint>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
lock (m_constraints)
|
||||||
|
{
|
||||||
|
foreach (BSConstraint cons in m_constraints)
|
||||||
|
{
|
||||||
|
cons.Dispose();
|
||||||
|
}
|
||||||
|
m_constraints.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AddConstraint(BSConstraint cons)
|
||||||
|
{
|
||||||
|
lock (m_constraints)
|
||||||
|
{
|
||||||
|
// There is only one constraint between any bodies. Remove any old just to make sure.
|
||||||
|
RemoveAndDestroyConstraint(cons.Body1, cons.Body2);
|
||||||
|
|
||||||
|
m_constraints.Add(cons);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the constraint between two bodies. There can be only one.
|
||||||
|
// Return 'true' if a constraint was found.
|
||||||
|
public bool TryGetConstraint(BulletBody body1, BulletBody body2, out BSConstraint returnConstraint)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
BSConstraint foundConstraint = null;
|
||||||
|
|
||||||
|
uint lookingID1 = body1.ID;
|
||||||
|
uint lookingID2 = body2.ID;
|
||||||
|
lock (m_constraints)
|
||||||
|
{
|
||||||
|
foreach (BSConstraint constrain in m_constraints)
|
||||||
|
{
|
||||||
|
if ((constrain.Body1.ID == lookingID1 && constrain.Body2.ID == lookingID2)
|
||||||
|
|| (constrain.Body1.ID == lookingID2 && constrain.Body2.ID == lookingID1))
|
||||||
|
{
|
||||||
|
foundConstraint = constrain;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnConstraint = foundConstraint;
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any constraint between the passed bodies.
|
||||||
|
// Presumed there is only one such constraint possible.
|
||||||
|
// Return 'true' if a constraint was found and destroyed.
|
||||||
|
public bool RemoveAndDestroyConstraint(BulletBody body1, BulletBody body2)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
lock (m_constraints)
|
||||||
|
{
|
||||||
|
BSConstraint constrain;
|
||||||
|
if (this.TryGetConstraint(body1, body2, out constrain))
|
||||||
|
{
|
||||||
|
// remove the constraint from our collection
|
||||||
|
RemoveAndDestroyConstraint(constrain);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The constraint MUST exist in the collection
|
||||||
|
public bool RemoveAndDestroyConstraint(BSConstraint constrain)
|
||||||
|
{
|
||||||
|
lock (m_constraints)
|
||||||
|
{
|
||||||
|
// remove the constraint from our collection
|
||||||
|
m_constraints.Remove(constrain);
|
||||||
|
}
|
||||||
|
// tell the engine that all its structures need to be freed
|
||||||
|
constrain.Dispose();
|
||||||
|
// we destroyed something
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all constraints that reference the passed body.
|
||||||
|
// Return 'true' if any constraints were destroyed.
|
||||||
|
public bool RemoveAndDestroyConstraint(BulletBody body1)
|
||||||
|
{
|
||||||
|
List<BSConstraint> toRemove = new List<BSConstraint>();
|
||||||
|
uint lookingID = body1.ID;
|
||||||
|
lock (m_constraints)
|
||||||
|
{
|
||||||
|
foreach (BSConstraint constrain in m_constraints)
|
||||||
|
{
|
||||||
|
if (constrain.Body1.ID == lookingID || constrain.Body2.ID == lookingID)
|
||||||
|
{
|
||||||
|
toRemove.Add(constrain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (BSConstraint constrain in toRemove)
|
||||||
|
{
|
||||||
|
m_constraints.Remove(constrain);
|
||||||
|
constrain.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (toRemove.Count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RecalculateAllConstraints()
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
lock (m_constraints)
|
||||||
|
{
|
||||||
|
foreach (BSConstraint constrain in m_constraints)
|
||||||
|
{
|
||||||
|
constrain.CalculateTransforms();
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
public sealed class BSConstraintHinge : BSConstraint
|
||||||
|
{
|
||||||
|
public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } }
|
||||||
|
|
||||||
|
public BSConstraintHinge(BulletSim world, BulletBody obj1, BulletBody obj2,
|
||||||
|
Vector3 pivotInA, Vector3 pivotInB,
|
||||||
|
Vector3 axisInA, Vector3 axisInB,
|
||||||
|
bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies)
|
||||||
|
{
|
||||||
|
m_world = world;
|
||||||
|
m_body1 = obj1;
|
||||||
|
m_body2 = obj2;
|
||||||
|
m_constraint = new BulletConstraint(
|
||||||
|
BulletSimAPI.CreateHingeConstraint2(m_world.ptr, m_body1.ptr, m_body2.ptr,
|
||||||
|
pivotInA, pivotInB,
|
||||||
|
axisInA, axisInB,
|
||||||
|
useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies));
|
||||||
|
m_enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,333 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using OMV = OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
// A BSPrim can get individual information about its linkedness attached
|
||||||
|
// to it through an instance of a subclass of LinksetInfo.
|
||||||
|
// Each type of linkset will define the information needed for its type.
|
||||||
|
public abstract class BSLinksetInfo
|
||||||
|
{
|
||||||
|
public virtual void Clear() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class BSLinkset
|
||||||
|
{
|
||||||
|
// private static string LogHeader = "[BULLETSIM LINKSET]";
|
||||||
|
|
||||||
|
public enum LinksetImplementation
|
||||||
|
{
|
||||||
|
Constraint = 0, // linkset tied together with constraints
|
||||||
|
Compound = 1, // linkset tied together as a compound object
|
||||||
|
Manual = 2 // linkset tied together manually (code moves all the pieces)
|
||||||
|
}
|
||||||
|
// Create the correct type of linkset for this child
|
||||||
|
public static BSLinkset Factory(BSScene physScene, BSPhysObject parent)
|
||||||
|
{
|
||||||
|
BSLinkset ret = null;
|
||||||
|
|
||||||
|
switch ((int)BSParam.LinksetImplementation)
|
||||||
|
{
|
||||||
|
case (int)LinksetImplementation.Constraint:
|
||||||
|
ret = new BSLinksetConstraints(physScene, parent);
|
||||||
|
break;
|
||||||
|
case (int)LinksetImplementation.Compound:
|
||||||
|
ret = new BSLinksetCompound(physScene, parent);
|
||||||
|
break;
|
||||||
|
case (int)LinksetImplementation.Manual:
|
||||||
|
// ret = new BSLinksetManual(physScene, parent);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = new BSLinksetCompound(physScene, parent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BSPhysObject LinksetRoot { get; protected set; }
|
||||||
|
|
||||||
|
public BSScene PhysicsScene { get; private set; }
|
||||||
|
|
||||||
|
static int m_nextLinksetID = 1;
|
||||||
|
public int LinksetID { get; private set; }
|
||||||
|
|
||||||
|
// The children under the root in this linkset.
|
||||||
|
protected HashSet<BSPhysObject> m_children;
|
||||||
|
|
||||||
|
// We lock the diddling of linkset classes to prevent any badness.
|
||||||
|
// This locks the modification of the instances of this class. Changes
|
||||||
|
// to the physical representation is done via the tainting mechenism.
|
||||||
|
protected object m_linksetActivityLock = new Object();
|
||||||
|
|
||||||
|
// Some linksets have a preferred physical shape.
|
||||||
|
// Returns SHAPE_UNKNOWN if there is no preference. Causes the correct shape to be selected.
|
||||||
|
public virtual BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
|
||||||
|
{
|
||||||
|
return BSPhysicsShapeType.SHAPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We keep the prim's mass in the linkset structure since it could be dependent on other prims
|
||||||
|
protected float m_mass;
|
||||||
|
public float LinksetMass
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return m_mass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool LinksetIsColliding { get { return false; } }
|
||||||
|
|
||||||
|
public OMV.Vector3 CenterOfMass
|
||||||
|
{
|
||||||
|
get { return ComputeLinksetCenterOfMass(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public OMV.Vector3 GeometricCenter
|
||||||
|
{
|
||||||
|
get { return ComputeLinksetGeometricCenter(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected BSLinkset(BSScene scene, BSPhysObject parent)
|
||||||
|
{
|
||||||
|
// A simple linkset of one (no children)
|
||||||
|
LinksetID = m_nextLinksetID++;
|
||||||
|
// We create LOTS of linksets.
|
||||||
|
if (m_nextLinksetID <= 0)
|
||||||
|
m_nextLinksetID = 1;
|
||||||
|
PhysicsScene = scene;
|
||||||
|
LinksetRoot = parent;
|
||||||
|
m_children = new HashSet<BSPhysObject>();
|
||||||
|
m_mass = parent.RawMass;
|
||||||
|
Rebuilding = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link to a linkset where the child knows the parent.
|
||||||
|
// Parent changing should not happen so do some sanity checking.
|
||||||
|
// We return the parent's linkset so the child can track its membership.
|
||||||
|
// Called at runtime.
|
||||||
|
public BSLinkset AddMeToLinkset(BSPhysObject child)
|
||||||
|
{
|
||||||
|
lock (m_linksetActivityLock)
|
||||||
|
{
|
||||||
|
// Don't add the root to its own linkset
|
||||||
|
if (!IsRoot(child))
|
||||||
|
AddChildToLinkset(child);
|
||||||
|
m_mass = ComputeLinksetMass();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a child from a linkset.
|
||||||
|
// Returns a new linkset for the child which is a linkset of one (just the
|
||||||
|
// orphened child).
|
||||||
|
// Called at runtime.
|
||||||
|
public BSLinkset RemoveMeFromLinkset(BSPhysObject child)
|
||||||
|
{
|
||||||
|
lock (m_linksetActivityLock)
|
||||||
|
{
|
||||||
|
if (IsRoot(child))
|
||||||
|
{
|
||||||
|
// Cannot remove the root from a linkset.
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
RemoveChildFromLinkset(child);
|
||||||
|
m_mass = ComputeLinksetMass();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The child is down to a linkset of just itself
|
||||||
|
return BSLinkset.Factory(PhysicsScene, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return 'true' if the passed object is the root object of this linkset
|
||||||
|
public bool IsRoot(BSPhysObject requestor)
|
||||||
|
{
|
||||||
|
return (requestor.LocalID == LinksetRoot.LocalID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int NumberOfChildren { get { return m_children.Count; } }
|
||||||
|
|
||||||
|
// Return 'true' if this linkset has any children (more than the root member)
|
||||||
|
public bool HasAnyChildren { get { return (m_children.Count > 0); } }
|
||||||
|
|
||||||
|
// Return 'true' if this child is in this linkset
|
||||||
|
public bool HasChild(BSPhysObject child)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
lock (m_linksetActivityLock)
|
||||||
|
{
|
||||||
|
ret = m_children.Contains(child);
|
||||||
|
/* Safer version but the above should work
|
||||||
|
foreach (BSPhysObject bp in m_children)
|
||||||
|
{
|
||||||
|
if (child.LocalID == bp.LocalID)
|
||||||
|
{
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform an action on each member of the linkset including root prim.
|
||||||
|
// Depends on the action on whether this should be done at taint time.
|
||||||
|
public delegate bool ForEachMemberAction(BSPhysObject obj);
|
||||||
|
public virtual bool ForEachMember(ForEachMemberAction action)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
lock (m_linksetActivityLock)
|
||||||
|
{
|
||||||
|
action(LinksetRoot);
|
||||||
|
foreach (BSPhysObject po in m_children)
|
||||||
|
{
|
||||||
|
if (action(po))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// I am the root of a linkset and a new child is being added
|
||||||
|
// Called while LinkActivity is locked.
|
||||||
|
protected abstract void AddChildToLinkset(BSPhysObject child);
|
||||||
|
|
||||||
|
// I am the root of a linkset and one of my children is being removed.
|
||||||
|
// Safe to call even if the child is not really in my linkset.
|
||||||
|
protected abstract void RemoveChildFromLinkset(BSPhysObject child);
|
||||||
|
|
||||||
|
// When physical properties are changed the linkset needs to recalculate
|
||||||
|
// its internal properties.
|
||||||
|
// May be called at runtime or taint-time.
|
||||||
|
public abstract void Refresh(BSPhysObject requestor);
|
||||||
|
|
||||||
|
// Flag denoting the linkset is in the process of being rebuilt.
|
||||||
|
// Used to know not the schedule a rebuild in the middle of a rebuild.
|
||||||
|
protected bool Rebuilding { get; set; }
|
||||||
|
|
||||||
|
// The object is going dynamic (physical). Do any setup necessary
|
||||||
|
// for a dynamic linkset.
|
||||||
|
// Only the state of the passed object can be modified. The rest of the linkset
|
||||||
|
// has not yet been fully constructed.
|
||||||
|
// Return 'true' if any properties updated on the passed object.
|
||||||
|
// Called at taint-time!
|
||||||
|
public abstract bool MakeDynamic(BSPhysObject child);
|
||||||
|
|
||||||
|
// The object is going static (non-physical). Do any setup necessary
|
||||||
|
// for a static linkset.
|
||||||
|
// Return 'true' if any properties updated on the passed object.
|
||||||
|
// Called at taint-time!
|
||||||
|
public abstract bool MakeStatic(BSPhysObject child);
|
||||||
|
|
||||||
|
// Called when a parameter update comes from the physics engine for any object
|
||||||
|
// of the linkset is received.
|
||||||
|
// Passed flag is update came from physics engine (true) or the user (false).
|
||||||
|
// Called at taint-time!!
|
||||||
|
public abstract void UpdateProperties(BSPhysObject physObject, bool physicalUpdate);
|
||||||
|
|
||||||
|
// Routine used when rebuilding the body of the root of the linkset
|
||||||
|
// Destroy all the constraints have have been made to root.
|
||||||
|
// This is called when the root body is changing.
|
||||||
|
// Returns 'true' of something was actually removed and would need restoring
|
||||||
|
// Called at taint-time!!
|
||||||
|
public abstract bool RemoveBodyDependencies(BSPrim child);
|
||||||
|
|
||||||
|
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||||
|
// this routine will restore the removed constraints.
|
||||||
|
// Called at taint-time!!
|
||||||
|
public abstract void RestoreBodyDependencies(BSPrim child);
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
protected virtual float ComputeLinksetMass()
|
||||||
|
{
|
||||||
|
float mass = LinksetRoot.RawMass;
|
||||||
|
if (HasAnyChildren)
|
||||||
|
{
|
||||||
|
lock (m_linksetActivityLock)
|
||||||
|
{
|
||||||
|
foreach (BSPhysObject bp in m_children)
|
||||||
|
{
|
||||||
|
mass += bp.RawMass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mass;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual OMV.Vector3 ComputeLinksetCenterOfMass()
|
||||||
|
{
|
||||||
|
OMV.Vector3 com;
|
||||||
|
lock (m_linksetActivityLock)
|
||||||
|
{
|
||||||
|
com = LinksetRoot.Position * LinksetRoot.RawMass;
|
||||||
|
float totalMass = LinksetRoot.RawMass;
|
||||||
|
|
||||||
|
foreach (BSPhysObject bp in m_children)
|
||||||
|
{
|
||||||
|
com += bp.Position * bp.RawMass;
|
||||||
|
totalMass += bp.RawMass;
|
||||||
|
}
|
||||||
|
if (totalMass != 0f)
|
||||||
|
com /= totalMass;
|
||||||
|
}
|
||||||
|
|
||||||
|
return com;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual OMV.Vector3 ComputeLinksetGeometricCenter()
|
||||||
|
{
|
||||||
|
OMV.Vector3 com;
|
||||||
|
lock (m_linksetActivityLock)
|
||||||
|
{
|
||||||
|
com = LinksetRoot.Position;
|
||||||
|
|
||||||
|
foreach (BSPhysObject bp in m_children)
|
||||||
|
{
|
||||||
|
com += bp.Position * bp.RawMass;
|
||||||
|
}
|
||||||
|
com /= (m_children.Count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return com;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke the detailed logger and output something if it's enabled.
|
||||||
|
protected void DetailLog(string msg, params Object[] args)
|
||||||
|
{
|
||||||
|
if (PhysicsScene.PhysicsLogging.Enabled)
|
||||||
|
PhysicsScene.DetailLog(msg, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,396 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using OpenSim.Framework;
|
||||||
|
|
||||||
|
using OMV = OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
// When a child is linked, the relationship position of the child to the parent
|
||||||
|
// is remembered so the child's world position can be recomputed when it is
|
||||||
|
// removed from the linkset.
|
||||||
|
sealed class BSLinksetCompoundInfo : BSLinksetInfo
|
||||||
|
{
|
||||||
|
public OMV.Vector3 OffsetPos;
|
||||||
|
public OMV.Quaternion OffsetRot;
|
||||||
|
public BSLinksetCompoundInfo(OMV.Vector3 p, OMV.Quaternion r)
|
||||||
|
{
|
||||||
|
OffsetPos = p;
|
||||||
|
OffsetRot = r;
|
||||||
|
}
|
||||||
|
public override void Clear()
|
||||||
|
{
|
||||||
|
OffsetPos = OMV.Vector3.Zero;
|
||||||
|
OffsetRot = OMV.Quaternion.Identity;
|
||||||
|
}
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder buff = new StringBuilder();
|
||||||
|
buff.Append("<p=");
|
||||||
|
buff.Append(OffsetPos.ToString());
|
||||||
|
buff.Append(",r=");
|
||||||
|
buff.Append(OffsetRot.ToString());
|
||||||
|
buff.Append(">");
|
||||||
|
return buff.ToString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public sealed class BSLinksetCompound : BSLinkset
|
||||||
|
{
|
||||||
|
private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
|
||||||
|
|
||||||
|
public BSLinksetCompound(BSScene scene, BSPhysObject parent) : base(scene, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// For compound implimented linksets, if there are children, use compound shape for the root.
|
||||||
|
public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
|
||||||
|
{
|
||||||
|
// Returning 'unknown' means we don't have a preference.
|
||||||
|
BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
|
||||||
|
if (IsRoot(requestor) && HasAnyChildren)
|
||||||
|
{
|
||||||
|
ret = BSPhysicsShapeType.SHAPE_COMPOUND;
|
||||||
|
}
|
||||||
|
// DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When physical properties are changed the linkset needs to recalculate
|
||||||
|
// its internal properties.
|
||||||
|
public override void Refresh(BSPhysObject requestor)
|
||||||
|
{
|
||||||
|
// Something changed so do the rebuilding thing
|
||||||
|
// ScheduleRebuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule a refresh to happen after all the other taint processing.
|
||||||
|
private void ScheduleRebuild(BSPhysObject requestor)
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,rebuilding={1}",
|
||||||
|
requestor.LocalID, Rebuilding);
|
||||||
|
// When rebuilding, it is possible to set properties that would normally require a rebuild.
|
||||||
|
// If already rebuilding, don't request another rebuild.
|
||||||
|
if (!Rebuilding)
|
||||||
|
{
|
||||||
|
PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", LinksetRoot.LocalID, delegate()
|
||||||
|
{
|
||||||
|
if (HasAnyChildren)
|
||||||
|
RecomputeLinksetCompound();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The object is going dynamic (physical). Do any setup necessary
|
||||||
|
// for a dynamic linkset.
|
||||||
|
// Only the state of the passed object can be modified. The rest of the linkset
|
||||||
|
// has not yet been fully constructed.
|
||||||
|
// Return 'true' if any properties updated on the passed object.
|
||||||
|
// Called at taint-time!
|
||||||
|
public override bool MakeDynamic(BSPhysObject child)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
|
||||||
|
if (IsRoot(child))
|
||||||
|
{
|
||||||
|
// The root is going dynamic. Make sure mass is properly set.
|
||||||
|
m_mass = ComputeLinksetMass();
|
||||||
|
ScheduleRebuild(LinksetRoot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The origional prims are removed from the world as the shape of the root compound
|
||||||
|
// shape takes over.
|
||||||
|
BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
|
||||||
|
BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
|
||||||
|
// We don't want collisions from the old linkset children.
|
||||||
|
BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||||
|
|
||||||
|
child.PhysBody.collisionType = CollisionType.LinksetChild;
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The object is going static (non-physical). Do any setup necessary for a static linkset.
|
||||||
|
// Return 'true' if any properties updated on the passed object.
|
||||||
|
// This doesn't normally happen -- OpenSim removes the objects from the physical
|
||||||
|
// world if it is a static linkset.
|
||||||
|
// Called at taint-time!
|
||||||
|
public override bool MakeStatic(BSPhysObject child)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
|
||||||
|
if (IsRoot(child))
|
||||||
|
{
|
||||||
|
ScheduleRebuild(LinksetRoot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The non-physical children can come back to life.
|
||||||
|
BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
|
||||||
|
|
||||||
|
child.PhysBody.collisionType = CollisionType.LinksetChild;
|
||||||
|
|
||||||
|
// Don't force activation so setting of DISABLE_SIMULATION can stay if used.
|
||||||
|
BulletSimAPI.Activate2(child.PhysBody.ptr, false);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateProperties(BSPhysObject updated, bool physicalUpdate)
|
||||||
|
{
|
||||||
|
// The user moving a child around requires the rebuilding of the linkset compound shape
|
||||||
|
// One problem is this happens when a border is crossed -- the simulator implementation
|
||||||
|
// is to store the position into the group which causes the move of the object
|
||||||
|
// but it also means all the child positions get updated.
|
||||||
|
// What would cause an unnecessary rebuild so we make sure the linkset is in a
|
||||||
|
// region before bothering to do a rebuild.
|
||||||
|
if (!IsRoot(updated)
|
||||||
|
&& !physicalUpdate
|
||||||
|
&& PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
|
||||||
|
{
|
||||||
|
updated.LinksetInfo = null;
|
||||||
|
ScheduleRebuild(updated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routine called when rebuilding the body of some member of the linkset.
|
||||||
|
// Since we don't keep in world relationships, do nothing unless it's a child changing.
|
||||||
|
// Returns 'true' of something was actually removed and would need restoring
|
||||||
|
// Called at taint-time!!
|
||||||
|
public override bool RemoveBodyDependencies(BSPrim child)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
|
||||||
|
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), IsRoot(child));
|
||||||
|
|
||||||
|
if (!IsRoot(child))
|
||||||
|
{
|
||||||
|
// Because it is a convenient time, recompute child world position and rotation based on
|
||||||
|
// its position in the linkset.
|
||||||
|
RecomputeChildWorldPosition(child, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cannot schedule a refresh/rebuild here because this routine is called when
|
||||||
|
// the linkset is being rebuilt.
|
||||||
|
// InternalRefresh(LinksetRoot);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||||
|
// this routine will restore the removed constraints.
|
||||||
|
// Called at taint-time!!
|
||||||
|
public override void RestoreBodyDependencies(BSPrim child)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the linkset is built, the child shape is added to the compound shape relative to the
|
||||||
|
// root shape. The linkset then moves around but this does not move the actual child
|
||||||
|
// prim. The child prim's location must be recomputed based on the location of the root shape.
|
||||||
|
private void RecomputeChildWorldPosition(BSPhysObject child, bool inTaintTime)
|
||||||
|
{
|
||||||
|
BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo;
|
||||||
|
if (lci != null)
|
||||||
|
{
|
||||||
|
if (inTaintTime)
|
||||||
|
{
|
||||||
|
OMV.Vector3 oldPos = child.RawPosition;
|
||||||
|
child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetPos;
|
||||||
|
child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot;
|
||||||
|
DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}",
|
||||||
|
child.LocalID, oldPos, lci, child.RawPosition);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TaintedObject is not used here so the raw position is set now and not at taint-time.
|
||||||
|
child.Position = LinksetRoot.RawPosition + lci.OffsetPos;
|
||||||
|
child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This happens when children have been added to the linkset but the linkset
|
||||||
|
// has not been constructed yet. So like, at taint time, adding children to a linkset
|
||||||
|
// and then changing properties of the children (makePhysical, for instance)
|
||||||
|
// but the post-print action of actually rebuilding the linkset has not yet happened.
|
||||||
|
// PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}",
|
||||||
|
// LogHeader, child.LocalID);
|
||||||
|
DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
// Add a new child to the linkset.
|
||||||
|
// Called while LinkActivity is locked.
|
||||||
|
protected override void AddChildToLinkset(BSPhysObject child)
|
||||||
|
{
|
||||||
|
if (!HasChild(child))
|
||||||
|
{
|
||||||
|
m_children.Add(child);
|
||||||
|
|
||||||
|
DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
|
||||||
|
|
||||||
|
// Rebuild the compound shape with the new child shape included
|
||||||
|
ScheduleRebuild(child);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the specified child from the linkset.
|
||||||
|
// Safe to call even if the child is not really in the linkset.
|
||||||
|
protected override void RemoveChildFromLinkset(BSPhysObject child)
|
||||||
|
{
|
||||||
|
if (m_children.Remove(child))
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
|
||||||
|
child.LocalID,
|
||||||
|
LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(),
|
||||||
|
child.LocalID, child.PhysBody.ptr.ToString());
|
||||||
|
|
||||||
|
// Cause the child's body to be rebuilt and thus restored to normal operation
|
||||||
|
RecomputeChildWorldPosition(child, false);
|
||||||
|
child.ForceBodyShapeRebuild(false);
|
||||||
|
|
||||||
|
if (!HasAnyChildren)
|
||||||
|
{
|
||||||
|
// The linkset is now empty. The root needs rebuilding.
|
||||||
|
LinksetRoot.ForceBodyShapeRebuild(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Rebuild the compound shape with the child removed
|
||||||
|
ScheduleRebuild(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called before the simulation step to make sure the compound based linkset
|
||||||
|
// is all initialized.
|
||||||
|
// Constraint linksets are rebuilt every time.
|
||||||
|
// Note that this works for rebuilding just the root after a linkset is taken apart.
|
||||||
|
// Called at taint time!!
|
||||||
|
private void RecomputeLinksetCompound()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Suppress rebuilding while rebuilding
|
||||||
|
Rebuilding = true;
|
||||||
|
|
||||||
|
// Cause the root shape to be rebuilt as a compound object with just the root in it
|
||||||
|
LinksetRoot.ForceBodyShapeRebuild(true);
|
||||||
|
|
||||||
|
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
|
||||||
|
LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
|
||||||
|
|
||||||
|
// Add a shape for each of the other children in the linkset
|
||||||
|
ForEachMember(delegate(BSPhysObject cPrim)
|
||||||
|
{
|
||||||
|
if (!IsRoot(cPrim))
|
||||||
|
{
|
||||||
|
// Compute the displacement of the child from the root of the linkset.
|
||||||
|
// This info is saved in the child prim so the relationship does not
|
||||||
|
// change over time and the new child position can be computed
|
||||||
|
// when the linkset is being disassembled (the linkset may have moved).
|
||||||
|
BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
|
||||||
|
if (lci == null)
|
||||||
|
{
|
||||||
|
// Each child position and rotation is given relative to the root.
|
||||||
|
OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
|
||||||
|
OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
|
||||||
|
OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
|
||||||
|
|
||||||
|
// Save relative position for recomputing child's world position after moving linkset.
|
||||||
|
lci = new BSLinksetCompoundInfo(displacementPos, displacementRot);
|
||||||
|
cPrim.LinksetInfo = lci;
|
||||||
|
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
|
||||||
|
}
|
||||||
|
|
||||||
|
DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
|
||||||
|
LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot);
|
||||||
|
|
||||||
|
if (cPrim.PhysShape.isNativeShape)
|
||||||
|
{
|
||||||
|
// A native shape is turning into a hull collision shape because native
|
||||||
|
// shapes are not shared so we have to hullify it so it will be tracked
|
||||||
|
// and freed at the correct time. This also solves the scaling problem
|
||||||
|
// (native shapes scaled but hull/meshes are assumed to not be).
|
||||||
|
// TODO: decide of the native shape can just be used in the compound shape.
|
||||||
|
// Use call to CreateGeomNonSpecial().
|
||||||
|
BulletShape saveShape = cPrim.PhysShape;
|
||||||
|
cPrim.PhysShape.Clear(); // Don't let the create free the child's shape
|
||||||
|
// PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null);
|
||||||
|
PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
|
||||||
|
BulletShape newShape = cPrim.PhysShape;
|
||||||
|
cPrim.PhysShape = saveShape;
|
||||||
|
BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, lci.OffsetPos, lci.OffsetRot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For the shared shapes (meshes and hulls), just use the shape in the child.
|
||||||
|
// The reference count added here will be decremented when the compound shape
|
||||||
|
// is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced).
|
||||||
|
if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
|
||||||
|
{
|
||||||
|
PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
|
||||||
|
LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
|
||||||
|
}
|
||||||
|
BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, lci.OffsetPos, lci.OffsetRot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false; // 'false' says to move onto the next child in the list
|
||||||
|
});
|
||||||
|
|
||||||
|
// With all of the linkset packed into the root prim, it has the mass of everyone.
|
||||||
|
float linksetMass = LinksetMass;
|
||||||
|
LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Rebuilding = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr);
|
||||||
|
|
||||||
|
// DEBUG: see of inter-linkset collisions are causing problems for constraint linksets.
|
||||||
|
// BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
|
||||||
|
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,314 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using OMV = OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
public sealed class BSLinksetConstraints : BSLinkset
|
||||||
|
{
|
||||||
|
// private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]";
|
||||||
|
|
||||||
|
public BSLinksetConstraints(BSScene scene, BSPhysObject parent) : base(scene, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// When physical properties are changed the linkset needs to recalculate
|
||||||
|
// its internal properties.
|
||||||
|
// This is queued in the 'post taint' queue so the
|
||||||
|
// refresh will happen once after all the other taints are applied.
|
||||||
|
public override void Refresh(BSPhysObject requestor)
|
||||||
|
{
|
||||||
|
// Queue to happen after all the other taint processing
|
||||||
|
PhysicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate()
|
||||||
|
{
|
||||||
|
if (HasAnyChildren && IsRoot(requestor))
|
||||||
|
RecomputeLinksetConstraints();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// The object is going dynamic (physical). Do any setup necessary
|
||||||
|
// for a dynamic linkset.
|
||||||
|
// Only the state of the passed object can be modified. The rest of the linkset
|
||||||
|
// has not yet been fully constructed.
|
||||||
|
// Return 'true' if any properties updated on the passed object.
|
||||||
|
// Called at taint-time!
|
||||||
|
public override bool MakeDynamic(BSPhysObject child)
|
||||||
|
{
|
||||||
|
// What is done for each object in BSPrim is what we want.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The object is going static (non-physical). Do any setup necessary for a static linkset.
|
||||||
|
// Return 'true' if any properties updated on the passed object.
|
||||||
|
// This doesn't normally happen -- OpenSim removes the objects from the physical
|
||||||
|
// world if it is a static linkset.
|
||||||
|
// Called at taint-time!
|
||||||
|
public override bool MakeStatic(BSPhysObject child)
|
||||||
|
{
|
||||||
|
// What is done for each object in BSPrim is what we want.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called at taint-time!!
|
||||||
|
public override void UpdateProperties(BSPhysObject updated, bool inTaintTime)
|
||||||
|
{
|
||||||
|
// Nothing to do for constraints on property updates
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routine called when rebuilding the body of some member of the linkset.
|
||||||
|
// Destroy all the constraints have have been made to root and set
|
||||||
|
// up to rebuild the constraints before the next simulation step.
|
||||||
|
// Returns 'true' of something was actually removed and would need restoring
|
||||||
|
// Called at taint-time!!
|
||||||
|
public override bool RemoveBodyDependencies(BSPrim child)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
DetailLog("{0},BSLinksetConstraint.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}",
|
||||||
|
child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString());
|
||||||
|
|
||||||
|
lock (m_linksetActivityLock)
|
||||||
|
{
|
||||||
|
// Just undo all the constraints for this linkset. Rebuild at the end of the step.
|
||||||
|
ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
|
||||||
|
// Cause the constraints, et al to be rebuilt before the next simulation step.
|
||||||
|
Refresh(LinksetRoot);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
|
||||||
|
// this routine will restore the removed constraints.
|
||||||
|
// Called at taint-time!!
|
||||||
|
public override void RestoreBodyDependencies(BSPrim child)
|
||||||
|
{
|
||||||
|
// The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ================================================================
|
||||||
|
|
||||||
|
// Add a new child to the linkset.
|
||||||
|
// Called while LinkActivity is locked.
|
||||||
|
protected override void AddChildToLinkset(BSPhysObject child)
|
||||||
|
{
|
||||||
|
if (!HasChild(child))
|
||||||
|
{
|
||||||
|
m_children.Add(child);
|
||||||
|
|
||||||
|
DetailLog("{0},BSLinksetConstraints.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
|
||||||
|
|
||||||
|
// Cause constraints and assorted properties to be recomputed before the next simulation step.
|
||||||
|
Refresh(LinksetRoot);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the specified child from the linkset.
|
||||||
|
// Safe to call even if the child is not really in my linkset.
|
||||||
|
protected override void RemoveChildFromLinkset(BSPhysObject child)
|
||||||
|
{
|
||||||
|
if (m_children.Remove(child))
|
||||||
|
{
|
||||||
|
BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
|
||||||
|
BSPhysObject childx = child;
|
||||||
|
|
||||||
|
DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
|
||||||
|
childx.LocalID,
|
||||||
|
rootx.LocalID, rootx.PhysBody.ptr.ToString(),
|
||||||
|
childx.LocalID, childx.PhysBody.ptr.ToString());
|
||||||
|
|
||||||
|
PhysicsScene.TaintedObject("BSLinksetConstraints.RemoveChildFromLinkset", delegate()
|
||||||
|
{
|
||||||
|
PhysicallyUnlinkAChildFromRoot(rootx, childx);
|
||||||
|
});
|
||||||
|
// See that the linkset parameters are recomputed at the end of the taint time.
|
||||||
|
Refresh(LinksetRoot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Non-fatal occurance.
|
||||||
|
// PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a constraint between me (root of linkset) and the passed prim (the child).
|
||||||
|
// Called at taint time!
|
||||||
|
private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||||
|
{
|
||||||
|
// Don't build the constraint when asked. Put it off until just before the simulation step.
|
||||||
|
Refresh(rootPrim);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BSConstraint BuildConstraint(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||||
|
{
|
||||||
|
// Zero motion for children so they don't interpolate
|
||||||
|
childPrim.ZeroMotion(true);
|
||||||
|
|
||||||
|
// Relative position normalized to the root prim
|
||||||
|
// Essentually a vector pointing from center of rootPrim to center of childPrim
|
||||||
|
OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position;
|
||||||
|
|
||||||
|
// real world coordinate of midpoint between the two objects
|
||||||
|
OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
|
||||||
|
|
||||||
|
DetailLog("{0},BSLinksetConstraint.BuildConstraint,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
|
||||||
|
rootPrim.LocalID,
|
||||||
|
rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
|
||||||
|
childPrim.LocalID, childPrim.PhysBody.ptr.ToString(),
|
||||||
|
rootPrim.Position, childPrim.Position, midPoint);
|
||||||
|
|
||||||
|
// create a constraint that allows no freedom of movement between the two objects
|
||||||
|
// http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
|
||||||
|
|
||||||
|
BSConstraint6Dof constrain = new BSConstraint6Dof(
|
||||||
|
PhysicsScene.World, rootPrim.PhysBody, childPrim.PhysBody, midPoint, true, true );
|
||||||
|
// PhysicsScene.World, childPrim.BSBody, rootPrim.BSBody, midPoint, true, true );
|
||||||
|
|
||||||
|
/* NOTE: below is an attempt to build constraint with full frame computation, etc.
|
||||||
|
* Using the midpoint is easier since it lets the Bullet code manipulate the transforms
|
||||||
|
* of the objects.
|
||||||
|
* Code left for future programmers.
|
||||||
|
// ==================================================================================
|
||||||
|
// relative position normalized to the root prim
|
||||||
|
OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation);
|
||||||
|
OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation;
|
||||||
|
|
||||||
|
// relative rotation of the child to the parent
|
||||||
|
OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation;
|
||||||
|
OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation);
|
||||||
|
|
||||||
|
DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
|
||||||
|
BS6DofConstraint constrain = new BS6DofConstraint(
|
||||||
|
PhysicsScene.World, rootPrim.Body, childPrim.Body,
|
||||||
|
OMV.Vector3.Zero,
|
||||||
|
OMV.Quaternion.Inverse(rootPrim.Orientation),
|
||||||
|
OMV.Vector3.Zero,
|
||||||
|
OMV.Quaternion.Inverse(childPrim.Orientation),
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
// ==================================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
PhysicsScene.Constraints.AddConstraint(constrain);
|
||||||
|
|
||||||
|
// zero linear and angular limits makes the objects unable to move in relation to each other
|
||||||
|
constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
|
||||||
|
constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
|
||||||
|
|
||||||
|
// tweek the constraint to increase stability
|
||||||
|
constrain.UseFrameOffset(BSParam.BoolNumeric(BSParam.LinkConstraintUseFrameOffset));
|
||||||
|
constrain.TranslationalLimitMotor(BSParam.BoolNumeric(BSParam.LinkConstraintEnableTransMotor),
|
||||||
|
BSParam.LinkConstraintTransMotorMaxVel,
|
||||||
|
BSParam.LinkConstraintTransMotorMaxForce);
|
||||||
|
constrain.SetCFMAndERP(BSParam.LinkConstraintCFM, BSParam.LinkConstraintERP);
|
||||||
|
if (BSParam.LinkConstraintSolverIterations != 0f)
|
||||||
|
{
|
||||||
|
constrain.SetSolverIterations(BSParam.LinkConstraintSolverIterations);
|
||||||
|
}
|
||||||
|
return constrain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove linkage between the linkset root and a particular child
|
||||||
|
// The root and child bodies are passed in because we need to remove the constraint between
|
||||||
|
// the bodies that were present at unlink time.
|
||||||
|
// Called at taint time!
|
||||||
|
private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
|
||||||
|
rootPrim.LocalID,
|
||||||
|
rootPrim.LocalID, rootPrim.PhysBody.ptr.ToString(),
|
||||||
|
childPrim.LocalID, childPrim.PhysBody.ptr.ToString());
|
||||||
|
|
||||||
|
// Find the constraint for this link and get rid of it from the overall collection and from my list
|
||||||
|
if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody))
|
||||||
|
{
|
||||||
|
// Make the child refresh its location
|
||||||
|
BulletSimAPI.PushUpdate2(childPrim.PhysBody.ptr);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove linkage between myself and any possible children I might have.
|
||||||
|
// Returns 'true' of any constraints were destroyed.
|
||||||
|
// Called at taint time!
|
||||||
|
private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
|
||||||
|
|
||||||
|
return PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call each of the constraints that make up this linkset and recompute the
|
||||||
|
// various transforms and variables. Create constraints of not created yet.
|
||||||
|
// Called before the simulation step to make sure the constraint based linkset
|
||||||
|
// is all initialized.
|
||||||
|
// Called at taint time!!
|
||||||
|
private void RecomputeLinksetConstraints()
|
||||||
|
{
|
||||||
|
float linksetMass = LinksetMass;
|
||||||
|
LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
|
||||||
|
|
||||||
|
// DEBUG: see of inter-linkset collisions are causing problems
|
||||||
|
// BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr,
|
||||||
|
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
|
||||||
|
DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}",
|
||||||
|
LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), linksetMass);
|
||||||
|
|
||||||
|
foreach (BSPhysObject child in m_children)
|
||||||
|
{
|
||||||
|
// A child in the linkset physically shows the mass of the whole linkset.
|
||||||
|
// This allows Bullet to apply enough force on the child to move the whole linkset.
|
||||||
|
// (Also do the mass stuff before recomputing the constraint so mass is not zero.)
|
||||||
|
child.UpdatePhysicalMassProperties(linksetMass);
|
||||||
|
|
||||||
|
BSConstraint constrain;
|
||||||
|
if (!PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, child.PhysBody, out constrain))
|
||||||
|
{
|
||||||
|
// If constraint doesn't exist yet, create it.
|
||||||
|
constrain = BuildConstraint(LinksetRoot, child);
|
||||||
|
}
|
||||||
|
constrain.RecomputeConstraintVariables(linksetMass);
|
||||||
|
|
||||||
|
// DEBUG: see of inter-linkset collisions are causing problems
|
||||||
|
// BulletSimAPI.SetCollisionFilterMask2(child.BSBody.ptr,
|
||||||
|
// (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask);
|
||||||
|
|
||||||
|
// BulletSimAPI.DumpConstraint2(PhysicsScene.World.ptr, constrain.Constraint.ptr); // DEBUG DEBUG
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Reflection;
|
||||||
|
using Nini.Config;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
public struct MaterialAttributes
|
||||||
|
{
|
||||||
|
// Material type values that correspond with definitions for LSL
|
||||||
|
public enum Material : int
|
||||||
|
{
|
||||||
|
Stone = 0,
|
||||||
|
Metal,
|
||||||
|
Glass,
|
||||||
|
Wood,
|
||||||
|
Flesh,
|
||||||
|
Plastic,
|
||||||
|
Rubber,
|
||||||
|
Light,
|
||||||
|
// Hereafter are BulletSim additions
|
||||||
|
Avatar,
|
||||||
|
NumberOfTypes // the count of types in the enum.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Names must be in the order of the above enum.
|
||||||
|
// These names must coorespond to the lower case field names in the MaterialAttributes
|
||||||
|
// structure as reflection is used to select the field to put the value in.
|
||||||
|
public static readonly string[] MaterialAttribs = { "Density", "Friction", "Restitution"};
|
||||||
|
|
||||||
|
public MaterialAttributes(string t, float d, float f, float r)
|
||||||
|
{
|
||||||
|
type = t;
|
||||||
|
density = d;
|
||||||
|
friction = f;
|
||||||
|
restitution = r;
|
||||||
|
}
|
||||||
|
public string type;
|
||||||
|
public float density;
|
||||||
|
public float friction;
|
||||||
|
public float restitution;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BSMaterials
|
||||||
|
{
|
||||||
|
// Attributes for each material type
|
||||||
|
private static readonly MaterialAttributes[] Attributes;
|
||||||
|
|
||||||
|
// Map of material name to material type code
|
||||||
|
public static readonly Dictionary<string, MaterialAttributes.Material> MaterialMap;
|
||||||
|
|
||||||
|
static BSMaterials()
|
||||||
|
{
|
||||||
|
// Attribute sets for both the non-physical and physical instances of materials.
|
||||||
|
Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2];
|
||||||
|
|
||||||
|
// Map of name to type code.
|
||||||
|
MaterialMap = new Dictionary<string, MaterialAttributes.Material>();
|
||||||
|
MaterialMap.Add("Stone", MaterialAttributes.Material.Stone);
|
||||||
|
MaterialMap.Add("Metal", MaterialAttributes.Material.Metal);
|
||||||
|
MaterialMap.Add("Glass", MaterialAttributes.Material.Glass);
|
||||||
|
MaterialMap.Add("Wood", MaterialAttributes.Material.Wood);
|
||||||
|
MaterialMap.Add("Flesh", MaterialAttributes.Material.Flesh);
|
||||||
|
MaterialMap.Add("Plastic", MaterialAttributes.Material.Plastic);
|
||||||
|
MaterialMap.Add("Rubber", MaterialAttributes.Material.Rubber);
|
||||||
|
MaterialMap.Add("Light", MaterialAttributes.Material.Light);
|
||||||
|
MaterialMap.Add("Avatar", MaterialAttributes.Material.Avatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is where all the default material attributes are defined.
|
||||||
|
public static void InitializeFromDefaults(ConfigurationParameters parms)
|
||||||
|
{
|
||||||
|
// Values from http://wiki.secondlife.com/wiki/PRIM_MATERIAL
|
||||||
|
float dDensity = parms.defaultDensity;
|
||||||
|
float dFriction = parms.defaultFriction;
|
||||||
|
float dRestitution = parms.defaultRestitution;
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Stone] =
|
||||||
|
new MaterialAttributes("stone",dDensity, 0.8f, 0.4f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Metal] =
|
||||||
|
new MaterialAttributes("metal",dDensity, 0.3f, 0.4f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Glass] =
|
||||||
|
new MaterialAttributes("glass",dDensity, 0.2f, 0.7f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Wood] =
|
||||||
|
new MaterialAttributes("wood",dDensity, 0.6f, 0.5f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Flesh] =
|
||||||
|
new MaterialAttributes("flesh",dDensity, 0.9f, 0.3f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Plastic] =
|
||||||
|
new MaterialAttributes("plastic",dDensity, 0.4f, 0.7f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Rubber] =
|
||||||
|
new MaterialAttributes("rubber",dDensity, 0.9f, 0.9f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Light] =
|
||||||
|
new MaterialAttributes("light",dDensity, dFriction, dRestitution);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Avatar] =
|
||||||
|
new MaterialAttributes("avatar",60f, 0.2f, 0f);
|
||||||
|
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||||
|
new MaterialAttributes("stonePhysical",dDensity, 0.8f, 0.4f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||||
|
new MaterialAttributes("metalPhysical",dDensity, 0.8f, 0.4f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||||
|
new MaterialAttributes("glassPhysical",dDensity, 0.8f, 0.7f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||||
|
new MaterialAttributes("woodPhysical",dDensity, 0.8f, 0.5f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||||
|
new MaterialAttributes("fleshPhysical",dDensity, 0.8f, 0.3f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||||
|
new MaterialAttributes("plasticPhysical",dDensity, 0.8f, 0.7f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||||
|
new MaterialAttributes("rubberPhysical",dDensity, 0.8f, 0.9f);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||||
|
new MaterialAttributes("lightPhysical",dDensity, dFriction, dRestitution);
|
||||||
|
Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] =
|
||||||
|
new MaterialAttributes("avatarPhysical",60f, 0.2f, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Under the [BulletSim] section, one can change the individual material
|
||||||
|
// attribute values. The format of the configuration parameter is:
|
||||||
|
// <materialName><Attribute>["Physical"] = floatValue
|
||||||
|
// For instance:
|
||||||
|
// [BulletSim]
|
||||||
|
// StoneFriction = 0.2
|
||||||
|
// FleshRestitutionPhysical = 0.8
|
||||||
|
// Materials can have different parameters for their static and
|
||||||
|
// physical instantiations. When setting the non-physical value,
|
||||||
|
// both values are changed. Setting the physical value only changes
|
||||||
|
// the physical value.
|
||||||
|
public static void InitializefromParameters(IConfig pConfig)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, MaterialAttributes.Material> kvp in MaterialMap)
|
||||||
|
{
|
||||||
|
string matName = kvp.Key;
|
||||||
|
foreach (string attribName in MaterialAttributes.MaterialAttribs)
|
||||||
|
{
|
||||||
|
string paramName = matName + attribName;
|
||||||
|
if (pConfig.Contains(paramName))
|
||||||
|
{
|
||||||
|
float paramValue = pConfig.GetFloat(paramName);
|
||||||
|
SetAttributeValue((int)kvp.Value, attribName, paramValue);
|
||||||
|
// set the physical value also
|
||||||
|
SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
|
||||||
|
}
|
||||||
|
paramName += "Physical";
|
||||||
|
if (pConfig.Contains(paramName))
|
||||||
|
{
|
||||||
|
float paramValue = pConfig.GetFloat(paramName);
|
||||||
|
SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use reflection to set the value in the attribute structure.
|
||||||
|
private static void SetAttributeValue(int matType, string attribName, float val)
|
||||||
|
{
|
||||||
|
MaterialAttributes thisAttrib = Attributes[matType];
|
||||||
|
FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower());
|
||||||
|
if (fieldInfo != null)
|
||||||
|
{
|
||||||
|
fieldInfo.SetValue(thisAttrib, val);
|
||||||
|
Attributes[matType] = thisAttrib;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a material type, return a structure of attributes.
|
||||||
|
public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical)
|
||||||
|
{
|
||||||
|
int ind = (int)type;
|
||||||
|
if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes;
|
||||||
|
return Attributes[ind];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,347 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
public abstract class BSMotor
|
||||||
|
{
|
||||||
|
// Timescales and other things can be turned off by setting them to 'infinite'.
|
||||||
|
public const float Infinite = 12345.6f;
|
||||||
|
public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite);
|
||||||
|
|
||||||
|
public BSMotor(string useName)
|
||||||
|
{
|
||||||
|
UseName = useName;
|
||||||
|
PhysicsScene = null;
|
||||||
|
Enabled = true;
|
||||||
|
}
|
||||||
|
public virtual bool Enabled { get; set; }
|
||||||
|
public virtual void Reset() { }
|
||||||
|
public virtual void Zero() { }
|
||||||
|
public virtual void GenerateTestOutput(float timeStep) { }
|
||||||
|
|
||||||
|
// A name passed at motor creation for easily identifyable debugging messages.
|
||||||
|
public string UseName { get; private set; }
|
||||||
|
|
||||||
|
// Used only for outputting debug information. Might not be set so check for null.
|
||||||
|
public BSScene PhysicsScene { get; set; }
|
||||||
|
protected void MDetailLog(string msg, params Object[] parms)
|
||||||
|
{
|
||||||
|
if (PhysicsScene != null)
|
||||||
|
{
|
||||||
|
if (PhysicsScene.VehicleLoggingEnabled)
|
||||||
|
{
|
||||||
|
PhysicsScene.DetailLog(msg, parms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Motor which moves CurrentValue to TargetValue over TimeScale seconds.
|
||||||
|
// The TargetValue decays in TargetValueDecayTimeScale and
|
||||||
|
// the CurrentValue will be held back by FrictionTimeScale.
|
||||||
|
// This motor will "zero itself" over time in that the targetValue will
|
||||||
|
// decay to zero and the currentValue will follow it to that zero.
|
||||||
|
// The overall effect is for the returned correction value to go from large
|
||||||
|
// values (the total difference between current and target minus friction)
|
||||||
|
// to small and eventually zero values.
|
||||||
|
// TimeScale and TargetDelayTimeScale may be 'infinite' which means no decay.
|
||||||
|
|
||||||
|
// For instance, if something is moving at speed X and the desired speed is Y,
|
||||||
|
// CurrentValue is X and TargetValue is Y. As the motor is stepped, new
|
||||||
|
// values of CurrentValue are returned that approach the TargetValue.
|
||||||
|
// The feature of decaying TargetValue is so vehicles will eventually
|
||||||
|
// come to a stop rather than run forever. This can be disabled by
|
||||||
|
// setting TargetValueDecayTimescale to 'infinite'.
|
||||||
|
// The change from CurrentValue to TargetValue is linear over TimeScale seconds.
|
||||||
|
public class BSVMotor : BSMotor
|
||||||
|
{
|
||||||
|
// public Vector3 FrameOfReference { get; set; }
|
||||||
|
// public Vector3 Offset { get; set; }
|
||||||
|
|
||||||
|
public virtual float TimeScale { get; set; }
|
||||||
|
public virtual float TargetValueDecayTimeScale { get; set; }
|
||||||
|
public virtual Vector3 FrictionTimescale { get; set; }
|
||||||
|
public virtual float Efficiency { get; set; }
|
||||||
|
|
||||||
|
public virtual float ErrorZeroThreshold { get; set; }
|
||||||
|
|
||||||
|
public virtual Vector3 TargetValue { get; protected set; }
|
||||||
|
public virtual Vector3 CurrentValue { get; protected set; }
|
||||||
|
public virtual Vector3 LastError { get; protected set; }
|
||||||
|
|
||||||
|
public virtual bool ErrorIsZero
|
||||||
|
{ get {
|
||||||
|
return (LastError == Vector3.Zero || LastError.LengthSquared() <= ErrorZeroThreshold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BSVMotor(string useName)
|
||||||
|
: base(useName)
|
||||||
|
{
|
||||||
|
TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
|
||||||
|
Efficiency = 1f;
|
||||||
|
FrictionTimescale = BSMotor.InfiniteVector;
|
||||||
|
CurrentValue = TargetValue = Vector3.Zero;
|
||||||
|
ErrorZeroThreshold = 0.001f;
|
||||||
|
}
|
||||||
|
public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency)
|
||||||
|
: this(useName)
|
||||||
|
{
|
||||||
|
TimeScale = timeScale;
|
||||||
|
TargetValueDecayTimeScale = decayTimeScale;
|
||||||
|
FrictionTimescale = frictionTimeScale;
|
||||||
|
Efficiency = efficiency;
|
||||||
|
CurrentValue = TargetValue = Vector3.Zero;
|
||||||
|
}
|
||||||
|
public void SetCurrent(Vector3 current)
|
||||||
|
{
|
||||||
|
CurrentValue = current;
|
||||||
|
}
|
||||||
|
public void SetTarget(Vector3 target)
|
||||||
|
{
|
||||||
|
TargetValue = target;
|
||||||
|
}
|
||||||
|
public override void Zero()
|
||||||
|
{
|
||||||
|
base.Zero();
|
||||||
|
CurrentValue = TargetValue = Vector3.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the next step and return the new current value
|
||||||
|
public virtual Vector3 Step(float timeStep)
|
||||||
|
{
|
||||||
|
if (!Enabled) return TargetValue;
|
||||||
|
|
||||||
|
Vector3 origTarget = TargetValue; // DEBUG
|
||||||
|
Vector3 origCurrVal = CurrentValue; // DEBUG
|
||||||
|
|
||||||
|
Vector3 correction = Vector3.Zero;
|
||||||
|
Vector3 error = TargetValue - CurrentValue;
|
||||||
|
if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
|
||||||
|
{
|
||||||
|
correction = Step(timeStep, error);
|
||||||
|
|
||||||
|
CurrentValue += correction;
|
||||||
|
|
||||||
|
// The desired value reduces to zero which also reduces the difference with current.
|
||||||
|
// If the decay time is infinite, don't decay at all.
|
||||||
|
float decayFactor = 0f;
|
||||||
|
if (TargetValueDecayTimeScale != BSMotor.Infinite)
|
||||||
|
{
|
||||||
|
decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
|
||||||
|
TargetValue *= (1f - decayFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The amount we can correct the error is reduced by the friction
|
||||||
|
Vector3 frictionFactor = Vector3.Zero;
|
||||||
|
if (FrictionTimescale != BSMotor.InfiniteVector)
|
||||||
|
{
|
||||||
|
// frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
|
||||||
|
// Individual friction components can be 'infinite' so compute each separately.
|
||||||
|
frictionFactor.X = (FrictionTimescale.X == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.X);
|
||||||
|
frictionFactor.Y = (FrictionTimescale.Y == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Y);
|
||||||
|
frictionFactor.Z = (FrictionTimescale.Z == BSMotor.Infinite) ? 0f : (1f / FrictionTimescale.Z);
|
||||||
|
frictionFactor *= timeStep;
|
||||||
|
CurrentValue *= (Vector3.One - frictionFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}",
|
||||||
|
BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
|
||||||
|
timeStep, error, correction);
|
||||||
|
MDetailLog("{0}, BSVMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},frictTS={4},frictFact={5},tgt={6},curr={7}",
|
||||||
|
BSScene.DetailLogZero, UseName,
|
||||||
|
TargetValueDecayTimeScale, decayFactor, FrictionTimescale, frictionFactor,
|
||||||
|
TargetValue, CurrentValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Difference between what we have and target is small. Motor is done.
|
||||||
|
CurrentValue = TargetValue;
|
||||||
|
MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}",
|
||||||
|
BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CurrentValue;
|
||||||
|
}
|
||||||
|
public virtual Vector3 Step(float timeStep, Vector3 error)
|
||||||
|
{
|
||||||
|
if (!Enabled) return Vector3.Zero;
|
||||||
|
|
||||||
|
LastError = error;
|
||||||
|
Vector3 returnCorrection = Vector3.Zero;
|
||||||
|
if (!error.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
|
||||||
|
{
|
||||||
|
// correction = error / secondsItShouldTakeToCorrect
|
||||||
|
Vector3 correctionAmount;
|
||||||
|
if (TimeScale == 0f || TimeScale == BSMotor.Infinite)
|
||||||
|
correctionAmount = error * timeStep;
|
||||||
|
else
|
||||||
|
correctionAmount = error / TimeScale * timeStep;
|
||||||
|
|
||||||
|
returnCorrection = correctionAmount;
|
||||||
|
MDetailLog("{0}, BSVMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}",
|
||||||
|
BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount);
|
||||||
|
}
|
||||||
|
return returnCorrection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The user sets all the parameters and calls this which outputs values until error is zero.
|
||||||
|
public override void GenerateTestOutput(float timeStep)
|
||||||
|
{
|
||||||
|
// maximum number of outputs to generate.
|
||||||
|
int maxOutput = 50;
|
||||||
|
MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName);
|
||||||
|
MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},frictTS={4},eff={5},curr={6},tgt={7}",
|
||||||
|
BSScene.DetailLogZero, UseName,
|
||||||
|
TimeScale, TargetValueDecayTimeScale, FrictionTimescale, Efficiency,
|
||||||
|
CurrentValue, TargetValue);
|
||||||
|
|
||||||
|
LastError = BSMotor.InfiniteVector;
|
||||||
|
while (maxOutput-- > 0 && !LastError.ApproxEquals(Vector3.Zero, ErrorZeroThreshold))
|
||||||
|
{
|
||||||
|
Vector3 lastStep = Step(timeStep);
|
||||||
|
MDetailLog("{0},BSVMotor.Test,{1},cur={2},tgt={3},lastError={4},lastStep={5}",
|
||||||
|
BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep);
|
||||||
|
}
|
||||||
|
MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>",
|
||||||
|
UseName, CurrentValue, TargetValue, TargetValueDecayTimeScale, FrictionTimescale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BSFMotor : BSMotor
|
||||||
|
{
|
||||||
|
public float TimeScale { get; set; }
|
||||||
|
public float DecayTimeScale { get; set; }
|
||||||
|
public float Friction { get; set; }
|
||||||
|
public float Efficiency { get; set; }
|
||||||
|
|
||||||
|
public float Target { get; private set; }
|
||||||
|
public float CurrentValue { get; private set; }
|
||||||
|
|
||||||
|
public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency)
|
||||||
|
: base(useName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public void SetCurrent(float target)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public void SetTarget(float target)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public virtual float Step(float timeStep)
|
||||||
|
{
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proportional, Integral, Derivitive Motor
|
||||||
|
// Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors.
|
||||||
|
public class BSPIDVMotor : BSVMotor
|
||||||
|
{
|
||||||
|
// Larger makes more overshoot, smaller means converge quicker. Range of 0.1 to 10.
|
||||||
|
public Vector3 proportionFactor { get; set; }
|
||||||
|
public Vector3 integralFactor { get; set; }
|
||||||
|
public Vector3 derivFactor { get; set; }
|
||||||
|
|
||||||
|
// Arbritrary factor range.
|
||||||
|
// EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct.
|
||||||
|
public float EfficiencyHigh = 0.4f;
|
||||||
|
public float EfficiencyLow = 4.0f;
|
||||||
|
|
||||||
|
// Running integration of the error
|
||||||
|
Vector3 RunningIntegration { get; set; }
|
||||||
|
|
||||||
|
public BSPIDVMotor(string useName)
|
||||||
|
: base(useName)
|
||||||
|
{
|
||||||
|
proportionFactor = new Vector3(1.00f, 1.00f, 1.00f);
|
||||||
|
integralFactor = new Vector3(1.00f, 1.00f, 1.00f);
|
||||||
|
derivFactor = new Vector3(1.00f, 1.00f, 1.00f);
|
||||||
|
RunningIntegration = Vector3.Zero;
|
||||||
|
LastError = Vector3.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Zero()
|
||||||
|
{
|
||||||
|
base.Zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float Efficiency
|
||||||
|
{
|
||||||
|
get { return base.Efficiency; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
base.Efficiency = Util.Clamp(value, 0f, 1f);
|
||||||
|
// Compute factors based on efficiency.
|
||||||
|
// If efficiency is high (1f), use a factor value that moves the error value to zero with little overshoot.
|
||||||
|
// If efficiency is low (0f), use a factor value that overcorrects.
|
||||||
|
// TODO: might want to vary contribution of different factor depending on efficiency.
|
||||||
|
float factor = ((1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow) / 3f;
|
||||||
|
// float factor = (1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow;
|
||||||
|
proportionFactor = new Vector3(factor, factor, factor);
|
||||||
|
integralFactor = new Vector3(factor, factor, factor);
|
||||||
|
derivFactor = new Vector3(factor, factor, factor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore Current and Target Values and just advance the PID computation on this error.
|
||||||
|
public override Vector3 Step(float timeStep, Vector3 error)
|
||||||
|
{
|
||||||
|
if (!Enabled) return Vector3.Zero;
|
||||||
|
|
||||||
|
// Add up the error so we can integrate over the accumulated errors
|
||||||
|
RunningIntegration += error * timeStep;
|
||||||
|
|
||||||
|
// A simple derivitive is the rate of change from the last error.
|
||||||
|
Vector3 derivFactor = (error - LastError) * timeStep;
|
||||||
|
LastError = error;
|
||||||
|
|
||||||
|
// Correction = -(proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError)
|
||||||
|
Vector3 ret = -(
|
||||||
|
error * proportionFactor
|
||||||
|
+ RunningIntegration * integralFactor
|
||||||
|
+ derivFactor * derivFactor
|
||||||
|
);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,559 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using OpenSim.Region.Physics.Manager;
|
||||||
|
|
||||||
|
using OpenMetaverse;
|
||||||
|
using Nini.Config;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
public static class BSParam
|
||||||
|
{
|
||||||
|
// Level of Detail values kept as float because that's what the Meshmerizer wants
|
||||||
|
public static float MeshLOD { get; private set; }
|
||||||
|
public static float MeshMegaPrimLOD { get; private set; }
|
||||||
|
public static float MeshMegaPrimThreshold { get; private set; }
|
||||||
|
public static float SculptLOD { get; private set; }
|
||||||
|
|
||||||
|
public static float MinimumObjectMass { get; private set; }
|
||||||
|
public static float MaximumObjectMass { get; private set; }
|
||||||
|
|
||||||
|
public static float LinearDamping { get; private set; }
|
||||||
|
public static float AngularDamping { get; private set; }
|
||||||
|
public static float DeactivationTime { get; private set; }
|
||||||
|
public static float LinearSleepingThreshold { get; private set; }
|
||||||
|
public static float AngularSleepingThreshold { get; private set; }
|
||||||
|
public static float CcdMotionThreshold { get; private set; }
|
||||||
|
public static float CcdSweptSphereRadius { get; private set; }
|
||||||
|
public static float ContactProcessingThreshold { get; private set; }
|
||||||
|
|
||||||
|
public static bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
|
||||||
|
public static bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
|
||||||
|
public static bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects
|
||||||
|
|
||||||
|
public static float TerrainImplementation { get; private set; }
|
||||||
|
public static float TerrainFriction { get; private set; }
|
||||||
|
public static float TerrainHitFraction { get; private set; }
|
||||||
|
public static float TerrainRestitution { get; private set; }
|
||||||
|
public static float TerrainCollisionMargin { get; private set; }
|
||||||
|
|
||||||
|
// Avatar parameters
|
||||||
|
public static float AvatarFriction { get; private set; }
|
||||||
|
public static float AvatarStandingFriction { get; private set; }
|
||||||
|
public static float AvatarDensity { get; private set; }
|
||||||
|
public static float AvatarRestitution { get; private set; }
|
||||||
|
public static float AvatarCapsuleWidth { get; private set; }
|
||||||
|
public static float AvatarCapsuleDepth { get; private set; }
|
||||||
|
public static float AvatarCapsuleHeight { get; private set; }
|
||||||
|
public static float AvatarContactProcessingThreshold { get; private set; }
|
||||||
|
|
||||||
|
public static float VehicleAngularDamping { get; private set; }
|
||||||
|
|
||||||
|
public static float LinksetImplementation { get; private set; }
|
||||||
|
public static float LinkConstraintUseFrameOffset { get; private set; }
|
||||||
|
public static float LinkConstraintEnableTransMotor { get; private set; }
|
||||||
|
public static float LinkConstraintTransMotorMaxVel { get; private set; }
|
||||||
|
public static float LinkConstraintTransMotorMaxForce { get; private set; }
|
||||||
|
public static float LinkConstraintERP { get; private set; }
|
||||||
|
public static float LinkConstraintCFM { get; private set; }
|
||||||
|
public static float LinkConstraintSolverIterations { get; private set; }
|
||||||
|
|
||||||
|
public static float PID_D { get; private set; } // derivative
|
||||||
|
public static float PID_P { get; private set; } // proportional
|
||||||
|
|
||||||
|
public delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val);
|
||||||
|
public delegate float ParamGet(BSScene scene);
|
||||||
|
public delegate void ParamSet(BSScene scene, string paramName, uint localID, float val);
|
||||||
|
public delegate void SetOnObject(BSScene scene, BSPhysObject obj, float val);
|
||||||
|
|
||||||
|
public struct ParameterDefn
|
||||||
|
{
|
||||||
|
public string name; // string name of the parameter
|
||||||
|
public string desc; // a short description of what the parameter means
|
||||||
|
public float defaultValue; // default value if not specified anywhere else
|
||||||
|
public ParamUser userParam; // get the value from the configuration file
|
||||||
|
public ParamGet getter; // return the current value stored for this parameter
|
||||||
|
public ParamSet setter; // set the current value for this parameter
|
||||||
|
public SetOnObject onObject; // set the value on an object in the physical domain
|
||||||
|
public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s)
|
||||||
|
{
|
||||||
|
name = n;
|
||||||
|
desc = d;
|
||||||
|
defaultValue = v;
|
||||||
|
userParam = u;
|
||||||
|
getter = g;
|
||||||
|
setter = s;
|
||||||
|
onObject = null;
|
||||||
|
}
|
||||||
|
public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s, SetOnObject o)
|
||||||
|
{
|
||||||
|
name = n;
|
||||||
|
desc = d;
|
||||||
|
defaultValue = v;
|
||||||
|
userParam = u;
|
||||||
|
getter = g;
|
||||||
|
setter = s;
|
||||||
|
onObject = o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of all of the externally visible parameters.
|
||||||
|
// For each parameter, this table maps a text name to getter and setters.
|
||||||
|
// To add a new externally referencable/settable parameter, add the paramter storage
|
||||||
|
// location somewhere in the program and make an entry in this table with the
|
||||||
|
// getters and setters.
|
||||||
|
// It is easiest to find an existing definition and copy it.
|
||||||
|
// Parameter values are floats. Booleans are converted to a floating value.
|
||||||
|
//
|
||||||
|
// A ParameterDefn() takes the following parameters:
|
||||||
|
// -- the text name of the parameter. This is used for console input and ini file.
|
||||||
|
// -- a short text description of the parameter. This shows up in the console listing.
|
||||||
|
// -- a default value (float)
|
||||||
|
// -- a delegate for fetching the parameter from the ini file.
|
||||||
|
// Should handle fetching the right type from the ini file and converting it.
|
||||||
|
// -- a delegate for getting the value as a float
|
||||||
|
// -- a delegate for setting the value from a float
|
||||||
|
// -- an optional delegate to update the value in the world. Most often used to
|
||||||
|
// push the new value to an in-world object.
|
||||||
|
//
|
||||||
|
// The single letter parameters for the delegates are:
|
||||||
|
// s = BSScene
|
||||||
|
// o = BSPhysObject
|
||||||
|
// p = string parameter name
|
||||||
|
// l = localID of referenced object
|
||||||
|
// v = value (float)
|
||||||
|
// cf = parameter configuration class (for fetching values from ini file)
|
||||||
|
private static ParameterDefn[] ParameterDefinitions =
|
||||||
|
{
|
||||||
|
new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
|
||||||
|
ConfigurationParameters.numericTrue,
|
||||||
|
(s,cf,p,v) => { ShouldMeshSculptedPrim = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
|
||||||
|
(s) => { return BSParam.NumericBool(ShouldMeshSculptedPrim); },
|
||||||
|
(s,p,l,v) => { ShouldMeshSculptedPrim = BSParam.BoolNumeric(v); } ),
|
||||||
|
new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
|
||||||
|
ConfigurationParameters.numericFalse,
|
||||||
|
(s,cf,p,v) => { ShouldForceSimplePrimMeshing = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
|
||||||
|
(s) => { return BSParam.NumericBool(ShouldForceSimplePrimMeshing); },
|
||||||
|
(s,p,l,v) => { ShouldForceSimplePrimMeshing = BSParam.BoolNumeric(v); } ),
|
||||||
|
new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects",
|
||||||
|
ConfigurationParameters.numericTrue,
|
||||||
|
(s,cf,p,v) => { ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, BSParam.BoolNumeric(v)); },
|
||||||
|
(s) => { return BSParam.NumericBool(ShouldUseHullsForPhysicalObjects); },
|
||||||
|
(s,p,l,v) => { ShouldUseHullsForPhysicalObjects = BSParam.BoolNumeric(v); } ),
|
||||||
|
|
||||||
|
new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
|
||||||
|
8f,
|
||||||
|
(s,cf,p,v) => { MeshLOD = (float)cf.GetInt(p, (int)v); },
|
||||||
|
(s) => { return MeshLOD; },
|
||||||
|
(s,p,l,v) => { MeshLOD = v; } ),
|
||||||
|
new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters",
|
||||||
|
16f,
|
||||||
|
(s,cf,p,v) => { MeshMegaPrimLOD = (float)cf.GetInt(p, (int)v); },
|
||||||
|
(s) => { return MeshMegaPrimLOD; },
|
||||||
|
(s,p,l,v) => { MeshMegaPrimLOD = v; } ),
|
||||||
|
new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD",
|
||||||
|
10f,
|
||||||
|
(s,cf,p,v) => { MeshMegaPrimThreshold = (float)cf.GetInt(p, (int)v); },
|
||||||
|
(s) => { return MeshMegaPrimThreshold; },
|
||||||
|
(s,p,l,v) => { MeshMegaPrimThreshold = v; } ),
|
||||||
|
new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)",
|
||||||
|
32f,
|
||||||
|
(s,cf,p,v) => { SculptLOD = (float)cf.GetInt(p, (int)v); },
|
||||||
|
(s) => { return SculptLOD; },
|
||||||
|
(s,p,l,v) => { SculptLOD = v; } ),
|
||||||
|
|
||||||
|
new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps",
|
||||||
|
10f,
|
||||||
|
(s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); },
|
||||||
|
(s) => { return (float)s.m_maxSubSteps; },
|
||||||
|
(s,p,l,v) => { s.m_maxSubSteps = (int)v; } ),
|
||||||
|
new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)",
|
||||||
|
1f / 60f,
|
||||||
|
(s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return (float)s.m_fixedTimeStep; },
|
||||||
|
(s,p,l,v) => { s.m_fixedTimeStep = v; } ),
|
||||||
|
new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame",
|
||||||
|
2048f,
|
||||||
|
(s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); },
|
||||||
|
(s) => { return (float)s.m_maxCollisionsPerFrame; },
|
||||||
|
(s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ),
|
||||||
|
new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame",
|
||||||
|
8000f,
|
||||||
|
(s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); },
|
||||||
|
(s) => { return (float)s.m_maxUpdatesPerFrame; },
|
||||||
|
(s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ),
|
||||||
|
new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step",
|
||||||
|
500f,
|
||||||
|
(s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); },
|
||||||
|
(s) => { return (float)s.m_taintsToProcessPerStep; },
|
||||||
|
(s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ),
|
||||||
|
new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)",
|
||||||
|
0.0001f,
|
||||||
|
(s,cf,p,v) => { MinimumObjectMass = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return (float)MinimumObjectMass; },
|
||||||
|
(s,p,l,v) => { MinimumObjectMass = v; } ),
|
||||||
|
new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
|
||||||
|
10000.01f,
|
||||||
|
(s,cf,p,v) => { MaximumObjectMass = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return (float)MaximumObjectMass; },
|
||||||
|
(s,p,l,v) => { MaximumObjectMass = v; } ),
|
||||||
|
|
||||||
|
new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
|
||||||
|
2200f,
|
||||||
|
(s,cf,p,v) => { PID_D = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return (float)PID_D; },
|
||||||
|
(s,p,l,v) => { PID_D = v; } ),
|
||||||
|
new ParameterDefn("PID_P", "Parameteric factor for motion smoothing",
|
||||||
|
900f,
|
||||||
|
(s,cf,p,v) => { PID_P = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return (float)PID_P; },
|
||||||
|
(s,p,l,v) => { PID_P = v; } ),
|
||||||
|
|
||||||
|
new ParameterDefn("DefaultFriction", "Friction factor used on new objects",
|
||||||
|
0.2f,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].defaultFriction = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].defaultFriction; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].defaultFriction = v; } ),
|
||||||
|
new ParameterDefn("DefaultDensity", "Density for new objects" ,
|
||||||
|
10.000006836f, // Aluminum g/cm3
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].defaultDensity = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].defaultDensity; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].defaultDensity = v; } ),
|
||||||
|
new ParameterDefn("DefaultRestitution", "Bouncyness of an object" ,
|
||||||
|
0f,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].defaultRestitution = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].defaultRestitution; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].defaultRestitution = v; } ),
|
||||||
|
new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)",
|
||||||
|
0.04f,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].collisionMargin = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].collisionMargin; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].collisionMargin = v; } ),
|
||||||
|
new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)",
|
||||||
|
-9.80665f,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].gravity = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].gravity; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{s.UnmanagedParams[0].gravity=x;}, p, PhysParameterEntry.APPLY_TO_NONE, v); },
|
||||||
|
(s,o,v) => { BulletSimAPI.SetGravity2(s.World.ptr, new Vector3(0f,0f,v)); } ),
|
||||||
|
|
||||||
|
|
||||||
|
new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)",
|
||||||
|
0f,
|
||||||
|
(s,cf,p,v) => { LinearDamping = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return LinearDamping; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearDamping=x;}, p, l, v); },
|
||||||
|
(s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, v, AngularDamping); } ),
|
||||||
|
new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)",
|
||||||
|
0f,
|
||||||
|
(s,cf,p,v) => { AngularDamping = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return AngularDamping; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularDamping=x;}, p, l, v); },
|
||||||
|
(s,o,v) => { BulletSimAPI.SetDamping2(o.PhysBody.ptr, LinearDamping, v); } ),
|
||||||
|
new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static",
|
||||||
|
0.2f,
|
||||||
|
(s,cf,p,v) => { DeactivationTime = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return DeactivationTime; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{DeactivationTime=x;}, p, l, v); },
|
||||||
|
(s,o,v) => { BulletSimAPI.SetDeactivationTime2(o.PhysBody.ptr, v); } ),
|
||||||
|
new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static",
|
||||||
|
0.8f,
|
||||||
|
(s,cf,p,v) => { LinearSleepingThreshold = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return LinearSleepingThreshold; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{LinearSleepingThreshold=x;}, p, l, v); },
|
||||||
|
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
|
||||||
|
new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static",
|
||||||
|
1.0f,
|
||||||
|
(s,cf,p,v) => { AngularSleepingThreshold = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return AngularSleepingThreshold; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AngularSleepingThreshold=x;}, p, l, v); },
|
||||||
|
(s,o,v) => { BulletSimAPI.SetSleepingThresholds2(o.PhysBody.ptr, v, v); } ),
|
||||||
|
new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ,
|
||||||
|
0f, // set to zero to disable
|
||||||
|
(s,cf,p,v) => { CcdMotionThreshold = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return CcdMotionThreshold; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdMotionThreshold=x;}, p, l, v); },
|
||||||
|
(s,o,v) => { BulletSimAPI.SetCcdMotionThreshold2(o.PhysBody.ptr, v); } ),
|
||||||
|
new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" ,
|
||||||
|
0f,
|
||||||
|
(s,cf,p,v) => { CcdSweptSphereRadius = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return CcdSweptSphereRadius; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{CcdSweptSphereRadius=x;}, p, l, v); },
|
||||||
|
(s,o,v) => { BulletSimAPI.SetCcdSweptSphereRadius2(o.PhysBody.ptr, v); } ),
|
||||||
|
new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" ,
|
||||||
|
0.1f,
|
||||||
|
(s,cf,p,v) => { ContactProcessingThreshold = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return ContactProcessingThreshold; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{ContactProcessingThreshold=x;}, p, l, v); },
|
||||||
|
(s,o,v) => { BulletSimAPI.SetContactProcessingThreshold2(o.PhysBody.ptr, v); } ),
|
||||||
|
|
||||||
|
new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)",
|
||||||
|
(float)BSTerrainPhys.TerrainImplementation.Mesh,
|
||||||
|
(s,cf,p,v) => { TerrainImplementation = cf.GetFloat(p,v); },
|
||||||
|
(s) => { return TerrainImplementation; },
|
||||||
|
(s,p,l,v) => { TerrainImplementation = v; } ),
|
||||||
|
new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
|
||||||
|
0.3f,
|
||||||
|
(s,cf,p,v) => { TerrainFriction = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return TerrainFriction; },
|
||||||
|
(s,p,l,v) => { TerrainFriction = v; /* TODO: set on real terrain */} ),
|
||||||
|
new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" ,
|
||||||
|
0.8f,
|
||||||
|
(s,cf,p,v) => { TerrainHitFraction = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return TerrainHitFraction; },
|
||||||
|
(s,p,l,v) => { TerrainHitFraction = v; /* TODO: set on real terrain */ } ),
|
||||||
|
new ParameterDefn("TerrainRestitution", "Bouncyness" ,
|
||||||
|
0f,
|
||||||
|
(s,cf,p,v) => { TerrainRestitution = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return TerrainRestitution; },
|
||||||
|
(s,p,l,v) => { TerrainRestitution = v; /* TODO: set on real terrain */ } ),
|
||||||
|
new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" ,
|
||||||
|
0.04f,
|
||||||
|
(s,cf,p,v) => { TerrainCollisionMargin = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return TerrainCollisionMargin; },
|
||||||
|
(s,p,l,v) => { TerrainCollisionMargin = v; /* TODO: set on real terrain */ } ),
|
||||||
|
|
||||||
|
new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
|
||||||
|
0.2f,
|
||||||
|
(s,cf,p,v) => { AvatarFriction = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return AvatarFriction; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarFriction=x;}, p, l, v); } ),
|
||||||
|
new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
|
||||||
|
10.0f,
|
||||||
|
(s,cf,p,v) => { AvatarStandingFriction = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return AvatarStandingFriction; },
|
||||||
|
(s,p,l,v) => { AvatarStandingFriction = v; } ),
|
||||||
|
new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.",
|
||||||
|
60f,
|
||||||
|
(s,cf,p,v) => { AvatarDensity = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return AvatarDensity; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarDensity=x;}, p, l, v); } ),
|
||||||
|
new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.",
|
||||||
|
0f,
|
||||||
|
(s,cf,p,v) => { AvatarRestitution = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return AvatarRestitution; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarRestitution=x;}, p, l, v); } ),
|
||||||
|
new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule",
|
||||||
|
0.6f,
|
||||||
|
(s,cf,p,v) => { AvatarCapsuleWidth = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return AvatarCapsuleWidth; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleWidth=x;}, p, l, v); } ),
|
||||||
|
new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule",
|
||||||
|
0.45f,
|
||||||
|
(s,cf,p,v) => { AvatarCapsuleDepth = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return AvatarCapsuleDepth; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleDepth=x;}, p, l, v); } ),
|
||||||
|
new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar",
|
||||||
|
1.5f,
|
||||||
|
(s,cf,p,v) => { AvatarCapsuleHeight = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return AvatarCapsuleHeight; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarCapsuleHeight=x;}, p, l, v); } ),
|
||||||
|
new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions",
|
||||||
|
0.1f,
|
||||||
|
(s,cf,p,v) => { AvatarContactProcessingThreshold = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return AvatarContactProcessingThreshold; },
|
||||||
|
(s,p,l,v) => { s.UpdateParameterObject((x)=>{AvatarContactProcessingThreshold=x;}, p, l, v); } ),
|
||||||
|
|
||||||
|
new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)",
|
||||||
|
0.95f,
|
||||||
|
(s,cf,p,v) => { VehicleAngularDamping = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return VehicleAngularDamping; },
|
||||||
|
(s,p,l,v) => { VehicleAngularDamping = v; } ),
|
||||||
|
|
||||||
|
new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
|
||||||
|
0f,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].maxPersistantManifoldPoolSize; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].maxPersistantManifoldPoolSize = v; } ),
|
||||||
|
new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)",
|
||||||
|
0f,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = v; } ),
|
||||||
|
new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count",
|
||||||
|
ConfigurationParameters.numericFalse,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = v; } ),
|
||||||
|
new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step",
|
||||||
|
ConfigurationParameters.numericFalse,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].shouldForceUpdateAllAabbs; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].shouldForceUpdateAllAabbs = v; } ),
|
||||||
|
new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction",
|
||||||
|
ConfigurationParameters.numericTrue,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].shouldRandomizeSolverOrder; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].shouldRandomizeSolverOrder = v; } ),
|
||||||
|
new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands",
|
||||||
|
ConfigurationParameters.numericTrue,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].shouldSplitSimulationIslands; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].shouldSplitSimulationIslands = v; } ),
|
||||||
|
new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching",
|
||||||
|
ConfigurationParameters.numericFalse,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].shouldEnableFrictionCaching; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].shouldEnableFrictionCaching = v; } ),
|
||||||
|
new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)",
|
||||||
|
0f, // zero says use Bullet default
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].numberOfSolverIterations = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return s.UnmanagedParams[0].numberOfSolverIterations; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].numberOfSolverIterations = v; } ),
|
||||||
|
|
||||||
|
new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)",
|
||||||
|
(float)BSLinkset.LinksetImplementation.Compound,
|
||||||
|
(s,cf,p,v) => { LinksetImplementation = cf.GetFloat(p,v); },
|
||||||
|
(s) => { return LinksetImplementation; },
|
||||||
|
(s,p,l,v) => { LinksetImplementation = v; } ),
|
||||||
|
new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.",
|
||||||
|
ConfigurationParameters.numericFalse,
|
||||||
|
(s,cf,p,v) => { LinkConstraintUseFrameOffset = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||||
|
(s) => { return LinkConstraintUseFrameOffset; },
|
||||||
|
(s,p,l,v) => { LinkConstraintUseFrameOffset = v; } ),
|
||||||
|
new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints",
|
||||||
|
ConfigurationParameters.numericTrue,
|
||||||
|
(s,cf,p,v) => { LinkConstraintEnableTransMotor = BSParam.NumericBool(cf.GetBoolean(p, BSParam.BoolNumeric(v))); },
|
||||||
|
(s) => { return LinkConstraintEnableTransMotor; },
|
||||||
|
(s,p,l,v) => { LinkConstraintEnableTransMotor = v; } ),
|
||||||
|
new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints",
|
||||||
|
5.0f,
|
||||||
|
(s,cf,p,v) => { LinkConstraintTransMotorMaxVel = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return LinkConstraintTransMotorMaxVel; },
|
||||||
|
(s,p,l,v) => { LinkConstraintTransMotorMaxVel = v; } ),
|
||||||
|
new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints",
|
||||||
|
0.1f,
|
||||||
|
(s,cf,p,v) => { LinkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return LinkConstraintTransMotorMaxForce; },
|
||||||
|
(s,p,l,v) => { LinkConstraintTransMotorMaxForce = v; } ),
|
||||||
|
new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
|
||||||
|
0.1f,
|
||||||
|
(s,cf,p,v) => { LinkConstraintCFM = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return LinkConstraintCFM; },
|
||||||
|
(s,p,l,v) => { LinkConstraintCFM = v; } ),
|
||||||
|
new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2",
|
||||||
|
0.1f,
|
||||||
|
(s,cf,p,v) => { LinkConstraintERP = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return LinkConstraintERP; },
|
||||||
|
(s,p,l,v) => { LinkConstraintERP = v; } ),
|
||||||
|
new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
|
||||||
|
40,
|
||||||
|
(s,cf,p,v) => { LinkConstraintSolverIterations = cf.GetFloat(p, v); },
|
||||||
|
(s) => { return LinkConstraintSolverIterations; },
|
||||||
|
(s,p,l,v) => { LinkConstraintSolverIterations = v; } ),
|
||||||
|
|
||||||
|
new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)",
|
||||||
|
0f,
|
||||||
|
(s,cf,p,v) => { s.UnmanagedParams[0].physicsLoggingFrames = cf.GetInt(p, (int)v); },
|
||||||
|
(s) => { return (float)s.UnmanagedParams[0].physicsLoggingFrames; },
|
||||||
|
(s,p,l,v) => { s.UnmanagedParams[0].physicsLoggingFrames = (int)v; } ),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert a boolean to our numeric true and false values
|
||||||
|
public static float NumericBool(bool b)
|
||||||
|
{
|
||||||
|
return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert numeric true and false values to a boolean
|
||||||
|
public static bool BoolNumeric(float b)
|
||||||
|
{
|
||||||
|
return (b == ConfigurationParameters.numericTrue ? true : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search through the parameter definitions and return the matching
|
||||||
|
// ParameterDefn structure.
|
||||||
|
// Case does not matter as names are compared after converting to lower case.
|
||||||
|
// Returns 'false' if the parameter is not found.
|
||||||
|
internal static bool TryGetParameter(string paramName, out ParameterDefn defn)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
ParameterDefn foundDefn = new ParameterDefn();
|
||||||
|
string pName = paramName.ToLower();
|
||||||
|
|
||||||
|
foreach (ParameterDefn parm in ParameterDefinitions)
|
||||||
|
{
|
||||||
|
if (pName == parm.name.ToLower())
|
||||||
|
{
|
||||||
|
foundDefn = parm;
|
||||||
|
ret = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defn = foundDefn;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass through the settable parameters and set the default values
|
||||||
|
internal static void SetParameterDefaultValues(BSScene physicsScene)
|
||||||
|
{
|
||||||
|
foreach (ParameterDefn parm in ParameterDefinitions)
|
||||||
|
{
|
||||||
|
parm.setter(physicsScene, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user set values out of the ini file.
|
||||||
|
internal static void SetParameterConfigurationValues(BSScene physicsScene, IConfig cfg)
|
||||||
|
{
|
||||||
|
foreach (ParameterDefn parm in ParameterDefinitions)
|
||||||
|
{
|
||||||
|
parm.userParam(physicsScene, cfg, parm.name, parm.defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1];
|
||||||
|
|
||||||
|
// This creates an array in the correct format for returning the list of
|
||||||
|
// parameters. This is used by the 'list' option of the 'physics' command.
|
||||||
|
internal static void BuildParameterTable()
|
||||||
|
{
|
||||||
|
if (SettableParameters.Length < ParameterDefinitions.Length)
|
||||||
|
{
|
||||||
|
List<PhysParameterEntry> entries = new List<PhysParameterEntry>();
|
||||||
|
for (int ii = 0; ii < ParameterDefinitions.Length; ii++)
|
||||||
|
{
|
||||||
|
ParameterDefn pd = ParameterDefinitions[ii];
|
||||||
|
entries.Add(new PhysParameterEntry(pd.name, pd.desc));
|
||||||
|
}
|
||||||
|
|
||||||
|
// make the list in alphabetical order for estetic reasons
|
||||||
|
entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2)
|
||||||
|
{
|
||||||
|
return ppe1.name.CompareTo(ppe2.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
SettableParameters = entries.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,345 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using OMV = OpenMetaverse;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Region.Physics.Manager;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Class to wrap all objects.
|
||||||
|
* The rest of BulletSim doesn't need to keep checking for avatars or prims
|
||||||
|
* unless the difference is significant.
|
||||||
|
*
|
||||||
|
* Variables in the physicsl objects are in three forms:
|
||||||
|
* VariableName: used by the simulator and performs taint operations, etc
|
||||||
|
* RawVariableName: direct reference to the BulletSim storage for the variable value
|
||||||
|
* ForceVariableName: direct reference (store and fetch) to the value in the physics engine.
|
||||||
|
* The last two (and certainly the last one) should be referenced only in taint-time.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As of 20121221, the following are the call sequences (going down) for different script physical functions:
|
||||||
|
* llApplyImpulse llApplyRotImpulse llSetTorque llSetForce
|
||||||
|
* SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce
|
||||||
|
* SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse
|
||||||
|
* PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v
|
||||||
|
* BS.ApplyCentralForce BS.ApplyTorque
|
||||||
|
*/
|
||||||
|
|
||||||
|
public abstract class BSPhysObject : PhysicsActor
|
||||||
|
{
|
||||||
|
protected BSPhysObject()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName)
|
||||||
|
{
|
||||||
|
PhysicsScene = parentScene;
|
||||||
|
LocalID = localID;
|
||||||
|
PhysObjectName = name;
|
||||||
|
TypeName = typeName;
|
||||||
|
|
||||||
|
Linkset = BSLinkset.Factory(PhysicsScene, this);
|
||||||
|
LastAssetBuildFailed = false;
|
||||||
|
|
||||||
|
// Default material type
|
||||||
|
Material = MaterialAttributes.Material.Wood;
|
||||||
|
|
||||||
|
CollisionCollection = new CollisionEventUpdate();
|
||||||
|
SubscribedEventsMs = 0;
|
||||||
|
CollidingStep = 0;
|
||||||
|
CollidingGroundStep = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the object to clean up.
|
||||||
|
public virtual void Destroy()
|
||||||
|
{
|
||||||
|
UnRegisterAllPreStepActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BSScene PhysicsScene { get; protected set; }
|
||||||
|
// public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor
|
||||||
|
public string PhysObjectName { get; protected set; }
|
||||||
|
public string TypeName { get; protected set; }
|
||||||
|
|
||||||
|
public BSLinkset Linkset { get; set; }
|
||||||
|
public BSLinksetInfo LinksetInfo { get; set; }
|
||||||
|
|
||||||
|
// Return the object mass without calculating it or having side effects
|
||||||
|
public abstract float RawMass { get; }
|
||||||
|
// Set the raw mass but also update physical mass properties (inertia, ...)
|
||||||
|
public abstract void UpdatePhysicalMassProperties(float mass);
|
||||||
|
|
||||||
|
// The last value calculated for the prim's inertia
|
||||||
|
public OMV.Vector3 Inertia { get; set; }
|
||||||
|
|
||||||
|
// Reference to the physical body (btCollisionObject) of this object
|
||||||
|
public BulletBody PhysBody;
|
||||||
|
// Reference to the physical shape (btCollisionShape) of this object
|
||||||
|
public BulletShape PhysShape;
|
||||||
|
|
||||||
|
// 'true' if the mesh's underlying asset failed to build.
|
||||||
|
// This will keep us from looping after the first time the build failed.
|
||||||
|
public bool LastAssetBuildFailed { get; set; }
|
||||||
|
|
||||||
|
// The objects base shape information. Null if not a prim type shape.
|
||||||
|
public PrimitiveBaseShape BaseShape { get; protected set; }
|
||||||
|
// Some types of objects have preferred physical representations.
|
||||||
|
// Returns SHAPE_UNKNOWN if there is no preference.
|
||||||
|
public virtual BSPhysicsShapeType PreferredPhysicalShape
|
||||||
|
{
|
||||||
|
get { return BSPhysicsShapeType.SHAPE_UNKNOWN; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the physical properties are updated, an EntityProperty holds the update values.
|
||||||
|
// Keep the current and last EntityProperties to enable computation of differences
|
||||||
|
// between the current update and the previous values.
|
||||||
|
public EntityProperties CurrentEntityProperties { get; set; }
|
||||||
|
public EntityProperties LastEntityProperties { get; set; }
|
||||||
|
|
||||||
|
public virtual OMV.Vector3 Scale { get; set; }
|
||||||
|
public abstract bool IsSolid { get; }
|
||||||
|
public abstract bool IsStatic { get; }
|
||||||
|
|
||||||
|
// Materialness
|
||||||
|
public MaterialAttributes.Material Material { get; private set; }
|
||||||
|
public override void SetMaterial(int material)
|
||||||
|
{
|
||||||
|
Material = (MaterialAttributes.Material)material;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop all physical motion.
|
||||||
|
public abstract void ZeroMotion(bool inTaintTime);
|
||||||
|
public abstract void ZeroAngularMotion(bool inTaintTime);
|
||||||
|
|
||||||
|
// Step the vehicle simulation for this object. A NOOP if the vehicle was not configured.
|
||||||
|
public virtual void StepVehicle(float timeStep) { }
|
||||||
|
|
||||||
|
// Update the physical location and motion of the object. Called with data from Bullet.
|
||||||
|
public abstract void UpdateProperties(EntityProperties entprop);
|
||||||
|
|
||||||
|
public abstract OMV.Vector3 RawPosition { get; set; }
|
||||||
|
public abstract OMV.Vector3 ForcePosition { get; set; }
|
||||||
|
|
||||||
|
public abstract OMV.Quaternion RawOrientation { get; set; }
|
||||||
|
public abstract OMV.Quaternion ForceOrientation { get; set; }
|
||||||
|
|
||||||
|
// The system is telling us the velocity it wants to move at.
|
||||||
|
// protected OMV.Vector3 m_targetVelocity; // use the definition in PhysicsActor
|
||||||
|
public override OMV.Vector3 TargetVelocity
|
||||||
|
{
|
||||||
|
get { return m_targetVelocity; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
m_targetVelocity = value;
|
||||||
|
Velocity = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public abstract OMV.Vector3 ForceVelocity { get; set; }
|
||||||
|
|
||||||
|
public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
|
||||||
|
|
||||||
|
public abstract float ForceBuoyancy { get; set; }
|
||||||
|
|
||||||
|
public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
|
||||||
|
|
||||||
|
#region Collisions
|
||||||
|
|
||||||
|
// Requested number of milliseconds between collision events. Zero means disabled.
|
||||||
|
protected int SubscribedEventsMs { get; set; }
|
||||||
|
// Given subscription, the time that a collision may be passed up
|
||||||
|
protected int NextCollisionOkTime { get; set; }
|
||||||
|
// The simulation step that last had a collision
|
||||||
|
protected long CollidingStep { get; set; }
|
||||||
|
// The simulation step that last had a collision with the ground
|
||||||
|
protected long CollidingGroundStep { get; set; }
|
||||||
|
// The collision flags we think are set in Bullet
|
||||||
|
protected CollisionFlags CurrentCollisionFlags { get; set; }
|
||||||
|
|
||||||
|
// The collisions that have been collected this tick
|
||||||
|
protected CollisionEventUpdate CollisionCollection;
|
||||||
|
|
||||||
|
// The simulation step is telling this object about a collision.
|
||||||
|
// Return 'true' if a collision was processed and should be sent up.
|
||||||
|
// Called at taint time from within the Step() function
|
||||||
|
public virtual bool Collide(uint collidingWith, BSPhysObject collidee,
|
||||||
|
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
// The following lines make IsColliding() and IsCollidingGround() work
|
||||||
|
CollidingStep = PhysicsScene.SimulationStep;
|
||||||
|
if (collidingWith <= PhysicsScene.TerrainManager.HighestTerrainID)
|
||||||
|
{
|
||||||
|
CollidingGroundStep = PhysicsScene.SimulationStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prims in the same linkset cannot collide with each other
|
||||||
|
if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
|
||||||
|
{
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if someone has subscribed for collision events....
|
||||||
|
if (SubscribedEvents()) {
|
||||||
|
CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
|
||||||
|
DetailLog("{0},{1}.Collison.AddCollider,call,with={2},point={3},normal={4},depth={5}",
|
||||||
|
LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth);
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the collected collisions into the simulator.
|
||||||
|
// Called at taint time from within the Step() function thus no locking problems
|
||||||
|
// with CollisionCollection and ObjectsWithNoMoreCollisions.
|
||||||
|
// Return 'true' if there were some actual collisions passed up
|
||||||
|
public virtual bool SendCollisions()
|
||||||
|
{
|
||||||
|
bool ret = true;
|
||||||
|
// If the 'no collision' call, force it to happen right now so quick collision_end
|
||||||
|
bool force = (CollisionCollection.Count == 0);
|
||||||
|
|
||||||
|
// throttle the collisions to the number of milliseconds specified in the subscription
|
||||||
|
if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
|
||||||
|
{
|
||||||
|
NextCollisionOkTime = PhysicsScene.SimulationNowTime + SubscribedEventsMs;
|
||||||
|
|
||||||
|
// We are called if we previously had collisions. If there are no collisions
|
||||||
|
// this time, send up one last empty event so OpenSim can sense collision end.
|
||||||
|
if (CollisionCollection.Count == 0)
|
||||||
|
{
|
||||||
|
// If I have no collisions this time, remove me from the list of objects with collisions.
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
|
||||||
|
base.SendCollisionUpdate(CollisionCollection);
|
||||||
|
|
||||||
|
// The CollisionCollection instance is passed around in the simulator.
|
||||||
|
// Make sure we don't have a handle to that one and that a new one is used for next time.
|
||||||
|
// This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
|
||||||
|
// a race condition is created for the other users of this instance.
|
||||||
|
CollisionCollection = new CollisionEventUpdate();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe for collision events.
|
||||||
|
// Parameter is the millisecond rate the caller wishes collision events to occur.
|
||||||
|
public override void SubscribeEvents(int ms) {
|
||||||
|
// DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms);
|
||||||
|
SubscribedEventsMs = ms;
|
||||||
|
if (ms > 0)
|
||||||
|
{
|
||||||
|
// make sure first collision happens
|
||||||
|
NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs);
|
||||||
|
|
||||||
|
PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
|
||||||
|
{
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Subscribing for zero or less is the same as unsubscribing
|
||||||
|
UnSubscribeEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override void UnSubscribeEvents() {
|
||||||
|
// DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName);
|
||||||
|
SubscribedEventsMs = 0;
|
||||||
|
PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
|
||||||
|
{
|
||||||
|
// Make sure there is a body there because sometimes destruction happens in an un-ideal order.
|
||||||
|
if (PhysBody.HasPhysicalBody)
|
||||||
|
CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Return 'true' if the simulator wants collision events
|
||||||
|
public override bool SubscribedEvents() {
|
||||||
|
return (SubscribedEventsMs > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Collisions
|
||||||
|
|
||||||
|
#region Per Simulation Step actions
|
||||||
|
// There are some actions that must be performed for a physical object before each simulation step.
|
||||||
|
// These actions are optional so, rather than scanning all the physical objects and asking them
|
||||||
|
// if they have anything to do, a physical object registers for an event call before the step is performed.
|
||||||
|
// This bookkeeping makes it easy to add, remove and clean up after all these registrations.
|
||||||
|
private Dictionary<string, BSScene.PreStepAction> RegisteredActions = new Dictionary<string, BSScene.PreStepAction>();
|
||||||
|
protected void RegisterPreStepAction(string op, uint id, BSScene.PreStepAction actn)
|
||||||
|
{
|
||||||
|
string identifier = op + "-" + id.ToString();
|
||||||
|
RegisteredActions[identifier] = actn;
|
||||||
|
PhysicsScene.BeforeStep += actn;
|
||||||
|
DetailLog("{0},BSPhysObject.RegisterPreStepAction,id={1}", LocalID, identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister a pre step action. Safe to call if the action has not been registered.
|
||||||
|
protected void UnRegisterPreStepAction(string op, uint id)
|
||||||
|
{
|
||||||
|
string identifier = op + "-" + id.ToString();
|
||||||
|
bool removed = false;
|
||||||
|
if (RegisteredActions.ContainsKey(identifier))
|
||||||
|
{
|
||||||
|
PhysicsScene.BeforeStep -= RegisteredActions[identifier];
|
||||||
|
RegisteredActions.Remove(identifier);
|
||||||
|
removed = true;
|
||||||
|
}
|
||||||
|
DetailLog("{0},BSPhysObject.UnRegisterPreStepAction,id={1},removed={2}", LocalID, identifier, removed);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void UnRegisterAllPreStepActions()
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, BSScene.PreStepAction> kvp in RegisteredActions)
|
||||||
|
{
|
||||||
|
PhysicsScene.BeforeStep -= kvp.Value;
|
||||||
|
}
|
||||||
|
RegisteredActions.Clear();
|
||||||
|
DetailLog("{0},BSPhysObject.UnRegisterAllPreStepActions,", LocalID);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion // Per Simulation Step actions
|
||||||
|
|
||||||
|
// High performance detailed logging routine used by the physical objects.
|
||||||
|
protected void DetailLog(string msg, params Object[] args)
|
||||||
|
{
|
||||||
|
if (PhysicsScene.PhysicsLogging.Enabled)
|
||||||
|
PhysicsScene.DetailLog(msg, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Region.Physics.Manager;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim.
|
||||||
|
/// This module interfaces to an unmanaged C++ library which makes the
|
||||||
|
/// actual calls into the Bullet physics engine.
|
||||||
|
/// The unmanaged library is found in opensim-libs::trunk/unmanaged/BulletSim/.
|
||||||
|
/// The unmanaged library is compiled and linked statically with Bullet
|
||||||
|
/// to create BulletSim.dll and libBulletSim.so (for both 32 and 64 bit).
|
||||||
|
/// </summary>
|
||||||
|
public class BSPlugin : IPhysicsPlugin
|
||||||
|
{
|
||||||
|
//private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
private BSScene _mScene;
|
||||||
|
|
||||||
|
public BSPlugin()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Init()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PhysicsScene GetScene(String sceneIdentifier)
|
||||||
|
{
|
||||||
|
if (_mScene == null)
|
||||||
|
{
|
||||||
|
|
||||||
|
// If not Windows, loading is performed by the
|
||||||
|
// Mono loader as specified in
|
||||||
|
// "bin/Physics/OpenSim.Region.Physics.BulletSNPlugin.dll.config".
|
||||||
|
|
||||||
|
_mScene = new BSScene(sceneIdentifier);
|
||||||
|
}
|
||||||
|
return (_mScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetName()
|
||||||
|
{
|
||||||
|
return ("BulletSimN");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,954 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Region.Framework;
|
||||||
|
using OpenSim.Region.CoreModules;
|
||||||
|
using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging;
|
||||||
|
using OpenSim.Region.Physics.Manager;
|
||||||
|
using Nini.Config;
|
||||||
|
using log4net;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
|
||||||
|
// Based on material, set density and friction
|
||||||
|
// More efficient memory usage when passing hull information from BSPrim to BulletSim
|
||||||
|
// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
|
||||||
|
// Implement LockAngularMotion
|
||||||
|
// Add PID movement operations. What does ScenePresence.MoveToTarget do?
|
||||||
|
// Check terrain size. 128 or 127?
|
||||||
|
// Raycast
|
||||||
|
//
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
public sealed class BSScene : PhysicsScene, IPhysicsParameters
|
||||||
|
{
|
||||||
|
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
private static readonly string LogHeader = "[BULLETS SCENE]";
|
||||||
|
|
||||||
|
// The name of the region we're working for.
|
||||||
|
public string RegionName { get; private set; }
|
||||||
|
|
||||||
|
public string BulletSimVersion = "?";
|
||||||
|
|
||||||
|
public Dictionary<uint, BSPhysObject> PhysObjects;
|
||||||
|
public BSShapeCollection Shapes;
|
||||||
|
|
||||||
|
// Keeping track of the objects with collisions so we can report begin and end of a collision
|
||||||
|
public HashSet<BSPhysObject> ObjectsWithCollisions = new HashSet<BSPhysObject>();
|
||||||
|
public HashSet<BSPhysObject> ObjectsWithNoMoreCollisions = new HashSet<BSPhysObject>();
|
||||||
|
// Keep track of all the avatars so we can send them a collision event
|
||||||
|
// every tick so OpenSim will update its animation.
|
||||||
|
private HashSet<BSPhysObject> m_avatars = new HashSet<BSPhysObject>();
|
||||||
|
|
||||||
|
// let my minuions use my logger
|
||||||
|
public ILog Logger { get { return m_log; } }
|
||||||
|
|
||||||
|
public IMesher mesher;
|
||||||
|
public uint WorldID { get; private set; }
|
||||||
|
public BulletSim World { get; private set; }
|
||||||
|
|
||||||
|
// All the constraints that have been allocated in this instance.
|
||||||
|
public BSConstraintCollection Constraints { get; private set; }
|
||||||
|
|
||||||
|
// Simulation parameters
|
||||||
|
internal int m_maxSubSteps;
|
||||||
|
internal float m_fixedTimeStep;
|
||||||
|
internal long m_simulationStep = 0;
|
||||||
|
public long SimulationStep { get { return m_simulationStep; } }
|
||||||
|
internal int m_taintsToProcessPerStep;
|
||||||
|
internal float LastTimeStep { get; private set; }
|
||||||
|
|
||||||
|
// Physical objects can register for prestep or poststep events
|
||||||
|
public delegate void PreStepAction(float timeStep);
|
||||||
|
public delegate void PostStepAction(float timeStep);
|
||||||
|
public event PreStepAction BeforeStep;
|
||||||
|
public event PreStepAction AfterStep;
|
||||||
|
|
||||||
|
// A value of the time now so all the collision and update routines do not have to get their own
|
||||||
|
// Set to 'now' just before all the prims and actors are called for collisions and updates
|
||||||
|
public int SimulationNowTime { get; private set; }
|
||||||
|
|
||||||
|
// True if initialized and ready to do simulation steps
|
||||||
|
private bool m_initialized = false;
|
||||||
|
|
||||||
|
// Flag which is true when processing taints.
|
||||||
|
// Not guaranteed to be correct all the time (don't depend on this) but good for debugging.
|
||||||
|
public bool InTaintTime { get; private set; }
|
||||||
|
|
||||||
|
// Pinned memory used to pass step information between managed and unmanaged
|
||||||
|
internal int m_maxCollisionsPerFrame;
|
||||||
|
private List<BulletXNA.CollisionDesc> m_collisionArray;
|
||||||
|
//private GCHandle m_collisionArrayPinnedHandle;
|
||||||
|
|
||||||
|
internal int m_maxUpdatesPerFrame;
|
||||||
|
private List<BulletXNA.EntityProperties> m_updateArray;
|
||||||
|
//private GCHandle m_updateArrayPinnedHandle;
|
||||||
|
|
||||||
|
|
||||||
|
public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero
|
||||||
|
public const uint GROUNDPLANE_ID = 1;
|
||||||
|
public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
|
||||||
|
|
||||||
|
public float SimpleWaterLevel { get; set; }
|
||||||
|
public BSTerrainManager TerrainManager { get; private set; }
|
||||||
|
|
||||||
|
public ConfigurationParameters Params
|
||||||
|
{
|
||||||
|
get { return UnmanagedParams[0]; }
|
||||||
|
}
|
||||||
|
public Vector3 DefaultGravity
|
||||||
|
{
|
||||||
|
get { return new Vector3(0f, 0f, Params.gravity); }
|
||||||
|
}
|
||||||
|
// Just the Z value of the gravity
|
||||||
|
public float DefaultGravityZ
|
||||||
|
{
|
||||||
|
get { return Params.gravity; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// When functions in the unmanaged code must be called, it is only
|
||||||
|
// done at a known time just before the simulation step. The taint
|
||||||
|
// system saves all these function calls and executes them in
|
||||||
|
// order before the simulation.
|
||||||
|
public delegate void TaintCallback();
|
||||||
|
private struct TaintCallbackEntry
|
||||||
|
{
|
||||||
|
public String ident;
|
||||||
|
public TaintCallback callback;
|
||||||
|
public TaintCallbackEntry(string i, TaintCallback c)
|
||||||
|
{
|
||||||
|
ident = i;
|
||||||
|
callback = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private Object _taintLock = new Object(); // lock for using the next object
|
||||||
|
private List<TaintCallbackEntry> _taintOperations;
|
||||||
|
private Dictionary<string, TaintCallbackEntry> _postTaintOperations;
|
||||||
|
private List<TaintCallbackEntry> _postStepOperations;
|
||||||
|
|
||||||
|
// A pointer to an instance if this structure is passed to the C++ code
|
||||||
|
// Used to pass basic configuration values to the unmanaged code.
|
||||||
|
internal ConfigurationParameters[] UnmanagedParams;
|
||||||
|
//GCHandle m_paramsHandle;
|
||||||
|
|
||||||
|
// Handle to the callback used by the unmanaged code to call into the managed code.
|
||||||
|
// Used for debug logging.
|
||||||
|
// Need to store the handle in a persistant variable so it won't be freed.
|
||||||
|
private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle;
|
||||||
|
|
||||||
|
// Sometimes you just have to log everything.
|
||||||
|
public Logging.LogWriter PhysicsLogging;
|
||||||
|
private bool m_physicsLoggingEnabled;
|
||||||
|
private string m_physicsLoggingDir;
|
||||||
|
private string m_physicsLoggingPrefix;
|
||||||
|
private int m_physicsLoggingFileMinutes;
|
||||||
|
private bool m_physicsLoggingDoFlush;
|
||||||
|
// 'true' of the vehicle code is to log lots of details
|
||||||
|
public bool VehicleLoggingEnabled { get; private set; }
|
||||||
|
public bool VehiclePhysicalLoggingEnabled { get; private set; }
|
||||||
|
|
||||||
|
#region Construction and Initialization
|
||||||
|
public BSScene(string identifier)
|
||||||
|
{
|
||||||
|
m_initialized = false;
|
||||||
|
// we are passed the name of the region we're working for.
|
||||||
|
RegionName = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialise(IMesher meshmerizer, IConfigSource config)
|
||||||
|
{
|
||||||
|
mesher = meshmerizer;
|
||||||
|
_taintOperations = new List<TaintCallbackEntry>();
|
||||||
|
_postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
|
||||||
|
_postStepOperations = new List<TaintCallbackEntry>();
|
||||||
|
PhysObjects = new Dictionary<uint, BSPhysObject>();
|
||||||
|
Shapes = new BSShapeCollection(this);
|
||||||
|
|
||||||
|
// Allocate pinned memory to pass parameters.
|
||||||
|
UnmanagedParams = new ConfigurationParameters[1];
|
||||||
|
//m_paramsHandle = GCHandle.Alloc(UnmanagedParams, GCHandleType.Pinned);
|
||||||
|
|
||||||
|
// Set default values for physics parameters plus any overrides from the ini file
|
||||||
|
GetInitialParameterValues(config);
|
||||||
|
|
||||||
|
// allocate more pinned memory close to the above in an attempt to get the memory all together
|
||||||
|
m_collisionArray = new List<BulletXNA.CollisionDesc>();
|
||||||
|
//m_collisionArrayPinnedHandle = GCHandle.Alloc(m_collisionArray, GCHandleType.Pinned);
|
||||||
|
m_updateArray = new List<BulletXNA.EntityProperties>();
|
||||||
|
//m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned);
|
||||||
|
|
||||||
|
// Enable very detailed logging.
|
||||||
|
// By creating an empty logger when not logging, the log message invocation code
|
||||||
|
// can be left in and every call doesn't have to check for null.
|
||||||
|
if (m_physicsLoggingEnabled)
|
||||||
|
{
|
||||||
|
PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
|
||||||
|
PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PhysicsLogging = new Logging.LogWriter();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If Debug logging level, enable logging from the unmanaged code
|
||||||
|
m_DebugLogCallbackHandle = null;
|
||||||
|
if (m_log.IsDebugEnabled || PhysicsLogging.Enabled)
|
||||||
|
{
|
||||||
|
m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader);
|
||||||
|
if (PhysicsLogging.Enabled)
|
||||||
|
// The handle is saved in a variable to make sure it doesn't get freed after this call
|
||||||
|
m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog);
|
||||||
|
else
|
||||||
|
m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the version of the DLL
|
||||||
|
// TODO: this doesn't work yet. Something wrong with marshaling the returned string.
|
||||||
|
// BulletSimVersion = BulletSimAPI.GetVersion();
|
||||||
|
// m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion);
|
||||||
|
|
||||||
|
// The bounding box for the simulated world. The origin is 0,0,0 unless we're
|
||||||
|
// a child in a mega-region.
|
||||||
|
// Bullet actually doesn't care about the extents of the simulated
|
||||||
|
// area. It tracks active objects no matter where they are.
|
||||||
|
Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
|
||||||
|
|
||||||
|
// m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader);
|
||||||
|
|
||||||
|
World = new BulletSim(0, this, BulletSimAPI.Initialize2(worldExtent, UnmanagedParams,
|
||||||
|
m_maxCollisionsPerFrame, ref m_collisionArray,
|
||||||
|
m_maxUpdatesPerFrame,ref m_updateArray,
|
||||||
|
m_DebugLogCallbackHandle));
|
||||||
|
|
||||||
|
Constraints = new BSConstraintCollection(World);
|
||||||
|
|
||||||
|
TerrainManager = new BSTerrainManager(this);
|
||||||
|
TerrainManager.CreateInitialGroundPlaneAndTerrain();
|
||||||
|
|
||||||
|
m_log.WarnFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation);
|
||||||
|
|
||||||
|
InTaintTime = false;
|
||||||
|
m_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All default parameter values are set here. There should be no values set in the
|
||||||
|
// variable definitions.
|
||||||
|
private void GetInitialParameterValues(IConfigSource config)
|
||||||
|
{
|
||||||
|
ConfigurationParameters parms = new ConfigurationParameters();
|
||||||
|
UnmanagedParams[0] = parms;
|
||||||
|
|
||||||
|
BSParam.SetParameterDefaultValues(this);
|
||||||
|
|
||||||
|
if (config != null)
|
||||||
|
{
|
||||||
|
// If there are specifications in the ini file, use those values
|
||||||
|
IConfig pConfig = config.Configs["BulletSim"];
|
||||||
|
if (pConfig != null)
|
||||||
|
{
|
||||||
|
BSParam.SetParameterConfigurationValues(this, pConfig);
|
||||||
|
|
||||||
|
// Very detailed logging for physics debugging
|
||||||
|
m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false);
|
||||||
|
m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
|
||||||
|
m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
|
||||||
|
m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
|
||||||
|
m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
|
||||||
|
// Very detailed logging for vehicle debugging
|
||||||
|
VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
|
||||||
|
VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
|
||||||
|
|
||||||
|
// Do any replacements in the parameters
|
||||||
|
m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The material characteristics.
|
||||||
|
BSMaterials.InitializeFromDefaults(Params);
|
||||||
|
if (pConfig != null)
|
||||||
|
{
|
||||||
|
// Let the user add new and interesting material property values.
|
||||||
|
BSMaterials.InitializefromParameters(pConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper function that handles a true/false parameter and returns the proper float number encoding
|
||||||
|
float ParamBoolean(IConfig config, string parmName, float deflt)
|
||||||
|
{
|
||||||
|
float ret = deflt;
|
||||||
|
if (config.Contains(parmName))
|
||||||
|
{
|
||||||
|
ret = ConfigurationParameters.numericFalse;
|
||||||
|
if (config.GetBoolean(parmName, false))
|
||||||
|
{
|
||||||
|
ret = ConfigurationParameters.numericTrue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called directly from unmanaged code so don't do much
|
||||||
|
private void BulletLogger(string msg)
|
||||||
|
{
|
||||||
|
m_log.Debug("[BULLETS UNMANAGED]:" + msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called directly from unmanaged code so don't do much
|
||||||
|
private void BulletLoggerPhysLog(string msg)
|
||||||
|
{
|
||||||
|
DetailLog("[BULLETS UNMANAGED]:" + msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
// m_log.DebugFormat("{0}: Dispose()", LogHeader);
|
||||||
|
|
||||||
|
// make sure no stepping happens while we're deleting stuff
|
||||||
|
m_initialized = false;
|
||||||
|
|
||||||
|
foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
|
||||||
|
{
|
||||||
|
kvp.Value.Destroy();
|
||||||
|
}
|
||||||
|
PhysObjects.Clear();
|
||||||
|
|
||||||
|
// Now that the prims are all cleaned up, there should be no constraints left
|
||||||
|
if (Constraints != null)
|
||||||
|
{
|
||||||
|
Constraints.Dispose();
|
||||||
|
Constraints = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Shapes != null)
|
||||||
|
{
|
||||||
|
Shapes.Dispose();
|
||||||
|
Shapes = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TerrainManager != null)
|
||||||
|
{
|
||||||
|
TerrainManager.ReleaseGroundPlaneAndTerrain();
|
||||||
|
TerrainManager.Dispose();
|
||||||
|
TerrainManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anything left in the unmanaged code should be cleaned out
|
||||||
|
BulletSimAPI.Shutdown2(World.ptr);
|
||||||
|
|
||||||
|
// Not logging any more
|
||||||
|
PhysicsLogging.Close();
|
||||||
|
}
|
||||||
|
#endregion // Construction and Initialization
|
||||||
|
|
||||||
|
#region Prim and Avatar addition and removal
|
||||||
|
|
||||||
|
public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying)
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying)
|
||||||
|
{
|
||||||
|
// m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName);
|
||||||
|
|
||||||
|
if (!m_initialized) return null;
|
||||||
|
|
||||||
|
BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying);
|
||||||
|
lock (PhysObjects) PhysObjects.Add(localID, actor);
|
||||||
|
|
||||||
|
// TODO: Remove kludge someday.
|
||||||
|
// We must generate a collision for avatars whether they collide or not.
|
||||||
|
// This is required by OpenSim to update avatar animations, etc.
|
||||||
|
lock (m_avatars) m_avatars.Add(actor);
|
||||||
|
|
||||||
|
return actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RemoveAvatar(PhysicsActor actor)
|
||||||
|
{
|
||||||
|
// m_log.DebugFormat("{0}: RemoveAvatar", LogHeader);
|
||||||
|
|
||||||
|
if (!m_initialized) return;
|
||||||
|
|
||||||
|
BSCharacter bsactor = actor as BSCharacter;
|
||||||
|
if (bsactor != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lock (PhysObjects) PhysObjects.Remove(actor.LocalID);
|
||||||
|
// Remove kludge someday
|
||||||
|
lock (m_avatars) m_avatars.Remove(bsactor);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e);
|
||||||
|
}
|
||||||
|
bsactor.Destroy();
|
||||||
|
// bsactor.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void RemovePrim(PhysicsActor prim)
|
||||||
|
{
|
||||||
|
if (!m_initialized) return;
|
||||||
|
|
||||||
|
BSPrim bsprim = prim as BSPrim;
|
||||||
|
if (bsprim != null)
|
||||||
|
{
|
||||||
|
DetailLog("{0},RemovePrim,call", bsprim.LocalID);
|
||||||
|
// m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e);
|
||||||
|
}
|
||||||
|
bsprim.Destroy();
|
||||||
|
// bsprim.dispose();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
|
||||||
|
Vector3 size, Quaternion rotation, bool isPhysical, uint localID)
|
||||||
|
{
|
||||||
|
// m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName);
|
||||||
|
|
||||||
|
if (!m_initialized) return null;
|
||||||
|
|
||||||
|
DetailLog("{0},AddPrimShape,call", localID);
|
||||||
|
|
||||||
|
BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical);
|
||||||
|
lock (PhysObjects) PhysObjects.Add(localID, prim);
|
||||||
|
return prim;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a call from the simulator saying that some physical property has been updated.
|
||||||
|
// The BulletSim driver senses the changing of relevant properties so this taint
|
||||||
|
// information call is not needed.
|
||||||
|
public override void AddPhysicsActorTaint(PhysicsActor prim) { }
|
||||||
|
|
||||||
|
#endregion // Prim and Avatar addition and removal
|
||||||
|
|
||||||
|
#region Simulation
|
||||||
|
// Simulate one timestep
|
||||||
|
public override float Simulate(float timeStep)
|
||||||
|
{
|
||||||
|
// prevent simulation until we've been initialized
|
||||||
|
if (!m_initialized) return 5.0f;
|
||||||
|
|
||||||
|
LastTimeStep = timeStep;
|
||||||
|
|
||||||
|
int updatedEntityCount = 0;
|
||||||
|
//Object updatedEntitiesPtr;
|
||||||
|
int collidersCount = 0;
|
||||||
|
//Object collidersPtr;
|
||||||
|
|
||||||
|
int beforeTime = 0;
|
||||||
|
int simTime = 0;
|
||||||
|
|
||||||
|
// update the prim states while we know the physics engine is not busy
|
||||||
|
int numTaints = _taintOperations.Count;
|
||||||
|
|
||||||
|
InTaintTime = true; // Only used for debugging so locking is not necessary.
|
||||||
|
|
||||||
|
ProcessTaints();
|
||||||
|
|
||||||
|
// Some of the physical objects requre individual, pre-step calls
|
||||||
|
TriggerPreStepEvent(timeStep);
|
||||||
|
|
||||||
|
// the prestep actions might have added taints
|
||||||
|
ProcessTaints();
|
||||||
|
|
||||||
|
InTaintTime = false; // Only used for debugging so locking is not necessary.
|
||||||
|
|
||||||
|
// step the physical world one interval
|
||||||
|
m_simulationStep++;
|
||||||
|
int numSubSteps = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG
|
||||||
|
if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
|
||||||
|
|
||||||
|
numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
|
||||||
|
out updatedEntityCount, out m_updateArray, out collidersCount, out m_collisionArray);
|
||||||
|
|
||||||
|
if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
|
||||||
|
DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
|
||||||
|
DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
|
||||||
|
updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}",
|
||||||
|
LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e);
|
||||||
|
DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}",
|
||||||
|
DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount);
|
||||||
|
updatedEntityCount = 0;
|
||||||
|
collidersCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't have to use the pointers passed back since we know it is the same pinned memory we passed in.
|
||||||
|
|
||||||
|
// Get a value for 'now' so all the collision and update routines don't have to get their own.
|
||||||
|
SimulationNowTime = Util.EnvironmentTickCount();
|
||||||
|
|
||||||
|
// If there were collisions, process them by sending the event to the prim.
|
||||||
|
// Collisions must be processed before updates.
|
||||||
|
if (collidersCount > 0)
|
||||||
|
{
|
||||||
|
for (int ii = 0; ii < collidersCount; ii++)
|
||||||
|
{
|
||||||
|
uint cA = m_collisionArray[ii].aID;
|
||||||
|
uint cB = m_collisionArray[ii].bID;
|
||||||
|
Vector3 point = new Vector3(m_collisionArray[ii].point.X, m_collisionArray[ii].point.Y,
|
||||||
|
m_collisionArray[ii].point.Z);
|
||||||
|
Vector3 normal = new Vector3(m_collisionArray[ii].normal.X, m_collisionArray[ii].normal.Y,
|
||||||
|
m_collisionArray[ii].normal.Z);
|
||||||
|
SendCollision(cA, cB, point, normal, 0.01f);
|
||||||
|
SendCollision(cB, cA, point, -normal, 0.01f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The above SendCollision's batch up the collisions on the objects.
|
||||||
|
// Now push the collisions into the simulator.
|
||||||
|
if (ObjectsWithCollisions.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (BSPhysObject bsp in ObjectsWithCollisions)
|
||||||
|
if (!bsp.SendCollisions())
|
||||||
|
{
|
||||||
|
// If the object is done colliding, see that it's removed from the colliding list
|
||||||
|
ObjectsWithNoMoreCollisions.Add(bsp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a kludge to get avatar movement updates.
|
||||||
|
// The simulator expects collisions for avatars even if there are have been no collisions.
|
||||||
|
// The event updates avatar animations and stuff.
|
||||||
|
// If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
|
||||||
|
foreach (BSPhysObject bsp in m_avatars)
|
||||||
|
if (!ObjectsWithCollisions.Contains(bsp)) // don't call avatars twice
|
||||||
|
bsp.SendCollisions();
|
||||||
|
|
||||||
|
// Objects that are done colliding are removed from the ObjectsWithCollisions list.
|
||||||
|
// Not done above because it is inside an iteration of ObjectWithCollisions.
|
||||||
|
// This complex collision processing is required to create an empty collision
|
||||||
|
// event call after all collisions have happened on an object. This enables
|
||||||
|
// the simulator to generate the 'collision end' event.
|
||||||
|
if (ObjectsWithNoMoreCollisions.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
|
||||||
|
ObjectsWithCollisions.Remove(po);
|
||||||
|
ObjectsWithNoMoreCollisions.Clear();
|
||||||
|
}
|
||||||
|
// Done with collisions.
|
||||||
|
|
||||||
|
// If any of the objects had updated properties, tell the object it has been changed by the physics engine
|
||||||
|
if (updatedEntityCount > 0)
|
||||||
|
{
|
||||||
|
for (int ii = 0; ii < updatedEntityCount; ii++)
|
||||||
|
{
|
||||||
|
|
||||||
|
BulletXNA.EntityProperties entprop = m_updateArray[ii];
|
||||||
|
BSPhysObject pobj;
|
||||||
|
if (PhysObjects.TryGetValue(entprop.ID, out pobj))
|
||||||
|
{
|
||||||
|
EntityProperties prop = new EntityProperties()
|
||||||
|
{
|
||||||
|
Acceleration = new Vector3(entprop.Acceleration.X, entprop.Acceleration.Y, entprop.Acceleration.Z),
|
||||||
|
ID = entprop.ID,
|
||||||
|
Position = new Vector3(entprop.Position.X,entprop.Position.Y,entprop.Position.Z),
|
||||||
|
Rotation = new Quaternion(entprop.Rotation.X,entprop.Rotation.Y,entprop.Rotation.Z,entprop.Rotation.W),
|
||||||
|
RotationalVelocity = new Vector3(entprop.AngularVelocity.X,entprop.AngularVelocity.Y,entprop.AngularVelocity.Z),
|
||||||
|
Velocity = new Vector3(entprop.Velocity.X,entprop.Velocity.Y,entprop.Velocity.Z)
|
||||||
|
};
|
||||||
|
//m_log.Debug(pobj.Name + ":" + prop.ToString() + "\n");
|
||||||
|
pobj.UpdateProperties(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerPostStepEvent(timeStep);
|
||||||
|
|
||||||
|
// The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
|
||||||
|
// Only enable this in a limited test world with few objects.
|
||||||
|
// BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
|
||||||
|
|
||||||
|
// The physics engine returns the number of milliseconds it simulated this call.
|
||||||
|
// These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
|
||||||
|
// Multiply by 55 to give a nominal frame rate of 55.
|
||||||
|
return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Something has collided
|
||||||
|
private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration)
|
||||||
|
{
|
||||||
|
if (localID <= TerrainManager.HighestTerrainID)
|
||||||
|
{
|
||||||
|
return; // don't send collisions to the terrain
|
||||||
|
}
|
||||||
|
|
||||||
|
BSPhysObject collider;
|
||||||
|
if (!PhysObjects.TryGetValue(localID, out collider))
|
||||||
|
{
|
||||||
|
// If the object that is colliding cannot be found, just ignore the collision.
|
||||||
|
DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The terrain is not in the physical object list so 'collidee' can be null when Collide() is called.
|
||||||
|
BSPhysObject collidee = null;
|
||||||
|
PhysObjects.TryGetValue(collidingWith, out collidee);
|
||||||
|
|
||||||
|
// DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
|
||||||
|
|
||||||
|
if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
|
||||||
|
{
|
||||||
|
// If a collision was posted, remember to send it to the simulator
|
||||||
|
ObjectsWithCollisions.Add(collider);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Simulation
|
||||||
|
|
||||||
|
public override void GetResults() { }
|
||||||
|
|
||||||
|
#region Terrain
|
||||||
|
|
||||||
|
public override void SetTerrain(float[] heightMap) {
|
||||||
|
TerrainManager.SetTerrain(heightMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetWaterLevel(float baseheight)
|
||||||
|
{
|
||||||
|
SimpleWaterLevel = baseheight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DeleteTerrain()
|
||||||
|
{
|
||||||
|
// m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Although no one seems to check this, I do support combining.
|
||||||
|
public override bool SupportsCombining()
|
||||||
|
{
|
||||||
|
return TerrainManager.SupportsCombining();
|
||||||
|
}
|
||||||
|
// This call says I am a child to region zero in a mega-region. 'pScene' is that
|
||||||
|
// of region zero, 'offset' is my offset from regions zero's origin, and
|
||||||
|
// 'extents' is the largest XY that is handled in my region.
|
||||||
|
public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
|
||||||
|
{
|
||||||
|
TerrainManager.Combine(pScene, offset, extents);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unhook all the combining that I know about.
|
||||||
|
public override void UnCombine(PhysicsScene pScene)
|
||||||
|
{
|
||||||
|
TerrainManager.UnCombine(pScene);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Terrain
|
||||||
|
|
||||||
|
public override Dictionary<uint, float> GetTopColliders()
|
||||||
|
{
|
||||||
|
return new Dictionary<uint, float>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsThreaded { get { return false; } }
|
||||||
|
|
||||||
|
#region Taints
|
||||||
|
// The simulation execution order is:
|
||||||
|
// Simulate()
|
||||||
|
// DoOneTimeTaints
|
||||||
|
// TriggerPreStepEvent
|
||||||
|
// DoOneTimeTaints
|
||||||
|
// Step()
|
||||||
|
// ProcessAndForwardCollisions
|
||||||
|
// ProcessAndForwardPropertyUpdates
|
||||||
|
// TriggerPostStepEvent
|
||||||
|
|
||||||
|
// Calls to the PhysicsActors can't directly call into the physics engine
|
||||||
|
// because it might be busy. We delay changes to a known time.
|
||||||
|
// We rely on C#'s closure to save and restore the context for the delegate.
|
||||||
|
public void TaintedObject(String ident, TaintCallback callback)
|
||||||
|
{
|
||||||
|
if (!m_initialized) return;
|
||||||
|
|
||||||
|
lock (_taintLock)
|
||||||
|
{
|
||||||
|
_taintOperations.Add(new TaintCallbackEntry(ident, callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sometimes a potentially tainted operation can be used in and out of taint time.
|
||||||
|
// This routine executes the command immediately if in taint-time otherwise it is queued.
|
||||||
|
public void TaintedObject(bool inTaintTime, string ident, TaintCallback callback)
|
||||||
|
{
|
||||||
|
if (inTaintTime)
|
||||||
|
callback();
|
||||||
|
else
|
||||||
|
TaintedObject(ident, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerPreStepEvent(float timeStep)
|
||||||
|
{
|
||||||
|
PreStepAction actions = BeforeStep;
|
||||||
|
if (actions != null)
|
||||||
|
actions(timeStep);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerPostStepEvent(float timeStep)
|
||||||
|
{
|
||||||
|
PreStepAction actions = AfterStep;
|
||||||
|
if (actions != null)
|
||||||
|
actions(timeStep);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// When someone tries to change a property on a BSPrim or BSCharacter, the object queues
|
||||||
|
// a callback into itself to do the actual property change. That callback is called
|
||||||
|
// here just before the physics engine is called to step the simulation.
|
||||||
|
public void ProcessTaints()
|
||||||
|
{
|
||||||
|
ProcessRegularTaints();
|
||||||
|
ProcessPostTaintTaints();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessRegularTaints()
|
||||||
|
{
|
||||||
|
if (_taintOperations.Count > 0) // save allocating new list if there is nothing to process
|
||||||
|
{
|
||||||
|
// swizzle a new list into the list location so we can process what's there
|
||||||
|
List<TaintCallbackEntry> oldList;
|
||||||
|
lock (_taintLock)
|
||||||
|
{
|
||||||
|
oldList = _taintOperations;
|
||||||
|
_taintOperations = new List<TaintCallbackEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (TaintCallbackEntry tcbe in oldList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG
|
||||||
|
tcbe.callback();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oldList.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule an update to happen after all the regular taints are processed.
|
||||||
|
// Note that new requests for the same operation ("ident") for the same object ("ID")
|
||||||
|
// will replace any previous operation by the same object.
|
||||||
|
public void PostTaintObject(String ident, uint ID, TaintCallback callback)
|
||||||
|
{
|
||||||
|
string uniqueIdent = ident + "-" + ID.ToString();
|
||||||
|
lock (_taintLock)
|
||||||
|
{
|
||||||
|
_postTaintOperations[uniqueIdent] = new TaintCallbackEntry(uniqueIdent, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taints that happen after the normal taint processing but before the simulation step.
|
||||||
|
private void ProcessPostTaintTaints()
|
||||||
|
{
|
||||||
|
if (_postTaintOperations.Count > 0)
|
||||||
|
{
|
||||||
|
Dictionary<string, TaintCallbackEntry> oldList;
|
||||||
|
lock (_taintLock)
|
||||||
|
{
|
||||||
|
oldList = _postTaintOperations;
|
||||||
|
_postTaintOperations = new Dictionary<string, TaintCallbackEntry>();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string,TaintCallbackEntry> kvp in oldList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG
|
||||||
|
kvp.Value.callback();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oldList.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only used for debugging. Does not change state of anything so locking is not necessary.
|
||||||
|
public bool AssertInTaintTime(string whereFrom)
|
||||||
|
{
|
||||||
|
if (!InTaintTime)
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom);
|
||||||
|
m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom);
|
||||||
|
Util.PrintCallStack(); // Prints the stack into the DEBUG log file.
|
||||||
|
}
|
||||||
|
return InTaintTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Taints
|
||||||
|
|
||||||
|
#region INI and command line parameter processing
|
||||||
|
|
||||||
|
#region IPhysicsParameters
|
||||||
|
// Get the list of parameters this physics engine supports
|
||||||
|
public PhysParameterEntry[] GetParameterList()
|
||||||
|
{
|
||||||
|
BSParam.BuildParameterTable();
|
||||||
|
return BSParam.SettableParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set parameter on a specific or all instances.
|
||||||
|
// Return 'false' if not able to set the parameter.
|
||||||
|
// Setting the value in the m_params block will change the value the physics engine
|
||||||
|
// will use the next time since it's pinned and shared memory.
|
||||||
|
// Some of the values require calling into the physics engine to get the new
|
||||||
|
// value activated ('terrainFriction' for instance).
|
||||||
|
public bool SetPhysicsParameter(string parm, float val, uint localID)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
BSParam.ParameterDefn theParam;
|
||||||
|
if (BSParam.TryGetParameter(parm, out theParam))
|
||||||
|
{
|
||||||
|
theParam.setter(this, parm, localID, val);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update all the localIDs specified
|
||||||
|
// If the local ID is APPLY_TO_NONE, just change the default value
|
||||||
|
// If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs
|
||||||
|
// If the localID is a specific object, apply the parameter change to only that object
|
||||||
|
internal delegate void AssignVal(float x);
|
||||||
|
internal void UpdateParameterObject(AssignVal setDefault, string parm, uint localID, float val)
|
||||||
|
{
|
||||||
|
List<uint> objectIDs = new List<uint>();
|
||||||
|
switch (localID)
|
||||||
|
{
|
||||||
|
case PhysParameterEntry.APPLY_TO_NONE:
|
||||||
|
setDefault(val); // setting only the default value
|
||||||
|
// This will cause a call into the physical world if some operation is specified (SetOnObject).
|
||||||
|
objectIDs.Add(TERRAIN_ID);
|
||||||
|
TaintedUpdateParameter(parm, objectIDs, val);
|
||||||
|
break;
|
||||||
|
case PhysParameterEntry.APPLY_TO_ALL:
|
||||||
|
setDefault(val); // setting ALL also sets the default value
|
||||||
|
lock (PhysObjects) objectIDs = new List<uint>(PhysObjects.Keys);
|
||||||
|
TaintedUpdateParameter(parm, objectIDs, val);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// setting only one localID
|
||||||
|
objectIDs.Add(localID);
|
||||||
|
TaintedUpdateParameter(parm, objectIDs, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// schedule the actual updating of the paramter to when the phys engine is not busy
|
||||||
|
private void TaintedUpdateParameter(string parm, List<uint> lIDs, float val)
|
||||||
|
{
|
||||||
|
float xval = val;
|
||||||
|
List<uint> xlIDs = lIDs;
|
||||||
|
string xparm = parm;
|
||||||
|
TaintedObject("BSScene.UpdateParameterSet", delegate() {
|
||||||
|
BSParam.ParameterDefn thisParam;
|
||||||
|
if (BSParam.TryGetParameter(xparm, out thisParam))
|
||||||
|
{
|
||||||
|
if (thisParam.onObject != null)
|
||||||
|
{
|
||||||
|
foreach (uint lID in xlIDs)
|
||||||
|
{
|
||||||
|
BSPhysObject theObject = null;
|
||||||
|
PhysObjects.TryGetValue(lID, out theObject);
|
||||||
|
thisParam.onObject(this, theObject, xval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get parameter.
|
||||||
|
// Return 'false' if not able to get the parameter.
|
||||||
|
public bool GetPhysicsParameter(string parm, out float value)
|
||||||
|
{
|
||||||
|
float val = 0f;
|
||||||
|
bool ret = false;
|
||||||
|
BSParam.ParameterDefn theParam;
|
||||||
|
if (BSParam.TryGetParameter(parm, out theParam))
|
||||||
|
{
|
||||||
|
val = theParam.getter(this);
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
value = val;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IPhysicsParameters
|
||||||
|
|
||||||
|
#endregion Runtime settable parameters
|
||||||
|
|
||||||
|
// Invoke the detailed logger and output something if it's enabled.
|
||||||
|
public void DetailLog(string msg, params Object[] args)
|
||||||
|
{
|
||||||
|
PhysicsLogging.Write(msg, args);
|
||||||
|
// Add the Flush() if debugging crashes. Gets all the messages written out.
|
||||||
|
if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
|
||||||
|
}
|
||||||
|
// Used to fill in the LocalID when there isn't one. It's the correct number of characters.
|
||||||
|
public const string DetailLogZero = "0000000000";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,208 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
public abstract class BSShape
|
||||||
|
{
|
||||||
|
public Object ptr { get; set; }
|
||||||
|
public BSPhysicsShapeType type { get; set; }
|
||||||
|
public System.UInt64 key { get; set; }
|
||||||
|
public int referenceCount { get; set; }
|
||||||
|
public DateTime lastReferenced { get; set; }
|
||||||
|
|
||||||
|
public BSShape()
|
||||||
|
{
|
||||||
|
ptr = null;
|
||||||
|
type = BSPhysicsShapeType.SHAPE_UNKNOWN;
|
||||||
|
key = 0;
|
||||||
|
referenceCount = 0;
|
||||||
|
lastReferenced = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a reference to a physical shape. Create if it doesn't exist
|
||||||
|
public static BSShape GetShapeReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
|
||||||
|
{
|
||||||
|
BSShape ret = null;
|
||||||
|
|
||||||
|
if (prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
|
||||||
|
{
|
||||||
|
// an avatar capsule is close to a native shape (it is not shared)
|
||||||
|
ret = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_CAPSULE,
|
||||||
|
FixedShapeKey.KEY_CAPSULE);
|
||||||
|
physicsScene.DetailLog("{0},BSShape.GetShapeReference,avatarCapsule,shape={1}", prim.LocalID, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compound shapes are handled special as they are rebuilt from scratch.
|
||||||
|
// This isn't too great a hardship since most of the child shapes will already been created.
|
||||||
|
if (ret == null && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
|
||||||
|
{
|
||||||
|
// Getting a reference to a compound shape gets you the compound shape with the root prim shape added
|
||||||
|
ret = BSShapeCompound.GetReference(prim);
|
||||||
|
physicsScene.DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == null)
|
||||||
|
ret = GetShapeReferenceNonSpecial(physicsScene, forceRebuild, prim);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
public static BSShape GetShapeReferenceNonSpecial(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public static BSShape GetShapeReferenceNonNative(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release the use of a physical shape.
|
||||||
|
public abstract void Dereference(BSScene physicsScene);
|
||||||
|
|
||||||
|
// All shapes have a static call to get a reference to the physical shape
|
||||||
|
// protected abstract static BSShape GetReference();
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder buff = new StringBuilder();
|
||||||
|
buff.Append("<p=");
|
||||||
|
buff.Append(ptr.ToString());
|
||||||
|
buff.Append(",s=");
|
||||||
|
buff.Append(type.ToString());
|
||||||
|
buff.Append(",k=");
|
||||||
|
buff.Append(key.ToString("X"));
|
||||||
|
buff.Append(",c=");
|
||||||
|
buff.Append(referenceCount.ToString());
|
||||||
|
buff.Append(">");
|
||||||
|
return buff.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BSShapeNull : BSShape
|
||||||
|
{
|
||||||
|
public BSShapeNull() : base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public static BSShape GetReference() { return new BSShapeNull(); }
|
||||||
|
public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BSShapeNative : BSShape
|
||||||
|
{
|
||||||
|
private static string LogHeader = "[BULLETSIM SHAPE NATIVE]";
|
||||||
|
public BSShapeNative() : base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim,
|
||||||
|
BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
|
||||||
|
{
|
||||||
|
// Native shapes are not shared and are always built anew.
|
||||||
|
return new BSShapeNative(physicsScene, prim, shapeType, shapeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BSShapeNative(BSScene physicsScene, BSPhysObject prim,
|
||||||
|
BSPhysicsShapeType shapeType, FixedShapeKey shapeKey)
|
||||||
|
{
|
||||||
|
ShapeData nativeShapeData = new ShapeData();
|
||||||
|
nativeShapeData.Type = shapeType;
|
||||||
|
nativeShapeData.ID = prim.LocalID;
|
||||||
|
nativeShapeData.Scale = prim.Scale;
|
||||||
|
nativeShapeData.Size = prim.Scale;
|
||||||
|
nativeShapeData.MeshKey = (ulong)shapeKey;
|
||||||
|
nativeShapeData.HullKey = (ulong)shapeKey;
|
||||||
|
|
||||||
|
|
||||||
|
if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE)
|
||||||
|
{
|
||||||
|
ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale);
|
||||||
|
physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ptr = BulletSimAPI.BuildNativeShape2(physicsScene.World.ptr, nativeShapeData);
|
||||||
|
}
|
||||||
|
if (ptr == null)
|
||||||
|
{
|
||||||
|
physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
|
||||||
|
LogHeader, prim.LocalID, shapeType);
|
||||||
|
}
|
||||||
|
type = shapeType;
|
||||||
|
key = (UInt64)shapeKey;
|
||||||
|
}
|
||||||
|
// Make this reference to the physical shape go away since native shapes are not shared.
|
||||||
|
public override void Dereference(BSScene physicsScene)
|
||||||
|
{
|
||||||
|
// Native shapes are not tracked and are released immediately
|
||||||
|
physicsScene.DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this);
|
||||||
|
BulletSimAPI.DeleteCollisionShape2(physicsScene.World.ptr, ptr);
|
||||||
|
ptr = null;
|
||||||
|
// Garbage collection will free up this instance.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BSShapeMesh : BSShape
|
||||||
|
{
|
||||||
|
private static string LogHeader = "[BULLETSIM SHAPE MESH]";
|
||||||
|
private static Dictionary<System.UInt64, BSShapeMesh> Meshes = new Dictionary<System.UInt64, BSShapeMesh>();
|
||||||
|
|
||||||
|
public BSShapeMesh() : base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public static BSShape GetReference() { return new BSShapeNull(); }
|
||||||
|
public override void Dereference(BSScene physicsScene) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BSShapeHull : BSShape
|
||||||
|
{
|
||||||
|
private static string LogHeader = "[BULLETSIM SHAPE HULL]";
|
||||||
|
private static Dictionary<System.UInt64, BSShapeHull> Hulls = new Dictionary<System.UInt64, BSShapeHull>();
|
||||||
|
|
||||||
|
public BSShapeHull() : base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public static BSShape GetReference() { return new BSShapeNull(); }
|
||||||
|
public override void Dereference(BSScene physicsScene) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BSShapeCompound : BSShape
|
||||||
|
{
|
||||||
|
private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]";
|
||||||
|
public BSShapeCompound() : base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public static BSShape GetReference(BSPhysObject prim)
|
||||||
|
{
|
||||||
|
return new BSShapeNull();
|
||||||
|
}
|
||||||
|
public override void Dereference(BSScene physicsScene) { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Region.Framework;
|
||||||
|
using OpenSim.Region.CoreModules;
|
||||||
|
using OpenSim.Region.Physics.Manager;
|
||||||
|
|
||||||
|
using Nini.Config;
|
||||||
|
using log4net;
|
||||||
|
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
public sealed class BSTerrainHeightmap : BSTerrainPhys
|
||||||
|
{
|
||||||
|
static string LogHeader = "[BULLETSIM TERRAIN HEIGHTMAP]";
|
||||||
|
|
||||||
|
BulletHeightMapInfo m_mapInfo = null;
|
||||||
|
|
||||||
|
// Constructor to build a default, flat heightmap terrain.
|
||||||
|
public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
|
||||||
|
: base(physicsScene, regionBase, id)
|
||||||
|
{
|
||||||
|
Vector3 minTerrainCoords = new Vector3(0f, 0f, BSTerrainManager.HEIGHT_INITIALIZATION - BSTerrainManager.HEIGHT_EQUAL_FUDGE);
|
||||||
|
Vector3 maxTerrainCoords = new Vector3(regionSize.X, regionSize.Y, BSTerrainManager.HEIGHT_INITIALIZATION);
|
||||||
|
int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y;
|
||||||
|
float[] initialMap = new float[totalHeights];
|
||||||
|
for (int ii = 0; ii < totalHeights; ii++)
|
||||||
|
{
|
||||||
|
initialMap[ii] = BSTerrainManager.HEIGHT_INITIALIZATION;
|
||||||
|
}
|
||||||
|
m_mapInfo = new BulletHeightMapInfo(id, initialMap, null);
|
||||||
|
m_mapInfo.minCoords = minTerrainCoords;
|
||||||
|
m_mapInfo.maxCoords = maxTerrainCoords;
|
||||||
|
m_mapInfo.terrainRegionBase = TerrainBase;
|
||||||
|
// Don't have to free any previous since we just got here.
|
||||||
|
BuildHeightmapTerrain();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This minCoords and maxCoords passed in give the size of the terrain (min and max Z
|
||||||
|
// are the high and low points of the heightmap).
|
||||||
|
public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
|
||||||
|
Vector3 minCoords, Vector3 maxCoords)
|
||||||
|
: base(physicsScene, regionBase, id)
|
||||||
|
{
|
||||||
|
m_mapInfo = new BulletHeightMapInfo(id, initialMap, null);
|
||||||
|
m_mapInfo.minCoords = minCoords;
|
||||||
|
m_mapInfo.maxCoords = maxCoords;
|
||||||
|
m_mapInfo.minZ = minCoords.Z;
|
||||||
|
m_mapInfo.maxZ = maxCoords.Z;
|
||||||
|
m_mapInfo.terrainRegionBase = TerrainBase;
|
||||||
|
|
||||||
|
// Don't have to free any previous since we just got here.
|
||||||
|
BuildHeightmapTerrain();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
ReleaseHeightMapTerrain();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using the information in m_mapInfo, create the physical representation of the heightmap.
|
||||||
|
private void BuildHeightmapTerrain()
|
||||||
|
{
|
||||||
|
m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID,
|
||||||
|
m_mapInfo.minCoords, m_mapInfo.maxCoords,
|
||||||
|
m_mapInfo.heightMap, BSParam.TerrainCollisionMargin);
|
||||||
|
|
||||||
|
// Create the terrain shape from the mapInfo
|
||||||
|
m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr),
|
||||||
|
BSPhysicsShapeType.SHAPE_TERRAIN);
|
||||||
|
|
||||||
|
// The terrain object initial position is at the center of the object
|
||||||
|
Vector3 centerPos;
|
||||||
|
centerPos.X = m_mapInfo.minCoords.X + (m_mapInfo.sizeX / 2f);
|
||||||
|
centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f);
|
||||||
|
centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f + 0.5f);
|
||||||
|
|
||||||
|
m_mapInfo.terrainBody = new BulletBody(m_mapInfo.ID,
|
||||||
|
BulletSimAPI.CreateBodyWithDefaultMotionState2(m_mapInfo.terrainShape.ptr,
|
||||||
|
m_mapInfo.ID, centerPos, Quaternion.Identity));
|
||||||
|
|
||||||
|
// Set current terrain attributes
|
||||||
|
BulletSimAPI.SetFriction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainFriction);
|
||||||
|
BulletSimAPI.SetHitFraction2(m_mapInfo.terrainBody.ptr, BSParam.TerrainHitFraction);
|
||||||
|
BulletSimAPI.SetRestitution2(m_mapInfo.terrainBody.ptr, BSParam.TerrainRestitution);
|
||||||
|
BulletSimAPI.SetCollisionFlags2(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
|
||||||
|
|
||||||
|
// Return the new terrain to the world of physical objects
|
||||||
|
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr, centerPos, Quaternion.Identity);
|
||||||
|
|
||||||
|
// redo its bounding box now that it is in the world
|
||||||
|
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
|
||||||
|
|
||||||
|
m_mapInfo.terrainBody.collisionType = CollisionType.Terrain;
|
||||||
|
m_mapInfo.terrainBody.ApplyCollisionMask();
|
||||||
|
|
||||||
|
// Make it so the terrain will not move or be considered for movement.
|
||||||
|
BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is information in m_mapInfo pointing to physical structures, release same.
|
||||||
|
private void ReleaseHeightMapTerrain()
|
||||||
|
{
|
||||||
|
if (m_mapInfo != null)
|
||||||
|
{
|
||||||
|
if (m_mapInfo.terrainBody.HasPhysicalBody)
|
||||||
|
{
|
||||||
|
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
|
||||||
|
// Frees both the body and the shape.
|
||||||
|
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
|
||||||
|
BulletSimAPI.ReleaseHeightMapInfo2(m_mapInfo.Ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_mapInfo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The passed position is relative to the base of the region.
|
||||||
|
public override float GetTerrainHeightAtXYZ(Vector3 pos)
|
||||||
|
{
|
||||||
|
float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||||
|
|
||||||
|
int mapIndex = (int)pos.Y * (int)m_mapInfo.sizeY + (int)pos.X;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ret = m_mapInfo.heightMap[mapIndex];
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
|
||||||
|
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
|
||||||
|
LogHeader, m_mapInfo.terrainRegionBase, pos);
|
||||||
|
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The passed position is relative to the base of the region.
|
||||||
|
public override float GetWaterLevelAtXYZ(Vector3 pos)
|
||||||
|
{
|
||||||
|
return PhysicsScene.SimpleWaterLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,460 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Region.Framework;
|
||||||
|
using OpenSim.Region.CoreModules;
|
||||||
|
using OpenSim.Region.Physics.Manager;
|
||||||
|
|
||||||
|
using Nini.Config;
|
||||||
|
using log4net;
|
||||||
|
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
// The physical implementation of the terrain is wrapped in this class.
|
||||||
|
public abstract class BSTerrainPhys : IDisposable
|
||||||
|
{
|
||||||
|
public enum TerrainImplementation
|
||||||
|
{
|
||||||
|
Heightmap = 0,
|
||||||
|
Mesh = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public BSScene PhysicsScene { get; private set; }
|
||||||
|
// Base of the region in world coordinates. Coordinates inside the region are relative to this.
|
||||||
|
public Vector3 TerrainBase { get; private set; }
|
||||||
|
public uint ID { get; private set; }
|
||||||
|
|
||||||
|
public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id)
|
||||||
|
{
|
||||||
|
PhysicsScene = physicsScene;
|
||||||
|
TerrainBase = regionBase;
|
||||||
|
ID = id;
|
||||||
|
}
|
||||||
|
public abstract void Dispose();
|
||||||
|
public abstract float GetTerrainHeightAtXYZ(Vector3 pos);
|
||||||
|
public abstract float GetWaterLevelAtXYZ(Vector3 pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================================================================
|
||||||
|
public sealed class BSTerrainManager : IDisposable
|
||||||
|
{
|
||||||
|
static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
|
||||||
|
|
||||||
|
// These height values are fractional so the odd values will be
|
||||||
|
// noticable when debugging.
|
||||||
|
public const float HEIGHT_INITIALIZATION = 24.987f;
|
||||||
|
public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f;
|
||||||
|
public const float HEIGHT_GETHEIGHT_RET = 24.765f;
|
||||||
|
public const float WATER_HEIGHT_GETHEIGHT_RET = 19.998f;
|
||||||
|
|
||||||
|
// If the min and max height are equal, we reduce the min by this
|
||||||
|
// amount to make sure that a bounding box is built for the terrain.
|
||||||
|
public const float HEIGHT_EQUAL_FUDGE = 0.2f;
|
||||||
|
|
||||||
|
// Until the whole simulator is changed to pass us the region size, we rely on constants.
|
||||||
|
public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
|
||||||
|
|
||||||
|
// The scene that I am part of
|
||||||
|
private BSScene PhysicsScene { get; set; }
|
||||||
|
|
||||||
|
// The ground plane created to keep thing from falling to infinity.
|
||||||
|
private BulletBody m_groundPlane;
|
||||||
|
|
||||||
|
// If doing mega-regions, if we're region zero we will be managing multiple
|
||||||
|
// region terrains since region zero does the physics for the whole mega-region.
|
||||||
|
private Dictionary<Vector3, BSTerrainPhys> m_terrains;
|
||||||
|
|
||||||
|
// Flags used to know when to recalculate the height.
|
||||||
|
private bool m_terrainModified = false;
|
||||||
|
|
||||||
|
// If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount.
|
||||||
|
// This is incremented before assigning to new region so it is the last ID allocated.
|
||||||
|
private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1;
|
||||||
|
public uint HighestTerrainID { get {return m_terrainCount; } }
|
||||||
|
|
||||||
|
// If doing mega-regions, this holds our offset from region zero of
|
||||||
|
// the mega-regions. "parentScene" points to the PhysicsScene of region zero.
|
||||||
|
private Vector3 m_worldOffset;
|
||||||
|
// If the parent region (region 0), this is the extent of the combined regions
|
||||||
|
// relative to the origin of region zero
|
||||||
|
private Vector3 m_worldMax;
|
||||||
|
private PhysicsScene MegaRegionParentPhysicsScene { get; set; }
|
||||||
|
|
||||||
|
public BSTerrainManager(BSScene physicsScene)
|
||||||
|
{
|
||||||
|
PhysicsScene = physicsScene;
|
||||||
|
m_terrains = new Dictionary<Vector3,BSTerrainPhys>();
|
||||||
|
|
||||||
|
// Assume one region of default size
|
||||||
|
m_worldOffset = Vector3.Zero;
|
||||||
|
m_worldMax = new Vector3(DefaultRegionSize);
|
||||||
|
MegaRegionParentPhysicsScene = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
ReleaseGroundPlaneAndTerrain();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the initial instance of terrain and the underlying ground plane.
|
||||||
|
// This is called from the initialization routine so we presume it is
|
||||||
|
// safe to call Bullet in real time. We hope no one is moving prims around yet.
|
||||||
|
public void CreateInitialGroundPlaneAndTerrain()
|
||||||
|
{
|
||||||
|
// The ground plane is here to catch things that are trying to drop to negative infinity
|
||||||
|
BulletShape groundPlaneShape = new BulletShape(
|
||||||
|
BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f,
|
||||||
|
BSParam.TerrainCollisionMargin),
|
||||||
|
BSPhysicsShapeType.SHAPE_GROUNDPLANE);
|
||||||
|
m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
|
||||||
|
BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
|
||||||
|
Vector3.Zero, Quaternion.Identity));
|
||||||
|
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr);
|
||||||
|
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_groundPlane.ptr);
|
||||||
|
// Ground plane does not move
|
||||||
|
BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
|
||||||
|
// Everything collides with the ground plane.
|
||||||
|
m_groundPlane.collisionType = CollisionType.Groundplane;
|
||||||
|
m_groundPlane.ApplyCollisionMask();
|
||||||
|
|
||||||
|
// Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
|
||||||
|
BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
|
||||||
|
m_terrains.Add(Vector3.Zero, initialTerrain);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release all the terrain structures we might have allocated
|
||||||
|
public void ReleaseGroundPlaneAndTerrain()
|
||||||
|
{
|
||||||
|
if (m_groundPlane.HasPhysicalBody)
|
||||||
|
{
|
||||||
|
if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr))
|
||||||
|
{
|
||||||
|
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr);
|
||||||
|
}
|
||||||
|
m_groundPlane.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseTerrain();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release all the terrain we have allocated
|
||||||
|
public void ReleaseTerrain()
|
||||||
|
{
|
||||||
|
lock (m_terrains)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
|
||||||
|
{
|
||||||
|
kvp.Value.Dispose();
|
||||||
|
}
|
||||||
|
m_terrains.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The simulator wants to set a new heightmap for the terrain.
|
||||||
|
public void SetTerrain(float[] heightMap) {
|
||||||
|
float[] localHeightMap = heightMap;
|
||||||
|
// If there are multiple requests for changes to the same terrain between ticks,
|
||||||
|
// only do that last one.
|
||||||
|
PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate()
|
||||||
|
{
|
||||||
|
if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
|
||||||
|
{
|
||||||
|
// If a child of a mega-region, we shouldn't have any terrain allocated for us
|
||||||
|
ReleaseGroundPlaneAndTerrain();
|
||||||
|
// If doing the mega-prim stuff and we are the child of the zero region,
|
||||||
|
// the terrain is added to our parent
|
||||||
|
if (MegaRegionParentPhysicsScene is BSScene)
|
||||||
|
{
|
||||||
|
DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
|
||||||
|
BSScene.DetailLogZero, m_worldOffset, m_worldMax);
|
||||||
|
((BSScene)MegaRegionParentPhysicsScene).TerrainManager.UpdateTerrain(
|
||||||
|
BSScene.CHILDTERRAIN_ID, localHeightMap,
|
||||||
|
m_worldOffset, m_worldOffset + DefaultRegionSize, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If not doing the mega-prim thing, just change the terrain
|
||||||
|
DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
|
||||||
|
|
||||||
|
UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap,
|
||||||
|
m_worldOffset, m_worldOffset + DefaultRegionSize, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If called with no mapInfo for the terrain, this will create a new mapInfo and terrain
|
||||||
|
// based on the passed information. The 'id' should be either the terrain id or
|
||||||
|
// BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used.
|
||||||
|
// The latter feature is for creating child terrains for mega-regions.
|
||||||
|
// If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new
|
||||||
|
// terrain shape is created and added to the body.
|
||||||
|
// This call is most often used to update the heightMap and parameters of the terrain.
|
||||||
|
// (The above does suggest that some simplification/refactoring is in order.)
|
||||||
|
// Called during taint-time.
|
||||||
|
private void UpdateTerrain(uint id, float[] heightMap,
|
||||||
|
Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
|
||||||
|
{
|
||||||
|
DetailLog("{0},BSTerrainManager.UpdateTerrain,call,minC={1},maxC={2},inTaintTime={3}",
|
||||||
|
BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime);
|
||||||
|
|
||||||
|
// Find high and low points of passed heightmap.
|
||||||
|
// The min and max passed in is usually the area objects can be in (maximum
|
||||||
|
// object height, for instance). The terrain wants the bounding box for the
|
||||||
|
// terrain so replace passed min and max Z with the actual terrain min/max Z.
|
||||||
|
float minZ = float.MaxValue;
|
||||||
|
float maxZ = float.MinValue;
|
||||||
|
foreach (float height in heightMap)
|
||||||
|
{
|
||||||
|
if (height < minZ) minZ = height;
|
||||||
|
if (height > maxZ) maxZ = height;
|
||||||
|
}
|
||||||
|
if (minZ == maxZ)
|
||||||
|
{
|
||||||
|
// If min and max are the same, reduce min a little bit so a good bounding box is created.
|
||||||
|
minZ -= BSTerrainManager.HEIGHT_EQUAL_FUDGE;
|
||||||
|
}
|
||||||
|
minCoords.Z = minZ;
|
||||||
|
maxCoords.Z = maxZ;
|
||||||
|
|
||||||
|
Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
|
||||||
|
|
||||||
|
lock (m_terrains)
|
||||||
|
{
|
||||||
|
BSTerrainPhys terrainPhys;
|
||||||
|
if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
|
||||||
|
{
|
||||||
|
// There is already a terrain in this spot. Free the old and build the new.
|
||||||
|
DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
|
||||||
|
BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
|
||||||
|
|
||||||
|
// Remove old terrain from the collection
|
||||||
|
m_terrains.Remove(terrainRegionBase);
|
||||||
|
// Release any physical memory it may be using.
|
||||||
|
terrainPhys.Dispose();
|
||||||
|
|
||||||
|
if (MegaRegionParentPhysicsScene == null)
|
||||||
|
{
|
||||||
|
BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
|
||||||
|
m_terrains.Add(terrainRegionBase, newTerrainPhys);
|
||||||
|
|
||||||
|
m_terrainModified = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// It's possible that Combine() was called after this code was queued.
|
||||||
|
// If we are a child of combined regions, we don't create any terrain for us.
|
||||||
|
DetailLog("{0},BSTerrainManager.UpdateTerrain:AmACombineChild,taint", BSScene.DetailLogZero);
|
||||||
|
|
||||||
|
// Get rid of any terrain that may have been allocated for us.
|
||||||
|
ReleaseGroundPlaneAndTerrain();
|
||||||
|
|
||||||
|
// I hate doing this, but just bail
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We don't know about this terrain so either we are creating a new terrain or
|
||||||
|
// our mega-prim child is giving us a new terrain to add to the phys world
|
||||||
|
|
||||||
|
// if this is a child terrain, calculate a unique terrain id
|
||||||
|
uint newTerrainID = id;
|
||||||
|
if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
|
||||||
|
newTerrainID = ++m_terrainCount;
|
||||||
|
|
||||||
|
DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
|
||||||
|
BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
|
||||||
|
BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
|
||||||
|
m_terrains.Add(terrainRegionBase, newTerrainPhys);
|
||||||
|
|
||||||
|
m_terrainModified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: redo terrain implementation selection to allow other base types than heightMap.
|
||||||
|
private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords)
|
||||||
|
{
|
||||||
|
PhysicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}",
|
||||||
|
LogHeader, PhysicsScene.RegionName, terrainRegionBase,
|
||||||
|
(BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation);
|
||||||
|
BSTerrainPhys newTerrainPhys = null;
|
||||||
|
switch ((int)BSParam.TerrainImplementation)
|
||||||
|
{
|
||||||
|
case (int)BSTerrainPhys.TerrainImplementation.Heightmap:
|
||||||
|
newTerrainPhys = new BSTerrainHeightmap(PhysicsScene, terrainRegionBase, id,
|
||||||
|
heightMap, minCoords, maxCoords);
|
||||||
|
break;
|
||||||
|
case (int)BSTerrainPhys.TerrainImplementation.Mesh:
|
||||||
|
newTerrainPhys = new BSTerrainMesh(PhysicsScene, terrainRegionBase, id,
|
||||||
|
heightMap, minCoords, maxCoords);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PhysicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}",
|
||||||
|
LogHeader,
|
||||||
|
(int)BSParam.TerrainImplementation,
|
||||||
|
BSParam.TerrainImplementation,
|
||||||
|
PhysicsScene.RegionName, terrainRegionBase);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return newTerrainPhys;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return 'true' of this position is somewhere in known physical terrain space
|
||||||
|
public bool IsWithinKnownTerrain(Vector3 pos)
|
||||||
|
{
|
||||||
|
Vector3 terrainBaseXYZ;
|
||||||
|
BSTerrainPhys physTerrain;
|
||||||
|
return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given an X and Y, find the height of the terrain.
|
||||||
|
// Since we could be handling multiple terrains for a mega-region,
|
||||||
|
// the base of the region is calcuated assuming all regions are
|
||||||
|
// the same size and that is the default.
|
||||||
|
// Once the heightMapInfo is found, we have all the information to
|
||||||
|
// compute the offset into the array.
|
||||||
|
private float lastHeightTX = 999999f;
|
||||||
|
private float lastHeightTY = 999999f;
|
||||||
|
private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
|
||||||
|
public float GetTerrainHeightAtXYZ(Vector3 pos)
|
||||||
|
{
|
||||||
|
float tX = pos.X;
|
||||||
|
float tY = pos.Y;
|
||||||
|
// You'd be surprized at the number of times this routine is called
|
||||||
|
// with the same parameters as last time.
|
||||||
|
if (!m_terrainModified && (lastHeightTX == tX) && (lastHeightTY == tY))
|
||||||
|
return lastHeight;
|
||||||
|
m_terrainModified = false;
|
||||||
|
|
||||||
|
lastHeightTX = tX;
|
||||||
|
lastHeightTY = tY;
|
||||||
|
float ret = HEIGHT_GETHEIGHT_RET;
|
||||||
|
|
||||||
|
Vector3 terrainBaseXYZ;
|
||||||
|
BSTerrainPhys physTerrain;
|
||||||
|
if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
|
||||||
|
{
|
||||||
|
ret = physTerrain.GetTerrainHeightAtXYZ(pos - terrainBaseXYZ);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
|
||||||
|
LogHeader, PhysicsScene.RegionName, tX, tY);
|
||||||
|
DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}",
|
||||||
|
BSScene.DetailLogZero, pos, terrainBaseXYZ);
|
||||||
|
}
|
||||||
|
lastHeight = ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetWaterLevelAtXYZ(Vector3 pos)
|
||||||
|
{
|
||||||
|
float ret = WATER_HEIGHT_GETHEIGHT_RET;
|
||||||
|
|
||||||
|
Vector3 terrainBaseXYZ;
|
||||||
|
BSTerrainPhys physTerrain;
|
||||||
|
if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
|
||||||
|
{
|
||||||
|
ret = physTerrain.GetWaterLevelAtXYZ(pos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}",
|
||||||
|
LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given an address, return 'true' of there is a description of that terrain and output
|
||||||
|
// the descriptor class and the 'base' fo the addresses therein.
|
||||||
|
private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase)
|
||||||
|
{
|
||||||
|
int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
|
||||||
|
int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
|
||||||
|
Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
|
||||||
|
|
||||||
|
BSTerrainPhys physTerrain = null;
|
||||||
|
lock (m_terrains)
|
||||||
|
{
|
||||||
|
m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain);
|
||||||
|
}
|
||||||
|
outTerrainBase = terrainBaseXYZ;
|
||||||
|
outPhysTerrain = physTerrain;
|
||||||
|
return (physTerrain != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Although no one seems to check this, I do support combining.
|
||||||
|
public bool SupportsCombining()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This routine is called two ways:
|
||||||
|
// One with 'offset' and 'pScene' zero and null but 'extents' giving the maximum
|
||||||
|
// extent of the combined regions. This is to inform the parent of the size
|
||||||
|
// of the combined regions.
|
||||||
|
// and one with 'offset' as the offset of the child region to the base region,
|
||||||
|
// 'pScene' pointing to the parent and 'extents' of zero. This informs the
|
||||||
|
// child of its relative base and new parent.
|
||||||
|
public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents)
|
||||||
|
{
|
||||||
|
m_worldOffset = offset;
|
||||||
|
m_worldMax = extents;
|
||||||
|
MegaRegionParentPhysicsScene = pScene;
|
||||||
|
if (pScene != null)
|
||||||
|
{
|
||||||
|
// We are a child.
|
||||||
|
// We want m_worldMax to be the highest coordinate of our piece of terrain.
|
||||||
|
m_worldMax = offset + DefaultRegionSize;
|
||||||
|
}
|
||||||
|
DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}",
|
||||||
|
BSScene.DetailLogZero, offset, extents, m_worldOffset, m_worldMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unhook all the combining that I know about.
|
||||||
|
public void UnCombine(PhysicsScene pScene)
|
||||||
|
{
|
||||||
|
// Just like ODE, we don't do anything yet.
|
||||||
|
DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void DetailLog(string msg, params Object[] args)
|
||||||
|
{
|
||||||
|
PhysicsScene.PhysicsLogging.Write(msg, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,267 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Region.Framework;
|
||||||
|
using OpenSim.Region.CoreModules;
|
||||||
|
using OpenSim.Region.Physics.Manager;
|
||||||
|
|
||||||
|
using Nini.Config;
|
||||||
|
using log4net;
|
||||||
|
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
public sealed class BSTerrainMesh : BSTerrainPhys
|
||||||
|
{
|
||||||
|
static string LogHeader = "[BULLETSIM TERRAIN MESH]";
|
||||||
|
|
||||||
|
private float[] m_savedHeightMap;
|
||||||
|
int m_sizeX;
|
||||||
|
int m_sizeY;
|
||||||
|
|
||||||
|
BulletShape m_terrainShape;
|
||||||
|
BulletBody m_terrainBody;
|
||||||
|
|
||||||
|
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize)
|
||||||
|
: base(physicsScene, regionBase, id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id /* parameters for making mesh */)
|
||||||
|
: base(physicsScene, regionBase, id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create terrain mesh from a heightmap.
|
||||||
|
public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
|
||||||
|
Vector3 minCoords, Vector3 maxCoords)
|
||||||
|
: base(physicsScene, regionBase, id)
|
||||||
|
{
|
||||||
|
int indicesCount;
|
||||||
|
int[] indices;
|
||||||
|
int verticesCount;
|
||||||
|
float[] vertices;
|
||||||
|
|
||||||
|
m_savedHeightMap = initialMap;
|
||||||
|
|
||||||
|
m_sizeX = (int)(maxCoords.X - minCoords.X);
|
||||||
|
m_sizeY = (int)(maxCoords.Y - minCoords.Y);
|
||||||
|
|
||||||
|
if (!BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene, initialMap,
|
||||||
|
m_sizeX, m_sizeY,
|
||||||
|
(float)m_sizeX, (float)m_sizeY,
|
||||||
|
Vector3.Zero, 1.0f,
|
||||||
|
out indicesCount, out indices, out verticesCount, out vertices))
|
||||||
|
{
|
||||||
|
// DISASTER!!
|
||||||
|
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap", ID);
|
||||||
|
PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase);
|
||||||
|
// Something is very messed up and a crash is in our future.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,indices={1},indSz={2},vertices={3},vertSz={4}",
|
||||||
|
ID, indicesCount, indices.Length, verticesCount, vertices.Length);
|
||||||
|
|
||||||
|
m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
|
||||||
|
indicesCount, indices, verticesCount, vertices),
|
||||||
|
BSPhysicsShapeType.SHAPE_MESH);
|
||||||
|
if (!m_terrainShape.HasPhysicalShape)
|
||||||
|
{
|
||||||
|
// DISASTER!!
|
||||||
|
PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID);
|
||||||
|
physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
|
||||||
|
// Something is very messed up and a crash is in our future.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 pos = regionBase;
|
||||||
|
Quaternion rot = Quaternion.Identity;
|
||||||
|
|
||||||
|
m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot));
|
||||||
|
if (!m_terrainBody.HasPhysicalBody)
|
||||||
|
{
|
||||||
|
// DISASTER!!
|
||||||
|
physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
|
||||||
|
// Something is very messed up and a crash is in our future.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set current terrain attributes
|
||||||
|
BulletSimAPI.SetFriction2(m_terrainBody.ptr, BSParam.TerrainFriction);
|
||||||
|
BulletSimAPI.SetHitFraction2(m_terrainBody.ptr, BSParam.TerrainHitFraction);
|
||||||
|
BulletSimAPI.SetRestitution2(m_terrainBody.ptr, BSParam.TerrainRestitution);
|
||||||
|
BulletSimAPI.SetCollisionFlags2(m_terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
|
||||||
|
|
||||||
|
// Static objects are not very massive.
|
||||||
|
BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);
|
||||||
|
|
||||||
|
// Put the new terrain to the world of physical objects
|
||||||
|
BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr, pos, rot);
|
||||||
|
|
||||||
|
// Redo its bounding box now that it is in the world
|
||||||
|
BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);
|
||||||
|
|
||||||
|
m_terrainBody.collisionType = CollisionType.Terrain;
|
||||||
|
m_terrainBody.ApplyCollisionMask();
|
||||||
|
|
||||||
|
// Make it so the terrain will not move or be considered for movement.
|
||||||
|
BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
if (m_terrainBody.HasPhysicalBody)
|
||||||
|
{
|
||||||
|
BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
|
||||||
|
// Frees both the body and the shape.
|
||||||
|
BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_terrainBody.ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override float GetTerrainHeightAtXYZ(Vector3 pos)
|
||||||
|
{
|
||||||
|
// For the moment use the saved heightmap to get the terrain height.
|
||||||
|
// TODO: raycast downward to find the true terrain below the position.
|
||||||
|
float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||||
|
|
||||||
|
int mapIndex = (int)pos.Y * m_sizeY + (int)pos.X;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ret = m_savedHeightMap[mapIndex];
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Sometimes they give us wonky values of X and Y. Give a warning and return something.
|
||||||
|
PhysicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}",
|
||||||
|
LogHeader, TerrainBase, pos);
|
||||||
|
ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The passed position is relative to the base of the region.
|
||||||
|
public override float GetWaterLevelAtXYZ(Vector3 pos)
|
||||||
|
{
|
||||||
|
return PhysicsScene.SimpleWaterLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
|
||||||
|
// Return 'true' if successfully created.
|
||||||
|
public static bool ConvertHeightmapToMesh(
|
||||||
|
BSScene physicsScene,
|
||||||
|
float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap
|
||||||
|
float extentX, float extentY, // zero based range for output vertices
|
||||||
|
Vector3 extentBase, // base to be added to all vertices
|
||||||
|
float magnification, // number of vertices to create between heightMap coords
|
||||||
|
out int indicesCountO, out int[] indicesO,
|
||||||
|
out int verticesCountO, out float[] verticesO)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
int indicesCount = 0;
|
||||||
|
int verticesCount = 0;
|
||||||
|
int[] indices = new int[0];
|
||||||
|
float[] vertices = new float[0];
|
||||||
|
|
||||||
|
// Simple mesh creation which assumes magnification == 1.
|
||||||
|
// TODO: do a more general solution that scales, adds new vertices and smoothes the result.
|
||||||
|
|
||||||
|
// Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop
|
||||||
|
// from zero to <= sizeX). The triangle indices are then generated as two triangles
|
||||||
|
// per heightmap point. There are sizeX by sizeY of these squares. The extra row and
|
||||||
|
// column of vertices are used to complete the triangles of the last row and column
|
||||||
|
// of the heightmap.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// One vertice per heightmap value plus the vertices off the top and bottom edge.
|
||||||
|
int totalVertices = (sizeX + 1) * (sizeY + 1);
|
||||||
|
vertices = new float[totalVertices * 3];
|
||||||
|
int totalIndices = sizeX * sizeY * 6;
|
||||||
|
indices = new int[totalIndices];
|
||||||
|
|
||||||
|
float magX = (float)sizeX / extentX;
|
||||||
|
float magY = (float)sizeY / extentY;
|
||||||
|
physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}",
|
||||||
|
BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY);
|
||||||
|
float minHeight = float.MaxValue;
|
||||||
|
// Note that sizeX+1 vertices are created since there is land between this and the next region.
|
||||||
|
for (int yy = 0; yy <= sizeY; yy++)
|
||||||
|
{
|
||||||
|
for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
|
||||||
|
{
|
||||||
|
int offset = yy * sizeX + xx;
|
||||||
|
// Extend the height with the height from the last row or column
|
||||||
|
if (yy == sizeY) offset -= sizeX;
|
||||||
|
if (xx == sizeX) offset -= 1;
|
||||||
|
float height = heightMap[offset];
|
||||||
|
minHeight = Math.Min(minHeight, height);
|
||||||
|
vertices[verticesCount + 0] = (float)xx * magX + extentBase.X;
|
||||||
|
vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y;
|
||||||
|
vertices[verticesCount + 2] = height + extentBase.Z;
|
||||||
|
verticesCount += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
verticesCount = verticesCount / 3;
|
||||||
|
|
||||||
|
for (int yy = 0; yy < sizeY; yy++)
|
||||||
|
{
|
||||||
|
for (int xx = 0; xx < sizeX; xx++)
|
||||||
|
{
|
||||||
|
int offset = yy * (sizeX + 1) + xx;
|
||||||
|
// Each vertices is presumed to be the upper left corner of a box of two triangles
|
||||||
|
indices[indicesCount + 0] = offset;
|
||||||
|
indices[indicesCount + 1] = offset + 1;
|
||||||
|
indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column
|
||||||
|
indices[indicesCount + 3] = offset + 1;
|
||||||
|
indices[indicesCount + 4] = offset + sizeX + 2;
|
||||||
|
indices[indicesCount + 5] = offset + sizeX + 1;
|
||||||
|
indicesCount += 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}",
|
||||||
|
LogHeader, physicsScene.RegionName, extentBase, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
indicesCountO = indicesCount;
|
||||||
|
indicesO = indices;
|
||||||
|
verticesCountO = verticesCount;
|
||||||
|
verticesO = vertices;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,280 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyrightD
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using OMV = OpenMetaverse;
|
||||||
|
|
||||||
|
namespace OpenSim.Region.Physics.BulletSNPlugin
|
||||||
|
{
|
||||||
|
// Classes to allow some type checking for the API
|
||||||
|
// These hold pointers to allocated objects in the unmanaged space.
|
||||||
|
|
||||||
|
// The physics engine controller class created at initialization
|
||||||
|
public struct BulletSim
|
||||||
|
{
|
||||||
|
public BulletSim(uint worldId, BSScene bss, object xx)
|
||||||
|
{
|
||||||
|
ptr = xx;
|
||||||
|
worldID = worldId;
|
||||||
|
physicsScene = bss;
|
||||||
|
}
|
||||||
|
public object ptr;
|
||||||
|
public uint worldID;
|
||||||
|
// The scene is only in here so very low level routines have a handle to print debug/error messages
|
||||||
|
public BSScene physicsScene;
|
||||||
|
}
|
||||||
|
|
||||||
|
// An allocated Bullet btRigidBody
|
||||||
|
public struct BulletBody
|
||||||
|
{
|
||||||
|
public BulletBody(uint id) : this(id, null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public BulletBody(uint id, object xx)
|
||||||
|
{
|
||||||
|
ID = id;
|
||||||
|
ptr = xx;
|
||||||
|
collisionType = CollisionType.Static;
|
||||||
|
}
|
||||||
|
public object ptr;
|
||||||
|
public uint ID;
|
||||||
|
public CollisionType collisionType;
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
ptr = null;
|
||||||
|
}
|
||||||
|
public bool HasPhysicalBody { get { return ptr != null; } }
|
||||||
|
|
||||||
|
// Apply the specificed collision mask into the physical world
|
||||||
|
public void ApplyCollisionMask()
|
||||||
|
{
|
||||||
|
// Should assert the body has been added to the physical world.
|
||||||
|
// (The collision masks are stored in the collision proxy cache which only exists for
|
||||||
|
// a collision body that is in the world.)
|
||||||
|
BulletSimAPI.SetCollisionGroupMask2(ptr,
|
||||||
|
BulletSimData.CollisionTypeMasks[collisionType].group,
|
||||||
|
BulletSimData.CollisionTypeMasks[collisionType].mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder buff = new StringBuilder();
|
||||||
|
buff.Append("<id=");
|
||||||
|
buff.Append(ID.ToString());
|
||||||
|
buff.Append(",p=");
|
||||||
|
buff.Append(ptr.ToString());
|
||||||
|
buff.Append(",c=");
|
||||||
|
buff.Append(collisionType);
|
||||||
|
buff.Append(">");
|
||||||
|
return buff.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct BulletShape
|
||||||
|
{
|
||||||
|
public BulletShape(object xx) : this(xx, BSPhysicsShapeType.SHAPE_UNKNOWN)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public BulletShape(object xx, BSPhysicsShapeType typ)
|
||||||
|
{
|
||||||
|
ptr = xx;
|
||||||
|
type = typ;
|
||||||
|
shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
|
||||||
|
isNativeShape = false;
|
||||||
|
}
|
||||||
|
public object ptr;
|
||||||
|
public BSPhysicsShapeType type;
|
||||||
|
public System.UInt64 shapeKey;
|
||||||
|
public bool isNativeShape;
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
ptr = null;
|
||||||
|
}
|
||||||
|
public bool HasPhysicalShape { get { return ptr != null; } }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder buff = new StringBuilder();
|
||||||
|
buff.Append("<p=");
|
||||||
|
buff.Append(ptr.ToString());
|
||||||
|
buff.Append(",s=");
|
||||||
|
buff.Append(type.ToString());
|
||||||
|
buff.Append(",k=");
|
||||||
|
buff.Append(shapeKey.ToString("X"));
|
||||||
|
buff.Append(",n=");
|
||||||
|
buff.Append(isNativeShape.ToString());
|
||||||
|
buff.Append(">");
|
||||||
|
return buff.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An allocated Bullet btConstraint
|
||||||
|
public struct BulletConstraint
|
||||||
|
{
|
||||||
|
public BulletConstraint(object xx)
|
||||||
|
{
|
||||||
|
ptr = xx;
|
||||||
|
}
|
||||||
|
public object ptr;
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
ptr = null;
|
||||||
|
}
|
||||||
|
public bool HasPhysicalConstraint { get { return ptr != null; } }
|
||||||
|
}
|
||||||
|
|
||||||
|
// An allocated HeightMapThing which holds various heightmap info.
|
||||||
|
// Made a class rather than a struct so there would be only one
|
||||||
|
// instance of this and C# will pass around pointers rather
|
||||||
|
// than making copies.
|
||||||
|
public class BulletHeightMapInfo
|
||||||
|
{
|
||||||
|
public BulletHeightMapInfo(uint id, float[] hm, object xx) {
|
||||||
|
ID = id;
|
||||||
|
Ptr = xx;
|
||||||
|
heightMap = hm;
|
||||||
|
terrainRegionBase = OMV.Vector3.Zero;
|
||||||
|
minCoords = new OMV.Vector3(100f, 100f, 25f);
|
||||||
|
maxCoords = new OMV.Vector3(101f, 101f, 26f);
|
||||||
|
minZ = maxZ = 0f;
|
||||||
|
sizeX = sizeY = 256f;
|
||||||
|
}
|
||||||
|
public uint ID;
|
||||||
|
public object Ptr;
|
||||||
|
public float[] heightMap;
|
||||||
|
public OMV.Vector3 terrainRegionBase;
|
||||||
|
public OMV.Vector3 minCoords;
|
||||||
|
public OMV.Vector3 maxCoords;
|
||||||
|
public float sizeX, sizeY;
|
||||||
|
public float minZ, maxZ;
|
||||||
|
public BulletShape terrainShape;
|
||||||
|
public BulletBody terrainBody;
|
||||||
|
|
||||||
|
public float collisionMargin { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// The general class of collsion object.
|
||||||
|
public enum CollisionType
|
||||||
|
{
|
||||||
|
Avatar,
|
||||||
|
Groundplane,
|
||||||
|
Terrain,
|
||||||
|
Static,
|
||||||
|
Dynamic,
|
||||||
|
VolumeDetect,
|
||||||
|
// Linkset, // A linkset should be either Static or Dynamic
|
||||||
|
LinksetChild,
|
||||||
|
Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hold specification of group and mask collision flags for a CollisionType
|
||||||
|
public struct CollisionTypeFilterGroup
|
||||||
|
{
|
||||||
|
public CollisionTypeFilterGroup(CollisionType t, uint g, uint m)
|
||||||
|
{
|
||||||
|
type = t;
|
||||||
|
group = g;
|
||||||
|
mask = m;
|
||||||
|
}
|
||||||
|
public CollisionType type;
|
||||||
|
public uint group;
|
||||||
|
public uint mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* NOTE: old definitions kept for reference. Delete when things are working.
|
||||||
|
// The collsion filters and masked are defined in one place -- don't want them scattered
|
||||||
|
AvatarGroup = BCharacterGroup,
|
||||||
|
AvatarMask = BAllGroup,
|
||||||
|
ObjectGroup = BSolidGroup,
|
||||||
|
ObjectMask = BAllGroup,
|
||||||
|
StaticObjectGroup = BStaticGroup,
|
||||||
|
StaticObjectMask = AvatarGroup | ObjectGroup, // static things don't interact with much
|
||||||
|
LinksetGroup = BLinksetGroup,
|
||||||
|
LinksetMask = BAllGroup,
|
||||||
|
LinksetChildGroup = BLinksetChildGroup,
|
||||||
|
LinksetChildMask = BNoneGroup, // Linkset children disappear from the world
|
||||||
|
VolumeDetectGroup = BSensorTrigger,
|
||||||
|
VolumeDetectMask = ~BSensorTrigger,
|
||||||
|
TerrainGroup = BTerrainGroup,
|
||||||
|
TerrainMask = BAllGroup & ~BStaticGroup, // static objects on the ground don't collide
|
||||||
|
GroundPlaneGroup = BGroundPlaneGroup,
|
||||||
|
GroundPlaneMask = BAllGroup
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static class BulletSimData
|
||||||
|
{
|
||||||
|
|
||||||
|
// Map of collisionTypes to flags for collision groups and masks.
|
||||||
|
// As mentioned above, don't use the CollisionFilterGroups definitions directly in the code
|
||||||
|
// but, instead, use references to this dictionary. Finding and debugging
|
||||||
|
// collision flag problems will be made easier.
|
||||||
|
public static Dictionary<CollisionType, CollisionTypeFilterGroup> CollisionTypeMasks
|
||||||
|
= new Dictionary<CollisionType, CollisionTypeFilterGroup>()
|
||||||
|
{
|
||||||
|
{ CollisionType.Avatar,
|
||||||
|
new CollisionTypeFilterGroup(CollisionType.Avatar,
|
||||||
|
(uint)CollisionFilterGroups.BCharacterGroup,
|
||||||
|
(uint)CollisionFilterGroups.BAllGroup)
|
||||||
|
},
|
||||||
|
{ CollisionType.Groundplane,
|
||||||
|
new CollisionTypeFilterGroup(CollisionType.Groundplane,
|
||||||
|
(uint)CollisionFilterGroups.BGroundPlaneGroup,
|
||||||
|
(uint)CollisionFilterGroups.BAllGroup)
|
||||||
|
},
|
||||||
|
{ CollisionType.Terrain,
|
||||||
|
new CollisionTypeFilterGroup(CollisionType.Terrain,
|
||||||
|
(uint)CollisionFilterGroups.BTerrainGroup,
|
||||||
|
(uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup))
|
||||||
|
},
|
||||||
|
{ CollisionType.Static,
|
||||||
|
new CollisionTypeFilterGroup(CollisionType.Static,
|
||||||
|
(uint)CollisionFilterGroups.BStaticGroup,
|
||||||
|
(uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup))
|
||||||
|
},
|
||||||
|
{ CollisionType.Dynamic,
|
||||||
|
new CollisionTypeFilterGroup(CollisionType.Dynamic,
|
||||||
|
(uint)CollisionFilterGroups.BSolidGroup,
|
||||||
|
(uint)(CollisionFilterGroups.BAllGroup))
|
||||||
|
},
|
||||||
|
{ CollisionType.VolumeDetect,
|
||||||
|
new CollisionTypeFilterGroup(CollisionType.VolumeDetect,
|
||||||
|
(uint)CollisionFilterGroups.BSensorTrigger,
|
||||||
|
(uint)(~CollisionFilterGroups.BSensorTrigger))
|
||||||
|
},
|
||||||
|
{ CollisionType.LinksetChild,
|
||||||
|
new CollisionTypeFilterGroup(CollisionType.LinksetChild,
|
||||||
|
(uint)CollisionFilterGroups.BTerrainGroup,
|
||||||
|
(uint)(CollisionFilterGroups.BNoneGroup))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
34
prebuild.xml
34
prebuild.xml
|
@ -1756,6 +1756,40 @@
|
||||||
</Files>
|
</Files>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
||||||
|
<Project frameworkVersion="v3_5" name="OpenSim.Region.Physics.BulletSNPlugin" path="OpenSim/Region/Physics/BulletSNPlugin" type="Library">
|
||||||
|
<Configuration name="Debug">
|
||||||
|
<Options>
|
||||||
|
<OutputPath>../../../../bin/Physics/</OutputPath>
|
||||||
|
</Options>
|
||||||
|
</Configuration>
|
||||||
|
<Configuration name="Release">
|
||||||
|
<Options>
|
||||||
|
<OutputPath>../../../../bin/Physics/</OutputPath>
|
||||||
|
</Options>
|
||||||
|
</Configuration>
|
||||||
|
|
||||||
|
<ReferencePath>../../../../bin/</ReferencePath>
|
||||||
|
<Reference name="System"/>
|
||||||
|
<Reference name="System.Core"/>
|
||||||
|
<Reference name="System.Xml"/>
|
||||||
|
<Reference name="OpenMetaverseTypes" path="../../../../bin/"/>
|
||||||
|
<Reference name="Nini.dll" path="../../../../bin/"/>
|
||||||
|
<Reference name="OpenSim.Framework"/>
|
||||||
|
<Reference name="OpenSim.Region.Framework"/>
|
||||||
|
<Reference name="OpenSim.Region.CoreModules"/>
|
||||||
|
<Reference name="OpenSim.Framework.Console"/>
|
||||||
|
<Reference name="OpenSim.Region.Physics.Manager"/>
|
||||||
|
<Reference name="OpenSim.Region.Physics.ConvexDecompositionDotNet"/>
|
||||||
|
<Reference name="BulletXNA.dll" path="../../../../bin/"/>
|
||||||
|
<Reference name="log4net.dll" path="../../../../bin/"/>
|
||||||
|
|
||||||
|
<Files>
|
||||||
|
<Match pattern="*.cs" recurse="true">
|
||||||
|
<Exclude name="Tests" pattern="Tests"/>
|
||||||
|
</Match>
|
||||||
|
</Files>
|
||||||
|
</Project>
|
||||||
|
|
||||||
<!-- OpenSim app -->
|
<!-- OpenSim app -->
|
||||||
<Project frameworkVersion="v3_5" name="OpenSim" path="OpenSim/Region/Application" type="Exe">
|
<Project frameworkVersion="v3_5" name="OpenSim" path="OpenSim/Region/Application" type="Exe">
|
||||||
<Configuration name="Debug">
|
<Configuration name="Debug">
|
||||||
|
|
Loading…
Reference in New Issue