Files
opensim/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs
T
MW 755ad9e3e0 First part of avatar persistence, currently only really works in standalone mode (with accounts_authenticate set to true), it also only currently has a mysql database connector. (sqlite one will follow soon). It also uses the tribalmedia database system, so this needs checking to see if the old problems with mono have been fixed.
To use, see the appearance section in opensim.ini.example, set "persist = true", then add the correct connection string for your database.(see mysql-AvatarAppearance.sql in share folder for a example of the table mysql table structure).
This could possible be used in a very small grid, but would mean each region server would need to connect to the same mysql database. 
But the work to move the code to one of the grid servers shouldn't be too much.
2008-02-04 12:04:02 +00:00

1696 lines
57 KiB
C#

/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSim 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.Drawing;
using System.Xml;
using System.Xml.Serialization;
using Axiom.Math;
using libsecondlife;
using libsecondlife.Packets;
using OpenSim.Framework;
using OpenSim.Framework.Console;
using OpenSim.Region.Environment.Interfaces;
using OpenSim.Region.Environment.Scenes.Scripting;
using OpenSim.Region.Physics.Manager;
namespace OpenSim.Region.Environment.Scenes
{
// I don't really know where to put this except here.
// Can't access the OpenSim.Region.ScriptEngine.Common.LSL_BaseClass.Changed constants
[Flags]
public enum Changed : uint
{
INVENTORY = 1,
COLOR = 2,
SHAPE = 4,
SCALE = 8,
TEXTURE = 16,
LINK = 32,
ALLOWED_DROP = 64,
OWNER = 128
}
[Flags]
public enum TextureAnimFlags : byte
{
NONE = 0x00,
ANIM_ON = 0x01,
LOOP = 0x02,
REVERSE = 0x04,
PING_PONG = 0x08,
SMOOTH = 0x10,
ROTATE = 0x20,
SCALE = 0x40
}
public partial class SceneObjectPart : IScriptHost
{
[XmlIgnore] public PhysicsActor PhysActor = null;
public LLUUID LastOwnerID;
public LLUUID OwnerID;
public LLUUID GroupID;
public int OwnershipCost;
public byte ObjectSaleType;
public int SalePrice;
public uint Category;
public Int32 CreationDate;
public uint ParentID = 0;
private Vector3 m_sitTargetPosition = new Vector3(0, 0, 0);
private Quaternion m_sitTargetOrientation = new Quaternion(0, 0, 0, 1);
private LLUUID m_sitTargetAvatar = LLUUID.Zero;
#region Permissions
public uint BaseMask = (uint)PermissionMask.All;
public uint OwnerMask = (uint)PermissionMask.All;
public uint GroupMask = (uint)PermissionMask.None;
public uint EveryoneMask = (uint)PermissionMask.None;
public uint NextOwnerMask = (uint)PermissionMask.All;
public LLObject.ObjectFlags Flags = LLObject.ObjectFlags.None;
public uint ObjectFlags
{
get { return (uint)Flags; }
set { Flags = (LLObject.ObjectFlags)value; }
}
#endregion
protected byte[] m_particleSystem = new byte[0];
[XmlIgnore] public uint TimeStampFull = 0;
[XmlIgnore] public uint TimeStampTerse = 0;
[XmlIgnore] public uint TimeStampLastActivity = 0; // Will be used for AutoReturn
/// <summary>
/// Only used internally to schedule client updates
/// </summary>
private byte m_updateFlag;
#region Properties
public LLUUID CreatorID;
public LLUUID ObjectCreator
{
get { return CreatorID; }
}
protected LLUUID m_uuid;
public LLUUID UUID
{
get { return m_uuid; }
set { m_uuid = value; }
}
protected uint m_localID;
public uint LocalID
{
get { return m_localID; }
set { m_localID = value; }
}
protected string m_name;
public virtual string Name
{
get { return m_name; }
set { m_name = value; }
}
protected LLObject.MaterialType m_material = 0;
public byte Material
{
get { return (byte) m_material; }
set { m_material = (LLObject.MaterialType) value; }
}
protected ulong m_regionHandle;
public ulong RegionHandle
{
get { return m_regionHandle; }
set { m_regionHandle = value; }
}
//unkown if this will be kept, added as a way of removing the group position from the group class
protected LLVector3 m_groupPosition;
/// <summary>
/// Method for a prim to get it's world position from the group.
/// Remember, the Group Position simply gives the position of the group itself
/// </summary>
/// <returns>A Linked Child Prim objects position in world</returns>
public LLVector3 GetWorldPosition()
{
Quaternion parentRot = new Quaternion(
ParentGroup.RootPart.RotationOffset.W,
ParentGroup.RootPart.RotationOffset.X,
ParentGroup.RootPart.RotationOffset.Y,
ParentGroup.RootPart.RotationOffset.Z);
Vector3 axPos
= new Vector3(
OffsetPosition.X,
OffsetPosition.Y,
OffsetPosition.Z);
axPos = parentRot * axPos;
LLVector3 translationOffsetPosition = new LLVector3(axPos.x, axPos.y, axPos.z);
return GroupPosition + translationOffsetPosition;
//return (new LLVector3(axiomPos.x, axiomPos.y, axiomPos.z) + AbsolutePosition);
}
/// <summary>
/// Gets the rotation of this prim offset by the group rotation
/// </summary>
/// <returns></returns>
public LLQuaternion GetWorldRotation()
{
Quaternion newRot;
if (this.LinkNum == 0)
{
newRot = new Quaternion(RotationOffset.W,RotationOffset.X,RotationOffset.Y,RotationOffset.Z);
}
else
{
Quaternion parentRot = new Quaternion(
ParentGroup.RootPart.RotationOffset.W,
ParentGroup.RootPart.RotationOffset.X,
ParentGroup.RootPart.RotationOffset.Y,
ParentGroup.RootPart.RotationOffset.Z);
Quaternion oldRot
= new Quaternion(
RotationOffset.W,
RotationOffset.X,
RotationOffset.Y,
RotationOffset.Z);
newRot = parentRot * oldRot;
}
return new LLQuaternion(newRot.x, newRot.y, newRot.z, newRot.w);
//return new LLQuaternion(axiomPartRotation.x, axiomPartRotation.y, axiomPartRotation.z, axiomPartRotation.w);
}
public LLVector3 GroupPosition
{
get
{
// If this is a linkset, we don't want the physics engine mucking up our group position here.
if (PhysActor != null && ParentID == 0)
{
m_groupPosition.X = PhysActor.Position.X;
m_groupPosition.Y = PhysActor.Position.Y;
m_groupPosition.Z = PhysActor.Position.Z;
}
return m_groupPosition;
}
set
{
m_groupPosition = value;
if (PhysActor != null)
{
try
{
// Root prim actually goes at Position
if (ParentID == 0)
{
PhysActor.Position = new PhysicsVector(value.X, value.Y, value.Z);
}
else
{
// To move the child prim in respect to the group position and rotation we have to calculate
LLVector3 resultingposition = GetWorldPosition();
PhysActor.Position = new PhysicsVector(resultingposition.X, resultingposition.Y, resultingposition.Z);
LLQuaternion resultingrot = GetWorldRotation();
PhysActor.Orientation = new Quaternion(resultingrot.W, resultingrot.X, resultingrot.Y, resultingrot.Z);
}
// Tell the physics engines that this prim changed.
m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
private byte[] m_TextureAnimation;
protected LLVector3 m_offsetPosition;
public LLVector3 OffsetPosition
{
get { return m_offsetPosition; }
set { m_offsetPosition = value;
try
{
// Hack to get the child prim to update world positions in the physics engine
ParentGroup.ResetChildPrimPhysicsPositions();
}
catch (System.NullReferenceException)
{
// Ignore, and skip over.
}
//MainLog.Instance.Verbose("PART", "OFFSET:" + m_offsetPosition, ToString());
}
}
public LLVector3 AbsolutePosition
{
get { return m_offsetPosition + m_groupPosition; }
}
protected LLQuaternion m_rotationOffset;
public LLQuaternion RotationOffset
{
get
{
// We don't want the physics engine mucking up the rotations in a linkset
if (PhysActor != null && ParentID == 0)
{
if (PhysActor.Orientation.x != 0 || PhysActor.Orientation.y != 0
|| PhysActor.Orientation.z != 0 || PhysActor.Orientation.w != 0)
{
m_rotationOffset.X = PhysActor.Orientation.x;
m_rotationOffset.Y = PhysActor.Orientation.y;
m_rotationOffset.Z = PhysActor.Orientation.z;
m_rotationOffset.W = PhysActor.Orientation.w;
}
}
return m_rotationOffset;
}
set
{
m_rotationOffset = value;
if (PhysActor != null)
{
try
{
// Root prim gets value directly
if (ParentID == 0)
{
PhysActor.Orientation = new Quaternion(value.W, value.X, value.Y, value.Z);
//MainLog.Instance.Verbose("PART", "RO1:" + PhysActor.Orientation.ToString());
}
else
{
// Child prim we have to calculate it's world rotationwel
LLQuaternion resultingrotation = GetWorldRotation();
PhysActor.Orientation = new Quaternion(resultingrotation.W, resultingrotation.X, resultingrotation.Y, resultingrotation.Z);
//MainLog.Instance.Verbose("PART", "RO2:" + PhysActor.Orientation.ToString());
}
m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor);
//}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
protected LLVector3 m_velocity;
protected LLVector3 m_rotationalvelocity;
/// <summary></summary>
public LLVector3 Velocity
{
get
{
//if (PhysActor.Velocity.x != 0 || PhysActor.Velocity.y != 0
//|| PhysActor.Velocity.z != 0)
//{
if (PhysActor != null)
{
if (PhysActor.IsPhysical)
{
m_velocity.X = PhysActor.Velocity.X;
m_velocity.Y = PhysActor.Velocity.Y;
m_velocity.Z = PhysActor.Velocity.Z;
}
}
return m_velocity;
}
set { m_velocity = value; }
}
public LLVector3 RotationalVelocity
{
get
{
//if (PhysActor.Velocity.x != 0 || PhysActor.Velocity.y != 0
//|| PhysActor.Velocity.z != 0)
//{
if (PhysActor != null)
{
if (PhysActor.IsPhysical)
{
m_rotationalvelocity.X = PhysActor.RotationalVelocity.X;
m_rotationalvelocity.Y = PhysActor.RotationalVelocity.Y;
m_rotationalvelocity.Z = PhysActor.RotationalVelocity.Z;
}
}
return m_rotationalvelocity;
}
set { m_rotationalvelocity = value; }
}
protected LLVector3 m_angularVelocity;
/// <summary></summary>
public LLVector3 AngularVelocity
{
get { return m_angularVelocity; }
set { m_angularVelocity = value; }
}
protected LLVector3 m_acceleration;
/// <summary></summary>
public LLVector3 Acceleration
{
get { return m_acceleration; }
set { m_acceleration = value; }
}
private string m_description = String.Empty;
public string Description
{
get { return m_description; }
set { m_description = value; }
}
private Color m_color = Color.Black;
public Color Color
{
get { return m_color; }
set
{
m_color = value;
TriggerScriptChangedEvent(Changed.COLOR);
/* ScheduleFullUpdate() need not be called b/c after
* setting the color, the text will be set, so then
* ScheduleFullUpdate() will be called. */
//ScheduleFullUpdate();
}
}
private string m_text = String.Empty;
public Vector3 SitTargetPosition
{
get { return m_sitTargetPosition; }
}
public Quaternion SitTargetOrientation
{
get { return m_sitTargetOrientation; }
}
public string Text
{
get { return m_text; }
set
{
m_text = value;
ScheduleFullUpdate();
}
}
private string m_sitName = String.Empty;
public string SitName
{
get { return m_sitName; }
set { m_sitName = value; }
}
private string m_touchName = String.Empty;
public string TouchName
{
get { return m_touchName; }
set { m_touchName = value; }
}
private int m_linkNum = 0;
public int LinkNum
{
get { return m_linkNum; }
set
{
m_linkNum = value;
TriggerScriptChangedEvent(Changed.LINK);
}
}
private byte m_clickAction = 0;
public byte ClickAction
{
get { return m_clickAction; }
set
{
m_clickAction = value;
ScheduleFullUpdate();
}
}
protected PrimitiveBaseShape m_shape;
/// <summary>
/// hook to the physics scene to apply impulse
/// This is sent up to the group, which then finds the root prim
/// and applies the force on the root prim of the group
/// </summary>
/// <param name="impulse">Vector force</param>
public void ApplyImpulse(LLVector3 impulsei)
{
PhysicsVector impulse = new PhysicsVector(impulsei.X, impulsei.Y, impulsei.Z);
if (m_parentGroup != null)
{
m_parentGroup.applyImpulse(impulse);
}
}
public void TriggerScriptChangedEvent(Changed val)
{
if (m_parentGroup != null)
{
if (m_parentGroup.Scene != null)
m_parentGroup.Scene.TriggerObjectChanged(LocalID, (uint)val);
}
}
public PrimitiveBaseShape Shape
{
get { return m_shape; }
set
{
m_shape = value;
TriggerScriptChangedEvent(Changed.SHAPE);
}
}
public LLVector3 Scale
{
get { return m_shape.Scale; }
set
{
m_shape.Scale = value;
TriggerScriptChangedEvent(Changed.SCALE);
}
}
public bool Stopped
{
get {
double threshold = 0.02;
return (Math.Abs(Velocity.X) < threshold &&
Math.Abs(Velocity.Y) < threshold &&
Math.Abs(Velocity.Z) < threshold &&
Math.Abs(AngularVelocity.X) < threshold &&
Math.Abs(AngularVelocity.Y) < threshold &&
Math.Abs(AngularVelocity.Z) < threshold);
}
}
#endregion
public LLUUID ObjectOwner
{
get { return OwnerID; }
}
// FIXME, TODO, ERROR: 'ParentGroup' can't be in here, move it out.
protected SceneObjectGroup m_parentGroup;
public SceneObjectGroup ParentGroup
{
get { return m_parentGroup; }
}
public byte UpdateFlag
{
get { return m_updateFlag; }
set { m_updateFlag = value; }
}
#region Constructors
/// <summary>
/// No arg constructor called by region restore db code
/// </summary>
public SceneObjectPart()
{
// It's not necessary to persist this
m_inventoryFileName = "taskinventory" + LLUUID.Random().ToString();
m_TextureAnimation = new byte[0];
}
public SceneObjectPart(ulong regionHandle, SceneObjectGroup parent, LLUUID ownerID, uint localID,
PrimitiveBaseShape shape, LLVector3 groupPosition, LLVector3 offsetPosition)
: this(regionHandle, parent, ownerID, localID, shape, groupPosition, LLQuaternion.Identity, offsetPosition)
{
}
/// <summary>
/// Create a completely new SceneObjectPart (prim)
/// </summary>
/// <param name="regionHandle"></param>
/// <param name="parent"></param>
/// <param name="ownerID"></param>
/// <param name="localID"></param>
/// <param name="shape"></param>
/// <param name="position"></param>
public SceneObjectPart(ulong regionHandle, SceneObjectGroup parent, LLUUID ownerID, uint localID,
PrimitiveBaseShape shape, LLVector3 groupPosition, LLQuaternion rotationOffset,
LLVector3 offsetPosition)
{
m_name = "Primitive";
m_regionHandle = regionHandle;
m_parentGroup = parent;
CreationDate = (Int32) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
OwnerID = ownerID;
CreatorID = OwnerID;
LastOwnerID = LLUUID.Zero;
UUID = LLUUID.Random();
LocalID = (uint) (localID);
Shape = shape;
// Todo: Add More Object Parameter from above!
OwnershipCost = 0;
ObjectSaleType = (byte) 0;
SalePrice = 0;
Category = (uint) 0;
LastOwnerID = CreatorID;
// End Todo: ///
GroupPosition = groupPosition;
OffsetPosition = offsetPosition;
RotationOffset = rotationOffset;
Velocity = new LLVector3(0, 0, 0);
m_rotationalvelocity = new LLVector3(0, 0, 0);
AngularVelocity = new LLVector3(0, 0, 0);
Acceleration = new LLVector3(0, 0, 0);
m_TextureAnimation = new byte[0];
m_inventoryFileName = "taskinventory" + LLUUID.Random().ToString();
m_folderID = LLUUID.Random();
Flags = 0;
Flags |= LLObject.ObjectFlags.Touch |
LLObject.ObjectFlags.AllowInventoryDrop |
LLObject.ObjectFlags.CreateSelected;
TrimPermissions();
ScheduleFullUpdate();
}
/// <summary>
/// Re/create a SceneObjectPart (prim)
/// currently not used, and maybe won't be
/// </summary>
/// <param name="regionHandle"></param>
/// <param name="parent"></param>
/// <param name="ownerID"></param>
/// <param name="localID"></param>
/// <param name="shape"></param>
/// <param name="position"></param>
public SceneObjectPart(ulong regionHandle, SceneObjectGroup parent, int creationDate, LLUUID ownerID,
LLUUID creatorID, LLUUID lastOwnerID, uint localID, PrimitiveBaseShape shape,
LLVector3 position, LLQuaternion rotation, uint flags)
{
m_regionHandle = regionHandle;
m_parentGroup = parent;
TimeStampTerse = (uint) Util.UnixTimeSinceEpoch();
CreationDate = creationDate;
OwnerID = ownerID;
CreatorID = creatorID;
LastOwnerID = lastOwnerID;
UUID = LLUUID.Random();
LocalID = (uint) (localID);
Shape = shape;
OwnershipCost = 0;
ObjectSaleType = (byte) 0;
SalePrice = 0;
Category = (uint) 0;
LastOwnerID = CreatorID;
OffsetPosition = position;
RotationOffset = rotation;
ObjectFlags = flags;
TrimPermissions();
// ApplyPhysics();
ScheduleFullUpdate();
}
#endregion
/// <summary>
///
/// </summary>
/// <param name="xmlreader"></param>
/// <returns></returns>
public static SceneObjectPart FromXml(XmlReader xmlReader)
{
XmlSerializer serializer = new XmlSerializer(typeof (SceneObjectPart));
SceneObjectPart newobject = (SceneObjectPart) serializer.Deserialize(xmlReader);
return newobject;
}
public void ApplyPhysics(uint rootObjectFlags, bool m_physicalPrim)
{
bool isPhysical = (((rootObjectFlags & (uint) LLObject.ObjectFlags.Physics) != 0) && m_physicalPrim);
bool isPhantom = ((rootObjectFlags & (uint) LLObject.ObjectFlags.Phantom) != 0);
// Added clarification.. since A rigid body is an object that you can kick around, etc.
bool RigidBody = isPhysical && !isPhantom;
// The only time the physics scene shouldn't know about the prim is if it's phantom
if (!isPhantom)
{
PhysActor = m_parentGroup.Scene.PhysicsScene.AddPrimShape(
Name,
Shape,
new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y,
AbsolutePosition.Z),
new PhysicsVector(Scale.X, Scale.Y, Scale.Z),
new Quaternion(RotationOffset.W, RotationOffset.X,
RotationOffset.Y, RotationOffset.Z), RigidBody);
DoPhysicsPropertyUpdate(RigidBody, true);
}
}
public void ApplyNextOwnerPermissions()
{
BaseMask = NextOwnerMask;
OwnerMask = NextOwnerMask;
TriggerScriptChangedEvent(Changed.OWNER);
}
public void TrimPermissions()
{
BaseMask &= (uint)PermissionMask.All;
OwnerMask &= (uint)PermissionMask.All;
GroupMask &= (uint)PermissionMask.All;
EveryoneMask &= (uint)PermissionMask.All;
NextOwnerMask &= (uint)PermissionMask.All;
}
/// <summary>
///
/// </summary>
/// <param name="xmlWriter"></param>
public void ToXml(XmlWriter xmlWriter)
{
XmlSerializer serializer = new XmlSerializer(typeof (SceneObjectPart));
serializer.Serialize(xmlWriter, this);
}
public EntityIntersection TestIntersection(Ray iray, Quaternion parentrot)
{
// In this case we're using a sphere with a radius of the largest dimention of the prim
// TODO: Change to take shape into account
EntityIntersection returnresult = new EntityIntersection();
Vector3 vAbsolutePosition = new Vector3(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z);
Vector3 vScale = new Vector3(Scale.X, Scale.Y, Scale.Z);
Quaternion qRotation =
new Quaternion(RotationOffset.W, RotationOffset.X, RotationOffset.Y, RotationOffset.Z);
//Quaternion worldRotation = (qRotation*parentrot);
//Matrix3 worldRotM = worldRotation.ToRotationMatrix();
Vector3 rOrigin = iray.Origin;
Vector3 rDirection = iray.Direction;
//rDirection = rDirection.Normalize();
// Buidling the first part of the Quadratic equation
Vector3 r2ndDirection = rDirection*rDirection;
float itestPart1 = r2ndDirection.x + r2ndDirection.y + r2ndDirection.z;
// Buidling the second part of the Quadratic equation
Vector3 tmVal2 = rOrigin - vAbsolutePosition;
Vector3 r2Direction = rDirection*2.0f;
Vector3 tmVal3 = r2Direction*tmVal2;
float itestPart2 = tmVal3.x + tmVal3.y + tmVal3.z;
// Buidling the third part of the Quadratic equation
Vector3 tmVal4 = rOrigin*rOrigin;
Vector3 tmVal5 = vAbsolutePosition*vAbsolutePosition;
Vector3 tmVal6 = vAbsolutePosition*rOrigin;
// Set Radius to the largest dimention of the prim
float radius = 0f;
if (vScale.x > radius)
radius = vScale.x;
if (vScale.y > radius)
radius = vScale.y;
if (vScale.z > radius)
radius = vScale.z;
//radius = radius;
float itestPart3 = tmVal4.x + tmVal4.y + tmVal4.z + tmVal5.x + tmVal5.y + tmVal5.z -
(2.0f*(tmVal6.x + tmVal6.y + tmVal6.z + (radius*radius)));
// Yuk Quadradrics.. Solve first
float rootsqr = (itestPart2*itestPart2) - (4.0f*itestPart1*itestPart3);
if (rootsqr < 0.0f)
{
// No intersection
return returnresult;
}
float root = ((-itestPart2) - (float) Math.Sqrt((double) rootsqr))/(itestPart1*2.0f);
if (root < 0.0f)
{
// perform second quadratic root solution
root = ((-itestPart2) + (float) Math.Sqrt((double) rootsqr))/(itestPart1*2.0f);
// is there any intersection?
if (root < 0.0f)
{
// nope, no intersection
return returnresult;
}
}
// We got an intersection. putting together an EntityIntersection object with the
// intersection information
Vector3 ipoint =
new Vector3(iray.Origin.x + (iray.Direction.x*root), iray.Origin.y + (iray.Direction.y*root),
iray.Origin.z + (iray.Direction.z*root));
returnresult.HitTF = true;
returnresult.ipoint = ipoint;
// Normal is calculated by the difference and then normalizing the result
Vector3 normalpart = ipoint - vAbsolutePosition;
returnresult.normal = normalpart.Normalize();
// It's funny how the LLVector3 object has a Distance function, but the Axiom.Math object doesnt.
// I can write a function to do it.. but I like the fact that this one is Static.
LLVector3 distanceConvert1 = new LLVector3(iray.Origin.x, iray.Origin.y, iray.Origin.z);
LLVector3 distanceConvert2 = new LLVector3(ipoint.x, ipoint.y, ipoint.z);
float distance = (float) Util.GetDistanceTo(distanceConvert1, distanceConvert2);
returnresult.distance = distance;
return returnresult;
}
/// <summary>
///
/// </summary>
public void SetParent(SceneObjectGroup parent)
{
m_parentGroup = parent;
}
public void SetSitTarget(Vector3 offset, Quaternion orientation)
{
m_sitTargetPosition = offset;
m_sitTargetOrientation = orientation;
}
public LLVector3 GetSitTargetPositionLL()
{
return new LLVector3(m_sitTargetPosition.x, m_sitTargetPosition.y, m_sitTargetPosition.z);
}
public LLQuaternion GetSitTargetOrientationLL()
{
return
new LLQuaternion(m_sitTargetOrientation.x, m_sitTargetOrientation.y, m_sitTargetOrientation.z,
m_sitTargetOrientation.w);
}
// Utility function so the databases don't have to reference axiom.math
public void SetSitTargetLL(LLVector3 offset, LLQuaternion orientation)
{
if (
!(offset.X == 0 && offset.Y == 0 && offset.Z == 0 && (orientation.W == 0 || orientation.W == 1) &&
orientation.X == 0 && orientation.Y == 0 && orientation.Z == 0))
{
m_sitTargetPosition = new Vector3(offset.X, offset.Y, offset.Z);
m_sitTargetOrientation = new Quaternion(orientation.W, orientation.X, orientation.Y, orientation.Z);
}
}
public Vector3 GetSitTargetPosition()
{
return m_sitTargetPosition;
}
public Quaternion GetSitTargetOrientation()
{
return m_sitTargetOrientation;
}
public void SetAvatarOnSitTarget(LLUUID avatarID)
{
m_sitTargetAvatar = avatarID;
TriggerScriptChangedEvent(Changed.LINK);
}
public LLUUID GetAvatarOnSitTarget()
{
return m_sitTargetAvatar;
}
public LLUUID GetRootPartUUID()
{
if (m_parentGroup != null)
{
return m_parentGroup.UUID;
}
return LLUUID.Zero;
}
public static SceneObjectPart Create()
{
SceneObjectPart part = new SceneObjectPart();
part.UUID = LLUUID.Random();
PrimitiveBaseShape shape = PrimitiveBaseShape.Create();
part.Shape = shape;
part.Name = "Primitive";
part.OwnerID = LLUUID.Random();
return part;
}
#region Copying
/// <summary>
/// Duplicates this part.
/// </summary>
/// <returns></returns>
public SceneObjectPart Copy(uint localID, LLUUID AgentID, LLUUID GroupID, int linkNum)
{
SceneObjectPart dupe = (SceneObjectPart) MemberwiseClone();
dupe.m_shape = m_shape.Copy();
dupe.m_regionHandle = m_regionHandle;
dupe.UUID = LLUUID.Random();
dupe.LocalID = localID;
dupe.OwnerID = AgentID;
dupe.GroupID = GroupID;
dupe.GroupPosition = new LLVector3(GroupPosition.X, GroupPosition.Y, GroupPosition.Z);
dupe.OffsetPosition = new LLVector3(OffsetPosition.X, OffsetPosition.Y, OffsetPosition.Z);
dupe.RotationOffset =
new LLQuaternion(RotationOffset.X, RotationOffset.Y, RotationOffset.Z, RotationOffset.W);
dupe.Velocity = new LLVector3(0, 0, 0);
dupe.Acceleration = new LLVector3(0, 0, 0);
dupe.AngularVelocity = new LLVector3(0, 0, 0);
dupe.ObjectFlags = ObjectFlags;
dupe.OwnershipCost = OwnershipCost;
dupe.ObjectSaleType = ObjectSaleType;
dupe.SalePrice = SalePrice;
dupe.Category = Category;
dupe.TaskInventory = (TaskInventoryDictionary)dupe.TaskInventory.Clone();
dupe.ResetIDs(linkNum);
// This may be wrong... it might have to be applied in SceneObjectGroup to the object that's being duplicated.
dupe.LastOwnerID = ObjectOwner;
byte[] extraP = new byte[Shape.ExtraParams.Length];
Array.Copy(Shape.ExtraParams, extraP, extraP.Length);
dupe.Shape.ExtraParams = extraP;
bool UsePhysics = ((dupe.ObjectFlags & (uint) LLObject.ObjectFlags.Physics) != 0);
dupe.DoPhysicsPropertyUpdate(UsePhysics, true);
return dupe;
}
#endregion
/// <summary>
/// Reset LLUUIDs for this part. This involves generate this part's own LLUUID and
/// generating new LLUUIDs for all the items in the inventory.
/// </summary>
/// <param name="linkNum'>Link number for the part</param>
public void ResetIDs(int linkNum)
{
UUID = LLUUID.Random();
LinkNum = linkNum;
ResetInventoryIDs();
}
#region Update Scheduling
/// <summary>
///
/// </summary>
private void ClearUpdateSchedule()
{
m_updateFlag = 0;
}
/// <summary>
///
/// </summary>
public void ScheduleFullUpdate()
{
if (m_parentGroup != null)
{
m_parentGroup.HasGroupChanged = true;
}
TimeStampFull = (uint) Util.UnixTimeSinceEpoch();
m_updateFlag = 2;
}
public void AddFlag(LLObject.ObjectFlags flag)
{
LLObject.ObjectFlags prevflag = Flags;
//uint objflags = Flags;
if ((ObjectFlags & (uint) flag) == 0)
{
//Console.WriteLine("Adding flag: " + ((LLObject.ObjectFlags) flag).ToString());
Flags |= flag;
}
//uint currflag = (uint)Flags;
//System.Console.WriteLine("Aprev: " + prevflag.ToString() + " curr: " + Flags.ToString());
//ScheduleFullUpdate();
}
public void RemFlag(LLObject.ObjectFlags flag)
{
LLObject.ObjectFlags prevflag = Flags;
if ((ObjectFlags & (uint) flag) != 0)
{
//Console.WriteLine("Removing flag: " + ((LLObject.ObjectFlags)flag).ToString());
Flags &= ~flag;
}
//System.Console.WriteLine("prev: " + prevflag.ToString() + " curr: " + Flags.ToString());
//ScheduleFullUpdate();
}
/// <summary>
///
/// </summary>
public void ScheduleTerseUpdate()
{
if (m_updateFlag < 1)
{
if (m_parentGroup != null)
{
m_parentGroup.HasGroupChanged = true;
}
TimeStampTerse = (uint) Util.UnixTimeSinceEpoch();
m_updateFlag = 1;
}
}
/// <summary>
///
/// </summary>
public void SendScheduledUpdates()
{
if (m_updateFlag == 1) //some change has been made so update the clients
{
AddTerseUpdateToAllAvatars();
ClearUpdateSchedule();
// This causes the Scene to 'poll' physical objects every couple of frames
// bad, so it's been replaced by an event driven method.
//if ((ObjectFlags & (uint)LLObject.ObjectFlags.Physics) != 0)
//{
// Only send the constant terse updates on physical objects!
//ScheduleTerseUpdate();
//}
}
else
{
if (m_updateFlag == 2) // is a new prim, just created/reloaded or has major changes
{
AddFullUpdateToAllAvatars();
ClearUpdateSchedule();
}
}
}
#endregion
#region Shape
/// <summary>
///
/// </summary>
/// <param name="shapeBlock"></param>
public void UpdateShape(ObjectShapePacket.ObjectDataBlock shapeBlock)
{
m_shape.PathBegin = shapeBlock.PathBegin;
m_shape.PathEnd = shapeBlock.PathEnd;
m_shape.PathScaleX = shapeBlock.PathScaleX;
m_shape.PathScaleY = shapeBlock.PathScaleY;
m_shape.PathShearX = shapeBlock.PathShearX;
m_shape.PathShearY = shapeBlock.PathShearY;
m_shape.PathSkew = shapeBlock.PathSkew;
m_shape.ProfileBegin = shapeBlock.ProfileBegin;
m_shape.ProfileEnd = shapeBlock.ProfileEnd;
m_shape.PathCurve = shapeBlock.PathCurve;
m_shape.ProfileCurve = shapeBlock.ProfileCurve;
m_shape.ProfileHollow = shapeBlock.ProfileHollow;
m_shape.PathRadiusOffset = shapeBlock.PathRadiusOffset;
m_shape.PathRevolutions = shapeBlock.PathRevolutions;
m_shape.PathTaperX = shapeBlock.PathTaperX;
m_shape.PathTaperY = shapeBlock.PathTaperY;
m_shape.PathTwist = shapeBlock.PathTwist;
m_shape.PathTwistBegin = shapeBlock.PathTwistBegin;
ScheduleFullUpdate();
}
#endregion
#region ExtraParams
public void UpdatePrimFlags(ushort type, bool inUse, byte[] data)
{
bool usePhysics = false;
bool IsTemporary = false;
bool IsPhantom = false;
bool castsShadows = false;
bool wasUsingPhysics = ((ObjectFlags & (uint) LLObject.ObjectFlags.Physics) != 0);
//bool IsLocked = false;
int i = 0;
try
{
i += 46;
//IsLocked = (data[i++] != 0) ? true : false;
usePhysics = ((data[i++] != 0) && m_parentGroup.Scene.m_physicalPrim) ? true : false;
//System.Console.WriteLine("U" + packet.ToBytes().Length.ToString());
IsTemporary = (data[i++] != 0) ? true : false;
IsPhantom = (data[i++] != 0) ? true : false;
castsShadows = (data[i++] != 0) ? true : false;
}
catch (Exception)
{
Console.WriteLine("Ignoring invalid Packet:");
//Silently ignore it - TODO: FIXME Quick
}
if (usePhysics)
{
AddFlag(LLObject.ObjectFlags.Physics);
if (!wasUsingPhysics)
{
DoPhysicsPropertyUpdate(usePhysics, false);
}
}
else
{
RemFlag(LLObject.ObjectFlags.Physics);
if (wasUsingPhysics)
{
DoPhysicsPropertyUpdate(usePhysics, false);
}
}
if (IsPhantom)
{
AddFlag(LLObject.ObjectFlags.Phantom);
if (PhysActor != null)
{
m_parentGroup.Scene.PhysicsScene.RemovePrim(PhysActor);
/// that's not wholesome. Had to make Scene public
PhysActor = null;
}
}
else
{
RemFlag(LLObject.ObjectFlags.Phantom);
if (PhysActor == null)
{
PhysActor = m_parentGroup.Scene.PhysicsScene.AddPrimShape(
Name,
Shape,
new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y,
AbsolutePosition.Z),
new PhysicsVector(Scale.X, Scale.Y, Scale.Z),
new Quaternion(RotationOffset.W, RotationOffset.X,
RotationOffset.Y, RotationOffset.Z), usePhysics);
DoPhysicsPropertyUpdate(usePhysics, true);
}
else
{
PhysActor.IsPhysical = usePhysics;
DoPhysicsPropertyUpdate(usePhysics, false);
}
}
if (IsTemporary)
{
AddFlag(LLObject.ObjectFlags.TemporaryOnRez);
}
else
{
RemFlag(LLObject.ObjectFlags.TemporaryOnRez);
}
// System.Console.WriteLine("Update: PHY:" + UsePhysics.ToString() + ", T:" + IsTemporary.ToString() + ", PHA:" + IsPhantom.ToString() + " S:" + CastsShadows.ToString());
ScheduleFullUpdate();
}
public void DoPhysicsPropertyUpdate(bool UsePhysics, bool isNew)
{
if (PhysActor != null)
{
if (UsePhysics != PhysActor.IsPhysical || isNew)
{
if (PhysActor.IsPhysical)
{
if (!isNew)
ParentGroup.Scene.RemovePhysicalPrim(1);
PhysActor.OnRequestTerseUpdate -= PhysicsRequestingTerseUpdate;
PhysActor.OnOutOfBounds -= PhysicsOutOfBounds;
}
PhysActor.IsPhysical = UsePhysics;
// If we're not what we're supposed to be in the physics scene, recreate ourselves.
//m_parentGroup.Scene.PhysicsScene.RemovePrim(PhysActor);
/// that's not wholesome. Had to make Scene public
//PhysActor = null;
if ((ObjectFlags & (uint) LLObject.ObjectFlags.Phantom) == 0)
{
//PhysActor = m_parentGroup.Scene.PhysicsScene.AddPrimShape(
//Name,
//Shape,
//new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y,
//AbsolutePosition.Z),
//new PhysicsVector(Scale.X, Scale.Y, Scale.Z),
//new Quaternion(RotationOffset.W, RotationOffset.X,
//RotationOffset.Y, RotationOffset.Z), UsePhysics);
if (UsePhysics)
{
ParentGroup.Scene.AddPhysicalPrim(1);
PhysActor.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate;
PhysActor.OnOutOfBounds += PhysicsOutOfBounds;
}
}
}
m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor);
}
}
public void UpdateExtraParam(ushort type, bool inUse, byte[] data)
{
m_shape.ExtraParams = new byte[data.Length + 7];
int i = 0;
uint length = (uint) data.Length;
m_shape.ExtraParams[i++] = 1;
m_shape.ExtraParams[i++] = (byte) (type%256);
m_shape.ExtraParams[i++] = (byte) ((type >> 8)%256);
m_shape.ExtraParams[i++] = (byte) (length%256);
m_shape.ExtraParams[i++] = (byte) ((length >> 8)%256);
m_shape.ExtraParams[i++] = (byte) ((length >> 16)%256);
m_shape.ExtraParams[i++] = (byte) ((length >> 24)%256);
Array.Copy(data, 0, m_shape.ExtraParams, i, data.Length);
ScheduleFullUpdate();
}
#endregion
#region Physics
public float GetMass()
{
if (PhysActor != null)
{
return PhysActor.Mass;
}
else
{
return 0;
}
}
public LLVector3 GetGeometricCenter()
{
if (PhysActor != null)
{
return new LLVector3(PhysActor.CenterOfMass.X, PhysActor.CenterOfMass.Y, PhysActor.CenterOfMass.Z);
}
else
{
return new LLVector3(0, 0, 0);
}
}
#endregion
#region Texture
/// <summary>
///
/// </summary>
/// <param name="textureEntry"></param>
public void UpdateTextureEntry(byte[] textureEntry)
{
m_shape.TextureEntry = textureEntry;
TriggerScriptChangedEvent(Changed.TEXTURE);
ScheduleFullUpdate();
}
// Added to handle bug in libsecondlife's TextureEntry.ToBytes()
// not handling RGBA properly. Cycles through, and "fixes" the color
// info
public void UpdateTexture(LLObject.TextureEntry tex)
{
//LLColor tmpcolor;
//for (uint i = 0; i < 32; i++)
//{
// if (tex.FaceTextures[i] != null)
// {
// tmpcolor = tex.GetFace((uint) i).RGBA;
// tmpcolor.A = tmpcolor.A*255;
// tmpcolor.R = tmpcolor.R*255;
// tmpcolor.G = tmpcolor.G*255;
// tmpcolor.B = tmpcolor.B*255;
// tex.FaceTextures[i].RGBA = tmpcolor;
// }
//}
//tmpcolor = tex.DefaultTexture.RGBA;
//tmpcolor.A = tmpcolor.A*255;
//tmpcolor.R = tmpcolor.R*255;
//tmpcolor.G = tmpcolor.G*255;
//tmpcolor.B = tmpcolor.B*255;
//tex.DefaultTexture.RGBA = tmpcolor;
UpdateTextureEntry(tex.ToBytes());
}
public byte ConvertScriptUintToByte(uint indata)
{
byte outdata = (byte)TextureAnimFlags.NONE;
if ((indata & 1) != 0) outdata |= (byte)TextureAnimFlags.ANIM_ON;
if ((indata & 2) != 0) outdata |= (byte)TextureAnimFlags.LOOP;
if ((indata & 4) != 0) outdata |= (byte)TextureAnimFlags.REVERSE;
if ((indata & 8) != 0) outdata |= (byte)TextureAnimFlags.PING_PONG;
if ((indata & 16) != 0) outdata |= (byte)TextureAnimFlags.SMOOTH;
if ((indata & 32) != 0) outdata |= (byte)TextureAnimFlags.ROTATE;
if ((indata & 64) != 0) outdata |= (byte)TextureAnimFlags.SCALE;
return outdata;
}
public void AddTextureAnimation(Primitive.TextureAnimation pTexAnim)
{
byte[] data = new byte[16];
int pos = 0;
// The flags don't like conversion from uint to byte, so we have to do
// it the crappy way. See the above function :(
data[pos] = ConvertScriptUintToByte(pTexAnim.Flags); pos++;
data[pos] = (byte)pTexAnim.Face; pos++;
data[pos] = (byte)pTexAnim.SizeX; pos++;
data[pos] = (byte)pTexAnim.SizeX; pos++;
Helpers.FloatToBytes(pTexAnim.Start).CopyTo(data, pos);
Helpers.FloatToBytes(pTexAnim.Length ).CopyTo(data, pos + 4);
Helpers.FloatToBytes(pTexAnim.Rate).CopyTo(data, pos + 8);
m_TextureAnimation = data;
}
#endregion
#region ParticleSystem
public void AddNewParticleSystem(Primitive.ParticleSystem pSystem)
{
m_particleSystem = pSystem.GetBytes();
}
#endregion
#region Position
/// <summary>
///
/// </summary>
/// <param name="pos"></param>
public void UpdateOffSet(LLVector3 pos)
{
LLVector3 newPos = new LLVector3(pos.X, pos.Y, pos.Z);
OffsetPosition = newPos;
ScheduleTerseUpdate();
}
public void UpdateGroupPosition(LLVector3 pos)
{
LLVector3 newPos = new LLVector3(pos.X, pos.Y, pos.Z);
GroupPosition = newPos;
ScheduleTerseUpdate();
}
#endregion
#region rotation
public void UpdateRotation(LLQuaternion rot)
{
RotationOffset = new LLQuaternion(rot.X, rot.Y, rot.Z, rot.W);
ScheduleTerseUpdate();
}
#endregion
#region Resizing/Scale
/// <summary>
///
/// </summary>
/// <param name="scale"></param>
public void Resize(LLVector3 scale)
{
m_shape.Scale = scale;
ScheduleFullUpdate();
}
#endregion
public void UpdatePermissions(LLUUID AgentID, byte field, uint localID, uint mask, byte addRemTF)
{
// Are we the owner?
if (AgentID == OwnerID)
{
MainLog.Instance.Verbose("PERMISSIONS",
"field: " + field.ToString() + ", mask: " + mask.ToString() + " addRemTF: " +
addRemTF.ToString());
//Field 8 = EveryoneMask
if (field == (byte) 8)
{
MainLog.Instance.Verbose("PERMISSIONS", "Left over: " + (OwnerMask - EveryoneMask));
if (addRemTF == (byte) 0)
{
//EveryoneMask = (uint)0;
EveryoneMask &= ~mask;
//EveryoneMask &= ~(uint)57344;
}
else
{
//EveryoneMask = (uint)0;
EveryoneMask |= mask;
//EveryoneMask |= (uint)57344;
}
//ScheduleFullUpdate();
SendFullUpdateToAllClients();
}
//Field 16 = NextownerMask
if (field == (byte) 16)
{
if (addRemTF == (byte) 0)
{
NextOwnerMask &= ~mask;
}
else
{
NextOwnerMask |= mask;
}
SendFullUpdateToAllClients();
}
}
}
#region Client Update Methods
public void AddFullUpdateToAllAvatars()
{
List<ScenePresence> avatars = m_parentGroup.GetScenePresences();
for (int i = 0; i < avatars.Count; i++)
{
avatars[i].QueuePartForUpdate(this);
}
}
public void AddFullUpdateToAvatar(ScenePresence presence)
{
presence.QueuePartForUpdate(this);
}
/// <summary>
///
/// </summary>
public void SendFullUpdateToAllClients()
{
List<ScenePresence> avatars = m_parentGroup.GetScenePresences();
for (int i = 0; i < avatars.Count; i++)
{
// Ugly reference :(
m_parentGroup.SendPartFullUpdate(avatars[i].ControllingClient, this,
avatars[i].GenerateClientFlags(UUID));
}
}
/// <summary>
///
/// </summary>
/// <param name="remoteClient"></param>
public void SendFullUpdate(IClientAPI remoteClient, uint clientFlags)
{
m_parentGroup.SendPartFullUpdate(remoteClient, this, clientFlags);
}
/// <summary>
///
/// </summary>
/// <param name="remoteClient"></param>
public void SendFullUpdateToClient(IClientAPI remoteClient, uint clientflags)
{
LLVector3 lPos;
lPos = OffsetPosition;
SendFullUpdateToClient(remoteClient, lPos, clientflags);
}
public void SendFullUpdateToClient(IClientAPI remoteClient, LLVector3 lPos, uint clientFlags)
{
LLQuaternion lRot;
lRot = RotationOffset;
clientFlags &= ~(uint) LLObject.ObjectFlags.CreateSelected;
if (remoteClient.AgentId == OwnerID)
{
if ((uint) (Flags & LLObject.ObjectFlags.CreateSelected) != 0)
{
clientFlags |= (uint) LLObject.ObjectFlags.CreateSelected;
Flags &= ~LLObject.ObjectFlags.CreateSelected;
}
}
byte[] color = new byte[] {m_color.R, m_color.G, m_color.B, m_color.A};
remoteClient.SendPrimitiveToClient(m_regionHandle, 64096, LocalID, m_shape, lPos, clientFlags, m_uuid,
OwnerID,
m_text, color, ParentID, m_particleSystem, lRot, m_clickAction, m_TextureAnimation);
}
/// Terse updates
public void AddTerseUpdateToAllAvatars()
{
List<ScenePresence> avatars = m_parentGroup.GetScenePresences();
for (int i = 0; i < avatars.Count; i++)
{
avatars[i].QueuePartForUpdate(this);
}
}
public void AddTerseUpdateToAvatar(ScenePresence presence)
{
presence.QueuePartForUpdate(this);
}
/// <summary>
///
/// </summary>
public void SendTerseUpdateToAllClients()
{
List<ScenePresence> avatars = m_parentGroup.GetScenePresences();
for (int i = 0; i < avatars.Count; i++)
{
m_parentGroup.SendPartTerseUpdate(avatars[i].ControllingClient, this);
}
}
/// <summary>
///
/// </summary>
/// <param name="remoteClient"></param>
public void SendTerseUpdate(IClientAPI remoteClient)
{
m_parentGroup.SendPartTerseUpdate(remoteClient, this);
}
public void SendTerseUpdateToClient(IClientAPI remoteClient)
{
LLVector3 lPos;
lPos = OffsetPosition;
LLQuaternion mRot = RotationOffset;
if ((ObjectFlags & (uint) LLObject.ObjectFlags.Physics) == 0)
{
remoteClient.SendPrimTerseUpdate(m_regionHandle, 64096, LocalID, lPos, mRot);
}
else
{
remoteClient.SendPrimTerseUpdate(m_regionHandle, 64096, LocalID, lPos, mRot, Velocity,
RotationalVelocity);
}
}
public void SendTerseUpdateToClient(IClientAPI remoteClient, LLVector3 lPos)
{
LLQuaternion mRot = RotationOffset;
if ((ObjectFlags & (uint) LLObject.ObjectFlags.Physics) == 0)
{
remoteClient.SendPrimTerseUpdate(m_regionHandle, 64096, LocalID, lPos, mRot);
}
else
{
remoteClient.SendPrimTerseUpdate(m_regionHandle, 64096, LocalID, lPos, mRot, Velocity,
RotationalVelocity);
//System.Console.WriteLine("RVel:" + RotationalVelocity);
}
}
#endregion
public virtual void UpdateMovement()
{
}
#region Events
public void PhysicsRequestingTerseUpdate()
{
ScheduleTerseUpdate();
//SendTerseUpdateToAllClients();
}
#endregion
public void PhysicsOutOfBounds(PhysicsVector pos)
{
MainLog.Instance.Verbose("PHYSICS", "Physical Object went out of bounds.");
RemFlag(LLObject.ObjectFlags.Physics);
DoPhysicsPropertyUpdate(false, true);
m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor);
}
public virtual void OnGrab(LLVector3 offsetPos, IClientAPI remoteClient)
{
}
public void SetText(string text, Vector3 color, double alpha)
{
Color = Color.FromArgb(0xff - (int) (alpha*0xff),
(int) (color.x*0xff),
(int) (color.y*0xff),
(int) (color.z*0xff));
Text = text;
}
}
}