Merge branch 'master' of ssh://melanie@3dhosting.de/var/git/careminster into careminster

avinationmerge
Melanie 2012-04-13 03:03:44 +01:00
commit 5e3a76361f
9 changed files with 545 additions and 355 deletions

View File

@ -3956,6 +3956,34 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
public Vector3 GetGeometricCenter()
{
// this is not real geometric center but a average of positions relative to root prim acording to
// http://wiki.secondlife.com/wiki/llGetGeometricCenter
// ignoring tortured prims details since sl also seems to ignore
// so no real use in doing it on physics
Vector3 gc = Vector3.Zero;
int nparts = m_parts.Count;
if (nparts <= 1)
return gc;
SceneObjectPart[] parts = m_parts.GetArray();
nparts = parts.Length; // just in case it changed
if (nparts <= 1)
return gc;
// average all parts positions
for (int i = 0; i < nparts; i++)
gc += parts[i].GetWorldPosition();
gc /= nparts;
// relative to root:
gc -= AbsolutePosition;
return gc;
}
public float GetMass() public float GetMass()
{ {
float retmass = 0f; float retmass = 0f;

View File

@ -123,6 +123,11 @@ namespace OpenSim.Region.Framework.Scenes
/// </value> /// </value>
public const int ALL_SIDES = -1; public const int ALL_SIDES = -1;
private const scriptEvents PhyscicsNeededSubsEvents = (
scriptEvents.collision | scriptEvents.collision_start | scriptEvents.collision_end |
scriptEvents.land_collision | scriptEvents.land_collision_start | scriptEvents.land_collision_end
);
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <value> /// <value>
@ -1821,18 +1826,20 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary> /// </summary>
/// <param name="rootObjectFlags"></param> /// <param name="rootObjectFlags"></param>
/// <param name="VolumeDetectActive"></param> /// <param name="VolumeDetectActive"></param>
/// <param name="building"></param>
public void ApplyPhysics(uint rootObjectFlags, bool VolumeDetectActive, bool building) public void ApplyPhysics(uint _ObjectFlags, bool _VolumeDetectActive, bool building)
{ {
VolumeDetectActive = _VolumeDetectActive; //?? as is used this is redundante
if (!ParentGroup.Scene.CollidablePrims) if (!ParentGroup.Scene.CollidablePrims)
return; return;
if (PhysicsShapeType == (byte)PhysShapeType.none) if (PhysicsShapeType == (byte)PhysShapeType.none)
return; return;
bool isPhysical = (rootObjectFlags & (uint) PrimFlags.Physics) != 0; bool isPhysical = (_ObjectFlags & (uint) PrimFlags.Physics) != 0;
bool isPhantom = (rootObjectFlags & (uint) PrimFlags.Phantom) != 0; bool isPhantom = (_ObjectFlags & (uint) PrimFlags.Phantom) != 0;
if (IsJoint()) if (IsJoint())
{ {
@ -1840,68 +1847,11 @@ namespace OpenSim.Region.Framework.Scenes
} }
else else
{ {
// Special case for VolumeDetection: If VolumeDetection is set, the phantom flag is locally ignored if ((!isPhantom || isPhysical || _VolumeDetectActive) && !ParentGroup.IsAttachment
// if (VolumeDetectActive) && !(Shape.PathCurve == (byte)Extrusion.Flexible))
// isPhantom = false; AddToPhysics(isPhysical, isPhantom, building, true);
else
// The only time the physics scene shouldn't know about the prim is if it's phantom or an attachment, which is phantom by definition PhysActor = null; // just to be sure
// or flexible
// if (!isPhantom && !ParentGroup.IsAttachment && !(Shape.PathCurve == (byte)Extrusion.Flexible))
if ((!isPhantom || isPhysical || VolumeDetectActive) && !ParentGroup.IsAttachment && !(Shape.PathCurve == (byte)Extrusion.Flexible))
{
Vector3 velocity = Velocity;
Vector3 rotationalVelocity = AngularVelocity;
try
{
PhysActor = ParentGroup.Scene.PhysicsScene.AddPrimShape(
string.Format("{0}/{1}", Name, UUID),
Shape,
AbsolutePosition,
Scale,
GetWorldRotation(),
isPhysical,
isPhantom,
PhysicsShapeType,
m_localId);
}
catch
{
m_log.ErrorFormat("[SCENE]: caught exception meshing object {0}. Object set to phantom.", m_uuid);
PhysActor = null;
}
PhysicsActor pa = PhysActor;
if (pa != null)
{
pa.SOPName = this.Name; // save object into the PhysActor so ODE internals know the joint/body info
pa.SetMaterial(Material);
// if root part apply vehicle
if (m_vehicle != null && LocalId == ParentGroup.RootPart.LocalId)
m_vehicle.SetVehicle(pa);
DoPhysicsPropertyUpdate(isPhysical, true);
if(VolumeDetectActive) // change if not the default only
pa.SetVolumeDetect(1);
if (!building)
pa.Building = false;
Velocity = velocity;
AngularVelocity = rotationalVelocity;
pa.Velocity = velocity;
pa.RotationalVelocity = rotationalVelocity;
// if not vehicle and root part apply force and torque
if ((m_vehicle == null || m_vehicle.Type == Vehicle.TYPE_NONE)
&& LocalId == ParentGroup.RootPart.LocalId)
{
pa.Force = Force;
pa.Torque = Torque;
}
}
}
} }
} }
@ -2310,18 +2260,29 @@ namespace OpenSim.Region.Framework.Scenes
public Vector3 GetGeometricCenter() public Vector3 GetGeometricCenter()
{ {
PhysicsActor pa = PhysActor; // this is not real geometric center but a average of positions relative to root prim acording to
// http://wiki.secondlife.com/wiki/llGetGeometricCenter
if (pa != null) // ignoring tortured prims details since sl also seems to ignore
{ // so no real use in doing it on physics
Vector3 vtmp = pa.CenterOfMass; if (ParentGroup.IsDeleted)
return vtmp;
}
else
return new Vector3(0, 0, 0); return new Vector3(0, 0, 0);
return ParentGroup.GetGeometricCenter();
/*
PhysicsActor pa = PhysActor;
if (pa != null)
{
Vector3 vtmp = pa.CenterOfMass;
return vtmp;
}
else
return new Vector3(0, 0, 0);
*/
} }
public float GetMass() public float GetMass()
{ {
PhysicsActor pa = PhysActor; PhysicsActor pa = PhysActor;
@ -4628,7 +4589,6 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="SetTemporary"></param> /// <param name="SetTemporary"></param>
/// <param name="SetPhantom"></param> /// <param name="SetPhantom"></param>
/// <param name="SetVD"></param> /// <param name="SetVD"></param>
// public void UpdatePrimFlags(bool UsePhysics, bool SetTemporary, bool SetPhantom, bool SetVD)
public void UpdatePrimFlags(bool UsePhysics, bool SetTemporary, bool SetPhantom, bool SetVD, bool building) public void UpdatePrimFlags(bool UsePhysics, bool SetTemporary, bool SetPhantom, bool SetVD, bool building)
{ {
bool wasUsingPhysics = ((Flags & PrimFlags.Physics) != 0); bool wasUsingPhysics = ((Flags & PrimFlags.Physics) != 0);
@ -4639,209 +4599,92 @@ namespace OpenSim.Region.Framework.Scenes
if ((UsePhysics == wasUsingPhysics) && (wasTemporary == SetTemporary) && (wasPhantom == SetPhantom) && (SetVD == wasVD)) if ((UsePhysics == wasUsingPhysics) && (wasTemporary == SetTemporary) && (wasPhantom == SetPhantom) && (SetVD == wasVD))
return; return;
// do this first
if (building && PhysActor != null && PhysActor.Building != building)
PhysActor.Building = building;
// Special cases for VD. VD can only be called from a script
// and can't be combined with changes to other states. So we can rely
// that...
// ... if VD is changed, all others are not.
// ... if one of the others is changed, VD is not.
/*
if (SetVD) // VD is active, special logic applies
volume detection is now independent of phantom in sl
{
// State machine logic for VolumeDetect
// More logic below
bool phanReset = (SetPhantom != wasPhantom) && !SetPhantom;
if (phanReset) // Phantom changes from on to off switch VD off too
{
SetVD = false; // Switch it of for the course of this routine
VolumeDetectActive = false; // and also permanently
if (PhysActor != null)
PhysActor.SetVolumeDetect(0); // Let physics know about it too
}
else
{
// If volumedetect is active we don't want phantom to be applied.
// If this is a new call to VD out of the state "phantom"
// this will also cause the prim to be visible to physics
SetPhantom = false;
}
}
else if (wasVD)
{
// Correspondingly, if VD is turned off, also turn off phantom
SetPhantom = false;
}
if (UsePhysics && IsJoint())
{
SetPhantom = true;
}
*/
if (UsePhysics) if (UsePhysics)
{
AddFlag(PrimFlags.Physics); AddFlag(PrimFlags.Physics);
/*
if (!wasUsingPhysics)
{
DoPhysicsPropertyUpdate(UsePhysics, false);
if (!ParentGroup.IsDeleted)
{
if (LocalId == ParentGroup.RootPart.LocalId)
{
ParentGroup.CheckSculptAndLoad();
}
}
}
*/
}
else else
{
RemFlag(PrimFlags.Physics); RemFlag(PrimFlags.Physics);
/*
if (wasUsingPhysics)
{
DoPhysicsPropertyUpdate(UsePhysics, false);
}
*/
}
if (SetPhantom) if (SetPhantom)
AddFlag(PrimFlags.Phantom); AddFlag(PrimFlags.Phantom);
else else
RemFlag(PrimFlags.Phantom); RemFlag(PrimFlags.Phantom);
if (SetTemporary)
AddFlag(PrimFlags.TemporaryOnRez);
else
RemFlag(PrimFlags.TemporaryOnRez);
VolumeDetectActive = SetVD;
if (ParentGroup.Scene == null)
return;
PhysicsActor pa = PhysActor;
if (pa != null && building && pa.Building != building)
pa.Building = building;
if ((SetPhantom && !UsePhysics) || ParentGroup.IsAttachment || PhysicsShapeType == (byte)PhysShapeType.none if ((SetPhantom && !UsePhysics) || ParentGroup.IsAttachment || PhysicsShapeType == (byte)PhysShapeType.none
|| (Shape.PathCurve == (byte)Extrusion.Flexible)) // note: this may have been changed above in the case of joints || (Shape.PathCurve == (byte)Extrusion.Flexible))
{ {
// AddFlag(PrimFlags.Phantom); if (pa != null)
{
ParentGroup.Scene.RemovePhysicalPrim(1);
RemoveFromPhysics();
}
Velocity = new Vector3(0, 0, 0); Velocity = new Vector3(0, 0, 0);
Acceleration = new Vector3(0, 0, 0); Acceleration = new Vector3(0, 0, 0);
if (ParentGroup.RootPart == this) if (ParentGroup.RootPart == this)
AngularVelocity = new Vector3(0, 0, 0); AngularVelocity = new Vector3(0, 0, 0);
if (PhysActor != null)
{
ParentGroup.Scene.RemovePhysicalPrim(1);
RemoveFromPhysics();
}
} }
else else
{ {
if (ParentGroup.Scene == null)
return;
if (ParentGroup.Scene.CollidablePrims) if (ParentGroup.Scene.CollidablePrims)
{ {
if (PhysActor == null) if (pa == null)
{ {
PhysActor = ParentGroup.Scene.PhysicsScene.AddPrimShape( AddToPhysics(UsePhysics, SetPhantom, building , false);
string.Format("{0}/{1}", Name, UUID), pa = PhysActor;
Shape,
AbsolutePosition,
Scale,
GetWorldRotation(), //physics wants world rotation like all other functions send
UsePhysics,
SetPhantom,
PhysicsShapeType,
m_localId);
PhysActor.SetMaterial(Material); if (pa != null)
// if root part apply vehicle
if (m_vehicle != null && LocalId == ParentGroup.RootPart.LocalId)
m_vehicle.SetVehicle(PhysActor);
DoPhysicsPropertyUpdate(UsePhysics, true);
if (!ParentGroup.IsDeleted)
{ {
if (LocalId == ParentGroup.RootPart.LocalId) if (
// ((AggregateScriptEvents & scriptEvents.collision) != 0) ||
// ((AggregateScriptEvents & scriptEvents.collision_end) != 0) ||
// ((AggregateScriptEvents & scriptEvents.collision_start) != 0) ||
// ((AggregateScriptEvents & scriptEvents.land_collision_start) != 0) ||
// ((AggregateScriptEvents & scriptEvents.land_collision) != 0) ||
// ((AggregateScriptEvents & scriptEvents.land_collision_end) != 0) ||
((AggregateScriptEvents & PhyscicsNeededSubsEvents) != 0) || (CollisionSound != UUID.Zero)
// (CollisionSound != UUID.Zero)
)
{ {
ParentGroup.CheckSculptAndLoad(); pa.OnCollisionUpdate += PhysicsCollision;
pa.SubscribeEvents(1000);
} }
} }
if (
((AggregateScriptEvents & scriptEvents.collision) != 0) ||
((AggregateScriptEvents & scriptEvents.collision_end) != 0) ||
((AggregateScriptEvents & scriptEvents.collision_start) != 0) ||
((AggregateScriptEvents & scriptEvents.land_collision_start) != 0) ||
((AggregateScriptEvents & scriptEvents.land_collision) != 0) ||
((AggregateScriptEvents & scriptEvents.land_collision_end) != 0) ||
(CollisionSound != UUID.Zero)
)
{
PhysActor.OnCollisionUpdate += PhysicsCollision;
PhysActor.SubscribeEvents(1000);
}
} }
else // it already has a physical representation else // it already has a physical representation
{ {
DoPhysicsPropertyUpdate(UsePhysics, false); // Update physical status. DoPhysicsPropertyUpdate(UsePhysics, false); // Update physical status.
if (!ParentGroup.IsDeleted) if(VolumeDetectActive)
{ pa.SetVolumeDetect(1);
if (LocalId == ParentGroup.RootPart.LocalId) else
{ pa.SetVolumeDetect(0);
ParentGroup.CheckSculptAndLoad();
} if (pa.Building != building)
} pa.Building = building;
} }
} }
} }
PhysicsActor pa = PhysActor;
if (SetVD)
{
// If the above logic worked (this is urgent candidate to unit tests!)
// we now have a physicsactor.
// Defensive programming calls for a check here.
// Better would be throwing an exception that could be catched by a unit test as the internal
// logic should make sure, this Physactor is always here.
if (pa != null)
{
pa.SetVolumeDetect(1);
// AddFlag(PrimFlags.Phantom); // We set this flag also if VD is active
this.VolumeDetectActive = true;
}
}
else
{
// Remove VolumeDetect in any case. Note, it's safe to call SetVolumeDetect as often as you like
// (mumbles, well, at least if you have infinte CPU powers :-))
if (pa != null)
{
pa.SetVolumeDetect(0);
this.VolumeDetectActive = false;
}
}
if (SetTemporary)
{
AddFlag(PrimFlags.TemporaryOnRez);
}
else
{
RemFlag(PrimFlags.TemporaryOnRez);
}
// m_log.Debug("Update: PHY:" + UsePhysics.ToString() + ", T:" + IsTemporary.ToString() + ", PHA:" + IsPhantom.ToString() + " S:" + CastsShadows.ToString()); // m_log.Debug("Update: PHY:" + UsePhysics.ToString() + ", T:" + IsTemporary.ToString() + ", PHA:" + IsPhantom.ToString() + " S:" + CastsShadows.ToString());
// and last in case we have a new actor and not building // and last in case we have a new actor and not building
if (pa != null && pa.Building != building)
pa.Building = building;
if (ParentGroup != null) if (ParentGroup != null)
{ {
ParentGroup.HasGroupChanged = true; ParentGroup.HasGroupChanged = true;
@ -4853,48 +4696,104 @@ namespace OpenSim.Region.Framework.Scenes
/// <summary> /// <summary>
/// Adds this part to the physics scene. /// Adds this part to the physics scene.
/// and sets the PhysActor property
/// </summary> /// </summary>
/// <remarks>This method also sets the PhysActor property.</remarks> /// <param name="isPhysical">Add this prim as physical.</param>
/// <param name="rigidBody">Add this prim with a rigid body.</param> /// <param name="isPhantom">Add this prim as phantom.</param>
/// <returns> /// <param name="building">tells physics to delay full construction of object</param>
/// The physics actor. null if there was a failure. /// <param name="applyDynamics">applies velocities, force and torque</param>
/// </returns> private void AddToPhysics(bool isPhysical, bool isPhantom, bool building, bool applyDynamics)
private PhysicsActor AddToPhysics(bool rigidBody)
{ {
PhysicsActor pa; PhysicsActor pa;
Vector3 velocity = Velocity;
Vector3 rotationalVelocity = AngularVelocity;;
try try
{ {
pa = ParentGroup.Scene.PhysicsScene.AddPrimShape( pa = ParentGroup.Scene.PhysicsScene.AddPrimShape(
string.Format("{0}/{1}", Name, UUID), string.Format("{0}/{1}", Name, UUID),
Shape, Shape,
AbsolutePosition, AbsolutePosition,
Scale, Scale,
RotationOffset, GetWorldRotation(),
rigidBody, isPhysical,
m_localId); isPhantom,
PhysicsShapeType,
m_localId);
} }
catch catch (Exception ex)
{ {
m_log.ErrorFormat("[SCENE]: caught exception meshing object {0}. Object set to phantom.", m_uuid); m_log.ErrorFormat("[SCENE]: AddToPhysics object {0} failed: {1}", m_uuid, ex.Message);
pa = null; pa = null;
} }
// FIXME: Ideally we wouldn't set the property here to reduce situations where threads changing physical
// properties can stop on each other. However, DoPhysicsPropertyUpdate() currently relies on PhysActor
// being set.
PhysActor = pa;
// Basic Physics can also return null as well as an exception catch.
if (pa != null) if (pa != null)
{ {
pa.SOPName = this.Name; // save object into the PhysActor so ODE internals know the joint/body info pa.SOPName = this.Name; // save object into the PhysActor so ODE internals know the joint/body info
pa.SetMaterial(Material); pa.SetMaterial(Material);
DoPhysicsPropertyUpdate(rigidBody, true);
if (VolumeDetectActive) // change if not the default only
pa.SetVolumeDetect(1);
if (m_vehicle != null && LocalId == ParentGroup.RootPart.LocalId)
m_vehicle.SetVehicle(pa);
// we are going to tell rest of code about physics so better have this here
PhysActor = pa;
// DoPhysicsPropertyUpdate(isPhysical, true);
// lets expand it here just with what it really needs to do
if (isPhysical)
{
if (ParentGroup.RootPart.KeyframeMotion != null)
ParentGroup.RootPart.KeyframeMotion.Stop();
ParentGroup.RootPart.KeyframeMotion = null;
ParentGroup.Scene.AddPhysicalPrim(1);
pa.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate;
pa.OnOutOfBounds += PhysicsOutOfBounds;
if (ParentID != 0 && ParentID != LocalId)
{
PhysicsActor parentPa = ParentGroup.RootPart.PhysActor;
if (parentPa != null)
{
pa.link(parentPa);
}
}
}
if (applyDynamics)
// do independent of isphysical so parameters get setted (at least some)
{
Velocity = velocity;
AngularVelocity = rotationalVelocity;
pa.Velocity = velocity;
pa.RotationalVelocity = rotationalVelocity;
// if not vehicle and root part apply force and torque
if ((m_vehicle == null || m_vehicle.Type == Vehicle.TYPE_NONE)
&& LocalId == ParentGroup.RootPart.LocalId)
{
pa.Force = Force;
pa.Torque = Torque;
}
}
if (Shape.SculptEntry)
CheckSculptAndLoad();
else
ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(pa);
if (!building)
pa.Building = false;
} }
return pa; PhysActor = pa;
} }
/// <summary> /// <summary>
/// This removes the part from the physics scene. /// This removes the part from the physics scene.
@ -5103,10 +5002,6 @@ namespace OpenSim.Region.Framework.Scenes
PhysicsActor pa = PhysActor; PhysicsActor pa = PhysActor;
if (pa != null) if (pa != null)
{ {
const scriptEvents NeededSubsEvents = (
scriptEvents.collision | scriptEvents.collision_start| scriptEvents.collision_end |
scriptEvents.land_collision | scriptEvents.land_collision_start | scriptEvents.land_collision_end
);
if ( if (
// ((AggregateScriptEvents & scriptEvents.collision) != 0) || // ((AggregateScriptEvents & scriptEvents.collision) != 0) ||
// ((AggregateScriptEvents & scriptEvents.collision_end) != 0) || // ((AggregateScriptEvents & scriptEvents.collision_end) != 0) ||
@ -5114,7 +5009,7 @@ namespace OpenSim.Region.Framework.Scenes
// ((AggregateScriptEvents & scriptEvents.land_collision_start) != 0) || // ((AggregateScriptEvents & scriptEvents.land_collision_start) != 0) ||
// ((AggregateScriptEvents & scriptEvents.land_collision) != 0) || // ((AggregateScriptEvents & scriptEvents.land_collision) != 0) ||
// ((AggregateScriptEvents & scriptEvents.land_collision_end) != 0) || // ((AggregateScriptEvents & scriptEvents.land_collision_end) != 0) ||
((AggregateScriptEvents & NeededSubsEvents) != 0) || (CollisionSound != UUID.Zero) ((AggregateScriptEvents & PhyscicsNeededSubsEvents) != 0) || (CollisionSound != UUID.Zero)
) )
{ {
// subscribe to physics updates. // subscribe to physics updates.

View File

@ -179,6 +179,8 @@ namespace OpenSim.Region.Physics.OdePlugin
public bool m_outofBounds; public bool m_outofBounds;
private float m_density = 10.000006836f; // Aluminum g/cm3; private float m_density = 10.000006836f; // Aluminum g/cm3;
private float m_primMass = 10.000006836f; // Aluminum g/cm3;
private byte m_shapetype; private byte m_shapetype;
private byte m_taintshapetype; private byte m_taintshapetype;
@ -538,7 +540,11 @@ namespace OpenSim.Region.Physics.OdePlugin
public override float Mass public override float Mass
{ {
get { return CalculateMass(); } get
{
CalculateMass();
return m_primMass;
}
} }
public override Vector3 Force public override Vector3 Force
@ -1316,6 +1322,9 @@ namespace OpenSim.Region.Physics.OdePlugin
m_primMass = returnMass;
if (m_primMass > _parent_scene.maximumMassObject)
m_primMass = _parent_scene.maximumMassObject;
// Recursively calculate mass // Recursively calculate mass
bool HasChildPrim = false; bool HasChildPrim = false;

View File

@ -395,7 +395,7 @@ namespace OpenSim.Region.Physics.OdePlugin
public override float Mass public override float Mass
{ {
get { return _mass; } get { return primMass; }
} }
public override Vector3 Force public override Vector3 Force

View File

@ -107,16 +107,17 @@ namespace OdeAPI
ConvexClass, ConvexClass,
GeomTransformClass, GeomTransformClass,
TriMeshClass, TriMeshClass,
HeightfieldClass, HeightfieldClass,
FirstSpaceClass, FirstSpaceClass,
SimpleSpaceClass = FirstSpaceClass, SimpleSpaceClass = FirstSpaceClass,
HashSpaceClass, HashSpaceClass,
QuadTreeSpaceClass, QuadTreeSpaceClass,
LastSpaceClass = QuadTreeSpaceClass, LastSpaceClass = QuadTreeSpaceClass,
UbitTerrainClass,
FirstUserClass, FirstUserClass,
LastUserClass = FirstUserClass + MaxUserClasses - 1, LastUserClass = FirstUserClass + MaxUserClasses - 1,
NumClasses, NumClasses,
MaxUserClasses = 4 MaxUserClasses = 5
} }
public enum JointType : int public enum JointType : int
@ -201,8 +202,11 @@ namespace OdeAPI
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void GeomDtorFn(IntPtr o); public delegate void GeomDtorFn(IntPtr o);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate dReal HeightfieldGetHeight(IntPtr p_user_data, int x, int z); public delegate dReal HeightfieldGetHeight(IntPtr p_user_data, int x, int z);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate dReal UbitTerrainGetHeight(IntPtr p_user_data, int x, int z);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void NearCallback(IntPtr data, IntPtr geom1, IntPtr geom2); public delegate void NearCallback(IntPtr data, IntPtr geom1, IntPtr geom2);
@ -729,6 +733,18 @@ namespace OdeAPI
return CreateiHeightfield(space, data, bPlaceable); return CreateiHeightfield(space, data, bPlaceable);
} }
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dCreateUbitTerrain"), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateiUbitTerrain(IntPtr space, IntPtr data, int bPlaceable);
public static IntPtr CreateUbitTerrain(IntPtr space, IntPtr data, int bPlaceable)
{
NTotalGeoms++;
return CreateiUbitTerrain(space, data, bPlaceable);
}
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dCreateGeom"), SuppressUnmanagedCodeSecurity] [DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dCreateGeom"), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateiGeom(int classnum); public static extern IntPtr CreateiGeom(int classnum);
public static IntPtr CreateGeom(int classnum) public static IntPtr CreateGeom(int classnum)
@ -964,6 +980,8 @@ namespace OdeAPI
dReal width, dReal depth, int widthSamples, int depthSamples, dReal width, dReal depth, int widthSamples, int depthSamples,
dReal scale, dReal offset, dReal thickness, int bWrap); dReal scale, dReal offset, dReal thickness, int bWrap);
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomHeightfieldDataBuildDouble"), SuppressUnmanagedCodeSecurity] [DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomHeightfieldDataBuildDouble"), SuppressUnmanagedCodeSecurity]
public static extern void GeomHeightfieldDataBuildDouble(IntPtr d, double[] pHeightData, int bCopyHeightData, public static extern void GeomHeightfieldDataBuildDouble(IntPtr d, double[] pHeightData, int bCopyHeightData,
dReal width, dReal depth, int widthSamples, int depthSamples, dReal width, dReal depth, int widthSamples, int depthSamples,
@ -989,6 +1007,33 @@ namespace OdeAPI
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomHeightfieldSetHeightfieldData"), SuppressUnmanagedCodeSecurity] [DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomHeightfieldSetHeightfieldData"), SuppressUnmanagedCodeSecurity]
public static extern void GeomHeightfieldSetHeightfieldData(IntPtr g, IntPtr d); public static extern void GeomHeightfieldSetHeightfieldData(IntPtr g, IntPtr d);
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomUbitTerrainDataBuild"), SuppressUnmanagedCodeSecurity]
public static extern void GeomUbitTerrainDataBuild(IntPtr d, float[] pHeightData, int bCopyHeightData,
dReal sampleSize, int widthSamples, int depthSamples,
dReal offset, dReal thickness, int bWrap);
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomUbitTerrainDataBuild"), SuppressUnmanagedCodeSecurity]
public static extern void GeomUbitTerrainDataBuild(IntPtr d, IntPtr pHeightData, int bCopyHeightData,
dReal sampleSize, int widthSamples, int depthSamples,
dReal thickness, int bWrap);
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomUbitTerrainDataCreate"), SuppressUnmanagedCodeSecurity]
public static extern IntPtr GeomUbitTerrainDataCreate();
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomUbitTerrainDataDestroy"), SuppressUnmanagedCodeSecurity]
public static extern void GeomUbitTerrainDataDestroy(IntPtr d);
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomUbitTerrainDataSetBounds"), SuppressUnmanagedCodeSecurity]
public static extern void GeomUbitTerrainDataSetBounds(IntPtr d, dReal minHeight, dReal maxHeight);
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomUbitTerrainGetHeightfieldData"), SuppressUnmanagedCodeSecurity]
public static extern IntPtr GeomUbitTerrainGetHeightfieldData(IntPtr g);
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomUbitTerrainSetHeightfieldData"), SuppressUnmanagedCodeSecurity]
public static extern void GeomUbitTerrainSetHeightfieldData(IntPtr g, IntPtr d);
[DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomIsEnabled"), SuppressUnmanagedCodeSecurity] [DllImport("ode", CallingConvention = CallingConvention.Cdecl, EntryPoint = "dGeomIsEnabled"), SuppressUnmanagedCodeSecurity]
public static extern bool GeomIsEnabled(IntPtr geom); public static extern bool GeomIsEnabled(IntPtr geom);

View File

@ -156,6 +156,7 @@ namespace OpenSim.Region.Physics.OdePlugin
private readonly ILog m_log; private readonly ILog m_log;
// private Dictionary<string, sCollisionData> m_storedCollisions = new Dictionary<string, sCollisionData>(); // private Dictionary<string, sCollisionData> m_storedCollisions = new Dictionary<string, sCollisionData>();
public bool OdeUbitLib = false;
// private int threadid = 0; // private int threadid = 0;
private Random fluidRandomizer = new Random(Environment.TickCount); private Random fluidRandomizer = new Random(Environment.TickCount);
@ -374,7 +375,14 @@ namespace OpenSim.Region.Physics.OdePlugin
mesher = meshmerizer; mesher = meshmerizer;
m_config = config; m_config = config;
// m_log.WarnFormat("ODE configuration: {0}", d.GetConfiguration("ODE")); string ode_config = d.GetConfiguration("ODE");
m_log.WarnFormat("ODE configuration: {0}", ode_config);
if (ode_config.Contains("ODE_Ubit"))
{
OdeUbitLib = true;
}
/* /*
if (region != null) if (region != null)
{ {
@ -527,13 +535,24 @@ namespace OpenSim.Region.Physics.OdePlugin
// sets a global contact for a joint for contactgeom , and base contact description) // sets a global contact for a joint for contactgeom , and base contact description)
private IntPtr CreateContacJoint(ref d.ContactGeom contactGeom, float mu, float bounce,float cfm,float erp) private IntPtr CreateContacJoint(ref d.ContactGeom contactGeom, float mu, float bounce, float cfm, float erpscale, float dscale)
{ {
if (GlobalContactsArray == IntPtr.Zero || m_global_contactcount >= maxContactsbeforedeath) if (GlobalContactsArray == IntPtr.Zero || m_global_contactcount >= maxContactsbeforedeath)
return IntPtr.Zero; return IntPtr.Zero;
float erp = contactGeom.depth;
erp *= erpscale;
if (erp < minERP)
erp = minERP;
else if (erp > MaxERP)
erp = MaxERP;
float depth = contactGeom.depth * dscale;
if (depth > 0.5f)
depth = 0.5f;
d.Contact newcontact = new d.Contact(); d.Contact newcontact = new d.Contact();
newcontact.geom.depth = contactGeom.depth; newcontact.geom.depth = depth;
newcontact.geom.g1 = contactGeom.g1; newcontact.geom.g1 = contactGeom.g1;
newcontact.geom.g2 = contactGeom.g2; newcontact.geom.g2 = contactGeom.g2;
newcontact.geom.pos = contactGeom.pos; newcontact.geom.pos = contactGeom.pos;
@ -692,6 +711,10 @@ namespace OpenSim.Region.Physics.OdePlugin
float bounce = 0; float bounce = 0;
float cfm = 0.0001f; float cfm = 0.0001f;
float erp = 0.1f; float erp = 0.1f;
float erpscale = 1.0f;
float dscale = 1.0f;
bool IgnoreNegSides = false;
ContactData contactdata1 = new ContactData(0, 0, false); ContactData contactdata1 = new ContactData(0, 0, false);
ContactData contactdata2 = new ContactData(0, 0, false); ContactData contactdata2 = new ContactData(0, 0, false);
@ -781,10 +804,14 @@ namespace OpenSim.Region.Physics.OdePlugin
cfm = p1.Mass; cfm = p1.Mass;
if (cfm > p2.Mass) if (cfm > p2.Mass)
cfm = p2.Mass; cfm = p2.Mass;
cfm = (float)Math.Sqrt(cfm); dscale = 10 / cfm;
cfm *= 0.0001f; dscale = (float)Math.Sqrt(dscale);
if (cfm > 0.8f) if (dscale > 1.0f)
cfm = 0.8f; dscale = 1.0f;
erpscale = cfm * 0.01f;
cfm = 0.0001f / cfm;
if (cfm > 0.01f)
cfm = 0.01f;
if ((Math.Abs(p2.Velocity.X - p1.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y - p1.Velocity.Y) > 0.1f)) if ((Math.Abs(p2.Velocity.X - p1.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y - p1.Velocity.Y) > 0.1f))
mu *= frictionMovementMult; mu *= frictionMovementMult;
@ -801,11 +828,22 @@ namespace OpenSim.Region.Physics.OdePlugin
if (Math.Abs(p1.Velocity.X) > 0.1f || Math.Abs(p1.Velocity.Y) > 0.1f) if (Math.Abs(p1.Velocity.X) > 0.1f || Math.Abs(p1.Velocity.Y) > 0.1f)
mu *= frictionMovementMult; mu *= frictionMovementMult;
p1.CollidingGround = true; p1.CollidingGround = true;
cfm = p1.Mass; cfm = p1.Mass;
cfm = (float)Math.Sqrt(cfm); dscale = 10 / cfm;
cfm *= 0.0001f; dscale = (float)Math.Sqrt(dscale);
if (cfm > 0.8f) if (dscale > 1.0f)
cfm = 0.8f; dscale = 1.0f;
erpscale = cfm * 0.01f;
cfm = 0.0001f / cfm;
if (cfm > 0.01f)
cfm = 0.01f;
if (d.GeomGetClass(g1) == d.GeomClassID.TriMeshClass)
{
if (curContact.side1 > 0)
IgnoreNegSides = true;
}
} }
else if (name == "Water") else if (name == "Water")
@ -830,11 +868,21 @@ namespace OpenSim.Region.Physics.OdePlugin
p2.getContactData(ref contactdata2); p2.getContactData(ref contactdata2);
bounce = contactdata2.bounce * TerrainBounce; bounce = contactdata2.bounce * TerrainBounce;
mu = (float)Math.Sqrt(contactdata2.mu * TerrainFriction); mu = (float)Math.Sqrt(contactdata2.mu * TerrainFriction);
cfm = p2.Mass; cfm = p2.Mass;
cfm = (float)Math.Sqrt(cfm); dscale = 10 / cfm;
cfm *= 0.0001f; dscale = (float)Math.Sqrt(dscale);
if (cfm > 0.8f)
cfm = 0.8f; if (dscale > 1.0f)
dscale = 1.0f;
erpscale = cfm * 0.01f;
cfm = 0.0001f / cfm;
if (cfm > 0.01f)
cfm = 0.01f;
if (curContact.side1 > 0) // should be 2 ?
IgnoreNegSides = true;
if (Math.Abs(p2.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y) > 0.1f) if (Math.Abs(p2.Velocity.X) > 0.1f || Math.Abs(p2.Velocity.Y) > 0.1f)
mu *= frictionMovementMult; mu *= frictionMovementMult;
@ -862,39 +910,45 @@ namespace OpenSim.Region.Physics.OdePlugin
int i = 0; int i = 0;
while(true) while(true)
{ {
if (dop1foot && (p1.Position.Z - curContact.pos.Z) > (p1.Size.Z - avCapRadius) * 0.5f)
p1.IsColliding = true;
if (dop2foot && (p2.Position.Z - curContact.pos.Z) > (p2.Size.Z - avCapRadius) * 0.5f)
p2.IsColliding = true;
if (IgnoreNegSides && curContact.side1 < 0)
erp = curContact.depth;
if (erp < minERP)
erp = minERP;
else if (erp > MaxERP)
erp = MaxERP;
Joint = CreateContacJoint(ref curContact, mu, bounce,cfm,erp);
d.JointAttach(Joint, b1, b2);
if (++m_global_contactcount >= maxContactsbeforedeath)
break;
if(++i >= count)
break;
if (!GetCurContactGeom(i, ref curContact))
break;
if (curContact.depth > maxDepthContact.PenetrationDepth)
{ {
maxDepthContact.Position.X = curContact.pos.X; if (++i >= count)
maxDepthContact.Position.Y = curContact.pos.Y; break;
maxDepthContact.Position.Z = curContact.pos.Z;
maxDepthContact.SurfaceNormal.X = curContact.normal.X; if (!GetCurContactGeom(i, ref curContact))
maxDepthContact.SurfaceNormal.Y = curContact.normal.Y; break;
maxDepthContact.SurfaceNormal.Z = curContact.normal.Z; }
maxDepthContact.PenetrationDepth = curContact.depth; else
{
if (dop1foot && (p1.Position.Z - curContact.pos.Z) > (p1.Size.Z - avCapRadius) * 0.5f)
p1.IsColliding = true;
if (dop2foot && (p2.Position.Z - curContact.pos.Z) > (p2.Size.Z - avCapRadius) * 0.5f)
p2.IsColliding = true;
Joint = CreateContacJoint(ref curContact, mu, bounce, cfm, erpscale, dscale);
d.JointAttach(Joint, b1, b2);
if (++m_global_contactcount >= maxContactsbeforedeath)
break;
if (++i >= count)
break;
if (!GetCurContactGeom(i, ref curContact))
break;
if (curContact.depth > maxDepthContact.PenetrationDepth)
{
maxDepthContact.Position.X = curContact.pos.X;
maxDepthContact.Position.Y = curContact.pos.Y;
maxDepthContact.Position.Z = curContact.pos.Z;
maxDepthContact.SurfaceNormal.X = curContact.normal.X;
maxDepthContact.SurfaceNormal.Y = curContact.normal.Y;
maxDepthContact.SurfaceNormal.Z = curContact.normal.Z;
maxDepthContact.PenetrationDepth = curContact.depth;
}
} }
} }
@ -1865,13 +1919,12 @@ namespace OpenSim.Region.Physics.OdePlugin
public float GetTerrainHeightAtXY(float x, float y) public float GetTerrainHeightAtXY(float x, float y)
{ {
// assumes 1m size grid and constante size square regions
// needs to know about sims around in future
// region offset in mega position
int offsetX = ((int)(x / (int)Constants.RegionSize)) * (int)Constants.RegionSize; int offsetX = ((int)(x / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
int offsetY = ((int)(y / (int)Constants.RegionSize)) * (int)Constants.RegionSize; int offsetY = ((int)(y / (int)Constants.RegionSize)) * (int)Constants.RegionSize;
IntPtr heightFieldGeom = IntPtr.Zero; IntPtr heightFieldGeom = IntPtr.Zero;
// get region map // get region map
@ -1903,28 +1956,55 @@ namespace OpenSim.Region.Physics.OdePlugin
int regsize = (int)Constants.RegionSize + 3; // map size see setterrain number of samples int regsize = (int)Constants.RegionSize + 3; // map size see setterrain number of samples
// we still have square fixed size regions if (OdeUbitLib)
// also flip x and y because of how map is done for ODE fliped axis
// so ix,iy,dx and dy are inter exchanged
if (x < regsize - 1)
{ {
iy = (int)x; if (x < regsize - 1)
dy = x - (float)iy; {
} ix = (int)x;
else // out world use external height dx = x - (float)ix;
{ }
iy = regsize - 1; else // out world use external height
dy = 0; {
} ix = regsize - 1;
if (y < regsize - 1) dx = 0;
{ }
ix = (int)y; if (y < regsize - 1)
dx = y - (float)ix; {
iy = (int)y;
dy = y - (float)iy;
}
else
{
iy = regsize - 1;
dy = 0;
}
} }
else else
{ {
ix = regsize - 1; // we still have square fixed size regions
dx = 0; // also flip x and y because of how map is done for ODE fliped axis
// so ix,iy,dx and dy are inter exchanged
if (x < regsize - 1)
{
iy = (int)x;
dy = x - (float)iy;
}
else // out world use external height
{
iy = regsize - 1;
dy = 0;
}
if (y < regsize - 1)
{
ix = (int)y;
dx = y - (float)ix;
}
else
{
ix = regsize - 1;
dx = 0;
}
} }
float h0; float h0;
@ -1951,6 +2031,8 @@ namespace OpenSim.Region.Physics.OdePlugin
return h0 + h1 + h2; return h0 + h1 + h2;
} }
public override void SetTerrain(float[] heightMap) public override void SetTerrain(float[] heightMap)
{ {
if (m_worldOffset != Vector3.Zero && m_parentScene != null) if (m_worldOffset != Vector3.Zero && m_parentScene != null)
@ -1972,7 +2054,15 @@ namespace OpenSim.Region.Physics.OdePlugin
} }
public void SetTerrain(float[] heightMap, Vector3 pOffset) public void SetTerrain(float[] heightMap, Vector3 pOffset)
{ {
if (OdeUbitLib)
UbitSetTerrain(heightMap, pOffset);
else
OriSetTerrain(heightMap, pOffset);
}
public void OriSetTerrain(float[] heightMap, Vector3 pOffset)
{
// assumes 1m size grid and constante size square regions // assumes 1m size grid and constante size square regions
// needs to know about sims around in future // needs to know about sims around in future
@ -2086,6 +2176,108 @@ namespace OpenSim.Region.Physics.OdePlugin
} }
} }
public void UbitSetTerrain(float[] heightMap, Vector3 pOffset)
{
// assumes 1m size grid and constante size square regions
// needs to know about sims around in future
float[] _heightmap;
uint heightmapWidth = Constants.RegionSize + 2;
uint heightmapHeight = Constants.RegionSize + 2;
uint heightmapWidthSamples = heightmapWidth + 1;
uint heightmapHeightSamples = heightmapHeight + 1;
_heightmap = new float[heightmapWidthSamples * heightmapHeightSamples];
uint regionsize = Constants.RegionSize;
float hfmin = float.MaxValue;
// float hfmax = float.MinValue;
float val;
uint maxXXYY = regionsize - 1;
// adding one margin all around so things don't fall in edges
uint xx;
uint yy = 0;
uint yt = 0;
for (uint y = 0; y < heightmapHeightSamples; y++)
{
if (y > 1 && y < maxXXYY)
yy += regionsize;
xx = 0;
for (uint x = 0; x < heightmapWidthSamples; x++)
{
if (x > 1 && x < maxXXYY)
xx++;
val = heightMap[yy + xx];
if (val < 0.0f)
val = 0.0f; // no neg terrain as in chode
_heightmap[yt + x] = val;
if (hfmin > val)
hfmin = val;
// if (hfmax < val)
// hfmax = val;
}
yt += heightmapWidthSamples;
}
lock (OdeLock)
{
IntPtr GroundGeom = IntPtr.Zero;
if (RegionTerrain.TryGetValue(pOffset, out GroundGeom))
{
RegionTerrain.Remove(pOffset);
if (GroundGeom != IntPtr.Zero)
{
if (TerrainHeightFieldHeights.ContainsKey(GroundGeom))
{
TerrainHeightFieldHeightsHandlers[GroundGeom].Free();
TerrainHeightFieldHeightsHandlers.Remove(GroundGeom);
TerrainHeightFieldHeights.Remove(GroundGeom);
}
d.SpaceRemove(StaticSpace, GroundGeom);
d.GeomDestroy(GroundGeom);
}
}
IntPtr HeightmapData = d.GeomHeightfieldDataCreate();
const int wrap = 0;
float thickness = hfmin;
if (thickness < 0)
thickness = 1;
GCHandle _heightmaphandler = GCHandle.Alloc(_heightmap, GCHandleType.Pinned);
d.GeomUbitTerrainDataBuild(HeightmapData, _heightmaphandler.AddrOfPinnedObject(), 0, 1.0f,
(int)heightmapWidthSamples, (int)heightmapHeightSamples,
thickness, wrap);
// d.GeomUbitTerrainDataSetBounds(HeightmapData, hfmin - 1, hfmax + 1);
GroundGeom = d.CreateUbitTerrain(StaticSpace, HeightmapData, 1);
if (GroundGeom != IntPtr.Zero)
{
d.GeomSetCategoryBits(GroundGeom, (int)(CollisionCategories.Land));
d.GeomSetCollideBits(GroundGeom, (int)(CollisionCategories.Space));
}
geom_name_map[GroundGeom] = "Terrain";
d.GeomSetPosition(GroundGeom, pOffset.X + (float)Constants.RegionSize * 0.5f, pOffset.Y + (float)Constants.RegionSize * 0.5f, 0);
RegionTerrain.Add(pOffset, GroundGeom, GroundGeom);
// TerrainHeightFieldHeights.Add(GroundGeom, ODElandMap);
TerrainHeightFieldHeights.Add(GroundGeom, _heightmap);
TerrainHeightFieldHeightsHandlers.Add(GroundGeom, _heightmaphandler);
}
}
public override void DeleteTerrain() public override void DeleteTerrain()
{ {
} }

View File

@ -3176,17 +3176,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
} }
else else
{ {
if (m_host.IsRoot) // new SL always returns object mass
{ // if (m_host.IsRoot)
// {
return m_host.ParentGroup.GetMass(); return m_host.ParentGroup.GetMass();
} // }
else // else
{ // {
return m_host.GetMass(); // return m_host.GetMass();
} // }
} }
} }
public LSL_Float llGetMassMKS()
{
return 100f * llGetMass();
}
public void llCollisionFilter(string name, string id, int accept) public void llCollisionFilter(string name, string id, int accept)
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
@ -4959,7 +4966,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{ {
float distance = (PusheePos - m_host.AbsolutePosition).Length(); float distance = (PusheePos - m_host.AbsolutePosition).Length();
float distance_term = distance * distance * distance; // Script Energy float distance_term = distance * distance * distance; // Script Energy
float pusher_mass = m_host.GetMass(); // use total object mass and not part
float pusher_mass = m_host.ParentGroup.GetMass();
float PUSH_ATTENUATION_DISTANCE = 17f; float PUSH_ATTENUATION_DISTANCE = 17f;
float PUSH_ATTENUATION_SCALE = 5f; float PUSH_ATTENUATION_SCALE = 5f;
@ -9964,9 +9972,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
{ {
try try
{ {
/*
SceneObjectPart obj = World.GetSceneObjectPart(World.Entities[key].LocalId); SceneObjectPart obj = World.GetSceneObjectPart(World.Entities[key].LocalId);
if (obj != null) if (obj != null)
return (double)obj.GetMass(); return (double)obj.GetMass();
*/
// return total object mass
SceneObjectGroup obj = World.GetGroupByPrim(World.Entities[key].LocalId);
if (obj != null)
return (double)obj.GetMass();
// the object is null so the key is for an avatar // the object is null so the key is for an avatar
ScenePresence avatar = World.GetScenePresence(key); ScenePresence avatar = World.GetScenePresence(key);
if (avatar != null) if (avatar != null)

View File

@ -148,7 +148,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces
LSL_Vector llGetLocalPos(); LSL_Vector llGetLocalPos();
LSL_Rotation llGetLocalRot(); LSL_Rotation llGetLocalRot();
LSL_Float llGetMass(); LSL_Float llGetMass();
void llGetNextEmail(string address, string subject); LSL_Float llGetMassMKS();
void llGetNextEmail(string address, string subject);
LSL_String llGetNotecardLine(string name, int line); LSL_String llGetNotecardLine(string name, int line);
LSL_Key llGetNumberOfNotecardLines(string name); LSL_Key llGetNumberOfNotecardLines(string name);
LSL_Integer llGetNumberOfPrims(); LSL_Integer llGetNumberOfPrims();

View File

@ -581,6 +581,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
return m_LSL_Functions.llGetMass(); return m_LSL_Functions.llGetMass();
} }
public LSL_Float llGetMassMKS()
{
return m_LSL_Functions.llGetMassMKS();
}
public void llGetNextEmail(string address, string subject) public void llGetNextEmail(string address, string subject)
{ {
m_LSL_Functions.llGetNextEmail(address, subject); m_LSL_Functions.llGetNextEmail(address, subject);