516 lines
19 KiB
C#
Executable File
516 lines
19 KiB
C#
Executable File
/*
|
|
* 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.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
using OpenSim.Framework;
|
|
using OpenSim.Region.CoreModules;
|
|
using OpenSim.Region.Framework;
|
|
using OpenSim.Region.Framework.Interfaces;
|
|
using OpenSim.Region.Framework.Scenes;
|
|
using OpenSim.Region.Physics.Manager;
|
|
|
|
using Mono.Addins;
|
|
using Nini.Config;
|
|
using log4net;
|
|
using OpenMetaverse;
|
|
|
|
namespace OpenSim.Region.OptionalModules.Scripting
|
|
{
|
|
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
|
|
public class ExtendedPhysics : INonSharedRegionModule
|
|
{
|
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
private static string LogHeader = "[EXTENDED PHYSICS]";
|
|
|
|
// =============================================================
|
|
// Since BulletSim is a plugin, this these values aren't defined easily in one place.
|
|
// This table must correspond to an identical table in BSScene.
|
|
|
|
// Per scene functions. See BSScene.
|
|
|
|
// Per avatar functions. See BSCharacter.
|
|
|
|
// Per prim functions. See BSPrim.
|
|
public const string PhysFunctGetLinksetType = "BulletSim.GetLinksetType";
|
|
public const string PhysFunctSetLinksetType = "BulletSim.SetLinksetType";
|
|
public const string PhysFunctChangeLinkFixed = "BulletSim.ChangeLinkFixed";
|
|
public const string PhysFunctChangeLinkType = "BulletSim.ChangeLinkType";
|
|
public const string PhysFunctChangeLinkParams = "BulletSim.ChangeLinkParams";
|
|
|
|
// =============================================================
|
|
|
|
private IConfig Configuration { get; set; }
|
|
private bool Enabled { get; set; }
|
|
private Scene BaseScene { get; set; }
|
|
private IScriptModuleComms Comms { get; set; }
|
|
|
|
#region INonSharedRegionModule
|
|
|
|
public string Name { get { return this.GetType().Name; } }
|
|
|
|
public void Initialise(IConfigSource config)
|
|
{
|
|
BaseScene = null;
|
|
Enabled = false;
|
|
Configuration = null;
|
|
Comms = null;
|
|
|
|
try
|
|
{
|
|
if ((Configuration = config.Configs["ExtendedPhysics"]) != null)
|
|
{
|
|
Enabled = Configuration.GetBoolean("Enabled", Enabled);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
m_log.ErrorFormat("{0} Initialization error: {0}", LogHeader, e);
|
|
}
|
|
|
|
m_log.InfoFormat("{0} module {1} enabled", LogHeader, (Enabled ? "is" : "is not"));
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
if (BaseScene != null)
|
|
{
|
|
BaseScene.EventManager.OnObjectAddedToScene -= EventManager_OnObjectAddedToScene;
|
|
BaseScene.EventManager.OnSceneObjectPartUpdated -= EventManager_OnSceneObjectPartUpdated;
|
|
BaseScene = null;
|
|
}
|
|
}
|
|
|
|
public void AddRegion(Scene scene)
|
|
{
|
|
}
|
|
|
|
public void RemoveRegion(Scene scene)
|
|
{
|
|
if (BaseScene != null && BaseScene == scene)
|
|
{
|
|
Close();
|
|
}
|
|
}
|
|
|
|
public void RegionLoaded(Scene scene)
|
|
{
|
|
if (!Enabled) return;
|
|
|
|
BaseScene = scene;
|
|
|
|
Comms = BaseScene.RequestModuleInterface<IScriptModuleComms>();
|
|
if (Comms == null)
|
|
{
|
|
m_log.WarnFormat("{0} ScriptModuleComms interface not defined", LogHeader);
|
|
Enabled = false;
|
|
|
|
return;
|
|
}
|
|
|
|
// Register as LSL functions all the [ScriptInvocation] marked methods.
|
|
Comms.RegisterScriptInvocations(this);
|
|
Comms.RegisterConstants(this);
|
|
|
|
// When an object is modified, we might need to update its extended physics parameters
|
|
BaseScene.EventManager.OnObjectAddedToScene += EventManager_OnObjectAddedToScene;
|
|
BaseScene.EventManager.OnSceneObjectPartUpdated += EventManager_OnSceneObjectPartUpdated;
|
|
|
|
}
|
|
|
|
public Type ReplaceableInterface { get { return null; } }
|
|
|
|
#endregion // INonSharedRegionModule
|
|
|
|
private void EventManager_OnObjectAddedToScene(SceneObjectGroup obj)
|
|
{
|
|
}
|
|
|
|
// Event generated when some property of a prim changes.
|
|
private void EventManager_OnSceneObjectPartUpdated(SceneObjectPart sop, bool isFullUpdate)
|
|
{
|
|
}
|
|
|
|
[ScriptConstant]
|
|
public static int PHYS_CENTER_OF_MASS = 1 << 0;
|
|
|
|
[ScriptInvocation]
|
|
public string physGetEngineType(UUID hostID, UUID scriptID)
|
|
{
|
|
string ret = string.Empty;
|
|
|
|
if (BaseScene.PhysicsScene != null)
|
|
{
|
|
ret = BaseScene.PhysicsScene.EngineType;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
[ScriptConstant]
|
|
public static int PHYS_LINKSET_TYPE_CONSTRAINT = 0;
|
|
[ScriptConstant]
|
|
public static int PHYS_LINKSET_TYPE_COMPOUND = 1;
|
|
[ScriptConstant]
|
|
public static int PHYS_LINKSET_TYPE_MANUAL = 2;
|
|
|
|
[ScriptInvocation]
|
|
public int physSetLinksetType(UUID hostID, UUID scriptID, int linksetType)
|
|
{
|
|
int ret = -1;
|
|
|
|
if (!Enabled) return ret;
|
|
|
|
// The part that is requesting the change.
|
|
SceneObjectPart requestingPart = BaseScene.GetSceneObjectPart(hostID);
|
|
|
|
if (requestingPart != null)
|
|
{
|
|
// The change is always made to the root of a linkset.
|
|
SceneObjectGroup containingGroup = requestingPart.ParentGroup;
|
|
SceneObjectPart rootPart = containingGroup.RootPart;
|
|
|
|
if (rootPart != null)
|
|
{
|
|
PhysicsActor rootPhysActor = rootPart.PhysActor;
|
|
if (rootPhysActor != null)
|
|
{
|
|
if (rootPhysActor.IsPhysical)
|
|
{
|
|
// Change a physical linkset by making non-physical, waiting for one heartbeat so all
|
|
// the prim and linkset state is updated, changing the type and making the
|
|
// linkset physical again.
|
|
containingGroup.ScriptSetPhysicsStatus(false);
|
|
Thread.Sleep(150); // longer than one heartbeat tick
|
|
|
|
// A kludge for the moment.
|
|
// Since compound linksets move the children but don't generate position updates to the
|
|
// simulator, it is possible for compound linkset children to have out-of-sync simulator
|
|
// and physical positions. The following causes the simulator to push the real child positions
|
|
// down into the physics engine to get everything synced.
|
|
containingGroup.UpdateGroupPosition(containingGroup.AbsolutePosition);
|
|
containingGroup.UpdateGroupRotationR(containingGroup.GroupRotation);
|
|
|
|
ret = MakeIntError(rootPhysActor.Extension(PhysFunctSetLinksetType, linksetType));
|
|
Thread.Sleep(150); // longer than one heartbeat tick
|
|
|
|
containingGroup.ScriptSetPhysicsStatus(true);
|
|
}
|
|
else
|
|
{
|
|
// Non-physical linksets don't have a physical instantiation so there is no state to
|
|
// worry about being updated.
|
|
ret = MakeIntError(rootPhysActor.Extension(PhysFunctSetLinksetType, linksetType));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} physSetLinksetType: root part does not have a physics actor. rootName={1}, hostID={2}",
|
|
LogHeader, rootPart.Name, hostID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} physSetLinksetType: root part does not exist. RequestingPartName={1}, hostID={2}",
|
|
LogHeader, requestingPart.Name, hostID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} physSetLinsetType: cannot find script object in scene. hostID={1}", LogHeader, hostID);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
[ScriptInvocation]
|
|
public int physGetLinksetType(UUID hostID, UUID scriptID)
|
|
{
|
|
int ret = -1;
|
|
if (!Enabled) return ret;
|
|
|
|
// The part that is requesting the change.
|
|
SceneObjectPart requestingPart = BaseScene.GetSceneObjectPart(hostID);
|
|
|
|
if (requestingPart != null)
|
|
{
|
|
// The type is is always on the root of a linkset.
|
|
SceneObjectGroup containingGroup = requestingPart.ParentGroup;
|
|
SceneObjectPart rootPart = containingGroup.RootPart;
|
|
|
|
if (rootPart != null)
|
|
{
|
|
PhysicsActor rootPhysActor = rootPart.PhysActor;
|
|
if (rootPhysActor != null)
|
|
{
|
|
ret = MakeIntError(rootPhysActor.Extension(PhysFunctGetLinksetType));
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} physGetLinksetType: root part does not have a physics actor. rootName={1}, hostID={2}",
|
|
LogHeader, rootPart.Name, hostID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} physGetLinksetType: root part does not exist. RequestingPartName={1}, hostID={2}",
|
|
LogHeader, requestingPart.Name, hostID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} physGetLinsetType: cannot find script object in scene. hostID={1}", LogHeader, hostID);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
[ScriptConstant]
|
|
public static int PHYS_LINK_TYPE_FIXED = 1234;
|
|
[ScriptConstant]
|
|
public static int PHYS_LINK_TYPE_HINGE = 4;
|
|
[ScriptConstant]
|
|
public static int PHYS_LINK_TYPE_SPRING = 9;
|
|
[ScriptConstant]
|
|
public static int PHYS_LINK_TYPE_6DOF = 6;
|
|
[ScriptConstant]
|
|
public static int PHYS_LINK_TYPE_SLIDER = 7;
|
|
|
|
// physChangeLinkType(integer linkNum, integer typeCode)
|
|
[ScriptInvocation]
|
|
public int physChangeLinkType(UUID hostID, UUID scriptID, int linkNum, int typeCode)
|
|
{
|
|
int ret = -1;
|
|
if (!Enabled) return ret;
|
|
|
|
PhysicsActor rootPhysActor;
|
|
PhysicsActor childPhysActor;
|
|
|
|
if (GetRootAndChildPhysActors(hostID, linkNum, out rootPhysActor, out childPhysActor))
|
|
{
|
|
ret = MakeIntError(rootPhysActor.Extension(PhysFunctChangeLinkType, childPhysActor, typeCode));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// physChangeLinkFixed(integer linkNum)
|
|
// Change the link between the root and the linkNum into a fixed, static physical connection.
|
|
[ScriptInvocation]
|
|
public int physChangeLinkFixed(UUID hostID, UUID scriptID, int linkNum)
|
|
{
|
|
int ret = -1;
|
|
if (!Enabled) return ret;
|
|
|
|
PhysicsActor rootPhysActor;
|
|
PhysicsActor childPhysActor;
|
|
|
|
if (GetRootAndChildPhysActors(hostID, linkNum, out rootPhysActor, out childPhysActor))
|
|
{
|
|
ret = MakeIntError(rootPhysActor.Extension(PhysFunctChangeLinkType, childPhysActor, PHYS_LINK_TYPE_FIXED));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Code for specifying params.
|
|
// The choice if 14400 is arbitrary and only serves to catch parameter code misuse.
|
|
public static int PHYS_PARAM_MIN = 14401;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_FRAMEINA_LOC = 14401;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_FRAMEINA_ROT = 14402;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_FRAMEINB_LOC = 14403;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_FRAMEINB_ROT = 14404;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_LINEAR_LIMIT_LOW = 14405;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_LINEAR_LIMIT_HIGH = 14406;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_ANGULAR_LIMIT_LOW = 14407;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_ANGULAR_LIMIT_HIGH = 14408;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_USE_FRAME_OFFSET = 14409;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_ENABLE_TRANSMOTOR = 14410;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_TRANSMOTOR_MAXVEL = 14411;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_TRANSMOTOR_MAXFORCE = 14412;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_CFM = 14413;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_ERP = 14414;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_SOLVER_ITERATIONS = 14415;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_SPRING_DAMPING = 14416;
|
|
[ScriptConstant]
|
|
public static int PHYS_PARAM_SPRING_STIFFNESS = 14417;
|
|
public static int PHYS_PARAM_MAX = 14417;
|
|
|
|
// physChangeLinkParams(integer linkNum, [ PHYS_PARAM_*, value, PHYS_PARAM_*, value, ...])
|
|
[ScriptInvocation]
|
|
public int physChangeLinkParams(UUID hostID, UUID scriptID, int linkNum, object[] parms)
|
|
{
|
|
int ret = -1;
|
|
if (!Enabled) return ret;
|
|
|
|
PhysicsActor rootPhysActor;
|
|
PhysicsActor childPhysActor;
|
|
|
|
if (GetRootAndChildPhysActors(hostID, linkNum, out rootPhysActor, out childPhysActor))
|
|
{
|
|
ret = MakeIntError(rootPhysActor.Extension(PhysFunctChangeLinkParams, childPhysActor, parms));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
private bool GetRootPhysActor(UUID hostID, out PhysicsActor rootPhysActor)
|
|
{
|
|
SceneObjectGroup containingGroup;
|
|
SceneObjectPart rootPart;
|
|
return GetRootPhysActor(hostID, out containingGroup, out rootPart, out rootPhysActor);
|
|
}
|
|
|
|
private bool GetRootPhysActor(UUID hostID, out SceneObjectGroup containingGroup, out SceneObjectPart rootPart, out PhysicsActor rootPhysActor)
|
|
{
|
|
bool ret = false;
|
|
rootPhysActor = null;
|
|
containingGroup = null;
|
|
rootPart = null;
|
|
|
|
SceneObjectPart requestingPart;
|
|
|
|
requestingPart = BaseScene.GetSceneObjectPart(hostID);
|
|
if (requestingPart != null)
|
|
{
|
|
// The type is is always on the root of a linkset.
|
|
containingGroup = requestingPart.ParentGroup;
|
|
if (containingGroup != null && !containingGroup.IsDeleted)
|
|
{
|
|
rootPart = containingGroup.RootPart;
|
|
if (rootPart != null)
|
|
{
|
|
rootPhysActor = rootPart.PhysActor;
|
|
if (rootPhysActor != null)
|
|
{
|
|
ret = true;
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} GetRootAndChildPhysActors: Root part does not have a physics actor. rootName={1}, hostID={2}",
|
|
LogHeader, rootPart.Name, hostID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} GetRootAndChildPhysActors: Root part does not exist. RequestingPartName={1}, hostID={2}",
|
|
LogHeader, requestingPart.Name, hostID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} GetRootAndChildPhysActors: Containing group missing or deleted. hostID={1}", LogHeader, hostID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} GetRootAndChildPhysActors: cannot find script object in scene. hostID={1}", LogHeader, hostID);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Find the root and child PhysActors based on the linkNum.
|
|
// Return 'true' if both are found and returned.
|
|
private bool GetRootAndChildPhysActors(UUID hostID, int linkNum, out PhysicsActor rootPhysActor, out PhysicsActor childPhysActor)
|
|
{
|
|
bool ret = false;
|
|
rootPhysActor = null;
|
|
childPhysActor = null;
|
|
|
|
SceneObjectGroup containingGroup;
|
|
SceneObjectPart rootPart;
|
|
|
|
if (GetRootPhysActor(hostID, out containingGroup, out rootPart, out rootPhysActor))
|
|
{
|
|
SceneObjectPart linkPart = containingGroup.GetLinkNumPart(linkNum);
|
|
if (linkPart != null)
|
|
{
|
|
childPhysActor = linkPart.PhysActor;
|
|
if (childPhysActor != null)
|
|
{
|
|
ret = true;
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} GetRootAndChildPhysActors: Link part has no physical actor. rootName={1}, hostID={2}, linknum={3}",
|
|
LogHeader, rootPart.Name, hostID, linkNum);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} GetRootAndChildPhysActors: Could not find linknum part. rootName={1}, hostID={2}, linknum={3}",
|
|
LogHeader, rootPart.Name, hostID, linkNum);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_log.WarnFormat("{0} GetRootAndChildPhysActors: Root part does not have a physics actor. rootName={1}, hostID={2}",
|
|
LogHeader, rootPart.Name, hostID);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Extension() returns an object. Convert that object into the integer error we expect to return.
|
|
private int MakeIntError(object extensionRet)
|
|
{
|
|
int ret = -1;
|
|
if (extensionRet != null)
|
|
{
|
|
try
|
|
{
|
|
ret = (int)extensionRet;
|
|
}
|
|
catch
|
|
{
|
|
ret = -1;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
}
|