From 1d6b33bc2da3b312cff1d1802a73aacdf72b0385 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 30 Aug 2015 20:06:53 -0700 Subject: Major renaming of Physics dlls / folders. No functional changes, just renames. --- .../Region/PhysicsModules/BulletS/BSAPIUnman.cs | 2120 ++++++++++++++++ OpenSim/Region/PhysicsModules/BulletS/BSAPIXNA.cs | 2589 ++++++++++++++++++++ .../PhysicsModules/BulletS/BSActorAvatarMove.cs | 457 ++++ .../Region/PhysicsModules/BulletS/BSActorHover.cs | 174 ++ .../PhysicsModules/BulletS/BSActorLockAxis.cs | 219 ++ .../PhysicsModules/BulletS/BSActorMoveToTarget.cs | 220 ++ .../PhysicsModules/BulletS/BSActorSetForce.cs | 138 ++ .../PhysicsModules/BulletS/BSActorSetTorque.cs | 139 ++ OpenSim/Region/PhysicsModules/BulletS/BSActors.cs | 154 ++ .../Region/PhysicsModules/BulletS/BSApiTemplate.cs | 763 ++++++ .../Region/PhysicsModules/BulletS/BSCharacter.cs | 813 ++++++ .../Region/PhysicsModules/BulletS/BSConstraint.cs | 144 ++ .../PhysicsModules/BulletS/BSConstraint6Dof.cs | 180 ++ .../BulletS/BSConstraintCollection.cs | 181 ++ .../BulletS/BSConstraintConeTwist.cs | 54 + .../PhysicsModules/BulletS/BSConstraintHinge.cs | 55 + .../PhysicsModules/BulletS/BSConstraintSlider.cs | 55 + .../PhysicsModules/BulletS/BSConstraintSpring.cs | 103 + .../Region/PhysicsModules/BulletS/BSDynamics.cs | 1800 ++++++++++++++ OpenSim/Region/PhysicsModules/BulletS/BSLinkset.cs | 503 ++++ .../PhysicsModules/BulletS/BSLinksetCompound.cs | 477 ++++ .../PhysicsModules/BulletS/BSLinksetConstraints.cs | 854 +++++++ .../Region/PhysicsModules/BulletS/BSMaterials.cs | 203 ++ OpenSim/Region/PhysicsModules/BulletS/BSMotors.cs | 451 ++++ OpenSim/Region/PhysicsModules/BulletS/BSParam.cs | 927 +++++++ .../Region/PhysicsModules/BulletS/BSPhysObject.cs | 620 +++++ OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs | 76 + OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs | 1896 ++++++++++++++ .../PhysicsModules/BulletS/BSPrimDisplaced.cs | 182 ++ .../PhysicsModules/BulletS/BSPrimLinkable.cs | 350 +++ OpenSim/Region/PhysicsModules/BulletS/BSScene.cs | 1281 ++++++++++ .../PhysicsModules/BulletS/BSShapeCollection.cs | 425 ++++ OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs | 1463 +++++++++++ .../PhysicsModules/BulletS/BSTerrainHeightmap.cs | 170 ++ .../PhysicsModules/BulletS/BSTerrainManager.cs | 585 +++++ .../Region/PhysicsModules/BulletS/BSTerrainMesh.cs | 441 ++++ .../Region/PhysicsModules/BulletS/BulletSimData.cs | 277 +++ .../PhysicsModules/BulletS/BulletSimTODO.txt | 379 +++ .../BulletS/Properties/AssemblyInfo.cs | 33 + .../PhysicsModules/BulletS/Tests/BasicVehicles.cs | 156 ++ .../PhysicsModules/BulletS/Tests/BulletSimTests.cs | 56 + .../BulletS/Tests/BulletSimTestsUtil.cs | 100 + .../PhysicsModules/BulletS/Tests/HullCreation.cs | 205 ++ 43 files changed, 22468 insertions(+) create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSAPIUnman.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSAPIXNA.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSActorLockAxis.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSActors.cs create mode 100644 OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs create mode 100644 OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSConstraint.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSConstraint6Dof.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSConstraintCollection.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSConstraintConeTwist.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSConstraintHinge.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSConstraintSlider.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSConstraintSpring.cs create mode 100644 OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSLinkset.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSLinksetCompound.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSMaterials.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSMotors.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSParam.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs create mode 100644 OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs create mode 100644 OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs create mode 100644 OpenSim/Region/PhysicsModules/BulletS/BSScene.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BulletSimData.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/BulletSimTODO.txt create mode 100644 OpenSim/Region/PhysicsModules/BulletS/Properties/AssemblyInfo.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTests.cs create mode 100755 OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs create mode 100644 OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs (limited to 'OpenSim/Region/PhysicsModules/BulletS') diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSAPIUnman.cs b/OpenSim/Region/PhysicsModules/BulletS/BSAPIUnman.cs new file mode 100755 index 0000000..3bd81d4 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSAPIUnman.cs @@ -0,0 +1,2120 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; + +using OpenSim.Framework; + +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public sealed class BSAPIUnman : BSAPITemplate +{ + +private sealed class BulletWorldUnman : BulletWorld +{ + public IntPtr ptr; + public BulletWorldUnman(uint id, BSScene physScene, IntPtr xx) + : base(id, physScene) + { + ptr = xx; + } +} + +private sealed class BulletBodyUnman : BulletBody +{ + public IntPtr ptr; + public BulletBodyUnman(uint id, IntPtr xx) + : base(id) + { + ptr = xx; + } + public override bool HasPhysicalBody + { + get { return ptr != IntPtr.Zero; } + } + public override void Clear() + { + ptr = IntPtr.Zero; + } + public override string AddrString + { + get { return ptr.ToString("X"); } + } +} + +private sealed class BulletShapeUnman : BulletShape +{ + public IntPtr ptr; + public BulletShapeUnman(IntPtr xx, BSPhysicsShapeType typ) + : base() + { + ptr = xx; + shapeType = typ; + } + public override bool HasPhysicalShape + { + get { return ptr != IntPtr.Zero; } + } + public override void Clear() + { + ptr = IntPtr.Zero; + } + public override BulletShape Clone() + { + return new BulletShapeUnman(ptr, shapeType); + } + public override bool ReferenceSame(BulletShape other) + { + BulletShapeUnman otheru = other as BulletShapeUnman; + return (otheru != null) && (this.ptr == otheru.ptr); + + } + public override string AddrString + { + get { return ptr.ToString("X"); } + } +} +private sealed class BulletConstraintUnman : BulletConstraint +{ + public BulletConstraintUnman(IntPtr xx) : base() + { + ptr = xx; + } + public IntPtr ptr; + + public override void Clear() + { + ptr = IntPtr.Zero; + } + public override bool HasPhysicalConstraint { get { return ptr != IntPtr.Zero; } } + + // Used for log messages for a unique display of the memory/object allocated to this instance + public override string AddrString + { + get { return ptr.ToString("X"); } + } +} + +// We pin the memory passed between the managed and unmanaged code. +GCHandle m_paramsHandle; +private GCHandle m_collisionArrayPinnedHandle; +private GCHandle m_updateArrayPinnedHandle; + +// Handle to the callback used by the unmanaged code to call into the managed code. +// Used for debug logging. +// Need to store the handle in a persistant variable so it won't be freed. +private BSAPICPP.DebugLogCallback m_DebugLogCallbackHandle; + +private BSScene PhysicsScene { get; set; } + +public override string BulletEngineName { get { return "BulletUnmanaged"; } } +public override string BulletEngineVersion { get; protected set; } + +public BSAPIUnman(string paramName, BSScene physScene) +{ + PhysicsScene = physScene; + + // Do something fancy with the paramName to get the right DLL implementation + // like "Bullet-2.80-OpenCL-Intel" loading the version for Intel based OpenCL implementation, etc. + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("BulletSim.dll"); + // If not Windows, loading is performed by the + // Mono loader as specified in + // "bin/Physics/OpenSim.Region.Physics.BulletSPlugin.dll.config". +} + +// Initialization and simulation +public override BulletWorld Initialize(Vector3 maxPosition, ConfigurationParameters parms, + int maxCollisions, ref CollisionDesc[] collisionArray, + int maxUpdates, ref EntityProperties[] updateArray + ) +{ + // Pin down the memory that will be used to pass object collisions and updates back from unmanaged code + m_paramsHandle = GCHandle.Alloc(parms, GCHandleType.Pinned); + m_collisionArrayPinnedHandle = GCHandle.Alloc(collisionArray, GCHandleType.Pinned); + m_updateArrayPinnedHandle = GCHandle.Alloc(updateArray, GCHandleType.Pinned); + + // If Debug logging level, enable logging from the unmanaged code + m_DebugLogCallbackHandle = null; + if (BSScene.m_log.IsDebugEnabled && PhysicsScene.PhysicsLogging.Enabled) + { + BSScene.m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", BSScene.LogHeader); + if (PhysicsScene.PhysicsLogging.Enabled) + // The handle is saved in a variable to make sure it doesn't get freed after this call + m_DebugLogCallbackHandle = new BSAPICPP.DebugLogCallback(BulletLoggerPhysLog); + else + m_DebugLogCallbackHandle = new BSAPICPP.DebugLogCallback(BulletLogger); + } + + // Get the version of the DLL + // TODO: this doesn't work yet. Something wrong with marshaling the returned string. + // BulletEngineVersion = BulletSimAPI.GetVersion2(); + BulletEngineVersion = ""; + + // Call the unmanaged code with the buffers and other information + return new BulletWorldUnman(0, PhysicsScene, BSAPICPP.Initialize2(maxPosition, m_paramsHandle.AddrOfPinnedObject(), + maxCollisions, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), + maxUpdates, m_updateArrayPinnedHandle.AddrOfPinnedObject(), + m_DebugLogCallbackHandle)); + +} + +// Called directly from unmanaged code so don't do much +private void BulletLogger(string msg) +{ + BSScene.m_log.Debug("[BULLETS UNMANAGED]:" + msg); +} + +// Called directly from unmanaged code so don't do much +private void BulletLoggerPhysLog(string msg) +{ + PhysicsScene.DetailLog("[BULLETS UNMANAGED]:" + msg); +} + +public override int PhysicsStep(BulletWorld world, float timeStep, int maxSubSteps, float fixedTimeStep, + out int updatedEntityCount, out int collidersCount) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + return BSAPICPP.PhysicsStep2(worldu.ptr, timeStep, maxSubSteps, fixedTimeStep, out updatedEntityCount, out collidersCount); +} + +public override void Shutdown(BulletWorld world) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BSAPICPP.Shutdown2(worldu.ptr); + + if (m_paramsHandle.IsAllocated) + { + m_paramsHandle.Free(); + } + if (m_collisionArrayPinnedHandle.IsAllocated) + { + m_collisionArrayPinnedHandle.Free(); + } + if (m_updateArrayPinnedHandle.IsAllocated) + { + m_updateArrayPinnedHandle.Free(); + } +} + +public override bool PushUpdate(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.PushUpdate2(bodyu.ptr); +} + +public override bool UpdateParameter(BulletWorld world, uint localID, String parm, float value) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + return BSAPICPP.UpdateParameter2(worldu.ptr, localID, parm, value); +} + +// ===================================================================================== +// Mesh, hull, shape and body creation helper routines +public override BulletShape CreateMeshShape(BulletWorld world, + int indicesCount, int[] indices, + int verticesCount, float[] vertices) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + return new BulletShapeUnman( + BSAPICPP.CreateMeshShape2(worldu.ptr, indicesCount, indices, verticesCount, vertices), + BSPhysicsShapeType.SHAPE_MESH); +} + +public override BulletShape CreateGImpactShape(BulletWorld world, + int indicesCount, int[] indices, + int verticesCount, float[] vertices) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + return new BulletShapeUnman( + BSAPICPP.CreateGImpactShape2(worldu.ptr, indicesCount, indices, verticesCount, vertices), + BSPhysicsShapeType.SHAPE_GIMPACT); +} + +public override BulletShape CreateHullShape(BulletWorld world, int hullCount, float[] hulls) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + return new BulletShapeUnman( + BSAPICPP.CreateHullShape2(worldu.ptr, hullCount, hulls), + BSPhysicsShapeType.SHAPE_HULL); +} + +public override BulletShape BuildHullShapeFromMesh(BulletWorld world, BulletShape meshShape, HACDParams parms) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletShapeUnman shapeu = meshShape as BulletShapeUnman; + return new BulletShapeUnman( + BSAPICPP.BuildHullShapeFromMesh2(worldu.ptr, shapeu.ptr, parms), + BSPhysicsShapeType.SHAPE_HULL); +} + +public override BulletShape BuildConvexHullShapeFromMesh(BulletWorld world, BulletShape meshShape) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletShapeUnman shapeu = meshShape as BulletShapeUnman; + return new BulletShapeUnman( + BSAPICPP.BuildConvexHullShapeFromMesh2(worldu.ptr, shapeu.ptr), + BSPhysicsShapeType.SHAPE_CONVEXHULL); +} + +public override BulletShape CreateConvexHullShape(BulletWorld world, + int indicesCount, int[] indices, + int verticesCount, float[] vertices) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + return new BulletShapeUnman( + BSAPICPP.CreateConvexHullShape2(worldu.ptr, indicesCount, indices, verticesCount, vertices), + BSPhysicsShapeType.SHAPE_CONVEXHULL); +} + +public override BulletShape BuildNativeShape(BulletWorld world, ShapeData shapeData) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + return new BulletShapeUnman(BSAPICPP.BuildNativeShape2(worldu.ptr, shapeData), shapeData.Type); +} + +public override bool IsNativeShape(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + if (shapeu != null && shapeu.HasPhysicalShape) + return BSAPICPP.IsNativeShape2(shapeu.ptr); + return false; +} + +public override void SetShapeCollisionMargin(BulletShape shape, float margin) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + if (shapeu != null && shapeu.HasPhysicalShape) + BSAPICPP.SetShapeCollisionMargin(shapeu.ptr, margin); +} + +public override BulletShape BuildCapsuleShape(BulletWorld world, float radius, float height, Vector3 scale) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + return new BulletShapeUnman( + BSAPICPP.BuildCapsuleShape2(worldu.ptr, radius, height, scale), + BSPhysicsShapeType.SHAPE_CAPSULE); +} + +public override BulletShape CreateCompoundShape(BulletWorld world, bool enableDynamicAabbTree) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + return new BulletShapeUnman( + BSAPICPP.CreateCompoundShape2(worldu.ptr, enableDynamicAabbTree), + BSPhysicsShapeType.SHAPE_COMPOUND); + +} + +public override int GetNumberOfCompoundChildren(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + if (shapeu != null && shapeu.HasPhysicalShape) + return BSAPICPP.GetNumberOfCompoundChildren2(shapeu.ptr); + return 0; +} + +public override void AddChildShapeToCompoundShape(BulletShape shape, BulletShape addShape, Vector3 pos, Quaternion rot) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + BulletShapeUnman addShapeu = addShape as BulletShapeUnman; + BSAPICPP.AddChildShapeToCompoundShape2(shapeu.ptr, addShapeu.ptr, pos, rot); +} + +public override BulletShape GetChildShapeFromCompoundShapeIndex(BulletShape shape, int indx) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return new BulletShapeUnman(BSAPICPP.GetChildShapeFromCompoundShapeIndex2(shapeu.ptr, indx), BSPhysicsShapeType.SHAPE_UNKNOWN); +} + +public override BulletShape RemoveChildShapeFromCompoundShapeIndex(BulletShape shape, int indx) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return new BulletShapeUnman(BSAPICPP.RemoveChildShapeFromCompoundShapeIndex2(shapeu.ptr, indx), BSPhysicsShapeType.SHAPE_UNKNOWN); +} + +public override void RemoveChildShapeFromCompoundShape(BulletShape shape, BulletShape removeShape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + BulletShapeUnman removeShapeu = removeShape as BulletShapeUnman; + BSAPICPP.RemoveChildShapeFromCompoundShape2(shapeu.ptr, removeShapeu.ptr); +} + +public override void UpdateChildTransform(BulletShape pShape, int childIndex, Vector3 pos, Quaternion rot, bool shouldRecalculateLocalAabb) +{ + BulletShapeUnman shapeu = pShape as BulletShapeUnman; + BSAPICPP.UpdateChildTransform2(shapeu.ptr, childIndex, pos, rot, shouldRecalculateLocalAabb); +} + +public override void RecalculateCompoundShapeLocalAabb(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + BSAPICPP.RecalculateCompoundShapeLocalAabb2(shapeu.ptr); +} + +public override BulletShape DuplicateCollisionShape(BulletWorld world, BulletShape srcShape, uint id) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletShapeUnman srcShapeu = srcShape as BulletShapeUnman; + return new BulletShapeUnman(BSAPICPP.DuplicateCollisionShape2(worldu.ptr, srcShapeu.ptr, id), srcShape.shapeType); +} + +public override bool DeleteCollisionShape(BulletWorld world, BulletShape shape) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.DeleteCollisionShape2(worldu.ptr, shapeu.ptr); +} + +public override CollisionObjectTypes GetBodyType(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return (CollisionObjectTypes)BSAPICPP.GetBodyType2(bodyu.ptr); +} + +public override BulletBody CreateBodyFromShape(BulletWorld world, BulletShape shape, uint id, Vector3 pos, Quaternion rot) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return new BulletBodyUnman(id, BSAPICPP.CreateBodyFromShape2(worldu.ptr, shapeu.ptr, id, pos, rot)); +} + +public override BulletBody CreateBodyWithDefaultMotionState(BulletShape shape, uint id, Vector3 pos, Quaternion rot) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return new BulletBodyUnman(id, BSAPICPP.CreateBodyWithDefaultMotionState2(shapeu.ptr, id, pos, rot)); +} + +public override BulletBody CreateGhostFromShape(BulletWorld world, BulletShape shape, uint id, Vector3 pos, Quaternion rot) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return new BulletBodyUnman(id, BSAPICPP.CreateGhostFromShape2(worldu.ptr, shapeu.ptr, id, pos, rot)); +} + +public override void DestroyObject(BulletWorld world, BulletBody obj) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.DestroyObject2(worldu.ptr, bodyu.ptr); +} + +// ===================================================================================== +// Terrain creation and helper routines +public override BulletShape CreateGroundPlaneShape(uint id, float height, float collisionMargin) +{ + return new BulletShapeUnman(BSAPICPP.CreateGroundPlaneShape2(id, height, collisionMargin), BSPhysicsShapeType.SHAPE_GROUNDPLANE); +} + +public override BulletShape CreateTerrainShape(uint id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, + float scaleFactor, float collisionMargin) +{ + return new BulletShapeUnman(BSAPICPP.CreateTerrainShape2(id, size, minHeight, maxHeight, heightMap, scaleFactor, collisionMargin), + BSPhysicsShapeType.SHAPE_TERRAIN); +} + +// ===================================================================================== +// Constraint creation and helper routines +public override BulletConstraint Create6DofConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.Create6DofConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, frame1loc, frame1rot, + frame2loc, frame2rot, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint Create6DofConstraintToPoint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 joinPoint, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.Create6DofConstraintToPoint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, + joinPoint, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint Create6DofConstraintFixed(BulletWorld world, BulletBody obj1, + Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.Create6DofConstraintFixed2(worldu.ptr, bodyu1.ptr, + frameInBloc, frameInBrot, useLinearReferenceFrameB, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint Create6DofSpringConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.Create6DofSpringConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, frame1loc, frame1rot, + frame2loc, frame2rot, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint CreateHingeConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 pivotinA, Vector3 pivotinB, + Vector3 axisInA, Vector3 axisInB, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.CreateHingeConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, + pivotinA, pivotinB, axisInA, axisInB, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint CreateSliderConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.CreateSliderConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, frame1loc, frame1rot, + frame2loc, frame2rot, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint CreateConeTwistConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.CreateConeTwistConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, frame1loc, frame1rot, + frame2loc, frame2rot, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint CreateGearConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 axisInA, Vector3 axisInB, + float ratio, bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.CreateGearConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, axisInA, axisInB, + ratio, disableCollisionsBetweenLinkedBodies)); +} + +public override BulletConstraint CreatePoint2PointConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 pivotInA, Vector3 pivotInB, + bool disableCollisionsBetweenLinkedBodies) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu1 = obj1 as BulletBodyUnman; + BulletBodyUnman bodyu2 = obj2 as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.CreatePoint2PointConstraint2(worldu.ptr, bodyu1.ptr, bodyu2.ptr, pivotInA, pivotInB, + disableCollisionsBetweenLinkedBodies)); +} + +public override void SetConstraintEnable(BulletConstraint constrain, float numericTrueFalse) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + BSAPICPP.SetConstraintEnable2(constrainu.ptr, numericTrueFalse); +} + +public override void SetConstraintNumSolverIterations(BulletConstraint constrain, float iterations) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + BSAPICPP.SetConstraintNumSolverIterations2(constrainu.ptr, iterations); +} + +public override bool SetFrames(BulletConstraint constrain, + Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.SetFrames2(constrainu.ptr, frameA, frameArot, frameB, frameBrot); +} + +public override bool SetLinearLimits(BulletConstraint constrain, Vector3 low, Vector3 hi) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.SetLinearLimits2(constrainu.ptr, low, hi); +} + +public override bool SetAngularLimits(BulletConstraint constrain, Vector3 low, Vector3 hi) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.SetAngularLimits2(constrainu.ptr, low, hi); +} + +public override bool UseFrameOffset(BulletConstraint constrain, float enable) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.UseFrameOffset2(constrainu.ptr, enable); +} + +public override bool TranslationalLimitMotor(BulletConstraint constrain, float enable, float targetVel, float maxMotorForce) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.TranslationalLimitMotor2(constrainu.ptr, enable, targetVel, maxMotorForce); +} + +public override bool SetBreakingImpulseThreshold(BulletConstraint constrain, float threshold) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.SetBreakingImpulseThreshold2(constrainu.ptr, threshold); +} + +public override bool HingeSetLimits(BulletConstraint constrain, float low, float high, float softness, float bias, float relaxation) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.HingeSetLimits2(constrainu.ptr, low, high, softness, bias, relaxation); +} + +public override bool SpringEnable(BulletConstraint constrain, int index, float numericTrueFalse) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.ConstraintSpringEnable2(constrainu.ptr, index, numericTrueFalse); +} + +public override bool SpringSetEquilibriumPoint(BulletConstraint constrain, int index, float equilibriumPoint) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.ConstraintSpringSetEquilibriumPoint2(constrainu.ptr, index, equilibriumPoint); +} + +public override bool SpringSetStiffness(BulletConstraint constrain, int index, float stiffnesss) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.ConstraintSpringSetStiffness2(constrainu.ptr, index, stiffnesss); +} + +public override bool SpringSetDamping(BulletConstraint constrain, int index, float damping) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.ConstraintSpringSetDamping2(constrainu.ptr, index, damping); +} + +public override bool SliderSetLimits(BulletConstraint constrain, int lowerUpper, int linAng, float val) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.SliderSetLimits2(constrainu.ptr, lowerUpper, linAng, val); +} + +public override bool SliderSet(BulletConstraint constrain, int softRestDamp, int dirLimOrtho, int linAng, float val) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.SliderSet2(constrainu.ptr, softRestDamp, dirLimOrtho, linAng, val); +} + +public override bool SliderMotorEnable(BulletConstraint constrain, int linAng, float numericTrueFalse) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.SliderMotorEnable2(constrainu.ptr, linAng, numericTrueFalse); +} + +public override bool SliderMotor(BulletConstraint constrain, int forceVel, int linAng, float val) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.SliderMotor2(constrainu.ptr, forceVel, linAng, val); +} + +public override bool CalculateTransforms(BulletConstraint constrain) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.CalculateTransforms2(constrainu.ptr); +} + +public override bool SetConstraintParam(BulletConstraint constrain, ConstraintParams paramIndex, float value, ConstraintParamAxis axis) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.SetConstraintParam2(constrainu.ptr, paramIndex, value, axis); +} + +public override bool DestroyConstraint(BulletWorld world, BulletConstraint constrain) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.DestroyConstraint2(worldu.ptr, constrainu.ptr); +} + +// ===================================================================================== +// btCollisionWorld entries +public override void UpdateSingleAabb(BulletWorld world, BulletBody obj) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.UpdateSingleAabb2(worldu.ptr, bodyu.ptr); +} + +public override void UpdateAabbs(BulletWorld world) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BSAPICPP.UpdateAabbs2(worldu.ptr); +} + +public override bool GetForceUpdateAllAabbs(BulletWorld world) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + return BSAPICPP.GetForceUpdateAllAabbs2(worldu.ptr); +} + +public override void SetForceUpdateAllAabbs(BulletWorld world, bool force) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BSAPICPP.SetForceUpdateAllAabbs2(worldu.ptr, force); +} + +// ===================================================================================== +// btDynamicsWorld entries +public override bool AddObjectToWorld(BulletWorld world, BulletBody obj) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu = obj as BulletBodyUnman; + + // Bullet resets several variables when an object is added to the world. + // Gravity is reset to world default depending on the static/dynamic + // type. Of course, the collision flags in the broadphase proxy are initialized to default. + Vector3 origGrav = BSAPICPP.GetGravity2(bodyu.ptr); + + bool ret = BSAPICPP.AddObjectToWorld2(worldu.ptr, bodyu.ptr); + + if (ret) + { + BSAPICPP.SetGravity2(bodyu.ptr, origGrav); + obj.ApplyCollisionMask(world.physicsScene); + } + return ret; +} + +public override bool RemoveObjectFromWorld(BulletWorld world, BulletBody obj) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.RemoveObjectFromWorld2(worldu.ptr, bodyu.ptr); +} + +public override bool ClearCollisionProxyCache(BulletWorld world, BulletBody obj) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.ClearCollisionProxyCache2(worldu.ptr, bodyu.ptr); +} + +public override bool AddConstraintToWorld(BulletWorld world, BulletConstraint constrain, bool disableCollisionsBetweenLinkedObjects) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.AddConstraintToWorld2(worldu.ptr, constrainu.ptr, disableCollisionsBetweenLinkedObjects); +} + +public override bool RemoveConstraintFromWorld(BulletWorld world, BulletConstraint constrain) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.RemoveConstraintFromWorld2(worldu.ptr, constrainu.ptr); +} +// ===================================================================================== +// btCollisionObject entries +public override Vector3 GetAnisotripicFriction(BulletConstraint constrain) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.GetAnisotripicFriction2(constrainu.ptr); +} + +public override Vector3 SetAnisotripicFriction(BulletConstraint constrain, Vector3 frict) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.SetAnisotripicFriction2(constrainu.ptr, frict); +} + +public override bool HasAnisotripicFriction(BulletConstraint constrain) +{ + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + return BSAPICPP.HasAnisotripicFriction2(constrainu.ptr); +} + +public override void SetContactProcessingThreshold(BulletBody obj, float val) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetContactProcessingThreshold2(bodyu.ptr, val); +} + +public override float GetContactProcessingThreshold(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetContactProcessingThreshold2(bodyu.ptr); +} + +public override bool IsStaticObject(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.IsStaticObject2(bodyu.ptr); +} + +public override bool IsKinematicObject(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.IsKinematicObject2(bodyu.ptr); +} + +public override bool IsStaticOrKinematicObject(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.IsStaticOrKinematicObject2(bodyu.ptr); +} + +public override bool HasContactResponse(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.HasContactResponse2(bodyu.ptr); +} + +public override void SetCollisionShape(BulletWorld world, BulletBody obj, BulletShape shape) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BulletShapeUnman shapeu = shape as BulletShapeUnman; + if (worldu != null && bodyu != null) + { + // Special case to allow the caller to zero out the reference to any physical shape + if (shapeu != null) + BSAPICPP.SetCollisionShape2(worldu.ptr, bodyu.ptr, shapeu.ptr); + else + BSAPICPP.SetCollisionShape2(worldu.ptr, bodyu.ptr, IntPtr.Zero); + } +} + +public override BulletShape GetCollisionShape(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return new BulletShapeUnman(BSAPICPP.GetCollisionShape2(bodyu.ptr), BSPhysicsShapeType.SHAPE_UNKNOWN); +} + +public override int GetActivationState(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetActivationState2(bodyu.ptr); +} + +public override void SetActivationState(BulletBody obj, int state) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetActivationState2(bodyu.ptr, state); +} + +public override void SetDeactivationTime(BulletBody obj, float dtime) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetDeactivationTime2(bodyu.ptr, dtime); +} + +public override float GetDeactivationTime(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetDeactivationTime2(bodyu.ptr); +} + +public override void ForceActivationState(BulletBody obj, ActivationState state) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.ForceActivationState2(bodyu.ptr, state); +} + +public override void Activate(BulletBody obj, bool forceActivation) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.Activate2(bodyu.ptr, forceActivation); +} + +public override bool IsActive(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.IsActive2(bodyu.ptr); +} + +public override void SetRestitution(BulletBody obj, float val) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetRestitution2(bodyu.ptr, val); +} + +public override float GetRestitution(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetRestitution2(bodyu.ptr); +} + +public override void SetFriction(BulletBody obj, float val) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetFriction2(bodyu.ptr, val); +} + +public override float GetFriction(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetFriction2(bodyu.ptr); +} + +public override Vector3 GetPosition(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetPosition2(bodyu.ptr); +} + +public override Quaternion GetOrientation(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetOrientation2(bodyu.ptr); +} + +public override void SetTranslation(BulletBody obj, Vector3 position, Quaternion rotation) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetTranslation2(bodyu.ptr, position, rotation); +} + + /* +public override IntPtr GetBroadphaseHandle(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetBroadphaseHandle2(bodyu.ptr); +} + +public override void SetBroadphaseHandle(BulletBody obj, IntPtr handle) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetUserPointer2(bodyu.ptr, handle); +} + */ + +public override void SetInterpolationLinearVelocity(BulletBody obj, Vector3 vel) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetInterpolationLinearVelocity2(bodyu.ptr, vel); +} + +public override void SetInterpolationAngularVelocity(BulletBody obj, Vector3 vel) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetInterpolationAngularVelocity2(bodyu.ptr, vel); +} + +public override void SetInterpolationVelocity(BulletBody obj, Vector3 linearVel, Vector3 angularVel) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetInterpolationVelocity2(bodyu.ptr, linearVel, angularVel); +} + +public override float GetHitFraction(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetHitFraction2(bodyu.ptr); +} + +public override void SetHitFraction(BulletBody obj, float val) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetHitFraction2(bodyu.ptr, val); +} + +public override CollisionFlags GetCollisionFlags(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetCollisionFlags2(bodyu.ptr); +} + +public override CollisionFlags SetCollisionFlags(BulletBody obj, CollisionFlags flags) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.SetCollisionFlags2(bodyu.ptr, flags); +} + +public override CollisionFlags AddToCollisionFlags(BulletBody obj, CollisionFlags flags) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.AddToCollisionFlags2(bodyu.ptr, flags); +} + +public override CollisionFlags RemoveFromCollisionFlags(BulletBody obj, CollisionFlags flags) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.RemoveFromCollisionFlags2(bodyu.ptr, flags); +} + +public override float GetCcdMotionThreshold(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetCcdMotionThreshold2(bodyu.ptr); +} + + +public override void SetCcdMotionThreshold(BulletBody obj, float val) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetCcdMotionThreshold2(bodyu.ptr, val); +} + +public override float GetCcdSweptSphereRadius(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetCcdSweptSphereRadius2(bodyu.ptr); +} + +public override void SetCcdSweptSphereRadius(BulletBody obj, float val) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetCcdSweptSphereRadius2(bodyu.ptr, val); +} + +public override IntPtr GetUserPointer(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetUserPointer2(bodyu.ptr); +} + +public override void SetUserPointer(BulletBody obj, IntPtr val) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetUserPointer2(bodyu.ptr, val); +} + +// ===================================================================================== +// btRigidBody entries +public override void ApplyGravity(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.ApplyGravity2(bodyu.ptr); +} + +public override void SetGravity(BulletBody obj, Vector3 val) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetGravity2(bodyu.ptr, val); +} + +public override Vector3 GetGravity(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetGravity2(bodyu.ptr); +} + +public override void SetDamping(BulletBody obj, float lin_damping, float ang_damping) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetDamping2(bodyu.ptr, lin_damping, ang_damping); +} + +public override void SetLinearDamping(BulletBody obj, float lin_damping) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetLinearDamping2(bodyu.ptr, lin_damping); +} + +public override void SetAngularDamping(BulletBody obj, float ang_damping) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetAngularDamping2(bodyu.ptr, ang_damping); +} + +public override float GetLinearDamping(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetLinearDamping2(bodyu.ptr); +} + +public override float GetAngularDamping(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetAngularDamping2(bodyu.ptr); +} + +public override float GetLinearSleepingThreshold(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetLinearSleepingThreshold2(bodyu.ptr); +} + +public override void ApplyDamping(BulletBody obj, float timeStep) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.ApplyDamping2(bodyu.ptr, timeStep); +} + +public override void SetMassProps(BulletBody obj, float mass, Vector3 inertia) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetMassProps2(bodyu.ptr, mass, inertia); +} + +public override Vector3 GetLinearFactor(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetLinearFactor2(bodyu.ptr); +} + +public override void SetLinearFactor(BulletBody obj, Vector3 factor) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetLinearFactor2(bodyu.ptr, factor); +} + +public override void SetCenterOfMassByPosRot(BulletBody obj, Vector3 pos, Quaternion rot) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetCenterOfMassByPosRot2(bodyu.ptr, pos, rot); +} + +// Add a force to the object as if its mass is one. +// Deep down in Bullet: m_totalForce += force*m_linearFactor; +public override void ApplyCentralForce(BulletBody obj, Vector3 force) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.ApplyCentralForce2(bodyu.ptr, force); +} + +// Set the force being applied to the object as if its mass is one. +public override void SetObjectForce(BulletBody obj, Vector3 force) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetObjectForce2(bodyu.ptr, force); +} + +public override Vector3 GetTotalForce(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetTotalForce2(bodyu.ptr); +} + +public override Vector3 GetTotalTorque(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetTotalTorque2(bodyu.ptr); +} + +public override Vector3 GetInvInertiaDiagLocal(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetInvInertiaDiagLocal2(bodyu.ptr); +} + +public override void SetInvInertiaDiagLocal(BulletBody obj, Vector3 inert) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetInvInertiaDiagLocal2(bodyu.ptr, inert); +} + +public override void SetSleepingThresholds(BulletBody obj, float lin_threshold, float ang_threshold) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetSleepingThresholds2(bodyu.ptr, lin_threshold, ang_threshold); +} + +// Deep down in Bullet: m_totalTorque += torque*m_angularFactor; +public override void ApplyTorque(BulletBody obj, Vector3 torque) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.ApplyTorque2(bodyu.ptr, torque); +} + +// Apply force at the given point. Will add torque to the object. +// Deep down in Bullet: applyCentralForce(force); +// applyTorque(rel_pos.cross(force*m_linearFactor)); +public override void ApplyForce(BulletBody obj, Vector3 force, Vector3 pos) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.ApplyForce2(bodyu.ptr, force, pos); +} + +// Apply impulse to the object. Same as "ApplycentralForce" but force scaled by object's mass. +// Deep down in Bullet: m_linearVelocity += impulse *m_linearFactor * m_inverseMass; +public override void ApplyCentralImpulse(BulletBody obj, Vector3 imp) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.ApplyCentralImpulse2(bodyu.ptr, imp); +} + +// Apply impulse to the object's torque. Force is scaled by object's mass. +// Deep down in Bullet: m_angularVelocity += m_invInertiaTensorWorld * torque * m_angularFactor; +public override void ApplyTorqueImpulse(BulletBody obj, Vector3 imp) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.ApplyTorqueImpulse2(bodyu.ptr, imp); +} + +// Apply impulse at the point given. For is scaled by object's mass and effects both linear and angular forces. +// Deep down in Bullet: applyCentralImpulse(impulse); +// applyTorqueImpulse(rel_pos.cross(impulse*m_linearFactor)); +public override void ApplyImpulse(BulletBody obj, Vector3 imp, Vector3 pos) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.ApplyImpulse2(bodyu.ptr, imp, pos); +} + +public override void ClearForces(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.ClearForces2(bodyu.ptr); +} + +public override void ClearAllForces(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.ClearAllForces2(bodyu.ptr); +} + +public override void UpdateInertiaTensor(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.UpdateInertiaTensor2(bodyu.ptr); +} + +public override Vector3 GetLinearVelocity(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetLinearVelocity2(bodyu.ptr); +} + +public override Vector3 GetAngularVelocity(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetAngularVelocity2(bodyu.ptr); +} + +public override void SetLinearVelocity(BulletBody obj, Vector3 vel) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetLinearVelocity2(bodyu.ptr, vel); +} + +public override void SetAngularVelocity(BulletBody obj, Vector3 angularVelocity) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetAngularVelocity2(bodyu.ptr, angularVelocity); +} + +public override Vector3 GetVelocityInLocalPoint(BulletBody obj, Vector3 pos) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetVelocityInLocalPoint2(bodyu.ptr, pos); +} + +public override void Translate(BulletBody obj, Vector3 trans) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.Translate2(bodyu.ptr, trans); +} + +public override void UpdateDeactivation(BulletBody obj, float timeStep) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.UpdateDeactivation2(bodyu.ptr, timeStep); +} + +public override bool WantsSleeping(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.WantsSleeping2(bodyu.ptr); +} + +public override void SetAngularFactor(BulletBody obj, float factor) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetAngularFactor2(bodyu.ptr, factor); +} + +public override void SetAngularFactorV(BulletBody obj, Vector3 factor) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BSAPICPP.SetAngularFactorV2(bodyu.ptr, factor); +} + +public override Vector3 GetAngularFactor(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetAngularFactor2(bodyu.ptr); +} + +public override bool IsInWorld(BulletWorld world, BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.IsInWorld2(bodyu.ptr); +} + +public override void AddConstraintRef(BulletBody obj, BulletConstraint constrain) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + BSAPICPP.AddConstraintRef2(bodyu.ptr, constrainu.ptr); +} + +public override void RemoveConstraintRef(BulletBody obj, BulletConstraint constrain) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + BSAPICPP.RemoveConstraintRef2(bodyu.ptr, constrainu.ptr); +} + +public override BulletConstraint GetConstraintRef(BulletBody obj, int index) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return new BulletConstraintUnman(BSAPICPP.GetConstraintRef2(bodyu.ptr, index)); +} + +public override int GetNumConstraintRefs(BulletBody obj) +{ + BulletBodyUnman bodyu = obj as BulletBodyUnman; + return BSAPICPP.GetNumConstraintRefs2(bodyu.ptr); +} + +public override bool SetCollisionGroupMask(BulletBody body, uint filter, uint mask) +{ + BulletBodyUnman bodyu = body as BulletBodyUnman; + return BSAPICPP.SetCollisionGroupMask2(bodyu.ptr, filter, mask); +} + +// ===================================================================================== +// btCollisionShape entries + +public override float GetAngularMotionDisc(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.GetAngularMotionDisc2(shapeu.ptr); +} + +public override float GetContactBreakingThreshold(BulletShape shape, float defaultFactor) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.GetContactBreakingThreshold2(shapeu.ptr, defaultFactor); +} + +public override bool IsPolyhedral(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.IsPolyhedral2(shapeu.ptr); +} + +public override bool IsConvex2d(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.IsConvex2d2(shapeu.ptr); +} + +public override bool IsConvex(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.IsConvex2(shapeu.ptr); +} + +public override bool IsNonMoving(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.IsNonMoving2(shapeu.ptr); +} + +public override bool IsConcave(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.IsConcave2(shapeu.ptr); +} + +public override bool IsCompound(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.IsCompound2(shapeu.ptr); +} + +public override bool IsSoftBody(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.IsSoftBody2(shapeu.ptr); +} + +public override bool IsInfinite(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.IsInfinite2(shapeu.ptr); +} + +public override void SetLocalScaling(BulletShape shape, Vector3 scale) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + BSAPICPP.SetLocalScaling2(shapeu.ptr, scale); +} + +public override Vector3 GetLocalScaling(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.GetLocalScaling2(shapeu.ptr); +} + +public override Vector3 CalculateLocalInertia(BulletShape shape, float mass) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.CalculateLocalInertia2(shapeu.ptr, mass); +} + +public override int GetShapeType(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.GetShapeType2(shapeu.ptr); +} + +public override void SetMargin(BulletShape shape, float val) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + BSAPICPP.SetMargin2(shapeu.ptr, val); +} + +public override float GetMargin(BulletShape shape) +{ + BulletShapeUnman shapeu = shape as BulletShapeUnman; + return BSAPICPP.GetMargin2(shapeu.ptr); +} + +// ===================================================================================== +// Debugging +public override void DumpRigidBody(BulletWorld world, BulletBody collisionObject) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletBodyUnman bodyu = collisionObject as BulletBodyUnman; + BSAPICPP.DumpRigidBody2(worldu.ptr, bodyu.ptr); +} + +public override void DumpCollisionShape(BulletWorld world, BulletShape collisionShape) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletShapeUnman shapeu = collisionShape as BulletShapeUnman; + BSAPICPP.DumpCollisionShape2(worldu.ptr, shapeu.ptr); +} + +public override void DumpConstraint(BulletWorld world, BulletConstraint constrain) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BulletConstraintUnman constrainu = constrain as BulletConstraintUnman; + BSAPICPP.DumpConstraint2(worldu.ptr, constrainu.ptr); +} + +public override void DumpActivationInfo(BulletWorld world) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BSAPICPP.DumpActivationInfo2(worldu.ptr); +} + +public override void DumpAllInfo(BulletWorld world) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BSAPICPP.DumpAllInfo2(worldu.ptr); +} + +public override void DumpPhysicsStatistics(BulletWorld world) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BSAPICPP.DumpPhysicsStatistics2(worldu.ptr); +} +public override void ResetBroadphasePool(BulletWorld world) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BSAPICPP.ResetBroadphasePool(worldu.ptr); +} +public override void ResetConstraintSolver(BulletWorld world) +{ + BulletWorldUnman worldu = world as BulletWorldUnman; + BSAPICPP.ResetConstraintSolver(worldu.ptr); +} + +// ===================================================================================== +// ===================================================================================== +// ===================================================================================== +// ===================================================================================== +// ===================================================================================== +// The actual interface to the unmanaged code +static class BSAPICPP +{ +// =============================================================================== +// Link back to the managed code for outputting log messages +[UnmanagedFunctionPointer(CallingConvention.Cdecl)] +public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg); + +// =============================================================================== +// Initialization and simulation +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms, + int maxCollisions, IntPtr collisionArray, + int maxUpdates, IntPtr updateArray, + DebugLogCallback logRoutine); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern int PhysicsStep2(IntPtr world, float timeStep, int maxSubSteps, float fixedTimeStep, + out int updatedEntityCount, out int collidersCount); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void Shutdown2(IntPtr sim); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool PushUpdate2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool UpdateParameter2(IntPtr world, uint localID, String parm, float value); + +// ===================================================================================== +// Mesh, hull, shape and body creation helper routines +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateMeshShape2(IntPtr world, + int indicesCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices, + int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices ); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateGImpactShape2(IntPtr world, + int indicesCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices, + int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices ); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateHullShape2(IntPtr world, + int hullCount, [MarshalAs(UnmanagedType.LPArray)] float[] hulls); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr BuildHullShapeFromMesh2(IntPtr world, IntPtr meshShape, HACDParams parms); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr BuildConvexHullShapeFromMesh2(IntPtr world, IntPtr meshShape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateConvexHullShape2(IntPtr world, + int indicesCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices, + int verticesCount, [MarshalAs(UnmanagedType.LPArray)] float[] vertices ); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsNativeShape2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetShapeCollisionMargin(IntPtr shape, float margin); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateCompoundShape2(IntPtr sim, bool enableDynamicAabbTree); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern int GetNumberOfCompoundChildren2(IntPtr cShape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void AddChildShapeToCompoundShape2(IntPtr cShape, IntPtr addShape, Vector3 pos, Quaternion rot); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr GetChildShapeFromCompoundShapeIndex2(IntPtr cShape, int indx); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr RemoveChildShapeFromCompoundShapeIndex2(IntPtr cShape, int indx); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void RemoveChildShapeFromCompoundShape2(IntPtr cShape, IntPtr removeShape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void UpdateChildTransform2(IntPtr pShape, int childIndex, Vector3 pos, Quaternion rot, bool shouldRecalculateLocalAabb); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void RecalculateCompoundShapeLocalAabb2(IntPtr cShape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr DuplicateCollisionShape2(IntPtr sim, IntPtr srcShape, uint id); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern int GetBodyType2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, uint id, Vector3 pos, Quaternion rot); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, uint id, Vector3 pos, Quaternion rot); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateGhostFromShape2(IntPtr sim, IntPtr shape, uint id, Vector3 pos, Quaternion rot); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DestroyObject2(IntPtr sim, IntPtr obj); + +// ===================================================================================== +// Terrain creation and helper routines +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateGroundPlaneShape2(uint id, float height, float collisionMargin); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateTerrainShape2(uint id, Vector3 size, float minHeight, float maxHeight, + [MarshalAs(UnmanagedType.LPArray)] float[] heightMap, + float scaleFactor, float collisionMargin); + +// ===================================================================================== +// Constraint creation and helper routines +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr Create6DofConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr Create6DofConstraintToPoint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 joinPoint, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr Create6DofConstraintFixed2(IntPtr world, IntPtr obj1, + Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr Create6DofSpringConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateHingeConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 pivotinA, Vector3 pivotinB, + Vector3 axisInA, Vector3 axisInB, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateSliderConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 frameInAloc, Quaternion frameInArot, + Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateConeTwistConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 frameInAloc, Quaternion frameInArot, + Vector3 frameInBloc, Quaternion frameInBrot, + bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateGearConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 axisInA, Vector3 axisInB, + float ratio, bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreatePoint2PointConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2, + Vector3 pivotInA, Vector3 pivotInB, + bool disableCollisionsBetweenLinkedBodies); + + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetConstraintEnable2(IntPtr constrain, float numericTrueFalse); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetConstraintNumSolverIterations2(IntPtr constrain, float iterations); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetFrames2(IntPtr constrain, + Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetLinearLimits2(IntPtr constrain, Vector3 low, Vector3 hi); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetAngularLimits2(IntPtr constrain, Vector3 low, Vector3 hi); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool UseFrameOffset2(IntPtr constrain, float enable); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool TranslationalLimitMotor2(IntPtr constrain, float enable, float targetVel, float maxMotorForce); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetBreakingImpulseThreshold2(IntPtr constrain, float threshold); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool HingeSetLimits2(IntPtr constrain, float low, float high, float softness, float bias, float relaxation); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool ConstraintSpringEnable2(IntPtr constrain, int index, float numericTrueFalse); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool ConstraintSpringSetEquilibriumPoint2(IntPtr constrain, int index, float equilibriumPoint); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool ConstraintSpringSetStiffness2(IntPtr constrain, int index, float stiffness); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool ConstraintSpringSetDamping2(IntPtr constrain, int index, float damping); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SliderSetLimits2(IntPtr constrain, int lowerUpper, int linAng, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SliderSet2(IntPtr constrain, int softRestDamp, int dirLimOrtho, int linAng, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SliderMotorEnable2(IntPtr constrain, int linAng, float numericTrueFalse); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SliderMotor2(IntPtr constrain, int forceVel, int linAng, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool CalculateTransforms2(IntPtr constrain); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetConstraintParam2(IntPtr constrain, ConstraintParams paramIndex, float value, ConstraintParamAxis axis); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool DestroyConstraint2(IntPtr world, IntPtr constrain); + +// ===================================================================================== +// btCollisionWorld entries +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void UpdateSingleAabb2(IntPtr world, IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void UpdateAabbs2(IntPtr world); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool GetForceUpdateAllAabbs2(IntPtr world); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetForceUpdateAllAabbs2(IntPtr world, bool force); + +// ===================================================================================== +// btDynamicsWorld entries +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool AddObjectToWorld2(IntPtr world, IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool RemoveObjectFromWorld2(IntPtr world, IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool ClearCollisionProxyCache2(IntPtr world, IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool AddConstraintToWorld2(IntPtr world, IntPtr constrain, bool disableCollisionsBetweenLinkedObjects); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool RemoveConstraintFromWorld2(IntPtr world, IntPtr constrain); +// ===================================================================================== +// btCollisionObject entries +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetAnisotripicFriction2(IntPtr constrain); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 SetAnisotripicFriction2(IntPtr constrain, Vector3 frict); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool HasAnisotripicFriction2(IntPtr constrain); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetContactProcessingThreshold2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetContactProcessingThreshold2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsStaticObject2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsKinematicObject2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsStaticOrKinematicObject2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool HasContactResponse2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetCollisionShape2(IntPtr sim, IntPtr obj, IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr GetCollisionShape2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern int GetActivationState2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetActivationState2(IntPtr obj, int state); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetDeactivationTime2(IntPtr obj, float dtime); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetDeactivationTime2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ForceActivationState2(IntPtr obj, ActivationState state); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void Activate2(IntPtr obj, bool forceActivation); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsActive2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetRestitution2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetRestitution2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetFriction2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetFriction2(IntPtr obj); + + /* Haven't defined the type 'Transform' +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Transform GetWorldTransform2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void setWorldTransform2(IntPtr obj, Transform trans); + */ + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetPosition2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Quaternion GetOrientation2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetTranslation2(IntPtr obj, Vector3 position, Quaternion rotation); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr GetBroadphaseHandle2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetBroadphaseHandle2(IntPtr obj, IntPtr handle); + + /* +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Transform GetInterpolationWorldTransform2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetInterpolationWorldTransform2(IntPtr obj, Transform trans); + */ + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetInterpolationLinearVelocity2(IntPtr obj, Vector3 vel); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetInterpolationAngularVelocity2(IntPtr obj, Vector3 vel); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetInterpolationVelocity2(IntPtr obj, Vector3 linearVel, Vector3 angularVel); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetHitFraction2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetHitFraction2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern CollisionFlags GetCollisionFlags2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern CollisionFlags SetCollisionFlags2(IntPtr obj, CollisionFlags flags); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern CollisionFlags AddToCollisionFlags2(IntPtr obj, CollisionFlags flags); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern CollisionFlags RemoveFromCollisionFlags2(IntPtr obj, CollisionFlags flags); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetCcdMotionThreshold2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetCcdMotionThreshold2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetCcdSweptSphereRadius2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetCcdSweptSphereRadius2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr GetUserPointer2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetUserPointer2(IntPtr obj, IntPtr val); + +// ===================================================================================== +// btRigidBody entries +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ApplyGravity2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetGravity2(IntPtr obj, Vector3 val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetGravity2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetDamping2(IntPtr obj, float lin_damping, float ang_damping); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetLinearDamping2(IntPtr obj, float lin_damping); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetAngularDamping2(IntPtr obj, float ang_damping); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetLinearDamping2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetAngularDamping2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetLinearSleepingThreshold2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetAngularSleepingThreshold2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ApplyDamping2(IntPtr obj, float timeStep); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetMassProps2(IntPtr obj, float mass, Vector3 inertia); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetLinearFactor2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetLinearFactor2(IntPtr obj, Vector3 factor); + + /* +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetCenterOfMassTransform2(IntPtr obj, Transform trans); + */ + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetCenterOfMassByPosRot2(IntPtr obj, Vector3 pos, Quaternion rot); + +// Add a force to the object as if its mass is one. +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ApplyCentralForce2(IntPtr obj, Vector3 force); + +// Set the force being applied to the object as if its mass is one. +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetObjectForce2(IntPtr obj, Vector3 force); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetTotalForce2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetTotalTorque2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetInvInertiaDiagLocal2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetInvInertiaDiagLocal2(IntPtr obj, Vector3 inert); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetSleepingThresholds2(IntPtr obj, float lin_threshold, float ang_threshold); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ApplyTorque2(IntPtr obj, Vector3 torque); + +// Apply force at the given point. Will add torque to the object. +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ApplyForce2(IntPtr obj, Vector3 force, Vector3 pos); + +// Apply impulse to the object. Same as "ApplycentralForce" but force scaled by object's mass. +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ApplyCentralImpulse2(IntPtr obj, Vector3 imp); + +// Apply impulse to the object's torque. Force is scaled by object's mass. +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ApplyTorqueImpulse2(IntPtr obj, Vector3 imp); + +// Apply impulse at the point given. For is scaled by object's mass and effects both linear and angular forces. +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ApplyImpulse2(IntPtr obj, Vector3 imp, Vector3 pos); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ClearForces2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ClearAllForces2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void UpdateInertiaTensor2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetCenterOfMassPosition2(IntPtr obj); + + /* +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Transform GetCenterOfMassTransform2(IntPtr obj); + */ + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetLinearVelocity2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetAngularVelocity2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetLinearVelocity2(IntPtr obj, Vector3 val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetAngularVelocity2(IntPtr obj, Vector3 angularVelocity); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetVelocityInLocalPoint2(IntPtr obj, Vector3 pos); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void Translate2(IntPtr obj, Vector3 trans); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void UpdateDeactivation2(IntPtr obj, float timeStep); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool WantsSleeping2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetAngularFactor2(IntPtr obj, float factor); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetAngularFactorV2(IntPtr obj, Vector3 factor); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetAngularFactor2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsInWorld2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void AddConstraintRef2(IntPtr obj, IntPtr constrain); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void RemoveConstraintRef2(IntPtr obj, IntPtr constrain); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr GetConstraintRef2(IntPtr obj, int index); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern int GetNumConstraintRefs2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetCollisionGroupMask2(IntPtr body, uint filter, uint mask); + +// ===================================================================================== +// btCollisionShape entries + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetAngularMotionDisc2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetContactBreakingThreshold2(IntPtr shape, float defaultFactor); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsPolyhedral2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsConvex2d2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsConvex2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsNonMoving2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsConcave2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsCompound2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsSoftBody2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool IsInfinite2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetLocalScaling2(IntPtr shape, Vector3 scale); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetLocalScaling2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 CalculateLocalInertia2(IntPtr shape, float mass); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern int GetShapeType2(IntPtr shape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetMargin2(IntPtr shape, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern float GetMargin2(IntPtr shape); + +// ===================================================================================== +// Debugging +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpCollisionShape2(IntPtr sim, IntPtr collisionShape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpConstraint2(IntPtr sim, IntPtr constrain); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpActivationInfo2(IntPtr sim); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpAllInfo2(IntPtr sim); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpPhysicsStatistics2(IntPtr sim); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ResetBroadphasePool(IntPtr sim); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void ResetConstraintSolver(IntPtr sim); + +} + +} + +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSAPIXNA.cs b/OpenSim/Region/PhysicsModules/BulletS/BSAPIXNA.cs new file mode 100755 index 0000000..741f8db --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSAPIXNA.cs @@ -0,0 +1,2589 @@ +/* + * 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.IO; +using System.Runtime.InteropServices; +using System.Text; + +using OpenSim.Framework; + +using OpenMetaverse; + +using BulletXNA; +using BulletXNA.LinearMath; +using BulletXNA.BulletCollision; +using BulletXNA.BulletDynamics; +using BulletXNA.BulletCollision.CollisionDispatch; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public sealed class BSAPIXNA : BSAPITemplate +{ +private sealed class BulletWorldXNA : BulletWorld +{ + public DiscreteDynamicsWorld world; + public BulletWorldXNA(uint id, BSScene physScene, DiscreteDynamicsWorld xx) + : base(id, physScene) + { + world = xx; + } +} + +private sealed class BulletBodyXNA : BulletBody +{ + public CollisionObject body; + public RigidBody rigidBody { get { return RigidBody.Upcast(body); } } + + public BulletBodyXNA(uint id, CollisionObject xx) + : base(id) + { + body = xx; + } + public override bool HasPhysicalBody + { + get { return body != null; } + } + public override void Clear() + { + body = null; + } + public override string AddrString + { + get { return "XNARigidBody"; } + } +} + +private sealed class BulletShapeXNA : BulletShape +{ + public CollisionShape shape; + public BulletShapeXNA(CollisionShape xx, BSPhysicsShapeType typ) + : base() + { + shape = xx; + shapeType = typ; + } + public override bool HasPhysicalShape + { + get { return shape != null; } + } + public override void Clear() + { + shape = null; + } + public override BulletShape Clone() + { + return new BulletShapeXNA(shape, shapeType); + } + public override bool ReferenceSame(BulletShape other) + { + BulletShapeXNA otheru = other as BulletShapeXNA; + return (otheru != null) && (this.shape == otheru.shape); + + } + public override string AddrString + { + get { return "XNACollisionShape"; } + } +} +private sealed class BulletConstraintXNA : BulletConstraint +{ + public TypedConstraint constrain; + public BulletConstraintXNA(TypedConstraint xx) : base() + { + constrain = xx; + } + + public override void Clear() + { + constrain = null; + } + public override bool HasPhysicalConstraint { get { return constrain != null; } } + + // Used for log messages for a unique display of the memory/object allocated to this instance + public override string AddrString + { + get { return "XNAConstraint"; } + } +} + internal int m_maxCollisions; + internal CollisionDesc[] UpdatedCollisions; + internal int LastCollisionDesc = 0; + internal int m_maxUpdatesPerFrame; + internal int LastEntityProperty = 0; + + internal EntityProperties[] UpdatedObjects; + internal Dictionary specialCollisionObjects; + + private static int m_collisionsThisFrame; + private BSScene PhysicsScene { get; set; } + + public override string BulletEngineName { get { return "BulletXNA"; } } + public override string BulletEngineVersion { get; protected set; } + + public BSAPIXNA(string paramName, BSScene physScene) + { + PhysicsScene = physScene; + } + + /// + /// + /// + /// + /// + public override bool RemoveObjectFromWorld(BulletWorld pWorld, BulletBody pBody) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody body = ((BulletBodyXNA)pBody).rigidBody; + CollisionObject collisionObject = ((BulletBodyXNA)pBody).body; + if (body != null) + world.RemoveRigidBody(body); + else if (collisionObject != null) + world.RemoveCollisionObject(collisionObject); + else + return false; + return true; + } + + public override bool ClearCollisionProxyCache(BulletWorld pWorld, BulletBody pBody) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody body = ((BulletBodyXNA)pBody).rigidBody; + CollisionObject collisionObject = ((BulletBodyXNA)pBody).body; + if (body != null && collisionObject != null && collisionObject.GetBroadphaseHandle() != null) + { + world.RemoveCollisionObject(collisionObject); + world.AddCollisionObject(collisionObject); + } + return true; + } + + public override bool AddConstraintToWorld(BulletWorld pWorld, BulletConstraint pConstraint, bool pDisableCollisionsBetweenLinkedObjects) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + TypedConstraint constraint = (pConstraint as BulletConstraintXNA).constrain; + world.AddConstraint(constraint, pDisableCollisionsBetweenLinkedObjects); + + return true; + + } + + public override bool RemoveConstraintFromWorld(BulletWorld pWorld, BulletConstraint pConstraint) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + TypedConstraint constraint = (pConstraint as BulletConstraintXNA).constrain; + world.RemoveConstraint(constraint); + return true; + } + + public override void SetRestitution(BulletBody pCollisionObject, float pRestitution) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + collisionObject.SetRestitution(pRestitution); + } + + public override int GetShapeType(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return (int)shape.GetShapeType(); + } + public override void SetMargin(BulletShape pShape, float pMargin) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + shape.SetMargin(pMargin); + } + + public override float GetMargin(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return shape.GetMargin(); + } + + public override void SetLocalScaling(BulletShape pShape, Vector3 pScale) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + IndexedVector3 vec = new IndexedVector3(pScale.X, pScale.Y, pScale.Z); + shape.SetLocalScaling(ref vec); + + } + + public override void SetContactProcessingThreshold(BulletBody pCollisionObject, float contactprocessingthreshold) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + collisionObject.SetContactProcessingThreshold(contactprocessingthreshold); + } + + public override void SetCcdMotionThreshold(BulletBody pCollisionObject, float pccdMotionThreashold) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + collisionObject.SetCcdMotionThreshold(pccdMotionThreashold); + } + + public override void SetCcdSweptSphereRadius(BulletBody pCollisionObject, float pCcdSweptSphereRadius) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + collisionObject.SetCcdSweptSphereRadius(pCcdSweptSphereRadius); + } + + public override void SetAngularFactorV(BulletBody pBody, Vector3 pAngularFactor) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + body.SetAngularFactor(new IndexedVector3(pAngularFactor.X, pAngularFactor.Y, pAngularFactor.Z)); + } + + public override CollisionFlags AddToCollisionFlags(BulletBody pCollisionObject, CollisionFlags pcollisionFlags) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + CollisionFlags existingcollisionFlags = (CollisionFlags)(uint)collisionObject.GetCollisionFlags(); + existingcollisionFlags |= pcollisionFlags; + collisionObject.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags)(uint)existingcollisionFlags); + return (CollisionFlags) (uint) existingcollisionFlags; + } + + public override bool AddObjectToWorld(BulletWorld pWorld, BulletBody pBody) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + CollisionObject cbody = (pBody as BulletBodyXNA).body; + RigidBody rbody = cbody as RigidBody; + + // Bullet resets several variables when an object is added to the world. In particular, + // BulletXNA resets position and rotation. Gravity is also reset depending on the static/dynamic + // type. Of course, the collision flags in the broadphase proxy are initialized to default. + IndexedMatrix origPos = cbody.GetWorldTransform(); + if (rbody != null) + { + IndexedVector3 origGrav = rbody.GetGravity(); + world.AddRigidBody(rbody); + rbody.SetGravity(origGrav); + } + else + { + world.AddCollisionObject(cbody); + } + cbody.SetWorldTransform(origPos); + + pBody.ApplyCollisionMask(pWorld.physicsScene); + + //if (body.GetBroadphaseHandle() != null) + // world.UpdateSingleAabb(body); + return true; + } + + public override void ForceActivationState(BulletBody pCollisionObject, ActivationState pActivationState) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + collisionObject.ForceActivationState((BulletXNA.BulletCollision.ActivationState)(uint)pActivationState); + } + + public override void UpdateSingleAabb(BulletWorld pWorld, BulletBody pCollisionObject) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + world.UpdateSingleAabb(collisionObject); + } + + public override void UpdateAabbs(BulletWorld pWorld) { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + world.UpdateAabbs(); + } + public override bool GetForceUpdateAllAabbs(BulletWorld pWorld) { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + return world.GetForceUpdateAllAabbs(); + + } + public override void SetForceUpdateAllAabbs(BulletWorld pWorld, bool pForce) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + world.SetForceUpdateAllAabbs(pForce); + } + + public override bool SetCollisionGroupMask(BulletBody pCollisionObject, uint pGroup, uint pMask) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + collisionObject.GetBroadphaseHandle().m_collisionFilterGroup = (BulletXNA.BulletCollision.CollisionFilterGroups) pGroup; + collisionObject.GetBroadphaseHandle().m_collisionFilterGroup = (BulletXNA.BulletCollision.CollisionFilterGroups) pGroup; + if ((uint) collisionObject.GetBroadphaseHandle().m_collisionFilterGroup == 0) + return false; + return true; + } + + public override void ClearAllForces(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + IndexedVector3 zeroVector = new IndexedVector3(0, 0, 0); + collisionObject.SetInterpolationLinearVelocity(ref zeroVector); + collisionObject.SetInterpolationAngularVelocity(ref zeroVector); + IndexedMatrix bodytransform = collisionObject.GetWorldTransform(); + + collisionObject.SetInterpolationWorldTransform(ref bodytransform); + + if (collisionObject is RigidBody) + { + RigidBody rigidbody = collisionObject as RigidBody; + rigidbody.SetLinearVelocity(zeroVector); + rigidbody.SetAngularVelocity(zeroVector); + rigidbody.ClearForces(); + } + } + + public override void SetInterpolationAngularVelocity(BulletBody pCollisionObject, Vector3 pVector3) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + IndexedVector3 vec = new IndexedVector3(pVector3.X, pVector3.Y, pVector3.Z); + collisionObject.SetInterpolationAngularVelocity(ref vec); + } + + public override void SetAngularVelocity(BulletBody pBody, Vector3 pVector3) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 vec = new IndexedVector3(pVector3.X, pVector3.Y, pVector3.Z); + body.SetAngularVelocity(ref vec); + } + public override Vector3 GetTotalForce(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 iv3 = body.GetTotalForce(); + return new Vector3(iv3.X, iv3.Y, iv3.Z); + } + public override Vector3 GetTotalTorque(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 iv3 = body.GetTotalTorque(); + return new Vector3(iv3.X, iv3.Y, iv3.Z); + } + public override Vector3 GetInvInertiaDiagLocal(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 iv3 = body.GetInvInertiaDiagLocal(); + return new Vector3(iv3.X, iv3.Y, iv3.Z); + } + public override void SetInvInertiaDiagLocal(BulletBody pBody, Vector3 inert) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 iv3 = new IndexedVector3(inert.X, inert.Y, inert.Z); + body.SetInvInertiaDiagLocal(ref iv3); + } + public override void ApplyForce(BulletBody pBody, Vector3 force, Vector3 pos) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 forceiv3 = new IndexedVector3(force.X, force.Y, force.Z); + IndexedVector3 posiv3 = new IndexedVector3(pos.X, pos.Y, pos.Z); + body.ApplyForce(ref forceiv3, ref posiv3); + } + public override void ApplyImpulse(BulletBody pBody, Vector3 imp, Vector3 pos) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 impiv3 = new IndexedVector3(imp.X, imp.Y, imp.Z); + IndexedVector3 posiv3 = new IndexedVector3(pos.X, pos.Y, pos.Z); + body.ApplyImpulse(ref impiv3, ref posiv3); + } + + public override void ClearForces(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + body.ClearForces(); + } + + public override void SetTranslation(BulletBody pCollisionObject, Vector3 _position, Quaternion _orientation) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + IndexedVector3 vposition = new IndexedVector3(_position.X, _position.Y, _position.Z); + IndexedQuaternion vquaternion = new IndexedQuaternion(_orientation.X, _orientation.Y, _orientation.Z, + _orientation.W); + IndexedMatrix mat = IndexedMatrix.CreateFromQuaternion(vquaternion); + mat._origin = vposition; + collisionObject.SetWorldTransform(mat); + + } + + public override Vector3 GetPosition(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + IndexedVector3 pos = collisionObject.GetInterpolationWorldTransform()._origin; + return new Vector3(pos.X, pos.Y, pos.Z); + } + + public override Vector3 CalculateLocalInertia(BulletShape pShape, float pphysMass) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + IndexedVector3 inertia = IndexedVector3.Zero; + shape.CalculateLocalInertia(pphysMass, out inertia); + return new Vector3(inertia.X, inertia.Y, inertia.Z); + } + + public override void SetMassProps(BulletBody pBody, float pphysMass, Vector3 plocalInertia) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + if (body != null) // Can't set mass props on collision object. + { + IndexedVector3 inertia = new IndexedVector3(plocalInertia.X, plocalInertia.Y, plocalInertia.Z); + body.SetMassProps(pphysMass, inertia); + } + } + + + public override void SetObjectForce(BulletBody pBody, Vector3 _force) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 force = new IndexedVector3(_force.X, _force.Y, _force.Z); + body.SetTotalForce(ref force); + } + + public override void SetFriction(BulletBody pCollisionObject, float _currentFriction) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + collisionObject.SetFriction(_currentFriction); + } + + public override void SetLinearVelocity(BulletBody pBody, Vector3 _velocity) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 velocity = new IndexedVector3(_velocity.X, _velocity.Y, _velocity.Z); + body.SetLinearVelocity(velocity); + } + + public override void Activate(BulletBody pCollisionObject, bool pforceactivation) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + collisionObject.Activate(pforceactivation); + + } + + public override Quaternion GetOrientation(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + IndexedQuaternion mat = collisionObject.GetInterpolationWorldTransform().GetRotation(); + return new Quaternion(mat.X, mat.Y, mat.Z, mat.W); + } + + public override CollisionFlags RemoveFromCollisionFlags(BulletBody pCollisionObject, CollisionFlags pcollisionFlags) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + CollisionFlags existingcollisionFlags = (CollisionFlags)(uint)collisionObject.GetCollisionFlags(); + existingcollisionFlags &= ~pcollisionFlags; + collisionObject.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags)(uint)existingcollisionFlags); + return (CollisionFlags)(uint)existingcollisionFlags; + } + + public override float GetCcdMotionThreshold(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + return collisionObject.GetCcdSquareMotionThreshold(); + } + + public override float GetCcdSweptSphereRadius(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + return collisionObject.GetCcdSweptSphereRadius(); + + } + + public override IntPtr GetUserPointer(BulletBody pCollisionObject) + { + CollisionObject shape = (pCollisionObject as BulletBodyXNA).body; + return (IntPtr)shape.GetUserPointer(); + } + + public override void SetUserPointer(BulletBody pCollisionObject, IntPtr val) + { + CollisionObject shape = (pCollisionObject as BulletBodyXNA).body; + shape.SetUserPointer(val); + } + + public override void SetGravity(BulletBody pBody, Vector3 pGravity) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + if (body != null) // Can't set collisionobject.set gravity + { + IndexedVector3 gravity = new IndexedVector3(pGravity.X, pGravity.Y, pGravity.Z); + body.SetGravity(gravity); + } + } + + public override bool DestroyConstraint(BulletWorld pWorld, BulletConstraint pConstraint) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + TypedConstraint constraint = (pConstraint as BulletConstraintXNA).constrain; + world.RemoveConstraint(constraint); + return true; + } + + public override bool SetLinearLimits(BulletConstraint pConstraint, Vector3 low, Vector3 high) + { + Generic6DofConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofConstraint; + IndexedVector3 lowlimit = new IndexedVector3(low.X, low.Y, low.Z); + IndexedVector3 highlimit = new IndexedVector3(high.X, high.Y, high.Z); + constraint.SetLinearLowerLimit(lowlimit); + constraint.SetLinearUpperLimit(highlimit); + return true; + } + + public override bool SetAngularLimits(BulletConstraint pConstraint, Vector3 low, Vector3 high) + { + Generic6DofConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofConstraint; + IndexedVector3 lowlimit = new IndexedVector3(low.X, low.Y, low.Z); + IndexedVector3 highlimit = new IndexedVector3(high.X, high.Y, high.Z); + constraint.SetAngularLowerLimit(lowlimit); + constraint.SetAngularUpperLimit(highlimit); + return true; + } + + public override void SetConstraintNumSolverIterations(BulletConstraint pConstraint, float cnt) + { + Generic6DofConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofConstraint; + constraint.SetOverrideNumSolverIterations((int)cnt); + } + + public override bool CalculateTransforms(BulletConstraint pConstraint) + { + Generic6DofConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofConstraint; + constraint.CalculateTransforms(); + return true; + } + + public override void SetConstraintEnable(BulletConstraint pConstraint, float p_2) + { + Generic6DofConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofConstraint; + constraint.SetEnabled((p_2 == 0) ? false : true); + } + + + public override BulletConstraint Create6DofConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, + bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) + + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody body1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody body2 = (pBody2 as BulletBodyXNA).rigidBody; + IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z); + IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W); + IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot); + frame1._origin = frame1v; + + IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z); + IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W); + IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot); + frame2._origin = frame1v; + + Generic6DofConstraint consttr = new Generic6DofConstraint(body1, body2, ref frame1, ref frame2, + puseLinearReferenceFrameA); + consttr.CalculateTransforms(); + world.AddConstraint(consttr,pdisableCollisionsBetweenLinkedBodies); + + return new BulletConstraintXNA(consttr); + } + + public override BulletConstraint Create6DofConstraintFixed(BulletWorld pWorld, BulletBody pBody1, + Vector3 pframe1, Quaternion pframe1rot, + bool pUseLinearReferenceFrameB, bool pdisableCollisionsBetweenLinkedBodies) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody body1 = (pBody1 as BulletBodyXNA).rigidBody; + IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z); + IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W); + IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot); + frame1._origin = frame1v; + + Generic6DofConstraint consttr = new Generic6DofConstraint(body1, ref frame1, pUseLinearReferenceFrameB); + consttr.CalculateTransforms(); + world.AddConstraint(consttr,pdisableCollisionsBetweenLinkedBodies); + + return new BulletConstraintXNA(consttr); + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public override BulletConstraint Create6DofConstraintToPoint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, Vector3 pjoinPoint, bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody body1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody body2 = (pBody2 as BulletBodyXNA).rigidBody; + IndexedMatrix frame1 = new IndexedMatrix(IndexedBasisMatrix.Identity, new IndexedVector3(0, 0, 0)); + IndexedMatrix frame2 = new IndexedMatrix(IndexedBasisMatrix.Identity, new IndexedVector3(0, 0, 0)); + + IndexedVector3 joinPoint = new IndexedVector3(pjoinPoint.X, pjoinPoint.Y, pjoinPoint.Z); + IndexedMatrix mat = IndexedMatrix.Identity; + mat._origin = new IndexedVector3(pjoinPoint.X, pjoinPoint.Y, pjoinPoint.Z); + frame1._origin = body1.GetWorldTransform().Inverse()*joinPoint; + frame2._origin = body2.GetWorldTransform().Inverse()*joinPoint; + + Generic6DofConstraint consttr = new Generic6DofConstraint(body1, body2, ref frame1, ref frame2, puseLinearReferenceFrameA); + consttr.CalculateTransforms(); + world.AddConstraint(consttr, pdisableCollisionsBetweenLinkedBodies); + + return new BulletConstraintXNA(consttr); + } + //SetFrames(m_constraint.ptr, frameA, frameArot, frameB, frameBrot); + public override bool SetFrames(BulletConstraint pConstraint, Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot) + { + Generic6DofConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofConstraint; + IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z); + IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W); + IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot); + frame1._origin = frame1v; + + IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z); + IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W); + IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot); + frame2._origin = frame2v; + constraint.SetFrames(ref frame1, ref frame2); + return true; + } + + public override Vector3 GetLinearVelocity(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 iv3 = body.GetLinearVelocity(); + return new Vector3(iv3.X, iv3.Y, iv3.Z); + } + public override Vector3 GetAngularVelocity(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 iv3 = body.GetAngularVelocity(); + return new Vector3(iv3.X, iv3.Y, iv3.Z); + } + public override Vector3 GetVelocityInLocalPoint(BulletBody pBody, Vector3 pos) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 posiv3 = new IndexedVector3(pos.X, pos.Y, pos.Z); + IndexedVector3 iv3 = body.GetVelocityInLocalPoint(ref posiv3); + return new Vector3(iv3.X, iv3.Y, iv3.Z); + } + public override void Translate(BulletBody pCollisionObject, Vector3 trans) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + collisionObject.Translate(new IndexedVector3(trans.X,trans.Y,trans.Z)); + } + public override void UpdateDeactivation(BulletBody pBody, float timeStep) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + body.UpdateDeactivation(timeStep); + } + + public override bool WantsSleeping(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + return body.WantsSleeping(); + } + + public override void SetAngularFactor(BulletBody pBody, float factor) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + body.SetAngularFactor(factor); + } + + public override Vector3 GetAngularFactor(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 iv3 = body.GetAngularFactor(); + return new Vector3(iv3.X, iv3.Y, iv3.Z); + } + + public override bool IsInWorld(BulletWorld pWorld, BulletBody pCollisionObject) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + return world.IsInWorld(collisionObject); + } + + public override void AddConstraintRef(BulletBody pBody, BulletConstraint pConstraint) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + TypedConstraint constrain = (pConstraint as BulletConstraintXNA).constrain; + body.AddConstraintRef(constrain); + } + + public override void RemoveConstraintRef(BulletBody pBody, BulletConstraint pConstraint) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + TypedConstraint constrain = (pConstraint as BulletConstraintXNA).constrain; + body.RemoveConstraintRef(constrain); + } + + public override BulletConstraint GetConstraintRef(BulletBody pBody, int index) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + return new BulletConstraintXNA(body.GetConstraintRef(index)); + } + + public override int GetNumConstraintRefs(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + return body.GetNumConstraintRefs(); + } + + public override void SetInterpolationLinearVelocity(BulletBody pCollisionObject, Vector3 VehicleVelocity) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + IndexedVector3 velocity = new IndexedVector3(VehicleVelocity.X, VehicleVelocity.Y, VehicleVelocity.Z); + collisionObject.SetInterpolationLinearVelocity(ref velocity); + } + + public override bool UseFrameOffset(BulletConstraint pConstraint, float onOff) + { + Generic6DofConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofConstraint; + constraint.SetUseFrameOffset((onOff == 0) ? false : true); + return true; + } + //SetBreakingImpulseThreshold(m_constraint.ptr, threshold); + public override bool SetBreakingImpulseThreshold(BulletConstraint pConstraint, float threshold) + { + Generic6DofConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofConstraint; + constraint.SetBreakingImpulseThreshold(threshold); + return true; + } + public override bool HingeSetLimits(BulletConstraint pConstraint, float low, float high, float softness, float bias, float relaxation) + { + HingeConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as HingeConstraint; + if (softness == HINGE_NOT_SPECIFIED) + constraint.SetLimit(low, high); + else + constraint.SetLimit(low, high, softness, bias, relaxation); + return true; + } + public override bool SpringEnable(BulletConstraint pConstraint, int index, float numericTrueFalse) + { + Generic6DofSpringConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofSpringConstraint; + constraint.EnableSpring(index, (numericTrueFalse == 0f ? false : true)); + return true; + } + + public override bool SpringSetEquilibriumPoint(BulletConstraint pConstraint, int index, float equilibriumPoint) + { + Generic6DofSpringConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofSpringConstraint; + if (index == SPRING_NOT_SPECIFIED) + { + constraint.SetEquilibriumPoint(); + } + else + { + if (equilibriumPoint == SPRING_NOT_SPECIFIED) + constraint.SetEquilibriumPoint(index); + else + constraint.SetEquilibriumPoint(index, equilibriumPoint); + } + return true; + } + + public override bool SpringSetStiffness(BulletConstraint pConstraint, int index, float stiffness) + { + Generic6DofSpringConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofSpringConstraint; + constraint.SetStiffness(index, stiffness); + return true; + } + + public override bool SpringSetDamping(BulletConstraint pConstraint, int index, float damping) + { + Generic6DofSpringConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as Generic6DofSpringConstraint; + constraint.SetDamping(index, damping); + return true; + } + + public override bool SliderSetLimits(BulletConstraint pConstraint, int lowerUpper, int linAng, float val) + { + SliderConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as SliderConstraint; + switch (lowerUpper) + { + case SLIDER_LOWER_LIMIT: + switch (linAng) + { + case SLIDER_LINEAR: + constraint.SetLowerLinLimit(val); + break; + case SLIDER_ANGULAR: + constraint.SetLowerAngLimit(val); + break; + } + break; + case SLIDER_UPPER_LIMIT: + switch (linAng) + { + case SLIDER_LINEAR: + constraint.SetUpperLinLimit(val); + break; + case SLIDER_ANGULAR: + constraint.SetUpperAngLimit(val); + break; + } + break; + } + return true; + } + public override bool SliderSet(BulletConstraint pConstraint, int softRestDamp, int dirLimOrtho, int linAng, float val) + { + SliderConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as SliderConstraint; + switch (softRestDamp) + { + case SLIDER_SET_SOFTNESS: + switch (dirLimOrtho) + { + case SLIDER_SET_DIRECTION: + switch (linAng) + { + case SLIDER_LINEAR: constraint.SetSoftnessDirLin(val); break; + case SLIDER_ANGULAR: constraint.SetSoftnessDirAng(val); break; + } + break; + case SLIDER_SET_LIMIT: + switch (linAng) + { + case SLIDER_LINEAR: constraint.SetSoftnessLimLin(val); break; + case SLIDER_ANGULAR: constraint.SetSoftnessLimAng(val); break; + } + break; + case SLIDER_SET_ORTHO: + switch (linAng) + { + case SLIDER_LINEAR: constraint.SetSoftnessOrthoLin(val); break; + case SLIDER_ANGULAR: constraint.SetSoftnessOrthoAng(val); break; + } + break; + } + break; + case SLIDER_SET_RESTITUTION: + switch (dirLimOrtho) + { + case SLIDER_SET_DIRECTION: + switch (linAng) + { + case SLIDER_LINEAR: constraint.SetRestitutionDirLin(val); break; + case SLIDER_ANGULAR: constraint.SetRestitutionDirAng(val); break; + } + break; + case SLIDER_SET_LIMIT: + switch (linAng) + { + case SLIDER_LINEAR: constraint.SetRestitutionLimLin(val); break; + case SLIDER_ANGULAR: constraint.SetRestitutionLimAng(val); break; + } + break; + case SLIDER_SET_ORTHO: + switch (linAng) + { + case SLIDER_LINEAR: constraint.SetRestitutionOrthoLin(val); break; + case SLIDER_ANGULAR: constraint.SetRestitutionOrthoAng(val); break; + } + break; + } + break; + case SLIDER_SET_DAMPING: + switch (dirLimOrtho) + { + case SLIDER_SET_DIRECTION: + switch (linAng) + { + case SLIDER_LINEAR: constraint.SetDampingDirLin(val); break; + case SLIDER_ANGULAR: constraint.SetDampingDirAng(val); break; + } + break; + case SLIDER_SET_LIMIT: + switch (linAng) + { + case SLIDER_LINEAR: constraint.SetDampingLimLin(val); break; + case SLIDER_ANGULAR: constraint.SetDampingLimAng(val); break; + } + break; + case SLIDER_SET_ORTHO: + switch (linAng) + { + case SLIDER_LINEAR: constraint.SetDampingOrthoLin(val); break; + case SLIDER_ANGULAR: constraint.SetDampingOrthoAng(val); break; + } + break; + } + break; + } + return true; + } + public override bool SliderMotorEnable(BulletConstraint pConstraint, int linAng, float numericTrueFalse) + { + SliderConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as SliderConstraint; + switch (linAng) + { + case SLIDER_LINEAR: + constraint.SetPoweredLinMotor(numericTrueFalse == 0.0 ? false : true); + break; + case SLIDER_ANGULAR: + constraint.SetPoweredAngMotor(numericTrueFalse == 0.0 ? false : true); + break; + } + return true; + } + public override bool SliderMotor(BulletConstraint pConstraint, int forceVel, int linAng, float val) + { + SliderConstraint constraint = (pConstraint as BulletConstraintXNA).constrain as SliderConstraint; + switch (forceVel) + { + case SLIDER_MOTOR_VELOCITY: + switch (linAng) + { + case SLIDER_LINEAR: + constraint.SetTargetLinMotorVelocity(val); + break; + case SLIDER_ANGULAR: + constraint.SetTargetAngMotorVelocity(val); + break; + } + break; + case SLIDER_MAX_MOTOR_FORCE: + switch (linAng) + { + case SLIDER_LINEAR: + constraint.SetMaxLinMotorForce(val); + break; + case SLIDER_ANGULAR: + constraint.SetMaxAngMotorForce(val); + break; + } + break; + } + return true; + } + + //BulletSimAPI.SetAngularDamping(Prim.PhysBody.ptr, angularDamping); + public override void SetAngularDamping(BulletBody pBody, float angularDamping) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + float lineardamping = body.GetLinearDamping(); + body.SetDamping(lineardamping, angularDamping); + + } + + public override void UpdateInertiaTensor(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + if (body != null) // can't update inertia tensor on CollisionObject + body.UpdateInertiaTensor(); + } + + public override void RecalculateCompoundShapeLocalAabb(BulletShape pCompoundShape) + { + CompoundShape shape = (pCompoundShape as BulletShapeXNA).shape as CompoundShape; + shape.RecalculateLocalAabb(); + } + + //BulletSimAPI.GetCollisionFlags(PhysBody.ptr) + public override CollisionFlags GetCollisionFlags(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + uint flags = (uint)collisionObject.GetCollisionFlags(); + return (CollisionFlags) flags; + } + + public override void SetDamping(BulletBody pBody, float pLinear, float pAngular) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + body.SetDamping(pLinear, pAngular); + } + //PhysBody.ptr, PhysicsScene.Params.deactivationTime); + public override void SetDeactivationTime(BulletBody pCollisionObject, float pDeactivationTime) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + collisionObject.SetDeactivationTime(pDeactivationTime); + } + //SetSleepingThresholds(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold); + public override void SetSleepingThresholds(BulletBody pBody, float plinearSleepingThreshold, float pangularSleepingThreshold) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + body.SetSleepingThresholds(plinearSleepingThreshold, pangularSleepingThreshold); + } + + public override CollisionObjectTypes GetBodyType(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + return (CollisionObjectTypes)(int) collisionObject.GetInternalType(); + } + + public override void ApplyGravity(BulletBody pBody) + { + + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + body.ApplyGravity(); + } + + public override Vector3 GetGravity(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 gravity = body.GetGravity(); + return new Vector3(gravity.X, gravity.Y, gravity.Z); + } + + public override void SetLinearDamping(BulletBody pBody, float lin_damping) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + float angularDamping = body.GetAngularDamping(); + body.SetDamping(lin_damping, angularDamping); + } + + public override float GetLinearDamping(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + return body.GetLinearDamping(); + } + + public override float GetAngularDamping(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + return body.GetAngularDamping(); + } + + public override float GetLinearSleepingThreshold(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + return body.GetLinearSleepingThreshold(); + } + + public override void ApplyDamping(BulletBody pBody, float timeStep) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + body.ApplyDamping(timeStep); + } + + public override Vector3 GetLinearFactor(BulletBody pBody) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 linearFactor = body.GetLinearFactor(); + return new Vector3(linearFactor.X, linearFactor.Y, linearFactor.Z); + } + + public override void SetLinearFactor(BulletBody pBody, Vector3 factor) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + body.SetLinearFactor(new IndexedVector3(factor.X, factor.Y, factor.Z)); + } + + public override void SetCenterOfMassByPosRot(BulletBody pBody, Vector3 pos, Quaternion rot) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedQuaternion quat = new IndexedQuaternion(rot.X, rot.Y, rot.Z,rot.W); + IndexedMatrix mat = IndexedMatrix.CreateFromQuaternion(quat); + mat._origin = new IndexedVector3(pos.X, pos.Y, pos.Z); + body.SetCenterOfMassTransform( ref mat); + /* TODO: double check this */ + } + + //BulletSimAPI.ApplyCentralForce(PhysBody.ptr, fSum); + public override void ApplyCentralForce(BulletBody pBody, Vector3 pfSum) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z); + body.ApplyCentralForce(ref fSum); + } + public override void ApplyCentralImpulse(BulletBody pBody, Vector3 pfSum) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z); + body.ApplyCentralImpulse(ref fSum); + } + public override void ApplyTorque(BulletBody pBody, Vector3 pfSum) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z); + body.ApplyTorque(ref fSum); + } + public override void ApplyTorqueImpulse(BulletBody pBody, Vector3 pfSum) + { + RigidBody body = (pBody as BulletBodyXNA).rigidBody; + IndexedVector3 fSum = new IndexedVector3(pfSum.X, pfSum.Y, pfSum.Z); + body.ApplyTorqueImpulse(ref fSum); + } + + public override void DestroyObject(BulletWorld pWorld, BulletBody pBody) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + CollisionObject co = (pBody as BulletBodyXNA).rigidBody; + RigidBody bo = co as RigidBody; + if (bo == null) + { + + if (world.IsInWorld(co)) + { + world.RemoveCollisionObject(co); + } + } + else + { + + if (world.IsInWorld(bo)) + { + world.RemoveRigidBody(bo); + } + } + if (co != null) + { + if (co.GetUserPointer() != null) + { + uint localId = (uint) co.GetUserPointer(); + if (specialCollisionObjects.ContainsKey(localId)) + { + specialCollisionObjects.Remove(localId); + } + } + } + + } + + public override void Shutdown(BulletWorld pWorld) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + world.Cleanup(); + } + + public override BulletShape DuplicateCollisionShape(BulletWorld pWorld, BulletShape pShape, uint id) + { + CollisionShape shape1 = (pShape as BulletShapeXNA).shape; + + // TODO: Turn this from a reference copy to a Value Copy. + BulletShapeXNA shape2 = new BulletShapeXNA(shape1, BSShapeTypeFromBroadPhaseNativeType(shape1.GetShapeType())); + + return shape2; + } + + public override bool DeleteCollisionShape(BulletWorld pWorld, BulletShape pShape) + { + //TODO: + return false; + } + //(sim.ptr, shape.ptr, prim.LocalID, prim.RawPosition, prim.RawOrientation); + + public override BulletBody CreateBodyFromShape(BulletWorld pWorld, BulletShape pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation) + { + CollisionWorld world = (pWorld as BulletWorldXNA).world; + IndexedMatrix mat = + IndexedMatrix.CreateFromQuaternion(new IndexedQuaternion(pRawOrientation.X, pRawOrientation.Y, + pRawOrientation.Z, pRawOrientation.W)); + mat._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z); + CollisionShape shape = (pShape as BulletShapeXNA).shape; + //UpdateSingleAabb(world, shape); + // TODO: Feed Update array into null + SimMotionState motionState = new SimMotionState(this, pLocalID, mat, null); + RigidBody body = new RigidBody(0,motionState,shape,IndexedVector3.Zero); + RigidBodyConstructionInfo constructionInfo = new RigidBodyConstructionInfo(0, motionState, shape, IndexedVector3.Zero) + { + m_mass = 0 + }; + /* + m_mass = mass; + m_motionState =motionState; + m_collisionShape = collisionShape; + m_localInertia = localInertia; + m_linearDamping = 0f; + m_angularDamping = 0f; + m_friction = 0.5f; + m_restitution = 0f; + m_linearSleepingThreshold = 0.8f; + m_angularSleepingThreshold = 1f; + m_additionalDamping = false; + m_additionalDampingFactor = 0.005f; + m_additionalLinearDampingThresholdSqr = 0.01f; + m_additionalAngularDampingThresholdSqr = 0.01f; + m_additionalAngularDampingFactor = 0.01f; + m_startWorldTransform = IndexedMatrix.Identity; + */ + body.SetUserPointer(pLocalID); + + return new BulletBodyXNA(pLocalID, body); + } + + + public override BulletBody CreateBodyWithDefaultMotionState( BulletShape pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation) + { + + IndexedMatrix mat = + IndexedMatrix.CreateFromQuaternion(new IndexedQuaternion(pRawOrientation.X, pRawOrientation.Y, + pRawOrientation.Z, pRawOrientation.W)); + mat._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z); + + CollisionShape shape = (pShape as BulletShapeXNA).shape; + + // TODO: Feed Update array into null + RigidBody body = new RigidBody(0, new DefaultMotionState( mat, IndexedMatrix.Identity), shape, IndexedVector3.Zero); + body.SetWorldTransform(mat); + body.SetUserPointer(pLocalID); + return new BulletBodyXNA(pLocalID, body); + } + //(m_mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT); + public override CollisionFlags SetCollisionFlags(BulletBody pCollisionObject, CollisionFlags collisionFlags) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + collisionObject.SetCollisionFlags((BulletXNA.BulletCollision.CollisionFlags) (uint) collisionFlags); + return (CollisionFlags)collisionObject.GetCollisionFlags(); + } + + public override Vector3 GetAnisotripicFriction(BulletConstraint pconstrain) + { + + /* TODO */ + return Vector3.Zero; + } + public override Vector3 SetAnisotripicFriction(BulletConstraint pconstrain, Vector3 frict) { /* TODO */ return Vector3.Zero; } + public override bool HasAnisotripicFriction(BulletConstraint pconstrain) { /* TODO */ return false; } + public override float GetContactProcessingThreshold(BulletBody pBody) { /* TODO */ return 0f; } + public override bool IsStaticObject(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + return collisionObject.IsStaticObject(); + + } + public override bool IsKinematicObject(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + return collisionObject.IsKinematicObject(); + } + public override bool IsStaticOrKinematicObject(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + return collisionObject.IsStaticOrKinematicObject(); + } + public override bool HasContactResponse(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + return collisionObject.HasContactResponse(); + } + public override int GetActivationState(BulletBody pBody) { /* TODO */ return 0; } + public override void SetActivationState(BulletBody pBody, int state) { /* TODO */ } + public override float GetDeactivationTime(BulletBody pBody) { /* TODO */ return 0f; } + public override bool IsActive(BulletBody pBody) { /* TODO */ return false; } + public override float GetRestitution(BulletBody pBody) { /* TODO */ return 0f; } + public override float GetFriction(BulletBody pBody) { /* TODO */ return 0f; } + public override void SetInterpolationVelocity(BulletBody pBody, Vector3 linearVel, Vector3 angularVel) { /* TODO */ } + public override float GetHitFraction(BulletBody pBody) { /* TODO */ return 0f; } + + //(m_mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainHitFraction); + public override void SetHitFraction(BulletBody pCollisionObject, float pHitFraction) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + collisionObject.SetHitFraction(pHitFraction); + } + //BuildCapsuleShape(physicsScene.World.ptr, 1f, 1f, prim.Scale); + public override BulletShape BuildCapsuleShape(BulletWorld pWorld, float pRadius, float pHeight, Vector3 pScale) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + IndexedVector3 scale = new IndexedVector3(pScale.X, pScale.Y, pScale.Z); + CapsuleShapeZ capsuleShapeZ = new CapsuleShapeZ(pRadius, pHeight); + capsuleShapeZ.SetMargin(world.WorldSettings.Params.collisionMargin); + capsuleShapeZ.SetLocalScaling(ref scale); + + return new BulletShapeXNA(capsuleShapeZ, BSPhysicsShapeType.SHAPE_CAPSULE); ; + } + + public override BulletWorld Initialize(Vector3 maxPosition, ConfigurationParameters parms, + int maxCollisions, ref CollisionDesc[] collisionArray, + int maxUpdates, ref EntityProperties[] updateArray + ) + { + + UpdatedObjects = updateArray; + UpdatedCollisions = collisionArray; + /* TODO */ + ConfigurationParameters[] configparms = new ConfigurationParameters[1]; + configparms[0] = parms; + Vector3 worldExtent = maxPosition; + m_maxCollisions = maxCollisions; + m_maxUpdatesPerFrame = maxUpdates; + specialCollisionObjects = new Dictionary(); + + return new BulletWorldXNA(1, PhysicsScene, BSAPIXNA.Initialize2(worldExtent, configparms, maxCollisions, ref collisionArray, maxUpdates, ref updateArray, null)); + } + + private static DiscreteDynamicsWorld Initialize2(Vector3 worldExtent, + ConfigurationParameters[] o, + int mMaxCollisionsPerFrame, ref CollisionDesc[] collisionArray, + int mMaxUpdatesPerFrame, ref EntityProperties[] updateArray, + object mDebugLogCallbackHandle) + { + CollisionWorld.WorldData.ParamData p = new CollisionWorld.WorldData.ParamData(); + + p.angularDamping = BSParam.AngularDamping; + p.defaultFriction = o[0].defaultFriction; + p.defaultFriction = o[0].defaultFriction; + p.defaultDensity = o[0].defaultDensity; + p.defaultRestitution = o[0].defaultRestitution; + p.collisionMargin = o[0].collisionMargin; + p.gravity = o[0].gravity; + + p.linearDamping = BSParam.LinearDamping; + p.angularDamping = BSParam.AngularDamping; + p.deactivationTime = BSParam.DeactivationTime; + p.linearSleepingThreshold = BSParam.LinearSleepingThreshold; + p.angularSleepingThreshold = BSParam.AngularSleepingThreshold; + p.ccdMotionThreshold = BSParam.CcdMotionThreshold; + p.ccdSweptSphereRadius = BSParam.CcdSweptSphereRadius; + p.contactProcessingThreshold = BSParam.ContactProcessingThreshold; + + p.terrainImplementation = BSParam.TerrainImplementation; + p.terrainFriction = BSParam.TerrainFriction; + + p.terrainHitFraction = BSParam.TerrainHitFraction; + p.terrainRestitution = BSParam.TerrainRestitution; + p.terrainCollisionMargin = BSParam.TerrainCollisionMargin; + + p.avatarFriction = BSParam.AvatarFriction; + p.avatarStandingFriction = BSParam.AvatarStandingFriction; + p.avatarDensity = BSParam.AvatarDensity; + p.avatarRestitution = BSParam.AvatarRestitution; + p.avatarCapsuleWidth = BSParam.AvatarCapsuleWidth; + p.avatarCapsuleDepth = BSParam.AvatarCapsuleDepth; + p.avatarCapsuleHeight = BSParam.AvatarCapsuleHeight; + p.avatarContactProcessingThreshold = BSParam.AvatarContactProcessingThreshold; + + p.vehicleAngularDamping = BSParam.VehicleAngularDamping; + + p.maxPersistantManifoldPoolSize = o[0].maxPersistantManifoldPoolSize; + p.maxCollisionAlgorithmPoolSize = o[0].maxCollisionAlgorithmPoolSize; + p.shouldDisableContactPoolDynamicAllocation = o[0].shouldDisableContactPoolDynamicAllocation; + p.shouldForceUpdateAllAabbs = o[0].shouldForceUpdateAllAabbs; + p.shouldRandomizeSolverOrder = o[0].shouldRandomizeSolverOrder; + p.shouldSplitSimulationIslands = o[0].shouldSplitSimulationIslands; + p.shouldEnableFrictionCaching = o[0].shouldEnableFrictionCaching; + p.numberOfSolverIterations = o[0].numberOfSolverIterations; + + p.linksetImplementation = BSParam.LinksetImplementation; + p.linkConstraintUseFrameOffset = BSParam.NumericBool(BSParam.LinkConstraintUseFrameOffset); + p.linkConstraintEnableTransMotor = BSParam.NumericBool(BSParam.LinkConstraintEnableTransMotor); + p.linkConstraintTransMotorMaxVel = BSParam.LinkConstraintTransMotorMaxVel; + p.linkConstraintTransMotorMaxForce = BSParam.LinkConstraintTransMotorMaxForce; + p.linkConstraintERP = BSParam.LinkConstraintERP; + p.linkConstraintCFM = BSParam.LinkConstraintCFM; + p.linkConstraintSolverIterations = BSParam.LinkConstraintSolverIterations; + p.physicsLoggingFrames = o[0].physicsLoggingFrames; + DefaultCollisionConstructionInfo ccci = new DefaultCollisionConstructionInfo(); + + DefaultCollisionConfiguration cci = new DefaultCollisionConfiguration(); + CollisionDispatcher m_dispatcher = new CollisionDispatcher(cci); + + + if (p.maxPersistantManifoldPoolSize > 0) + cci.m_persistentManifoldPoolSize = (int)p.maxPersistantManifoldPoolSize; + if (p.shouldDisableContactPoolDynamicAllocation !=0) + m_dispatcher.SetDispatcherFlags(DispatcherFlags.CD_DISABLE_CONTACTPOOL_DYNAMIC_ALLOCATION); + //if (p.maxCollisionAlgorithmPoolSize >0 ) + + DbvtBroadphase m_broadphase = new DbvtBroadphase(); + //IndexedVector3 aabbMin = new IndexedVector3(0, 0, 0); + //IndexedVector3 aabbMax = new IndexedVector3(256, 256, 256); + + //AxisSweep3Internal m_broadphase2 = new AxisSweep3Internal(ref aabbMin, ref aabbMax, Convert.ToInt32(0xfffe), 0xffff, ushort.MaxValue/2, null, true); + m_broadphase.GetOverlappingPairCache().SetInternalGhostPairCallback(new GhostPairCallback()); + + SequentialImpulseConstraintSolver m_solver = new SequentialImpulseConstraintSolver(); + + DiscreteDynamicsWorld world = new DiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, cci); + + world.LastCollisionDesc = 0; + world.LastEntityProperty = 0; + + world.WorldSettings.Params = p; + world.SetForceUpdateAllAabbs(p.shouldForceUpdateAllAabbs != 0); + world.GetSolverInfo().m_solverMode = SolverMode.SOLVER_USE_WARMSTARTING | SolverMode.SOLVER_SIMD; + if (p.shouldRandomizeSolverOrder != 0) + world.GetSolverInfo().m_solverMode |= SolverMode.SOLVER_RANDMIZE_ORDER; + + world.GetSimulationIslandManager().SetSplitIslands(p.shouldSplitSimulationIslands != 0); + //world.GetDispatchInfo().m_enableSatConvex Not implemented in C# port + + if (p.shouldEnableFrictionCaching != 0) + world.GetSolverInfo().m_solverMode |= SolverMode.SOLVER_ENABLE_FRICTION_DIRECTION_CACHING; + + if (p.numberOfSolverIterations > 0) + world.GetSolverInfo().m_numIterations = (int) p.numberOfSolverIterations; + + + world.GetSolverInfo().m_damping = world.WorldSettings.Params.linearDamping; + world.GetSolverInfo().m_restitution = world.WorldSettings.Params.defaultRestitution; + world.GetSolverInfo().m_globalCfm = 0.0f; + world.GetSolverInfo().m_tau = 0.6f; + world.GetSolverInfo().m_friction = 0.3f; + world.GetSolverInfo().m_maxErrorReduction = 20f; + world.GetSolverInfo().m_numIterations = 10; + world.GetSolverInfo().m_erp = 0.2f; + world.GetSolverInfo().m_erp2 = 0.1f; + world.GetSolverInfo().m_sor = 1.0f; + world.GetSolverInfo().m_splitImpulse = false; + world.GetSolverInfo().m_splitImpulsePenetrationThreshold = -0.02f; + world.GetSolverInfo().m_linearSlop = 0.0f; + world.GetSolverInfo().m_warmstartingFactor = 0.85f; + world.GetSolverInfo().m_restingContactRestitutionThreshold = 2; + world.SetForceUpdateAllAabbs(true); + + //BSParam.TerrainImplementation = 0; + world.SetGravity(new IndexedVector3(0,0,p.gravity)); + + // Turn off Pooling since globals and pooling are bad for threading. + BulletGlobals.VoronoiSimplexSolverPool.SetPoolingEnabled(false); + BulletGlobals.SubSimplexConvexCastPool.SetPoolingEnabled(false); + BulletGlobals.ManifoldPointPool.SetPoolingEnabled(false); + BulletGlobals.CastResultPool.SetPoolingEnabled(false); + BulletGlobals.SphereShapePool.SetPoolingEnabled(false); + BulletGlobals.DbvtNodePool.SetPoolingEnabled(false); + BulletGlobals.SingleRayCallbackPool.SetPoolingEnabled(false); + BulletGlobals.SubSimplexClosestResultPool.SetPoolingEnabled(false); + BulletGlobals.GjkPairDetectorPool.SetPoolingEnabled(false); + BulletGlobals.DbvtTreeColliderPool.SetPoolingEnabled(false); + BulletGlobals.SingleSweepCallbackPool.SetPoolingEnabled(false); + BulletGlobals.BroadphaseRayTesterPool.SetPoolingEnabled(false); + BulletGlobals.ClosestNotMeConvexResultCallbackPool.SetPoolingEnabled(false); + BulletGlobals.GjkEpaPenetrationDepthSolverPool.SetPoolingEnabled(false); + BulletGlobals.ContinuousConvexCollisionPool.SetPoolingEnabled(false); + BulletGlobals.DbvtStackDataBlockPool.SetPoolingEnabled(false); + + BulletGlobals.BoxBoxCollisionAlgorithmPool.SetPoolingEnabled(false); + BulletGlobals.CompoundCollisionAlgorithmPool.SetPoolingEnabled(false); + BulletGlobals.ConvexConcaveCollisionAlgorithmPool.SetPoolingEnabled(false); + BulletGlobals.ConvexConvexAlgorithmPool.SetPoolingEnabled(false); + BulletGlobals.ConvexPlaneAlgorithmPool.SetPoolingEnabled(false); + BulletGlobals.SphereBoxCollisionAlgorithmPool.SetPoolingEnabled(false); + BulletGlobals.SphereSphereCollisionAlgorithmPool.SetPoolingEnabled(false); + BulletGlobals.SphereTriangleCollisionAlgorithmPool.SetPoolingEnabled(false); + BulletGlobals.GImpactCollisionAlgorithmPool.SetPoolingEnabled(false); + BulletGlobals.GjkEpaSolver2MinkowskiDiffPool.SetPoolingEnabled(false); + BulletGlobals.PersistentManifoldPool.SetPoolingEnabled(false); + BulletGlobals.ManifoldResultPool.SetPoolingEnabled(false); + BulletGlobals.GJKPool.SetPoolingEnabled(false); + BulletGlobals.GIM_ShapeRetrieverPool.SetPoolingEnabled(false); + BulletGlobals.TriangleShapePool.SetPoolingEnabled(false); + BulletGlobals.SphereTriangleDetectorPool.SetPoolingEnabled(false); + BulletGlobals.CompoundLeafCallbackPool.SetPoolingEnabled(false); + BulletGlobals.GjkConvexCastPool.SetPoolingEnabled(false); + BulletGlobals.LocalTriangleSphereCastCallbackPool.SetPoolingEnabled(false); + BulletGlobals.BridgeTriangleRaycastCallbackPool.SetPoolingEnabled(false); + BulletGlobals.BridgeTriangleConcaveRaycastCallbackPool.SetPoolingEnabled(false); + BulletGlobals.BridgeTriangleConvexcastCallbackPool.SetPoolingEnabled(false); + BulletGlobals.MyNodeOverlapCallbackPool.SetPoolingEnabled(false); + BulletGlobals.ClosestRayResultCallbackPool.SetPoolingEnabled(false); + BulletGlobals.DebugDrawcallbackPool.SetPoolingEnabled(false); + + return world; + } + //m_constraint.ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL + public override bool SetConstraintParam(BulletConstraint pConstraint, ConstraintParams paramIndex, float paramvalue, ConstraintParamAxis axis) + { + Generic6DofConstraint constrain = (pConstraint as BulletConstraintXNA).constrain as Generic6DofConstraint; + if (axis == ConstraintParamAxis.AXIS_LINEAR_ALL || axis == ConstraintParamAxis.AXIS_ALL) + { + constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 0); + constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 1); + constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams) (int) paramIndex, paramvalue, 2); + } + if (axis == ConstraintParamAxis.AXIS_ANGULAR_ALL || axis == ConstraintParamAxis.AXIS_ALL) + { + constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 3); + constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 4); + constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, 5); + } + if (axis == ConstraintParamAxis.AXIS_LINEAR_ALL) + { + constrain.SetParam((BulletXNA.BulletDynamics.ConstraintParams)(int)paramIndex, paramvalue, (int)axis); + } + return true; + } + + public override bool PushUpdate(BulletBody pCollisionObject) + { + bool ret = false; + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + RigidBody rb = collisionObject as RigidBody; + if (rb != null) + { + SimMotionState sms = rb.GetMotionState() as SimMotionState; + if (sms != null) + { + IndexedMatrix wt = IndexedMatrix.Identity; + sms.GetWorldTransform(out wt); + sms.SetWorldTransform(ref wt, true); + ret = true; + } + } + return ret; + + } + + public override float GetAngularMotionDisc(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return shape.GetAngularMotionDisc(); + } + public override float GetContactBreakingThreshold(BulletShape pShape, float defaultFactor) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return shape.GetContactBreakingThreshold(defaultFactor); + } + public override bool IsCompound(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return shape.IsCompound(); + } + public override bool IsSoftBody(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return shape.IsSoftBody(); + } + public override bool IsPolyhedral(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return shape.IsPolyhedral(); + } + public override bool IsConvex2d(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return shape.IsConvex2d(); + } + public override bool IsConvex(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return shape.IsConvex(); + } + public override bool IsNonMoving(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return shape.IsNonMoving(); + } + public override bool IsConcave(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return shape.IsConcave(); + } + public override bool IsInfinite(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + return shape.IsInfinite(); + } + public override bool IsNativeShape(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + bool ret; + switch (shape.GetShapeType()) + { + case BroadphaseNativeTypes.BOX_SHAPE_PROXYTYPE: + case BroadphaseNativeTypes.CONE_SHAPE_PROXYTYPE: + case BroadphaseNativeTypes.SPHERE_SHAPE_PROXYTYPE: + case BroadphaseNativeTypes.CYLINDER_SHAPE_PROXYTYPE: + ret = true; + break; + default: + ret = false; + break; + } + return ret; + } + + public override void SetShapeCollisionMargin(BulletShape pShape, float pMargin) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + shape.SetMargin(pMargin); + } + + //sim.ptr, shape.ptr,prim.LocalID, prim.RawPosition, prim.RawOrientation + public override BulletBody CreateGhostFromShape(BulletWorld pWorld, BulletShape pShape, uint pLocalID, Vector3 pRawPosition, Quaternion pRawOrientation) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + IndexedMatrix bodyTransform = new IndexedMatrix(); + bodyTransform._origin = new IndexedVector3(pRawPosition.X, pRawPosition.Y, pRawPosition.Z); + bodyTransform.SetRotation(new IndexedQuaternion(pRawOrientation.X,pRawOrientation.Y,pRawOrientation.Z,pRawOrientation.W)); + GhostObject gObj = new PairCachingGhostObject(); + gObj.SetWorldTransform(bodyTransform); + CollisionShape shape = (pShape as BulletShapeXNA).shape; + gObj.SetCollisionShape(shape); + gObj.SetUserPointer(pLocalID); + + if (specialCollisionObjects.ContainsKey(pLocalID)) + specialCollisionObjects[pLocalID] = gObj; + else + specialCollisionObjects.Add(pLocalID, gObj); + + // TODO: Add to Special CollisionObjects! + return new BulletBodyXNA(pLocalID, gObj); + } + + public override void SetCollisionShape(BulletWorld pWorld, BulletBody pCollisionObject, BulletShape pShape) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).body; + if (pShape == null) + { + collisionObject.SetCollisionShape(new EmptyShape()); + } + else + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + collisionObject.SetCollisionShape(shape); + } + } + public override BulletShape GetCollisionShape(BulletBody pCollisionObject) + { + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + CollisionShape shape = collisionObject.GetCollisionShape(); + return new BulletShapeXNA(shape, BSShapeTypeFromBroadPhaseNativeType(shape.GetShapeType())); + } + + //(PhysicsScene.World.ptr, nativeShapeData) + public override BulletShape BuildNativeShape(BulletWorld pWorld, ShapeData pShapeData) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + CollisionShape shape = null; + switch (pShapeData.Type) + { + case BSPhysicsShapeType.SHAPE_BOX: + shape = new BoxShape(new IndexedVector3(0.5f,0.5f,0.5f)); + break; + case BSPhysicsShapeType.SHAPE_CONE: + shape = new ConeShapeZ(0.5f, 1.0f); + break; + case BSPhysicsShapeType.SHAPE_CYLINDER: + shape = new CylinderShapeZ(new IndexedVector3(0.5f, 0.5f, 0.5f)); + break; + case BSPhysicsShapeType.SHAPE_SPHERE: + shape = new SphereShape(0.5f); + break; + + } + if (shape != null) + { + IndexedVector3 scaling = new IndexedVector3(pShapeData.Scale.X, pShapeData.Scale.Y, pShapeData.Scale.Z); + shape.SetMargin(world.WorldSettings.Params.collisionMargin); + shape.SetLocalScaling(ref scaling); + + } + return new BulletShapeXNA(shape, pShapeData.Type); + } + //PhysicsScene.World.ptr, false + public override BulletShape CreateCompoundShape(BulletWorld pWorld, bool enableDynamicAabbTree) + { + return new BulletShapeXNA(new CompoundShape(enableDynamicAabbTree), BSPhysicsShapeType.SHAPE_COMPOUND); + } + + public override int GetNumberOfCompoundChildren(BulletShape pCompoundShape) + { + CompoundShape compoundshape = (pCompoundShape as BulletShapeXNA).shape as CompoundShape; + return compoundshape.GetNumChildShapes(); + } + //LinksetRoot.PhysShape.ptr, newShape.ptr, displacementPos, displacementRot + public override void AddChildShapeToCompoundShape(BulletShape pCShape, BulletShape paddShape, Vector3 displacementPos, Quaternion displacementRot) + { + IndexedMatrix relativeTransform = new IndexedMatrix(); + CompoundShape compoundshape = (pCShape as BulletShapeXNA).shape as CompoundShape; + CollisionShape addshape = (paddShape as BulletShapeXNA).shape; + + relativeTransform._origin = new IndexedVector3(displacementPos.X, displacementPos.Y, displacementPos.Z); + relativeTransform.SetRotation(new IndexedQuaternion(displacementRot.X,displacementRot.Y,displacementRot.Z,displacementRot.W)); + compoundshape.AddChildShape(ref relativeTransform, addshape); + + } + + public override BulletShape RemoveChildShapeFromCompoundShapeIndex(BulletShape pCShape, int pii) + { + CompoundShape compoundshape = (pCShape as BulletShapeXNA).shape as CompoundShape; + CollisionShape ret = null; + ret = compoundshape.GetChildShape(pii); + compoundshape.RemoveChildShapeByIndex(pii); + return new BulletShapeXNA(ret, BSShapeTypeFromBroadPhaseNativeType(ret.GetShapeType())); + } + + public override BulletShape GetChildShapeFromCompoundShapeIndex(BulletShape cShape, int indx) { + + if (cShape == null) + return null; + CompoundShape compoundShape = (cShape as BulletShapeXNA).shape as CompoundShape; + CollisionShape shape = compoundShape.GetChildShape(indx); + BulletShape retShape = new BulletShapeXNA(shape, BSShapeTypeFromBroadPhaseNativeType(shape.GetShapeType())); + + + return retShape; + } + + public BSPhysicsShapeType BSShapeTypeFromBroadPhaseNativeType(BroadphaseNativeTypes pin) + { + BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + switch (pin) + { + case BroadphaseNativeTypes.BOX_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_BOX; + break; + case BroadphaseNativeTypes.TRIANGLE_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + + case BroadphaseNativeTypes.TETRAHEDRAL_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_CONVEXHULL; + break; + case BroadphaseNativeTypes.CONVEX_HULL_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_HULL; + break; + case BroadphaseNativeTypes.CONVEX_POINT_CLOUD_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.CUSTOM_POLYHEDRAL_SHAPE_TYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + //implicit convex shapes + case BroadphaseNativeTypes.IMPLICIT_CONVEX_SHAPES_START_HERE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.SPHERE_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_SPHERE; + break; + case BroadphaseNativeTypes.MULTI_SPHERE_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.CAPSULE_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_CAPSULE; + break; + case BroadphaseNativeTypes.CONE_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_CONE; + break; + case BroadphaseNativeTypes.CONVEX_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_CONVEXHULL; + break; + case BroadphaseNativeTypes.CYLINDER_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_CYLINDER; + break; + case BroadphaseNativeTypes.UNIFORM_SCALING_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.MINKOWSKI_SUM_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.MINKOWSKI_DIFFERENCE_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.BOX_2D_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.CONVEX_2D_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.CUSTOM_CONVEX_SHAPE_TYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + //concave shape + case BroadphaseNativeTypes.CONCAVE_SHAPES_START_HERE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + //keep all the convex shapetype below here, for the check IsConvexShape in broadphase proxy! + case BroadphaseNativeTypes.TRIANGLE_MESH_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_MESH; + break; + case BroadphaseNativeTypes.SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_MESH; + break; + ///used for demo integration FAST/Swift collision library and Bullet + case BroadphaseNativeTypes.FAST_CONCAVE_MESH_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_MESH; + break; + //terrain + case BroadphaseNativeTypes.TERRAIN_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_HEIGHTMAP; + break; + ///Used for GIMPACT Trimesh integration + case BroadphaseNativeTypes.GIMPACT_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_GIMPACT; + break; + ///Multimaterial mesh + case BroadphaseNativeTypes.MULTIMATERIAL_TRIANGLE_MESH_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_MESH; + break; + + case BroadphaseNativeTypes.EMPTY_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.STATIC_PLANE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_GROUNDPLANE; + break; + case BroadphaseNativeTypes.CUSTOM_CONCAVE_SHAPE_TYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.CONCAVE_SHAPES_END_HERE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + + case BroadphaseNativeTypes.COMPOUND_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_COMPOUND; + break; + + case BroadphaseNativeTypes.SOFTBODY_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_MESH; + break; + case BroadphaseNativeTypes.HFFLUID_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.HFFLUID_BUOYANT_CONVEX_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + case BroadphaseNativeTypes.INVALID_SHAPE_PROXYTYPE: + ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + break; + } + return ret; + } + + public override void RemoveChildShapeFromCompoundShape(BulletShape cShape, BulletShape removeShape) { /* TODO */ } + public override void UpdateChildTransform(BulletShape pShape, int childIndex, Vector3 pos, Quaternion rot, bool shouldRecalculateLocalAabb) { /* TODO */ } + + public override BulletShape CreateGroundPlaneShape(uint pLocalId, float pheight, float pcollisionMargin) + { + StaticPlaneShape m_planeshape = new StaticPlaneShape(new IndexedVector3(0,0,1),(int)pheight ); + m_planeshape.SetMargin(pcollisionMargin); + m_planeshape.SetUserPointer(pLocalId); + return new BulletShapeXNA(m_planeshape, BSPhysicsShapeType.SHAPE_GROUNDPLANE); + } + + public override BulletConstraint Create6DofSpringConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, Vector3 pframe2, Quaternion pframe2rot, + bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) + + { + Generic6DofSpringConstraint constrain = null; + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody body1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody body2 = (pBody2 as BulletBodyXNA).rigidBody; + if (body1 != null && body2 != null) + { + IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z); + IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W); + IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot); + frame1._origin = frame1v; + + IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z); + IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W); + IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot); + frame2._origin = frame1v; + + constrain = new Generic6DofSpringConstraint(body1, body2, ref frame1, ref frame2, puseLinearReferenceFrameA); + world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); + + constrain.CalculateTransforms(); + } + + return new BulletConstraintXNA(constrain); + } + + public override BulletConstraint CreateHingeConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 ppivotInA, Vector3 ppivotInB, Vector3 paxisInA, Vector3 paxisInB, + bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) + { + HingeConstraint constrain = null; + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody rb1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody rb2 = (pBody2 as BulletBodyXNA).rigidBody; + if (rb1 != null && rb2 != null) + { + IndexedVector3 pivotInA = new IndexedVector3(ppivotInA.X, ppivotInA.Y, ppivotInA.Z); + IndexedVector3 pivotInB = new IndexedVector3(ppivotInB.X, ppivotInB.Y, ppivotInB.Z); + IndexedVector3 axisInA = new IndexedVector3(paxisInA.X, paxisInA.Y, paxisInA.Z); + IndexedVector3 axisInB = new IndexedVector3(paxisInB.X, paxisInB.Y, paxisInB.Z); + constrain = new HingeConstraint(rb1, rb2, ref pivotInA, ref pivotInB, ref axisInA, ref axisInB, puseLinearReferenceFrameA); + world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); + } + return new BulletConstraintXNA(constrain); + } + + public override BulletConstraint CreateSliderConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, + Vector3 pframe2, Quaternion pframe2rot, + bool puseLinearReferenceFrameA, bool pdisableCollisionsBetweenLinkedBodies) + { + SliderConstraint constrain = null; + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody rb1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody rb2 = (pBody2 as BulletBodyXNA).rigidBody; + if (rb1 != null && rb2 != null) + { + IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z); + IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W); + IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot); + frame1._origin = frame1v; + + IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z); + IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W); + IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot); + frame2._origin = frame1v; + + constrain = new SliderConstraint(rb1, rb2, ref frame1, ref frame2, puseLinearReferenceFrameA); + world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); + } + return new BulletConstraintXNA(constrain); + } + + public override BulletConstraint CreateConeTwistConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 pframe1, Quaternion pframe1rot, + Vector3 pframe2, Quaternion pframe2rot, + bool pdisableCollisionsBetweenLinkedBodies) + { + ConeTwistConstraint constrain = null; + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody rb1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody rb2 = (pBody2 as BulletBodyXNA).rigidBody; + if (rb1 != null && rb2 != null) + { + IndexedVector3 frame1v = new IndexedVector3(pframe1.X, pframe1.Y, pframe1.Z); + IndexedQuaternion frame1rot = new IndexedQuaternion(pframe1rot.X, pframe1rot.Y, pframe1rot.Z, pframe1rot.W); + IndexedMatrix frame1 = IndexedMatrix.CreateFromQuaternion(frame1rot); + frame1._origin = frame1v; + + IndexedVector3 frame2v = new IndexedVector3(pframe2.X, pframe2.Y, pframe2.Z); + IndexedQuaternion frame2rot = new IndexedQuaternion(pframe2rot.X, pframe2rot.Y, pframe2rot.Z, pframe2rot.W); + IndexedMatrix frame2 = IndexedMatrix.CreateFromQuaternion(frame2rot); + frame2._origin = frame1v; + + constrain = new ConeTwistConstraint(rb1, rb2, ref frame1, ref frame2); + world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); + } + return new BulletConstraintXNA(constrain); + } + + public override BulletConstraint CreateGearConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 paxisInA, Vector3 paxisInB, + float pratio, bool pdisableCollisionsBetweenLinkedBodies) + { + Generic6DofConstraint constrain = null; + /* BulletXNA does not have a gear constraint + GearConstraint constrain = null; + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody rb1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody rb2 = (pBody2 as BulletBodyXNA).rigidBody; + if (rb1 != null && rb2 != null) + { + IndexedVector3 axis1 = new IndexedVector3(paxisInA.X, paxisInA.Y, paxisInA.Z); + IndexedVector3 axis2 = new IndexedVector3(paxisInB.X, paxisInB.Y, paxisInB.Z); + constrain = new GearConstraint(rb1, rb2, ref axis1, ref axis2, pratio); + world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); + } + */ + return new BulletConstraintXNA(constrain); + } + + public override BulletConstraint CreatePoint2PointConstraint(BulletWorld pWorld, BulletBody pBody1, BulletBody pBody2, + Vector3 ppivotInA, Vector3 ppivotInB, + bool pdisableCollisionsBetweenLinkedBodies) + { + Point2PointConstraint constrain = null; + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + RigidBody rb1 = (pBody1 as BulletBodyXNA).rigidBody; + RigidBody rb2 = (pBody2 as BulletBodyXNA).rigidBody; + if (rb1 != null && rb2 != null) + { + IndexedVector3 pivotInA = new IndexedVector3(ppivotInA.X, ppivotInA.Y, ppivotInA.Z); + IndexedVector3 pivotInB = new IndexedVector3(ppivotInB.X, ppivotInB.Y, ppivotInB.Z); + constrain = new Point2PointConstraint(rb1, rb2, ref pivotInA, ref pivotInB); + world.AddConstraint(constrain, pdisableCollisionsBetweenLinkedBodies); + } + return new BulletConstraintXNA(constrain); + } + + public override BulletShape CreateHullShape(BulletWorld pWorld, int pHullCount, float[] pConvHulls) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + CompoundShape compoundshape = new CompoundShape(false); + + compoundshape.SetMargin(world.WorldSettings.Params.collisionMargin); + int ii = 1; + + for (int i = 0; i < pHullCount; i++) + { + int vertexCount = (int) pConvHulls[ii]; + + IndexedVector3 centroid = new IndexedVector3(pConvHulls[ii + 1], pConvHulls[ii + 2], pConvHulls[ii + 3]); + IndexedMatrix childTrans = IndexedMatrix.Identity; + childTrans._origin = centroid; + + List virts = new List(); + int ender = ((ii + 4) + (vertexCount*3)); + for (int iii = ii + 4; iii < ender; iii+=3) + { + + virts.Add(new IndexedVector3(pConvHulls[iii], pConvHulls[iii + 1], pConvHulls[iii +2])); + } + ConvexHullShape convexShape = new ConvexHullShape(virts, vertexCount); + convexShape.SetMargin(world.WorldSettings.Params.collisionMargin); + compoundshape.AddChildShape(ref childTrans, convexShape); + ii += (vertexCount*3 + 4); + } + + return new BulletShapeXNA(compoundshape, BSPhysicsShapeType.SHAPE_HULL); + } + + public override BulletShape BuildHullShapeFromMesh(BulletWorld world, BulletShape meshShape, HACDParams parms) + { + /* TODO */ return null; + } + + public override BulletShape BuildConvexHullShapeFromMesh(BulletWorld world, BulletShape meshShape) + { + /* TODO */ return null; + } + + public override BulletShape CreateConvexHullShape(BulletWorld pWorld, int pIndicesCount, int[] indices, int pVerticesCount, float[] verticesAsFloats) + { + /* TODO */ return null; + } + + public override BulletShape CreateMeshShape(BulletWorld pWorld, int pIndicesCount, int[] indices, int pVerticesCount, float[] verticesAsFloats) + { + //DumpRaw(indices,verticesAsFloats,pIndicesCount,pVerticesCount); + + for (int iter = 0; iter < pVerticesCount; iter++) + { + if (verticesAsFloats[iter] > 0 && verticesAsFloats[iter] < 0.0001) verticesAsFloats[iter] = 0; + if (verticesAsFloats[iter] < 0 && verticesAsFloats[iter] > -0.0001) verticesAsFloats[iter] = 0; + } + + ObjectArray indicesarr = new ObjectArray(indices); + ObjectArray vertices = new ObjectArray(verticesAsFloats); + DumpRaw(indicesarr,vertices,pIndicesCount,pVerticesCount); + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + IndexedMesh mesh = new IndexedMesh(); + mesh.m_indexType = PHY_ScalarType.PHY_INTEGER; + mesh.m_numTriangles = pIndicesCount/3; + mesh.m_numVertices = pVerticesCount; + mesh.m_triangleIndexBase = indicesarr; + mesh.m_vertexBase = vertices; + mesh.m_vertexStride = 3; + mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT; + mesh.m_triangleIndexStride = 3; + + TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray(); + tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER); + BvhTriangleMeshShape meshShape = new BvhTriangleMeshShape(tribuilder, true,true); + meshShape.SetMargin(world.WorldSettings.Params.collisionMargin); + // world.UpdateSingleAabb(meshShape); + return new BulletShapeXNA(meshShape, BSPhysicsShapeType.SHAPE_MESH); + + } + public override BulletShape CreateGImpactShape(BulletWorld pWorld, int pIndicesCount, int[] indices, int pVerticesCount, float[] verticesAsFloats) + { + // TODO: + return null; + } + public static void DumpRaw(ObjectArrayindices, ObjectArray vertices, int pIndicesCount,int pVerticesCount ) + { + + String fileName = "objTest3.raw"; + String completePath = System.IO.Path.Combine(Util.configDir(), fileName); + StreamWriter sw = new StreamWriter(completePath); + IndexedMesh mesh = new IndexedMesh(); + + mesh.m_indexType = PHY_ScalarType.PHY_INTEGER; + mesh.m_numTriangles = pIndicesCount / 3; + mesh.m_numVertices = pVerticesCount; + mesh.m_triangleIndexBase = indices; + mesh.m_vertexBase = vertices; + mesh.m_vertexStride = 3; + mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT; + mesh.m_triangleIndexStride = 3; + + TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray(); + tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER); + + + + for (int i = 0; i < pVerticesCount; i++) + { + + string s = vertices[indices[i * 3]].ToString("0.0000"); + s += " " + vertices[indices[i * 3 + 1]].ToString("0.0000"); + s += " " + vertices[indices[i * 3 + 2]].ToString("0.0000"); + + sw.Write(s + "\n"); + } + + sw.Close(); + } + public static void DumpRaw(int[] indices, float[] vertices, int pIndicesCount, int pVerticesCount) + { + + String fileName = "objTest6.raw"; + String completePath = System.IO.Path.Combine(Util.configDir(), fileName); + StreamWriter sw = new StreamWriter(completePath); + IndexedMesh mesh = new IndexedMesh(); + + mesh.m_indexType = PHY_ScalarType.PHY_INTEGER; + mesh.m_numTriangles = pIndicesCount / 3; + mesh.m_numVertices = pVerticesCount; + mesh.m_triangleIndexBase = indices; + mesh.m_vertexBase = vertices; + mesh.m_vertexStride = 3; + mesh.m_vertexType = PHY_ScalarType.PHY_FLOAT; + mesh.m_triangleIndexStride = 3; + + TriangleIndexVertexArray tribuilder = new TriangleIndexVertexArray(); + tribuilder.AddIndexedMesh(mesh, PHY_ScalarType.PHY_INTEGER); + + + sw.WriteLine("Indices"); + sw.WriteLine(string.Format("int[] indices = new int[{0}];",pIndicesCount)); + for (int iter = 0; iter < indices.Length; iter++) + { + sw.WriteLine(string.Format("indices[{0}]={1};",iter,indices[iter])); + } + sw.WriteLine("VerticesFloats"); + sw.WriteLine(string.Format("float[] vertices = new float[{0}];", pVerticesCount)); + for (int iter = 0; iter < vertices.Length; iter++) + { + sw.WriteLine(string.Format("Vertices[{0}]={1};", iter, vertices[iter].ToString("0.0000"))); + } + + // for (int i = 0; i < pVerticesCount; i++) + // { + // + // string s = vertices[indices[i * 3]].ToString("0.0000"); + // s += " " + vertices[indices[i * 3 + 1]].ToString("0.0000"); + // s += " " + vertices[indices[i * 3 + 2]].ToString("0.0000"); + // + // sw.Write(s + "\n"); + //} + + sw.Close(); + } + + public override BulletShape CreateTerrainShape(uint id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, + float scaleFactor, float collisionMargin) + { + const int upAxis = 2; + HeightfieldTerrainShape terrainShape = new HeightfieldTerrainShape((int)size.X, (int)size.Y, + heightMap, scaleFactor, + minHeight, maxHeight, upAxis, + false); + terrainShape.SetMargin(collisionMargin); + terrainShape.SetUseDiamondSubdivision(true); + terrainShape.SetUserPointer(id); + return new BulletShapeXNA(terrainShape, BSPhysicsShapeType.SHAPE_TERRAIN); + } + + public override bool TranslationalLimitMotor(BulletConstraint pConstraint, float ponOff, float targetVelocity, float maxMotorForce) + { + TypedConstraint tconstrain = (pConstraint as BulletConstraintXNA).constrain; + bool onOff = ponOff != 0; + bool ret = false; + + switch (tconstrain.GetConstraintType()) + { + case TypedConstraintType.D6_CONSTRAINT_TYPE: + Generic6DofConstraint constrain = tconstrain as Generic6DofConstraint; + constrain.GetTranslationalLimitMotor().m_enableMotor[0] = onOff; + constrain.GetTranslationalLimitMotor().m_targetVelocity[0] = targetVelocity; + constrain.GetTranslationalLimitMotor().m_maxMotorForce[0] = maxMotorForce; + ret = true; + break; + } + + + return ret; + + } + + public override int PhysicsStep(BulletWorld world, float timeStep, int maxSubSteps, float fixedTimeStep, + out int updatedEntityCount, out int collidersCount) + { + /* TODO */ + updatedEntityCount = 0; + collidersCount = 0; + + + int ret = PhysicsStep2(world,timeStep,maxSubSteps,fixedTimeStep,out updatedEntityCount,out world.physicsScene.m_updateArray, out collidersCount, out world.physicsScene.m_collisionArray); + + return ret; + } + + private int PhysicsStep2(BulletWorld pWorld, float timeStep, int m_maxSubSteps, float m_fixedTimeStep, + out int updatedEntityCount, out EntityProperties[] updatedEntities, + out int collidersCount, out CollisionDesc[] colliders) + { + int epic = PhysicsStepint(pWorld, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out updatedEntities, + out collidersCount, out colliders, m_maxCollisions, m_maxUpdatesPerFrame); + return epic; + } + + private int PhysicsStepint(BulletWorld pWorld,float timeStep, int m_maxSubSteps, float m_fixedTimeStep, out int updatedEntityCount, + out EntityProperties[] updatedEntities, out int collidersCount, out CollisionDesc[] colliders, int maxCollisions, int maxUpdates) + { + int numSimSteps = 0; + Array.Clear(UpdatedObjects, 0, UpdatedObjects.Length); + Array.Clear(UpdatedCollisions, 0, UpdatedCollisions.Length); + LastEntityProperty=0; + + + + + + + LastCollisionDesc=0; + + updatedEntityCount = 0; + collidersCount = 0; + + + if (pWorld is BulletWorldXNA) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + + world.LastCollisionDesc = 0; + world.LastEntityProperty = 0; + numSimSteps = world.StepSimulation(timeStep, m_maxSubSteps, m_fixedTimeStep); + + PersistentManifold contactManifold; + CollisionObject objA; + CollisionObject objB; + ManifoldPoint manifoldPoint; + PairCachingGhostObject pairCachingGhostObject; + + m_collisionsThisFrame = 0; + int numManifolds = world.GetDispatcher().GetNumManifolds(); + for (int j = 0; j < numManifolds; j++) + { + contactManifold = world.GetDispatcher().GetManifoldByIndexInternal(j); + int numContacts = contactManifold.GetNumContacts(); + if (numContacts == 0) + continue; + + objA = contactManifold.GetBody0() as CollisionObject; + objB = contactManifold.GetBody1() as CollisionObject; + + manifoldPoint = contactManifold.GetContactPoint(0); + //IndexedVector3 contactPoint = manifoldPoint.GetPositionWorldOnB(); + // IndexedVector3 contactNormal = -manifoldPoint.m_normalWorldOnB; // make relative to A + + RecordCollision(this, objA, objB, manifoldPoint.GetPositionWorldOnB(), -manifoldPoint.m_normalWorldOnB, manifoldPoint.GetDistance()); + m_collisionsThisFrame ++; + if (m_collisionsThisFrame >= 9999999) + break; + + + } + + foreach (GhostObject ghostObject in specialCollisionObjects.Values) + { + pairCachingGhostObject = ghostObject as PairCachingGhostObject; + if (pairCachingGhostObject != null) + { + RecordGhostCollisions(pairCachingGhostObject); + } + + } + + + updatedEntityCount = LastEntityProperty; + updatedEntities = UpdatedObjects; + + collidersCount = LastCollisionDesc; + colliders = UpdatedCollisions; + + + } + else + { + //if (updatedEntities is null) + //updatedEntities = new List(); + //updatedEntityCount = 0; + + + //collidersCount = 0; + + updatedEntities = new EntityProperties[0]; + + + colliders = new CollisionDesc[0]; + + } + return numSimSteps; + } + public void RecordGhostCollisions(PairCachingGhostObject obj) + { + IOverlappingPairCache cache = obj.GetOverlappingPairCache(); + ObjectArray pairs = cache.GetOverlappingPairArray(); + + DiscreteDynamicsWorld world = (PhysicsScene.World as BulletWorldXNA).world; + PersistentManifoldArray manifoldArray = new PersistentManifoldArray(); + BroadphasePair collisionPair; + PersistentManifold contactManifold; + + CollisionObject objA; + CollisionObject objB; + + ManifoldPoint pt; + + int numPairs = pairs.Count; + + for (int i = 0; i < numPairs; i++) + { + manifoldArray.Clear(); + if (LastCollisionDesc < UpdatedCollisions.Length) + break; + collisionPair = world.GetPairCache().FindPair(pairs[i].m_pProxy0, pairs[i].m_pProxy1); + if (collisionPair == null) + continue; + + collisionPair.m_algorithm.GetAllContactManifolds(manifoldArray); + for (int j = 0; j < manifoldArray.Count; j++) + { + contactManifold = manifoldArray[j]; + int numContacts = contactManifold.GetNumContacts(); + objA = contactManifold.GetBody0() as CollisionObject; + objB = contactManifold.GetBody1() as CollisionObject; + for (int p = 0; p < numContacts; p++) + { + pt = contactManifold.GetContactPoint(p); + if (pt.GetDistance() < 0.0f) + { + RecordCollision(this, objA, objB, pt.GetPositionWorldOnA(), -pt.m_normalWorldOnB,pt.GetDistance()); + break; + } + } + } + } + + } + private static void RecordCollision(BSAPIXNA world, CollisionObject objA, CollisionObject objB, IndexedVector3 contact, IndexedVector3 norm, float penetration) + { + + IndexedVector3 contactNormal = norm; + if ((objA.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0 && + (objB.GetCollisionFlags() & BulletXNA.BulletCollision.CollisionFlags.BS_WANTS_COLLISIONS) == 0) + { + return; + } + uint idA = (uint)objA.GetUserPointer(); + uint idB = (uint)objB.GetUserPointer(); + if (idA > idB) + { + uint temp = idA; + idA = idB; + idB = temp; + contactNormal = -contactNormal; + } + + //ulong collisionID = ((ulong) idA << 32) | idB; + + CollisionDesc cDesc = new CollisionDesc() + { + aID = idA, + bID = idB, + point = new Vector3(contact.X,contact.Y,contact.Z), + normal = new Vector3(contactNormal.X,contactNormal.Y,contactNormal.Z), + penetration = penetration + + }; + if (world.LastCollisionDesc < world.UpdatedCollisions.Length) + world.UpdatedCollisions[world.LastCollisionDesc++] = (cDesc); + m_collisionsThisFrame++; + + + } + private static EntityProperties GetDebugProperties(BulletWorld pWorld, BulletBody pCollisionObject) + { + EntityProperties ent = new EntityProperties(); + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + CollisionObject collisionObject = (pCollisionObject as BulletBodyXNA).rigidBody; + IndexedMatrix transform = collisionObject.GetWorldTransform(); + IndexedVector3 LinearVelocity = collisionObject.GetInterpolationLinearVelocity(); + IndexedVector3 AngularVelocity = collisionObject.GetInterpolationAngularVelocity(); + IndexedQuaternion rotation = transform.GetRotation(); + ent.Acceleration = Vector3.Zero; + ent.ID = (uint)collisionObject.GetUserPointer(); + ent.Position = new Vector3(transform._origin.X,transform._origin.Y,transform._origin.Z); + ent.Rotation = new Quaternion(rotation.X,rotation.Y,rotation.Z,rotation.W); + ent.Velocity = new Vector3(LinearVelocity.X, LinearVelocity.Y, LinearVelocity.Z); + ent.RotationalVelocity = new Vector3(AngularVelocity.X, AngularVelocity.Y, AngularVelocity.Z); + return ent; + } + + public override bool UpdateParameter(BulletWorld world, uint localID, String parm, float value) { /* TODO */ + return false; } + + public override Vector3 GetLocalScaling(BulletShape pShape) + { + CollisionShape shape = (pShape as BulletShapeXNA).shape; + IndexedVector3 scale = shape.GetLocalScaling(); + return new Vector3(scale.X,scale.Y,scale.Z); + } + + public bool RayCastGround(BulletWorld pWorld, Vector3 _RayOrigin, float pRayHeight, BulletBody NotMe) + { + DiscreteDynamicsWorld world = (pWorld as BulletWorldXNA).world; + if (world != null) + { + if (NotMe is BulletBodyXNA && NotMe.HasPhysicalBody) + { + CollisionObject AvoidBody = (NotMe as BulletBodyXNA).body; + + IndexedVector3 rOrigin = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z); + IndexedVector3 rEnd = new IndexedVector3(_RayOrigin.X, _RayOrigin.Y, _RayOrigin.Z - pRayHeight); + using ( + ClosestNotMeRayResultCallback rayCallback = + new ClosestNotMeRayResultCallback(rOrigin, rEnd, AvoidBody) + ) + { + world.RayTest(ref rOrigin, ref rEnd, rayCallback); + if (rayCallback.HasHit()) + { + IndexedVector3 hitLocation = rayCallback.m_hitPointWorld; + } + return rayCallback.HasHit(); + } + } + } + return false; + } +} + + + + + public class SimMotionState : DefaultMotionState + { + public RigidBody Rigidbody; + public Vector3 ZeroVect; + + private IndexedMatrix m_xform; + + private EntityProperties m_properties; + private EntityProperties m_lastProperties; + private BSAPIXNA m_world; + + const float POSITION_TOLERANCE = 0.05f; + const float VELOCITY_TOLERANCE = 0.001f; + const float ROTATION_TOLERANCE = 0.01f; + const float ANGULARVELOCITY_TOLERANCE = 0.01f; + + public SimMotionState(BSAPIXNA pWorld, uint id, IndexedMatrix starTransform, object frameUpdates) + { + IndexedQuaternion OrientationQuaterion = starTransform.GetRotation(); + m_properties = new EntityProperties() + { + ID = id, + Position = new Vector3(starTransform._origin.X, starTransform._origin.Y,starTransform._origin.Z), + Rotation = new Quaternion(OrientationQuaterion.X,OrientationQuaterion.Y,OrientationQuaterion.Z,OrientationQuaterion.W) + }; + m_lastProperties = new EntityProperties() + { + ID = id, + Position = new Vector3(starTransform._origin.X, starTransform._origin.Y, starTransform._origin.Z), + Rotation = new Quaternion(OrientationQuaterion.X, OrientationQuaterion.Y, OrientationQuaterion.Z, OrientationQuaterion.W) + }; + m_world = pWorld; + m_xform = starTransform; + } + + public override void GetWorldTransform(out IndexedMatrix worldTrans) + { + worldTrans = m_xform; + } + + public override void SetWorldTransform(IndexedMatrix worldTrans) + { + SetWorldTransform(ref worldTrans); + } + + public override void SetWorldTransform(ref IndexedMatrix worldTrans) + { + SetWorldTransform(ref worldTrans, false); + } + public void SetWorldTransform(ref IndexedMatrix worldTrans, bool force) + { + m_xform = worldTrans; + // Put the new transform into m_properties + IndexedQuaternion OrientationQuaternion = m_xform.GetRotation(); + IndexedVector3 LinearVelocityVector = Rigidbody.GetLinearVelocity(); + IndexedVector3 AngularVelocityVector = Rigidbody.GetAngularVelocity(); + m_properties.Position = new Vector3(m_xform._origin.X, m_xform._origin.Y, m_xform._origin.Z); + m_properties.Rotation = new Quaternion(OrientationQuaternion.X, OrientationQuaternion.Y, + OrientationQuaternion.Z, OrientationQuaternion.W); + // A problem with stock Bullet is that we don't get an event when an object is deactivated. + // This means that the last non-zero values for linear and angular velocity + // are left in the viewer who does dead reconning and the objects look like + // they float off. + // BulletSim ships with a patch to Bullet which creates such an event. + m_properties.Velocity = new Vector3(LinearVelocityVector.X, LinearVelocityVector.Y, LinearVelocityVector.Z); + m_properties.RotationalVelocity = new Vector3(AngularVelocityVector.X, AngularVelocityVector.Y, AngularVelocityVector.Z); + + if (force + + || !AlmostEqual(ref m_lastProperties.Position, ref m_properties.Position, POSITION_TOLERANCE) + || !AlmostEqual(ref m_properties.Rotation, ref m_lastProperties.Rotation, ROTATION_TOLERANCE) + // If the Velocity and AngularVelocity are zero, most likely the object has + // been deactivated. If they both are zero and they have become zero recently, + // make sure a property update is sent so the zeros make it to the viewer. + || ((m_properties.Velocity == ZeroVect && m_properties.RotationalVelocity == ZeroVect) + && + (m_properties.Velocity != m_lastProperties.Velocity || + m_properties.RotationalVelocity != m_lastProperties.RotationalVelocity)) + // If Velocity and AngularVelocity are non-zero but have changed, send an update. + || !AlmostEqual(ref m_properties.Velocity, ref m_lastProperties.Velocity, VELOCITY_TOLERANCE) + || + !AlmostEqual(ref m_properties.RotationalVelocity, ref m_lastProperties.RotationalVelocity, + ANGULARVELOCITY_TOLERANCE) + ) + + + { + // Add this update to the list of updates for this frame. + m_lastProperties = m_properties; + if (m_world.LastEntityProperty < m_world.UpdatedObjects.Length) + m_world.UpdatedObjects[m_world.LastEntityProperty++]=(m_properties); + + //(*m_updatesThisFrame)[m_properties.ID] = &m_properties; + } + + + + + } + public override void SetRigidBody(RigidBody body) + { + Rigidbody = body; + } + internal static bool AlmostEqual(ref Vector3 v1, ref Vector3 v2, float nEpsilon) + { + return + (((v1.X - nEpsilon) < v2.X) && (v2.X < (v1.X + nEpsilon))) && + (((v1.Y - nEpsilon) < v2.Y) && (v2.Y < (v1.Y + nEpsilon))) && + (((v1.Z - nEpsilon) < v2.Z) && (v2.Z < (v1.Z + nEpsilon))); + } + + internal static bool AlmostEqual(ref Quaternion v1, ref Quaternion v2, float nEpsilon) + { + return + (((v1.X - nEpsilon) < v2.X) && (v2.X < (v1.X + nEpsilon))) && + (((v1.Y - nEpsilon) < v2.Y) && (v2.Y < (v1.Y + nEpsilon))) && + (((v1.Z - nEpsilon) < v2.Z) && (v2.Z < (v1.Z + nEpsilon))) && + (((v1.W - nEpsilon) < v2.W) && (v2.W < (v1.W + nEpsilon))); + } + + } +} + diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs new file mode 100755 index 0000000..bde4557 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs @@ -0,0 +1,457 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSActorAvatarMove : BSActor +{ + BSVMotor m_velocityMotor; + + // Set to true if we think we're going up stairs. + // This state is remembered because collisions will turn on and off as we go up stairs. + int m_walkingUpStairs; + // The amount the step up is applying. Used to smooth stair walking. + float m_lastStepUp; + + // Jumping happens over several frames. If use applies up force while colliding, start the + // jump and allow the jump to continue for this number of frames. + int m_jumpFrames = 0; + float m_jumpVelocity = 0f; + + public BSActorAvatarMove(BSScene physicsScene, BSPhysObject pObj, string actorName) + : base(physicsScene, pObj, actorName) + { + m_velocityMotor = null; + m_walkingUpStairs = 0; + m_physicsScene.DetailLog("{0},BSActorAvatarMove,constructor", m_controllingPrim.LocalID); + } + + // BSActor.isActive + public override bool isActive + { + get { return Enabled && m_controllingPrim.IsPhysicallyActive; } + } + + // Release any connections and resources used by the actor. + // BSActor.Dispose() + public override void Dispose() + { + base.SetEnabled(false); + DeactivateAvatarMove(); + } + + // Called when physical parameters (properties set in Bullet) need to be re-applied. + // Called at taint-time. + // BSActor.Refresh() + public override void Refresh() + { + m_physicsScene.DetailLog("{0},BSActorAvatarMove,refresh", m_controllingPrim.LocalID); + + // If the object is physically active, add the hoverer prestep action + if (isActive) + { + ActivateAvatarMove(); + } + else + { + DeactivateAvatarMove(); + } + } + + // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). + // Register a prestep action to restore physical requirements before the next simulation step. + // Called at taint-time. + // BSActor.RemoveDependencies() + public override void RemoveDependencies() + { + // Nothing to do for the hoverer since it is all software at pre-step action time. + } + + // Usually called when target velocity changes to set the current velocity and the target + // into the movement motor. + public void SetVelocityAndTarget(OMV.Vector3 vel, OMV.Vector3 targ, bool inTaintTime) + { + m_physicsScene.TaintedObject(inTaintTime, m_controllingPrim.LocalID, "BSActorAvatarMove.setVelocityAndTarget", delegate() + { + if (m_velocityMotor != null) + { +// if (targ == OMV.Vector3.Zero) +// Util.PrintCallStack(); +// +// Console.WriteLine("SetVelocityAndTarget, {0} {1}", vel, targ); + m_velocityMotor.Reset(); + m_velocityMotor.SetTarget(targ); + m_velocityMotor.SetCurrent(vel); + m_velocityMotor.Enabled = true; + } + }); + } + + // If a hover motor has not been created, create one and start the hovering. + private void ActivateAvatarMove() + { + if (m_velocityMotor == null) + { + // Infinite decay and timescale values so motor only changes current to target values. + m_velocityMotor = new BSVMotor("BSCharacter.Velocity", + 0.2f, // time scale + BSMotor.Infinite, // decay time scale + 1f // efficiency + ); + m_velocityMotor.ErrorZeroThreshold = BSParam.AvatarStopZeroThreshold; + // _velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages. + SetVelocityAndTarget(m_controllingPrim.RawVelocity, m_controllingPrim.TargetVelocity, true /* inTaintTime */); + + m_physicsScene.BeforeStep += Mover; + m_controllingPrim.OnPreUpdateProperty += Process_OnPreUpdateProperty; + + m_walkingUpStairs = 0; + } + } + + private void DeactivateAvatarMove() + { + if (m_velocityMotor != null) + { + m_controllingPrim.OnPreUpdateProperty -= Process_OnPreUpdateProperty; + m_physicsScene.BeforeStep -= Mover; + m_velocityMotor = null; + } + } + + // Called just before the simulation step. Update the vertical position for hoverness. + private void Mover(float timeStep) + { + // Don't do movement while the object is selected. + if (!isActive) + return; + + // TODO: Decide if the step parameters should be changed depending on the avatar's + // state (flying, colliding, ...). There is code in ODE to do this. + + // COMMENTARY: when the user is making the avatar walk, except for falling, the velocity + // specified for the avatar is the one that should be used. For falling, if the avatar + // is not flying and is not colliding then it is presumed to be falling and the Z + // component is not fooled with (thus allowing gravity to do its thing). + // When the avatar is standing, though, the user has specified a velocity of zero and + // the avatar should be standing. But if the avatar is pushed by something in the world + // (raising elevator platform, moving vehicle, ...) the avatar should be allowed to + // move. Thus, the velocity cannot be forced to zero. The problem is that small velocity + // errors can creap in and the avatar will slowly float off in some direction. + // So, the problem is that, when an avatar is standing, we cannot tell creaping error + // from real pushing. + // The code below uses whether the collider is static or moving to decide whether to zero motion. + + m_velocityMotor.Step(timeStep); + m_controllingPrim.IsStationary = false; + + // If we're not supposed to be moving, make sure things are zero. + if (m_velocityMotor.ErrorIsZero() && m_velocityMotor.TargetValue == OMV.Vector3.Zero) + { + // The avatar shouldn't be moving + m_velocityMotor.Zero(); + + if (m_controllingPrim.IsColliding) + { + // If we are colliding with a stationary object, presume we're standing and don't move around + if (!m_controllingPrim.ColliderIsMoving && !m_controllingPrim.ColliderIsVolumeDetect) + { + m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,collidingWithStationary,zeroingMotion", m_controllingPrim.LocalID); + m_controllingPrim.IsStationary = true; + m_controllingPrim.ZeroMotion(true /* inTaintTime */); + } + + // Standing has more friction on the ground + if (m_controllingPrim.Friction != BSParam.AvatarStandingFriction) + { + m_controllingPrim.Friction = BSParam.AvatarStandingFriction; + m_physicsScene.PE.SetFriction(m_controllingPrim.PhysBody, m_controllingPrim.Friction); + } + } + else + { + if (m_controllingPrim.Flying) + { + // Flying and not colliding and velocity nearly zero. + m_controllingPrim.ZeroMotion(true /* inTaintTime */); + } + else + { + //We are falling but are not touching any keys make sure not falling too fast + if (m_controllingPrim.RawVelocity.Z < BSParam.AvatarTerminalVelocity) + { + + OMV.Vector3 slowingForce = new OMV.Vector3(0f, 0f, BSParam.AvatarTerminalVelocity - m_controllingPrim.RawVelocity.Z) * m_controllingPrim.Mass; + m_physicsScene.PE.ApplyCentralImpulse(m_controllingPrim.PhysBody, slowingForce); + } + + } + } + + m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,taint,stopping,target={1},colliding={2}", + m_controllingPrim.LocalID, m_velocityMotor.TargetValue, m_controllingPrim.IsColliding); + } + else + { + // Supposed to be moving. + OMV.Vector3 stepVelocity = m_velocityMotor.CurrentValue; + + if (m_controllingPrim.Friction != BSParam.AvatarFriction) + { + // Probably starting to walk. Set friction to moving friction. + m_controllingPrim.Friction = BSParam.AvatarFriction; + m_physicsScene.PE.SetFriction(m_controllingPrim.PhysBody, m_controllingPrim.Friction); + } + + if (!m_controllingPrim.Flying && !m_controllingPrim.IsColliding) + { + stepVelocity.Z = m_controllingPrim.RawVelocity.Z; + } + + // Colliding and not flying with an upward force. The avatar must be trying to jump. + if (!m_controllingPrim.Flying && m_controllingPrim.IsColliding && stepVelocity.Z > 0) + { + // We allow the upward force to happen for this many frames. + m_jumpFrames = BSParam.AvatarJumpFrames; + m_jumpVelocity = stepVelocity.Z; + } + + // The case where the avatar is not colliding and is not flying is special. + // The avatar is either falling or jumping and the user can be applying force to the avatar + // (force in some direction or force up or down). + // If the avatar has negative Z velocity and is not colliding, presume we're falling and keep the velocity. + // If the user is trying to apply upward force but we're not colliding, assume the avatar + // is trying to jump and don't apply the upward force if not touching the ground any more. + if (!m_controllingPrim.Flying && !m_controllingPrim.IsColliding) + { + // If upward velocity is being applied, this must be a jump and only allow that to go on so long + if (m_jumpFrames > 0) + { + // Since not touching the ground, only apply upward force for so long. + m_jumpFrames--; + stepVelocity.Z = m_jumpVelocity; + } + else + { + + // Since we're not affected by anything, the avatar must be falling and we do not want that to be too fast. + if (m_controllingPrim.RawVelocity.Z < BSParam.AvatarTerminalVelocity) + { + + stepVelocity.Z = BSParam.AvatarTerminalVelocity; + } + else + { + stepVelocity.Z = m_controllingPrim.RawVelocity.Z; + } + } + // DetailLog("{0},BSCharacter.MoveMotor,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity); + } + + //Alicia: Maintain minimum height when flying. + // SL has a flying effect that keeps the avatar flying above the ground by some margin + if (m_controllingPrim.Flying) + { + float hover_height = m_physicsScene.TerrainManager.GetTerrainHeightAtXYZ(m_controllingPrim.RawPosition) + + BSParam.AvatarFlyingGroundMargin; + + if( m_controllingPrim.Position.Z < hover_height) + { + stepVelocity.Z += BSParam.AvatarFlyingGroundUpForce; + } + } + + // 'stepVelocity' is now the speed we'd like the avatar to move in. Turn that into an instantanous force. + OMV.Vector3 moveForce = (stepVelocity - m_controllingPrim.RawVelocity) * m_controllingPrim.Mass; + + // Add special movement force to allow avatars to walk up stepped surfaces. + moveForce += WalkUpStairs(); + + m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", + m_controllingPrim.LocalID, stepVelocity, m_controllingPrim.RawVelocity, m_controllingPrim.Mass, moveForce); + m_physicsScene.PE.ApplyCentralImpulse(m_controllingPrim.PhysBody, moveForce); + } + } + + // Called just as the property update is received from the physics engine. + // Do any mode necessary for avatar movement. + private void Process_OnPreUpdateProperty(ref EntityProperties entprop) + { + // Don't change position if standing on a stationary object. + if (m_controllingPrim.IsStationary) + { + entprop.Position = m_controllingPrim.RawPosition; + entprop.Velocity = OMV.Vector3.Zero; + m_physicsScene.PE.SetTranslation(m_controllingPrim.PhysBody, entprop.Position, entprop.Rotation); + } + + } + + // Decide if the character is colliding with a low object and compute a force to pop the + // avatar up so it can walk up and over the low objects. + private OMV.Vector3 WalkUpStairs() + { + OMV.Vector3 ret = OMV.Vector3.Zero; + + m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs,IsColliding={1},flying={2},targSpeed={3},collisions={4},avHeight={5}", + m_controllingPrim.LocalID, m_controllingPrim.IsColliding, m_controllingPrim.Flying, + m_controllingPrim.TargetVelocitySpeed, m_controllingPrim.CollisionsLastTick.Count, m_controllingPrim.Size.Z); + + // Check for stairs climbing if colliding, not flying and moving forward + if ( m_controllingPrim.IsColliding + && !m_controllingPrim.Flying + && m_controllingPrim.TargetVelocitySpeed > 0.1f ) + { + // The range near the character's feet where we will consider stairs + // float nearFeetHeightMin = m_controllingPrim.RawPosition.Z - (m_controllingPrim.Size.Z / 2f) + 0.05f; + // Note: there is a problem with the computation of the capsule height. Thus RawPosition is off + // from the height. Revisit size and this computation when height is scaled properly. + float nearFeetHeightMin = m_controllingPrim.RawPosition.Z - (m_controllingPrim.Size.Z / 2f) - BSParam.AvatarStepGroundFudge; + float nearFeetHeightMax = nearFeetHeightMin + BSParam.AvatarStepHeight; + + // Look for a collision point that is near the character's feet and is oriented the same as the charactor is. + // Find the highest 'good' collision. + OMV.Vector3 highestTouchPosition = OMV.Vector3.Zero; + foreach (KeyValuePair kvp in m_controllingPrim.CollisionsLastTick.m_objCollisionList) + { + // Don't care about collisions with the terrain + if (kvp.Key > m_physicsScene.TerrainManager.HighestTerrainID) + { + BSPhysObject collisionObject; + if (m_physicsScene.PhysObjects.TryGetValue(kvp.Key, out collisionObject)) + { + if (!collisionObject.IsVolumeDetect) + { + OMV.Vector3 touchPosition = kvp.Value.Position; + m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs,min={1},max={2},touch={3}", + m_controllingPrim.LocalID, nearFeetHeightMin, nearFeetHeightMax, touchPosition); + if (touchPosition.Z >= nearFeetHeightMin && touchPosition.Z <= nearFeetHeightMax) + { + // This contact is within the 'near the feet' range. + // The step is presumed to be more or less vertical. Thus the Z component should + // be nearly horizontal. + OMV.Vector3 directionFacing = OMV.Vector3.UnitX * m_controllingPrim.RawOrientation; + OMV.Vector3 touchNormal = OMV.Vector3.Normalize(kvp.Value.SurfaceNormal); + const float PIOver2 = 1.571f; // Used to make unit vector axis into approx radian angles + // m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs,avNormal={1},colNormal={2},diff={3}", + // m_controllingPrim.LocalID, directionFacing, touchNormal, + // Math.Abs(OMV.Vector3.Distance(directionFacing, touchNormal)) ); + if ((Math.Abs(directionFacing.Z) * PIOver2) < BSParam.AvatarStepAngle + && (Math.Abs(touchNormal.Z) * PIOver2) < BSParam.AvatarStepAngle) + { + // The normal should be our contact point to the object so it is pointing away + // thus the difference between our facing orientation and the normal should be small. + float diff = Math.Abs(OMV.Vector3.Distance(directionFacing, touchNormal)); + if (diff < BSParam.AvatarStepApproachFactor) + { + if (highestTouchPosition.Z < touchPosition.Z) + highestTouchPosition = touchPosition; + } + } + } + } + } + } + } + m_walkingUpStairs = 0; + // If there is a good step sensing, move the avatar over the step. + if (highestTouchPosition != OMV.Vector3.Zero) + { + // Remember that we are going up stairs. This is needed because collisions + // will stop when we move up so this smoothes out that effect. + m_walkingUpStairs = BSParam.AvatarStepSmoothingSteps; + + m_lastStepUp = highestTouchPosition.Z - nearFeetHeightMin; + ret = ComputeStairCorrection(m_lastStepUp); + m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs,touchPos={1},nearFeetMin={2},ret={3}", + m_controllingPrim.LocalID, highestTouchPosition, nearFeetHeightMin, ret); + } + } + else + { + // If we used to be going up stairs but are not now, smooth the case where collision goes away while + // we are bouncing up the stairs. + if (m_walkingUpStairs > 0) + { + m_walkingUpStairs--; + ret = ComputeStairCorrection(m_lastStepUp); + } + } + + return ret; + } + + private OMV.Vector3 ComputeStairCorrection(float stepUp) + { + OMV.Vector3 ret = OMV.Vector3.Zero; + OMV.Vector3 displacement = OMV.Vector3.Zero; + + if (stepUp > 0f) + { + // Found the stairs contact point. Push up a little to raise the character. + if (BSParam.AvatarStepForceFactor > 0f) + { + float upForce = stepUp * m_controllingPrim.Mass * BSParam.AvatarStepForceFactor; + ret = new OMV.Vector3(0f, 0f, upForce); + } + + // Also move the avatar up for the new height + if (BSParam.AvatarStepUpCorrectionFactor > 0f) + { + // Move the avatar up related to the height of the collision + displacement = new OMV.Vector3(0f, 0f, stepUp * BSParam.AvatarStepUpCorrectionFactor); + m_controllingPrim.ForcePosition = m_controllingPrim.RawPosition + displacement; + } + else + { + if (BSParam.AvatarStepUpCorrectionFactor < 0f) + { + // Move the avatar up about the specified step height + displacement = new OMV.Vector3(0f, 0f, BSParam.AvatarStepHeight); + m_controllingPrim.ForcePosition = m_controllingPrim.RawPosition + displacement; + } + } + m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs.ComputeStairCorrection,stepUp={1},isp={2},force={3}", + m_controllingPrim.LocalID, stepUp, displacement, ret); + + } + return ret; + } +} +} + + diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs new file mode 100755 index 0000000..e54c27b --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs @@ -0,0 +1,174 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using OpenSim.Region.Physics.Manager; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSActorHover : BSActor +{ + private BSFMotor m_hoverMotor; + + public BSActorHover(BSScene physicsScene, BSPhysObject pObj, string actorName) + : base(physicsScene, pObj, actorName) + { + m_hoverMotor = null; + m_physicsScene.DetailLog("{0},BSActorHover,constructor", m_controllingPrim.LocalID); + } + + // BSActor.isActive + public override bool isActive + { + get { return Enabled; } + } + + // Release any connections and resources used by the actor. + // BSActor.Dispose() + public override void Dispose() + { + Enabled = false; + DeactivateHover(); + } + + // Called when physical parameters (properties set in Bullet) need to be re-applied. + // Called at taint-time. + // BSActor.Refresh() + public override void Refresh() + { + m_physicsScene.DetailLog("{0},BSActorHover,refresh", m_controllingPrim.LocalID); + + // If not active any more, turn me off + if (!m_controllingPrim.HoverActive) + { + SetEnabled(false); + } + + // If the object is physically active, add the hoverer prestep action + if (isActive) + { + ActivateHover(); + } + else + { + DeactivateHover(); + } + } + + // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). + // Register a prestep action to restore physical requirements before the next simulation step. + // Called at taint-time. + // BSActor.RemoveDependencies() + public override void RemoveDependencies() + { + // Nothing to do for the hoverer since it is all software at pre-step action time. + } + + // If a hover motor has not been created, create one and start the hovering. + private void ActivateHover() + { + if (m_hoverMotor == null) + { + // Turning the target on + m_hoverMotor = new BSFMotor("BSActorHover", + m_controllingPrim.HoverTau, // timeScale + BSMotor.Infinite, // decay time scale + 1f // efficiency + ); + m_hoverMotor.SetTarget(ComputeCurrentHoverHeight()); + m_hoverMotor.SetCurrent(m_controllingPrim.RawPosition.Z); + m_hoverMotor.PhysicsScene = m_physicsScene; // DEBUG DEBUG so motor will output detail log messages. + + m_physicsScene.BeforeStep += Hoverer; + } + } + + private void DeactivateHover() + { + if (m_hoverMotor != null) + { + m_physicsScene.BeforeStep -= Hoverer; + m_hoverMotor = null; + } + } + + // Called just before the simulation step. Update the vertical position for hoverness. + private void Hoverer(float timeStep) + { + // Don't do hovering while the object is selected. + if (!isActive) + return; + + m_hoverMotor.SetCurrent(m_controllingPrim.RawPosition.Z); + m_hoverMotor.SetTarget(ComputeCurrentHoverHeight()); + float targetHeight = m_hoverMotor.Step(timeStep); + + // 'targetHeight' is where we'd like the Z of the prim to be at this moment. + // Compute the amount of force to push us there. + float moveForce = (targetHeight - m_controllingPrim.RawPosition.Z) * m_controllingPrim.RawMass; + // Undo anything the object thinks it's doing at the moment + moveForce = -m_controllingPrim.RawVelocity.Z * m_controllingPrim.Mass; + + m_physicsScene.PE.ApplyCentralImpulse(m_controllingPrim.PhysBody, new OMV.Vector3(0f, 0f, moveForce)); + m_physicsScene.DetailLog("{0},BSPrim.Hover,move,targHt={1},moveForce={2},mass={3}", + m_controllingPrim.LocalID, targetHeight, moveForce, m_controllingPrim.RawMass); + } + + // Based on current position, determine what we should be hovering at now. + // Must recompute often. What if we walked offa cliff> + private float ComputeCurrentHoverHeight() + { + float ret = m_controllingPrim.HoverHeight; + float groundHeight = m_physicsScene.TerrainManager.GetTerrainHeightAtXYZ(m_controllingPrim.RawPosition); + + switch (m_controllingPrim.HoverType) + { + case PIDHoverType.Ground: + ret = groundHeight + m_controllingPrim.HoverHeight; + break; + case PIDHoverType.GroundAndWater: + float waterHeight = m_physicsScene.TerrainManager.GetWaterLevelAtXYZ(m_controllingPrim.RawPosition); + if (groundHeight > waterHeight) + { + ret = groundHeight + m_controllingPrim.HoverHeight; + } + else + { + ret = waterHeight + m_controllingPrim.HoverHeight; + } + break; + } + return ret; + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorLockAxis.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorLockAxis.cs new file mode 100755 index 0000000..3b3c161 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorLockAxis.cs @@ -0,0 +1,219 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSActorLockAxis : BSActor +{ + private BSConstraint LockAxisConstraint = null; + private bool HaveRegisteredForBeforeStepCallback = false; + + // The lock access flags (which axises were locked) when the contraint was built. + // Used to see if locking has changed since when the constraint was built. + OMV.Vector3 LockAxisLinearFlags; + OMV.Vector3 LockAxisAngularFlags; + + public BSActorLockAxis(BSScene physicsScene, BSPhysObject pObj, string actorName) + : base(physicsScene, pObj, actorName) + { + m_physicsScene.DetailLog("{0},BSActorLockAxis,constructor", m_controllingPrim.LocalID); + LockAxisConstraint = null; + HaveRegisteredForBeforeStepCallback = false; + } + + // BSActor.isActive + public override bool isActive + { + get { return Enabled && m_controllingPrim.IsPhysicallyActive; } + } + + // Release any connections and resources used by the actor. + // BSActor.Dispose() + public override void Dispose() + { + Enabled = false; + UnRegisterForBeforeStepCallback(); + RemoveAxisLockConstraint(); + } + + // Called when physical parameters (properties set in Bullet) need to be re-applied. + // Called at taint-time. + // BSActor.Refresh() + public override void Refresh() + { + // Since the axis logging is done with a constraint, Refresh() time is good for + // changing parameters but this needs to wait until the prim/linkset is physically + // constructed. Therefore, the constraint itself is placed at pre-step time. + + // If all the axis are free, we don't need to exist + // Refresh() only turns off. Enabling is done by InitializeAxisActor() + // whenever parameters are changed. + // This leaves 'enable' free to turn off an actor when it is not wanted to run. + if (m_controllingPrim.LockedAngularAxis == m_controllingPrim.LockedAxisFree + && m_controllingPrim.LockedLinearAxis == m_controllingPrim.LockedAxisFree) + { + Enabled = false; + } + + if (isActive) + { + RegisterForBeforeStepCallback(); + } + else + { + RemoveDependencies(); + UnRegisterForBeforeStepCallback(); + } + } + + // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). + // Register a prestep action to restore physical requirements before the next simulation step. + // Called at taint-time. + // BSActor.RemoveDependencies() + public override void RemoveDependencies() + { + RemoveAxisLockConstraint(); + } + + private void RegisterForBeforeStepCallback() + { + if (!HaveRegisteredForBeforeStepCallback) + { + m_physicsScene.BeforeStep += PhysicsScene_BeforeStep; + HaveRegisteredForBeforeStepCallback = true; + } + } + + private void UnRegisterForBeforeStepCallback() + { + if (HaveRegisteredForBeforeStepCallback) + { + m_physicsScene.BeforeStep -= PhysicsScene_BeforeStep; + HaveRegisteredForBeforeStepCallback = false; + } + } + + private void PhysicsScene_BeforeStep(float timestep) + { + // If all the axis are free, we don't need to exist + if (m_controllingPrim.LockedAngularAxis == m_controllingPrim.LockedAxisFree + && m_controllingPrim.LockedLinearAxis == m_controllingPrim.LockedAxisFree) + { + Enabled = false; + } + + // If the object is physically active, add the axis locking constraint + if (isActive) + { + // Check to see if the locking parameters have changed + if (m_controllingPrim.LockedLinearAxis != this.LockAxisLinearFlags + || m_controllingPrim.LockedAngularAxis != this.LockAxisAngularFlags) + { + // The locking has changed. Remove the old constraint and build a new one + RemoveAxisLockConstraint(); + } + + AddAxisLockConstraint(); + } + else + { + RemoveAxisLockConstraint(); + } + } + + // Note that this relies on being called at TaintTime + private void AddAxisLockConstraint() + { + if (LockAxisConstraint == null) + { + // Lock that axis by creating a 6DOF constraint that has one end in the world and + // the other in the object. + // http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=20817 + // http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=26380 + + // Remove any existing axis constraint (just to be sure) + RemoveAxisLockConstraint(); + + BSConstraint6Dof axisConstrainer = new BSConstraint6Dof(m_physicsScene.World, m_controllingPrim.PhysBody, + OMV.Vector3.Zero, OMV.Quaternion.Identity, + false /* useLinearReferenceFrameB */, true /* disableCollisionsBetweenLinkedBodies */); + LockAxisConstraint = axisConstrainer; + m_physicsScene.Constraints.AddConstraint(LockAxisConstraint); + + // Remember the clocking being inforced so we can notice if they have changed + LockAxisLinearFlags = m_controllingPrim.LockedLinearAxis; + LockAxisAngularFlags = m_controllingPrim.LockedAngularAxis; + + // The constraint is tied to the world and oriented to the prim. + + if (!axisConstrainer.SetLinearLimits(m_controllingPrim.LockedLinearAxisLow, m_controllingPrim.LockedLinearAxisHigh)) + { + m_physicsScene.DetailLog("{0},BSActorLockAxis.AddAxisLockConstraint,failedSetLinearLimits", + m_controllingPrim.LocalID); + } + + if (!axisConstrainer.SetAngularLimits(m_controllingPrim.LockedAngularAxisLow, m_controllingPrim.LockedAngularAxisHigh)) + { + m_physicsScene.DetailLog("{0},BSActorLockAxis.AddAxisLockConstraint,failedSetAngularLimits", + m_controllingPrim.LocalID); + } + + m_physicsScene.DetailLog("{0},BSActorLockAxis.AddAxisLockConstraint,create,linLow={1},linHi={2},angLow={3},angHi={4}", + m_controllingPrim.LocalID, + m_controllingPrim.LockedLinearAxisLow, + m_controllingPrim.LockedLinearAxisHigh, + m_controllingPrim.LockedAngularAxisLow, + m_controllingPrim.LockedAngularAxisHigh); + + // Constants from one of the posts mentioned above and used in Bullet's ConstraintDemo. + axisConstrainer.TranslationalLimitMotor(true /* enable */, 5.0f, 0.1f); + + axisConstrainer.RecomputeConstraintVariables(m_controllingPrim.RawMass); + + RegisterForBeforeStepCallback(); + } + } + + private void RemoveAxisLockConstraint() + { + UnRegisterForBeforeStepCallback(); + if (LockAxisConstraint != null) + { + m_physicsScene.Constraints.RemoveAndDestroyConstraint(LockAxisConstraint); + LockAxisConstraint = null; + m_physicsScene.DetailLog("{0},BSActorLockAxis.RemoveAxisLockConstraint,destroyingConstraint", m_controllingPrim.LocalID); + } + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs new file mode 100755 index 0000000..1145006 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs @@ -0,0 +1,220 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using OpenSim.Region.Physics.Manager; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSActorMoveToTarget : BSActor +{ + private BSVMotor m_targetMotor; + + public BSActorMoveToTarget(BSScene physicsScene, BSPhysObject pObj, string actorName) + : base(physicsScene, pObj, actorName) + { + m_targetMotor = null; + m_physicsScene.DetailLog("{0},BSActorMoveToTarget,constructor", m_controllingPrim.LocalID); + } + + // BSActor.isActive + public override bool isActive + { + // MoveToTarget only works on physical prims + get { return Enabled && m_controllingPrim.IsPhysicallyActive; } + } + + // Release any connections and resources used by the actor. + // BSActor.Dispose() + public override void Dispose() + { + Enabled = false; + DeactivateMoveToTarget(); + } + + // Called when physical parameters (properties set in Bullet) need to be re-applied. + // Called at taint-time. + // BSActor.Refresh() + public override void Refresh() + { + m_physicsScene.DetailLog("{0},BSActorMoveToTarget,refresh,enabled={1},active={2},target={3},tau={4}", + m_controllingPrim.LocalID, Enabled, m_controllingPrim.MoveToTargetActive, + m_controllingPrim.MoveToTargetTarget, m_controllingPrim.MoveToTargetTau ); + + // If not active any more... + if (!m_controllingPrim.MoveToTargetActive) + { + Enabled = false; + } + + if (isActive) + { + ActivateMoveToTarget(); + } + else + { + DeactivateMoveToTarget(); + } + } + + // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). + // Register a prestep action to restore physical requirements before the next simulation step. + // Called at taint-time. + // BSActor.RemoveDependencies() + public override void RemoveDependencies() + { + // Nothing to do for the moveToTarget since it is all software at pre-step action time. + } + + // If a hover motor has not been created, create one and start the hovering. + private void ActivateMoveToTarget() + { + if (m_targetMotor == null) + { + // We're taking over after this. + m_controllingPrim.ZeroMotion(true); + + /* Someday use the PID controller + m_targetMotor = new BSPIDVMotor("BSActorMoveToTarget-" + m_controllingPrim.LocalID.ToString()); + m_targetMotor.TimeScale = m_controllingPrim.MoveToTargetTau; + m_targetMotor.Efficiency = 1f; + */ + m_targetMotor = new BSVMotor("BSActorMoveToTarget-" + m_controllingPrim.LocalID.ToString(), + m_controllingPrim.MoveToTargetTau, // timeScale + BSMotor.Infinite, // decay time scale + 1f // efficiency + ); + m_targetMotor.PhysicsScene = m_physicsScene; // DEBUG DEBUG so motor will output detail log messages. + m_targetMotor.SetTarget(m_controllingPrim.MoveToTargetTarget); + m_targetMotor.SetCurrent(m_controllingPrim.RawPosition); + + // m_physicsScene.BeforeStep += Mover; + m_physicsScene.BeforeStep += Mover2; + } + else + { + // If already allocated, make sure the target and other paramters are current + m_targetMotor.SetTarget(m_controllingPrim.MoveToTargetTarget); + m_targetMotor.SetCurrent(m_controllingPrim.RawPosition); + } + } + + private void DeactivateMoveToTarget() + { + if (m_targetMotor != null) + { + // m_physicsScene.BeforeStep -= Mover; + m_physicsScene.BeforeStep -= Mover2; + m_targetMotor = null; + } + } + + // Origional mover that set the objects position to move to the target. + // The problem was that gravity would keep trying to push the object down so + // the overall downward velocity would increase to infinity. + // Called just before the simulation step. + private void Mover(float timeStep) + { + // Don't do hovering while the object is selected. + if (!isActive) + return; + + OMV.Vector3 origPosition = m_controllingPrim.RawPosition; // DEBUG DEBUG (for printout below) + + // 'movePosition' is where we'd like the prim to be at this moment. + OMV.Vector3 movePosition = m_controllingPrim.RawPosition + m_targetMotor.Step(timeStep); + + // If we are very close to our target, turn off the movement motor. + if (m_targetMotor.ErrorIsZero()) + { + m_physicsScene.DetailLog("{0},BSActorMoveToTarget.Mover,zeroMovement,movePos={1},pos={2},mass={3}", + m_controllingPrim.LocalID, movePosition, m_controllingPrim.RawPosition, m_controllingPrim.Mass); + m_controllingPrim.ForcePosition = m_targetMotor.TargetValue; + m_controllingPrim.ForceVelocity = OMV.Vector3.Zero; + // Setting the position does not cause the physics engine to generate a property update. Force it. + m_physicsScene.PE.PushUpdate(m_controllingPrim.PhysBody); + } + else + { + m_controllingPrim.ForcePosition = movePosition; + // Setting the position does not cause the physics engine to generate a property update. Force it. + m_physicsScene.PE.PushUpdate(m_controllingPrim.PhysBody); + } + m_physicsScene.DetailLog("{0},BSActorMoveToTarget.Mover,move,fromPos={1},movePos={2}", + m_controllingPrim.LocalID, origPosition, movePosition); + } + + // Version of mover that applies forces to move the physical object to the target. + // Also overcomes gravity so the object doesn't just drop to the ground. + // Called just before the simulation step. + private void Mover2(float timeStep) + { + // Don't do hovering while the object is selected. + if (!isActive) + return; + + OMV.Vector3 origPosition = m_controllingPrim.RawPosition; // DEBUG DEBUG (for printout below) + OMV.Vector3 addedForce = OMV.Vector3.Zero; + + // CorrectionVector is the movement vector required this step + OMV.Vector3 correctionVector = m_targetMotor.Step(timeStep, m_controllingPrim.RawPosition); + + // If we are very close to our target, turn off the movement motor. + if (m_targetMotor.ErrorIsZero()) + { + m_physicsScene.DetailLog("{0},BSActorMoveToTarget.Mover3,zeroMovement,pos={1},mass={2}", + m_controllingPrim.LocalID, m_controllingPrim.RawPosition, m_controllingPrim.Mass); + m_controllingPrim.ForcePosition = m_targetMotor.TargetValue; + m_controllingPrim.ForceVelocity = OMV.Vector3.Zero; + // Setting the position does not cause the physics engine to generate a property update. Force it. + m_physicsScene.PE.PushUpdate(m_controllingPrim.PhysBody); + } + else + { + // First force to move us there -- the motor return a timestep scaled value. + addedForce = correctionVector / timeStep; + // Remove the existing velocity (only the moveToTarget force counts) + addedForce -= m_controllingPrim.RawVelocity; + // Overcome gravity. + addedForce -= m_controllingPrim.Gravity; + + // Add enough force to overcome the mass of the object + addedForce *= m_controllingPrim.Mass; + + m_controllingPrim.AddForce(addedForce, false /* pushForce */, true /* inTaintTime */); + } + m_physicsScene.DetailLog("{0},BSActorMoveToTarget.Mover3,move,fromPos={1},addedForce={2}", + m_controllingPrim.LocalID, origPosition, addedForce); + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs new file mode 100755 index 0000000..4e81363 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs @@ -0,0 +1,138 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using OpenSim.Region.Physics.Manager; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSActorSetForce : BSActor +{ + BSFMotor m_forceMotor; + + public BSActorSetForce(BSScene physicsScene, BSPhysObject pObj, string actorName) + : base(physicsScene, pObj, actorName) + { + m_forceMotor = null; + m_physicsScene.DetailLog("{0},BSActorSetForce,constructor", m_controllingPrim.LocalID); + } + + // BSActor.isActive + public override bool isActive + { + get { return Enabled && m_controllingPrim.IsPhysicallyActive; } + } + + // Release any connections and resources used by the actor. + // BSActor.Dispose() + public override void Dispose() + { + Enabled = false; + DeactivateSetForce(); + } + + // Called when physical parameters (properties set in Bullet) need to be re-applied. + // Called at taint-time. + // BSActor.Refresh() + public override void Refresh() + { + m_physicsScene.DetailLog("{0},BSActorSetForce,refresh", m_controllingPrim.LocalID); + + // If not active any more, get rid of me (shouldn't ever happen, but just to be safe) + if (m_controllingPrim.RawForce == OMV.Vector3.Zero) + { + m_physicsScene.DetailLog("{0},BSActorSetForce,refresh,notSetForce,removing={1}", m_controllingPrim.LocalID, ActorName); + Enabled = false; + return; + } + + // If the object is physically active, add the hoverer prestep action + if (isActive) + { + ActivateSetForce(); + } + else + { + DeactivateSetForce(); + } + } + + // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). + // Register a prestep action to restore physical requirements before the next simulation step. + // Called at taint-time. + // BSActor.RemoveDependencies() + public override void RemoveDependencies() + { + // Nothing to do for the hoverer since it is all software at pre-step action time. + } + + // If a hover motor has not been created, create one and start the hovering. + private void ActivateSetForce() + { + if (m_forceMotor == null) + { + // A fake motor that might be used someday + m_forceMotor = new BSFMotor("setForce", 1f, 1f, 1f); + + m_physicsScene.BeforeStep += Mover; + } + } + + private void DeactivateSetForce() + { + if (m_forceMotor != null) + { + m_physicsScene.BeforeStep -= Mover; + m_forceMotor = null; + } + } + + // Called just before the simulation step. Update the vertical position for hoverness. + private void Mover(float timeStep) + { + // Don't do force while the object is selected. + if (!isActive) + return; + + m_physicsScene.DetailLog("{0},BSActorSetForce,preStep,force={1}", m_controllingPrim.LocalID, m_controllingPrim.RawForce); + if (m_controllingPrim.PhysBody.HasPhysicalBody) + { + m_physicsScene.PE.ApplyCentralForce(m_controllingPrim.PhysBody, m_controllingPrim.RawForce); + m_controllingPrim.ActivateIfPhysical(false); + } + + // TODO: + } +} +} + diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs new file mode 100755 index 0000000..79e1d38 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs @@ -0,0 +1,139 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using OpenSim.Region.Physics.Manager; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSActorSetTorque : BSActor +{ + BSFMotor m_torqueMotor; + + public BSActorSetTorque(BSScene physicsScene, BSPhysObject pObj, string actorName) + : base(physicsScene, pObj, actorName) + { + m_torqueMotor = null; + m_physicsScene.DetailLog("{0},BSActorSetTorque,constructor", m_controllingPrim.LocalID); + } + + // BSActor.isActive + public override bool isActive + { + get { return Enabled && m_controllingPrim.IsPhysicallyActive; } + } + + // Release any connections and resources used by the actor. + // BSActor.Dispose() + public override void Dispose() + { + Enabled = false; + DeactivateSetTorque(); + } + + // Called when physical parameters (properties set in Bullet) need to be re-applied. + // Called at taint-time. + // BSActor.Refresh() + public override void Refresh() + { + m_physicsScene.DetailLog("{0},BSActorSetTorque,refresh,torque={1}", m_controllingPrim.LocalID, m_controllingPrim.RawTorque); + + // If not active any more, get rid of me (shouldn't ever happen, but just to be safe) + if (m_controllingPrim.RawTorque == OMV.Vector3.Zero) + { + m_physicsScene.DetailLog("{0},BSActorSetTorque,refresh,notSetTorque,disabling={1}", m_controllingPrim.LocalID, ActorName); + Enabled = false; + return; + } + + // If the object is physically active, add the hoverer prestep action + if (isActive) + { + ActivateSetTorque(); + } + else + { + DeactivateSetTorque(); + } + } + + // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). + // Register a prestep action to restore physical requirements before the next simulation step. + // Called at taint-time. + // BSActor.RemoveDependencies() + public override void RemoveDependencies() + { + // Nothing to do for the hoverer since it is all software at pre-step action time. + } + + // If a hover motor has not been created, create one and start the hovering. + private void ActivateSetTorque() + { + if (m_torqueMotor == null) + { + // A fake motor that might be used someday + m_torqueMotor = new BSFMotor("setTorque", 1f, 1f, 1f); + + m_physicsScene.BeforeStep += Mover; + } + } + + private void DeactivateSetTorque() + { + if (m_torqueMotor != null) + { + m_physicsScene.BeforeStep -= Mover; + m_torqueMotor = null; + } + } + + // Called just before the simulation step. Update the vertical position for hoverness. + private void Mover(float timeStep) + { + // Don't do force while the object is selected. + if (!isActive) + return; + + m_physicsScene.DetailLog("{0},BSActorSetTorque,preStep,force={1}", m_controllingPrim.LocalID, m_controllingPrim.RawTorque); + if (m_controllingPrim.PhysBody.HasPhysicalBody) + { + m_controllingPrim.AddAngularForce(m_controllingPrim.RawTorque, false, true); + m_controllingPrim.ActivateIfPhysical(false); + } + + // TODO: + } +} +} + + diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActors.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActors.cs new file mode 100755 index 0000000..7f45e2c --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActors.cs @@ -0,0 +1,154 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSActorCollection +{ + private Dictionary m_actors; + + public BSActorCollection() + { + m_actors = new Dictionary(); + } + public void Add(string name, BSActor actor) + { + lock (m_actors) + { + if (!m_actors.ContainsKey(name)) + { + m_actors[name] = actor; + } + } + } + public bool RemoveAndRelease(string name) + { + bool ret = false; + lock (m_actors) + { + if (m_actors.ContainsKey(name)) + { + BSActor beingRemoved = m_actors[name]; + m_actors.Remove(name); + beingRemoved.Dispose(); + ret = true; + } + } + return ret; + } + public void Clear() + { + lock (m_actors) + { + ForEachActor(a => a.Dispose()); + m_actors.Clear(); + } + } + public void Dispose() + { + Clear(); + } + public bool HasActor(string name) + { + return m_actors.ContainsKey(name); + } + public bool TryGetActor(string actorName, out BSActor theActor) + { + return m_actors.TryGetValue(actorName, out theActor); + } + public void ForEachActor(Action act) + { + lock (m_actors) + { + foreach (KeyValuePair kvp in m_actors) + act(kvp.Value); + } + } + + public void Enable(bool enabl) + { + ForEachActor(a => a.SetEnabled(enabl)); + } + public void Refresh() + { + ForEachActor(a => a.Refresh()); + } + public void RemoveDependencies() + { + ForEachActor(a => a.RemoveDependencies()); + } +} + +// ============================================================================= +/// +/// Each physical object can have 'actors' who are pushing the object around. +/// This can be used for hover, locking axis, making vehicles, etc. +/// Each physical object can have multiple actors acting on it. +/// +/// An actor usually registers itself with physics scene events (pre-step action) +/// and modifies the parameters on the host physical object. +/// +public abstract class BSActor +{ + protected BSScene m_physicsScene { get; private set; } + protected BSPhysObject m_controllingPrim { get; private set; } + public virtual bool Enabled { get; set; } + public string ActorName { get; private set; } + + public BSActor(BSScene physicsScene, BSPhysObject pObj, string actorName) + { + m_physicsScene = physicsScene; + m_controllingPrim = pObj; + ActorName = actorName; + Enabled = true; + } + + // Return 'true' if activily updating the prim + public virtual bool isActive + { + get { return Enabled; } + } + + // Turn the actor on an off. Only used by ActorCollection to set all enabled/disabled. + // Anyone else should assign true/false to 'Enabled'. + public void SetEnabled(bool setEnabled) + { + Enabled = setEnabled; + } + // Release any connections and resources used by the actor. + public abstract void Dispose(); + // Called when physical parameters (properties set in Bullet) need to be re-applied. + public abstract void Refresh(); + // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). + // Register a prestep action to restore physical requirements before the next simulation step. + public abstract void RemoveDependencies(); + +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs b/OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs new file mode 100644 index 0000000..8491c0f --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs @@ -0,0 +1,763 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin { + + // Constraint type values as defined by Bullet +public enum ConstraintType : int +{ + POINT2POINT_CONSTRAINT_TYPE = 3, + HINGE_CONSTRAINT_TYPE, + CONETWIST_CONSTRAINT_TYPE, + D6_CONSTRAINT_TYPE, + SLIDER_CONSTRAINT_TYPE, + CONTACT_CONSTRAINT_TYPE, + D6_SPRING_CONSTRAINT_TYPE, + GEAR_CONSTRAINT_TYPE, // added in Bullet 2.82 + FIXED_CONSTRAINT_TYPE, // added in Bullet 2.82 + MAX_CONSTRAINT_TYPE, // last type defined by Bullet + // + BS_FIXED_CONSTRAINT_TYPE = 1234 // BulletSim constraint that is fixed and unmoving +} + +// =============================================================================== +[StructLayout(LayoutKind.Sequential)] +public struct ConvexHull +{ + Vector3 Offset; + int VertexCount; + Vector3[] Vertices; +} +public enum BSPhysicsShapeType +{ + SHAPE_UNKNOWN = 0, + SHAPE_CAPSULE = 1, + SHAPE_BOX = 2, + SHAPE_CONE = 3, + SHAPE_CYLINDER = 4, + SHAPE_SPHERE = 5, + SHAPE_MESH = 6, + SHAPE_HULL = 7, + // following defined by BulletSim + SHAPE_GROUNDPLANE = 20, + SHAPE_TERRAIN = 21, + SHAPE_COMPOUND = 22, + SHAPE_HEIGHTMAP = 23, + SHAPE_AVATAR = 24, + SHAPE_CONVEXHULL= 25, + SHAPE_GIMPACT = 26, +}; + +// The native shapes have predefined shape hash keys +public enum FixedShapeKey : ulong +{ + KEY_NONE = 0, + KEY_BOX = 1, + KEY_SPHERE = 2, + KEY_CONE = 3, + KEY_CYLINDER = 4, + KEY_CAPSULE = 5, + KEY_AVATAR = 6, +} + +[StructLayout(LayoutKind.Sequential)] +public struct ShapeData +{ + public UInt32 ID; + public BSPhysicsShapeType Type; + public Vector3 Position; + public Quaternion Rotation; + public Vector3 Velocity; + public Vector3 Scale; + public float Mass; + public float Buoyancy; + public System.UInt64 HullKey; + public System.UInt64 MeshKey; + public float Friction; + public float Restitution; + public float Collidable; // true of things bump into this + public float Static; // true if a static object. Otherwise gravity, etc. + public float Solid; // true if object cannot be passed through + public Vector3 Size; + + // note that bools are passed as floats since bool size changes by language and architecture + public const float numericTrue = 1f; + public const float numericFalse = 0f; +} +[StructLayout(LayoutKind.Sequential)] +public struct SweepHit +{ + public UInt32 ID; + public float Fraction; + public Vector3 Normal; + public Vector3 Point; +} +[StructLayout(LayoutKind.Sequential)] +public struct RaycastHit +{ + public UInt32 ID; + public float Fraction; + public Vector3 Normal; +} +[StructLayout(LayoutKind.Sequential)] +public struct CollisionDesc +{ + public UInt32 aID; + public UInt32 bID; + public Vector3 point; + public Vector3 normal; + public float penetration; +} +[StructLayout(LayoutKind.Sequential)] +public struct EntityProperties +{ + public UInt32 ID; + public Vector3 Position; + public Quaternion Rotation; + public Vector3 Velocity; + public Vector3 Acceleration; + public Vector3 RotationalVelocity; + + public override string ToString() + { + StringBuilder buff = new StringBuilder(); + buff.Append(""); + return buff.ToString(); + } +} + +// Format of this structure must match the definition in the C++ code +// NOTE: adding the X causes compile breaks if used. These are unused symbols +// that can be removed from both here and the unmanaged definition of this structure. +[StructLayout(LayoutKind.Sequential)] +public struct ConfigurationParameters +{ + public float defaultFriction; + public float defaultDensity; + public float defaultRestitution; + public float collisionMargin; + public float gravity; + + public float maxPersistantManifoldPoolSize; + public float maxCollisionAlgorithmPoolSize; + public float shouldDisableContactPoolDynamicAllocation; + public float shouldForceUpdateAllAabbs; + public float shouldRandomizeSolverOrder; + public float shouldSplitSimulationIslands; + public float shouldEnableFrictionCaching; + public float numberOfSolverIterations; + public float useSingleSidedMeshes; + public float globalContactBreakingThreshold; + + public float physicsLoggingFrames; + + public const float numericTrue = 1f; + public const float numericFalse = 0f; +} + +// Parameters passed for the conversion of a mesh to a hull using Bullet's HACD library. +[StructLayout(LayoutKind.Sequential)] +public struct HACDParams +{ + // usual default values + public float maxVerticesPerHull; // 100 + public float minClusters; // 2 + public float compacityWeight; // 0.1 + public float volumeWeight; // 0.0 + public float concavity; // 100 + public float addExtraDistPoints; // false + public float addNeighboursDistPoints; // false + public float addFacesPoints; // false + public float shouldAdjustCollisionMargin; // false + // VHACD + public float whichHACD; // zero if Bullet HACD, non-zero says VHACD + // http://kmamou.blogspot.ca/2014/12/v-hacd-20-parameters-description.html + public float vHACDresolution; // 100,000 max number of voxels generated during voxelization stage + public float vHACDdepth; // 20 max number of clipping stages + public float vHACDconcavity; // 0.0025 maximum concavity + public float vHACDplaneDownsampling; // 4 granularity of search for best clipping plane + public float vHACDconvexHullDownsampling; // 4 precision of hull gen process + public float vHACDalpha; // 0.05 bias toward clipping along symmetry planes + public float vHACDbeta; // 0.05 bias toward clipping along revolution axis + public float vHACDgamma; // 0.00125 max concavity when merging + public float vHACDpca; // 0 on/off normalizing mesh before decomp + public float vHACDmode; // 0 0:voxel based, 1: tetrahedron based + public float vHACDmaxNumVerticesPerCH; // 64 max triangles per convex hull + public float vHACDminVolumePerCH; // 0.0001 sampling of generated convex hulls +} + +// The states a bullet collision object can have +public enum ActivationState : uint +{ + ACTIVE_TAG = 1, + ISLAND_SLEEPING, + WANTS_DEACTIVATION, + DISABLE_DEACTIVATION, + DISABLE_SIMULATION, +} + +public enum CollisionObjectTypes : int +{ + CO_COLLISION_OBJECT = 1 << 0, + CO_RIGID_BODY = 1 << 1, + CO_GHOST_OBJECT = 1 << 2, + CO_SOFT_BODY = 1 << 3, + CO_HF_FLUID = 1 << 4, + CO_USER_TYPE = 1 << 5, +} + +// Values used by Bullet and BulletSim to control object properties. +// Bullet's "CollisionFlags" has more to do with operations on the +// object (if collisions happen, if gravity effects it, ...). +public enum CollisionFlags : uint +{ + CF_STATIC_OBJECT = 1 << 0, + CF_KINEMATIC_OBJECT = 1 << 1, + CF_NO_CONTACT_RESPONSE = 1 << 2, + CF_CUSTOM_MATERIAL_CALLBACK = 1 << 3, + CF_CHARACTER_OBJECT = 1 << 4, + CF_DISABLE_VISUALIZE_OBJECT = 1 << 5, + CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6, + // Following used by BulletSim to control collisions and updates + BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, // return collision events from unmanaged to managed + BS_FLOATS_ON_WATER = 1 << 11, // the object should float at water level + BS_VEHICLE_COLLISIONS = 1 << 12, // return collisions for vehicle ground checking + BS_RETURN_ROOT_COMPOUND_SHAPE = 1 << 13, // return the pos/rot of the root shape in a compound shape + BS_NONE = 0, + BS_ALL = 0x7FFF // collision flags are a signed short +}; + +// Values f collisions groups and masks +public enum CollisionFilterGroups : uint +{ + // Don't use the bit definitions!! Define the use in a + // filter/mask definition below. This way collision interactions + // are more easily found and debugged. + BNoneGroup = 0, + BDefaultGroup = 1 << 0, // 0001 + BStaticGroup = 1 << 1, // 0002 + BKinematicGroup = 1 << 2, // 0004 + BDebrisGroup = 1 << 3, // 0008 + BSensorTrigger = 1 << 4, // 0010 + BCharacterGroup = 1 << 5, // 0020 + BAllGroup = 0x0007FFF, // collision flags are a signed short + // Filter groups defined by BulletSim + BGroundPlaneGroup = 1 << 8, // 0400 + BTerrainGroup = 1 << 9, // 0800 + BRaycastGroup = 1 << 10, // 1000 + BSolidGroup = 1 << 11, // 2000 + // BLinksetGroup = xx // a linkset proper is either static or dynamic + BLinksetChildGroup = 1 << 12, // 4000 +}; + +// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0 +// ERP controls amount of correction per tick. Usable range=0.1..0.8. Default=0.2. +public enum ConstraintParams : int +{ + BT_CONSTRAINT_ERP = 1, // this one is not used in Bullet as of 20120730 + BT_CONSTRAINT_STOP_ERP, + BT_CONSTRAINT_CFM, + BT_CONSTRAINT_STOP_CFM, +}; +public enum ConstraintParamAxis : int +{ + AXIS_LINEAR_X = 0, + AXIS_LINEAR_Y, + AXIS_LINEAR_Z, + AXIS_ANGULAR_X, + AXIS_ANGULAR_Y, + AXIS_ANGULAR_Z, + AXIS_LINEAR_ALL = 20, // added by BulletSim so we don't have to do zillions of calls + AXIS_ANGULAR_ALL, + AXIS_ALL +}; + +public abstract class BSAPITemplate +{ +// Returns the name of the underlying Bullet engine +public abstract string BulletEngineName { get; } +public abstract string BulletEngineVersion { get; protected set;} + +// Initialization and simulation +public abstract BulletWorld Initialize(Vector3 maxPosition, ConfigurationParameters parms, + int maxCollisions, ref CollisionDesc[] collisionArray, + int maxUpdates, ref EntityProperties[] updateArray + ); + +public abstract int PhysicsStep(BulletWorld world, float timeStep, int maxSubSteps, float fixedTimeStep, + out int updatedEntityCount, out int collidersCount); + +public abstract bool UpdateParameter(BulletWorld world, UInt32 localID, String parm, float value); + +public abstract void Shutdown(BulletWorld sim); + +public abstract bool PushUpdate(BulletBody obj); + +// ===================================================================================== +// Mesh, hull, shape and body creation helper routines +public abstract BulletShape CreateMeshShape(BulletWorld world, + int indicesCount, int[] indices, + int verticesCount, float[] vertices ); + +public abstract BulletShape CreateGImpactShape(BulletWorld world, + int indicesCount, int[] indices, + int verticesCount, float[] vertices ); + +public abstract BulletShape CreateHullShape(BulletWorld world, + int hullCount, float[] hulls); + +public abstract BulletShape BuildHullShapeFromMesh(BulletWorld world, BulletShape meshShape, HACDParams parms); + +public abstract BulletShape BuildConvexHullShapeFromMesh(BulletWorld world, BulletShape meshShape); + +public abstract BulletShape CreateConvexHullShape(BulletWorld world, + int indicesCount, int[] indices, + int verticesCount, float[] vertices ); + +public abstract BulletShape BuildNativeShape(BulletWorld world, ShapeData shapeData); + +public abstract bool IsNativeShape(BulletShape shape); + +public abstract void SetShapeCollisionMargin(BulletShape shape, float margin); + +public abstract BulletShape BuildCapsuleShape(BulletWorld world, float radius, float height, Vector3 scale); + +public abstract BulletShape CreateCompoundShape(BulletWorld sim, bool enableDynamicAabbTree); + +public abstract int GetNumberOfCompoundChildren(BulletShape cShape); + +public abstract void AddChildShapeToCompoundShape(BulletShape cShape, BulletShape addShape, Vector3 pos, Quaternion rot); + +public abstract BulletShape GetChildShapeFromCompoundShapeIndex(BulletShape cShape, int indx); + +public abstract BulletShape RemoveChildShapeFromCompoundShapeIndex(BulletShape cShape, int indx); + +public abstract void RemoveChildShapeFromCompoundShape(BulletShape cShape, BulletShape removeShape); + +public abstract void UpdateChildTransform(BulletShape pShape, int childIndex, Vector3 pos, Quaternion rot, bool shouldRecalculateLocalAabb); + +public abstract void RecalculateCompoundShapeLocalAabb(BulletShape cShape); + +public abstract BulletShape DuplicateCollisionShape(BulletWorld sim, BulletShape srcShape, UInt32 id); + +public abstract bool DeleteCollisionShape(BulletWorld world, BulletShape shape); + +public abstract CollisionObjectTypes GetBodyType(BulletBody obj); + +public abstract BulletBody CreateBodyFromShape(BulletWorld sim, BulletShape shape, UInt32 id, Vector3 pos, Quaternion rot); + +public abstract BulletBody CreateBodyWithDefaultMotionState(BulletShape shape, UInt32 id, Vector3 pos, Quaternion rot); + +public abstract BulletBody CreateGhostFromShape(BulletWorld sim, BulletShape shape, UInt32 id, Vector3 pos, Quaternion rot); + +public abstract void DestroyObject(BulletWorld sim, BulletBody obj); + +// ===================================================================================== +public abstract BulletShape CreateGroundPlaneShape(UInt32 id, float height, float collisionMargin); + +public abstract BulletShape CreateTerrainShape(UInt32 id, Vector3 size, float minHeight, float maxHeight, float[] heightMap, + float scaleFactor, float collisionMargin); + +// ===================================================================================== +// Constraint creation and helper routines +public abstract BulletConstraint Create6DofConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint Create6DofConstraintToPoint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 joinPoint, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint Create6DofConstraintFixed(BulletWorld world, BulletBody obj1, + Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint Create6DofSpringConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint CreateHingeConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 pivotinA, Vector3 pivotinB, + Vector3 axisInA, Vector3 axisInB, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint CreateSliderConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frameInAloc, Quaternion frameInArot, + Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint CreateConeTwistConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frameInAloc, Quaternion frameInArot, + Vector3 frameInBloc, Quaternion frameInBrot, + bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint CreateGearConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 axisInA, Vector3 axisInB, + float ratio, bool disableCollisionsBetweenLinkedBodies); + +public abstract BulletConstraint CreatePoint2PointConstraint(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 pivotInA, Vector3 pivotInB, + bool disableCollisionsBetweenLinkedBodies); + +public abstract void SetConstraintEnable(BulletConstraint constrain, float numericTrueFalse); + +public abstract void SetConstraintNumSolverIterations(BulletConstraint constrain, float iterations); + +public abstract bool SetFrames(BulletConstraint constrain, + Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot); + +public abstract bool SetLinearLimits(BulletConstraint constrain, Vector3 low, Vector3 hi); + +public abstract bool SetAngularLimits(BulletConstraint constrain, Vector3 low, Vector3 hi); + +public abstract bool UseFrameOffset(BulletConstraint constrain, float enable); + +public abstract bool TranslationalLimitMotor(BulletConstraint constrain, float enable, float targetVel, float maxMotorForce); + +public abstract bool SetBreakingImpulseThreshold(BulletConstraint constrain, float threshold); + +public const int HINGE_NOT_SPECIFIED = -1; +public abstract bool HingeSetLimits(BulletConstraint constrain, float low, float high, float softness, float bias, float relaxation); + +public abstract bool SpringEnable(BulletConstraint constrain, int index, float numericTrueFalse); + +public const int SPRING_NOT_SPECIFIED = -1; +public abstract bool SpringSetEquilibriumPoint(BulletConstraint constrain, int index, float equilibriumPoint); + +public abstract bool SpringSetStiffness(BulletConstraint constrain, int index, float stiffnesss); + +public abstract bool SpringSetDamping(BulletConstraint constrain, int index, float damping); + +public const int SLIDER_LOWER_LIMIT = 0; +public const int SLIDER_UPPER_LIMIT = 1; +public const int SLIDER_LINEAR = 2; +public const int SLIDER_ANGULAR = 3; +public abstract bool SliderSetLimits(BulletConstraint constrain, int lowerUpper, int linAng, float val); + +public const int SLIDER_SET_SOFTNESS = 4; +public const int SLIDER_SET_RESTITUTION = 5; +public const int SLIDER_SET_DAMPING = 6; +public const int SLIDER_SET_DIRECTION = 7; +public const int SLIDER_SET_LIMIT = 8; +public const int SLIDER_SET_ORTHO = 9; +public abstract bool SliderSet(BulletConstraint constrain, int softRestDamp, int dirLimOrtho, int linAng, float val); + +public abstract bool SliderMotorEnable(BulletConstraint constrain, int linAng, float numericTrueFalse); + +public const int SLIDER_MOTOR_VELOCITY = 10; +public const int SLIDER_MAX_MOTOR_FORCE = 11; +public abstract bool SliderMotor(BulletConstraint constrain, int forceVel, int linAng, float val); + +public abstract bool CalculateTransforms(BulletConstraint constrain); + +public abstract bool SetConstraintParam(BulletConstraint constrain, ConstraintParams paramIndex, float value, ConstraintParamAxis axis); + +public abstract bool DestroyConstraint(BulletWorld world, BulletConstraint constrain); + +// ===================================================================================== +// btCollisionWorld entries +public abstract void UpdateSingleAabb(BulletWorld world, BulletBody obj); + +public abstract void UpdateAabbs(BulletWorld world); + +public abstract bool GetForceUpdateAllAabbs(BulletWorld world); + +public abstract void SetForceUpdateAllAabbs(BulletWorld world, bool force); + +// ===================================================================================== +// btDynamicsWorld entries +// public abstract bool AddObjectToWorld(BulletWorld world, BulletBody obj, Vector3 pos, Quaternion rot); +public abstract bool AddObjectToWorld(BulletWorld world, BulletBody obj); + +public abstract bool RemoveObjectFromWorld(BulletWorld world, BulletBody obj); + +public abstract bool ClearCollisionProxyCache(BulletWorld world, BulletBody obj); + +public abstract bool AddConstraintToWorld(BulletWorld world, BulletConstraint constrain, bool disableCollisionsBetweenLinkedObjects); + +public abstract bool RemoveConstraintFromWorld(BulletWorld world, BulletConstraint constrain); +// ===================================================================================== +// btCollisionObject entries +public abstract Vector3 GetAnisotripicFriction(BulletConstraint constrain); + +public abstract Vector3 SetAnisotripicFriction(BulletConstraint constrain, Vector3 frict); + +public abstract bool HasAnisotripicFriction(BulletConstraint constrain); + +public abstract void SetContactProcessingThreshold(BulletBody obj, float val); + +public abstract float GetContactProcessingThreshold(BulletBody obj); + +public abstract bool IsStaticObject(BulletBody obj); + +public abstract bool IsKinematicObject(BulletBody obj); + +public abstract bool IsStaticOrKinematicObject(BulletBody obj); + +public abstract bool HasContactResponse(BulletBody obj); + +public abstract void SetCollisionShape(BulletWorld sim, BulletBody obj, BulletShape shape); + +public abstract BulletShape GetCollisionShape(BulletBody obj); + +public abstract int GetActivationState(BulletBody obj); + +public abstract void SetActivationState(BulletBody obj, int state); + +public abstract void SetDeactivationTime(BulletBody obj, float dtime); + +public abstract float GetDeactivationTime(BulletBody obj); + +public abstract void ForceActivationState(BulletBody obj, ActivationState state); + +public abstract void Activate(BulletBody obj, bool forceActivation); + +public abstract bool IsActive(BulletBody obj); + +public abstract void SetRestitution(BulletBody obj, float val); + +public abstract float GetRestitution(BulletBody obj); + +public abstract void SetFriction(BulletBody obj, float val); + +public abstract float GetFriction(BulletBody obj); + +public abstract Vector3 GetPosition(BulletBody obj); + +public abstract Quaternion GetOrientation(BulletBody obj); + +public abstract void SetTranslation(BulletBody obj, Vector3 position, Quaternion rotation); + +// public abstract IntPtr GetBroadphaseHandle(BulletBody obj); + +// public abstract void SetBroadphaseHandle(BulletBody obj, IntPtr handle); + +public abstract void SetInterpolationLinearVelocity(BulletBody obj, Vector3 vel); + +public abstract void SetInterpolationAngularVelocity(BulletBody obj, Vector3 vel); + +public abstract void SetInterpolationVelocity(BulletBody obj, Vector3 linearVel, Vector3 angularVel); + +public abstract float GetHitFraction(BulletBody obj); + +public abstract void SetHitFraction(BulletBody obj, float val); + +public abstract CollisionFlags GetCollisionFlags(BulletBody obj); + +public abstract CollisionFlags SetCollisionFlags(BulletBody obj, CollisionFlags flags); + +public abstract CollisionFlags AddToCollisionFlags(BulletBody obj, CollisionFlags flags); + +public abstract CollisionFlags RemoveFromCollisionFlags(BulletBody obj, CollisionFlags flags); + +public abstract float GetCcdMotionThreshold(BulletBody obj); + +public abstract void SetCcdMotionThreshold(BulletBody obj, float val); + +public abstract float GetCcdSweptSphereRadius(BulletBody obj); + +public abstract void SetCcdSweptSphereRadius(BulletBody obj, float val); + +public abstract IntPtr GetUserPointer(BulletBody obj); + +public abstract void SetUserPointer(BulletBody obj, IntPtr val); + +// ===================================================================================== +// btRigidBody entries +public abstract void ApplyGravity(BulletBody obj); + +public abstract void SetGravity(BulletBody obj, Vector3 val); + +public abstract Vector3 GetGravity(BulletBody obj); + +public abstract void SetDamping(BulletBody obj, float lin_damping, float ang_damping); + +public abstract void SetLinearDamping(BulletBody obj, float lin_damping); + +public abstract void SetAngularDamping(BulletBody obj, float ang_damping); + +public abstract float GetLinearDamping(BulletBody obj); + +public abstract float GetAngularDamping(BulletBody obj); + +public abstract float GetLinearSleepingThreshold(BulletBody obj); + +public abstract void ApplyDamping(BulletBody obj, float timeStep); + +public abstract void SetMassProps(BulletBody obj, float mass, Vector3 inertia); + +public abstract Vector3 GetLinearFactor(BulletBody obj); + +public abstract void SetLinearFactor(BulletBody obj, Vector3 factor); + +public abstract void SetCenterOfMassByPosRot(BulletBody obj, Vector3 pos, Quaternion rot); + +// Add a force to the object as if its mass is one. +public abstract void ApplyCentralForce(BulletBody obj, Vector3 force); + +// Set the force being applied to the object as if its mass is one. +public abstract void SetObjectForce(BulletBody obj, Vector3 force); + +public abstract Vector3 GetTotalForce(BulletBody obj); + +public abstract Vector3 GetTotalTorque(BulletBody obj); + +public abstract Vector3 GetInvInertiaDiagLocal(BulletBody obj); + +public abstract void SetInvInertiaDiagLocal(BulletBody obj, Vector3 inert); + +public abstract void SetSleepingThresholds(BulletBody obj, float lin_threshold, float ang_threshold); + +public abstract void ApplyTorque(BulletBody obj, Vector3 torque); + +// Apply force at the given point. Will add torque to the object. +public abstract void ApplyForce(BulletBody obj, Vector3 force, Vector3 pos); + +// Apply impulse to the object. Same as "ApplycentralForce" but force scaled by object's mass. +public abstract void ApplyCentralImpulse(BulletBody obj, Vector3 imp); + +// Apply impulse to the object's torque. Force is scaled by object's mass. +public abstract void ApplyTorqueImpulse(BulletBody obj, Vector3 imp); + +// Apply impulse at the point given. For is scaled by object's mass and effects both linear and angular forces. +public abstract void ApplyImpulse(BulletBody obj, Vector3 imp, Vector3 pos); + +public abstract void ClearForces(BulletBody obj); + +public abstract void ClearAllForces(BulletBody obj); + +public abstract void UpdateInertiaTensor(BulletBody obj); + +public abstract Vector3 GetLinearVelocity(BulletBody obj); + +public abstract Vector3 GetAngularVelocity(BulletBody obj); + +public abstract void SetLinearVelocity(BulletBody obj, Vector3 val); + +public abstract void SetAngularVelocity(BulletBody obj, Vector3 angularVelocity); + +public abstract Vector3 GetVelocityInLocalPoint(BulletBody obj, Vector3 pos); + +public abstract void Translate(BulletBody obj, Vector3 trans); + +public abstract void UpdateDeactivation(BulletBody obj, float timeStep); + +public abstract bool WantsSleeping(BulletBody obj); + +public abstract void SetAngularFactor(BulletBody obj, float factor); + +public abstract void SetAngularFactorV(BulletBody obj, Vector3 factor); + +public abstract Vector3 GetAngularFactor(BulletBody obj); + +public abstract bool IsInWorld(BulletWorld world, BulletBody obj); + +public abstract void AddConstraintRef(BulletBody obj, BulletConstraint constrain); + +public abstract void RemoveConstraintRef(BulletBody obj, BulletConstraint constrain); + +public abstract BulletConstraint GetConstraintRef(BulletBody obj, int index); + +public abstract int GetNumConstraintRefs(BulletBody obj); + +public abstract bool SetCollisionGroupMask(BulletBody body, UInt32 filter, UInt32 mask); + +// ===================================================================================== +// btCollisionShape entries + +public abstract float GetAngularMotionDisc(BulletShape shape); + +public abstract float GetContactBreakingThreshold(BulletShape shape, float defaultFactor); + +public abstract bool IsPolyhedral(BulletShape shape); + +public abstract bool IsConvex2d(BulletShape shape); + +public abstract bool IsConvex(BulletShape shape); + +public abstract bool IsNonMoving(BulletShape shape); + +public abstract bool IsConcave(BulletShape shape); + +public abstract bool IsCompound(BulletShape shape); + +public abstract bool IsSoftBody(BulletShape shape); + +public abstract bool IsInfinite(BulletShape shape); + +public abstract void SetLocalScaling(BulletShape shape, Vector3 scale); + +public abstract Vector3 GetLocalScaling(BulletShape shape); + +public abstract Vector3 CalculateLocalInertia(BulletShape shape, float mass); + +public abstract int GetShapeType(BulletShape shape); + +public abstract void SetMargin(BulletShape shape, float val); + +public abstract float GetMargin(BulletShape shape); + +// ===================================================================================== +// Debugging +public virtual void DumpRigidBody(BulletWorld sim, BulletBody collisionObject) { } + +public virtual void DumpCollisionShape(BulletWorld sim, BulletShape collisionShape) { } + +public virtual void DumpConstraint(BulletWorld sim, BulletConstraint constrain) { } + +public virtual void DumpActivationInfo(BulletWorld sim) { } + +public virtual void DumpAllInfo(BulletWorld sim) { } + +public virtual void DumpPhysicsStatistics(BulletWorld sim) { } + +public virtual void ResetBroadphasePool(BulletWorld sim) { } + +public virtual void ResetConstraintSolver(BulletWorld sim) { } + +}; +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs new file mode 100644 index 0000000..9c3f160 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs @@ -0,0 +1,813 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using log4net; +using OMV = OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public sealed class BSCharacter : BSPhysObject +{ + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[BULLETS CHAR]"; + + // private bool _stopped; + private OMV.Vector3 _size; + private bool _grabbed; + private bool _selected; + private float _mass; + private float _avatarVolume; + private float _collisionScore; + private OMV.Vector3 _acceleration; + private int _physicsActorType; + private bool _isPhysical; + private bool _flying; + private bool _setAlwaysRun; + private bool _throttleUpdates; + private bool _floatOnWater; + private OMV.Vector3 _rotationalVelocity; + private bool _kinematic; + private float _buoyancy; + + private BSActorAvatarMove m_moveActor; + private const string AvatarMoveActorName = "BSCharacter.AvatarMove"; + + private OMV.Vector3 _PIDTarget; + private float _PIDTau; + +// public override OMV.Vector3 RawVelocity +// { get { return base.RawVelocity; } +// set { +// if (value != base.RawVelocity) +// Util.PrintCallStack(); +// Console.WriteLine("Set rawvel to {0}", value); +// base.RawVelocity = value; } +// } + + // Avatars are always complete (in the physics engine sense) + public override bool IsIncomplete { get { return false; } } + + public BSCharacter( + uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 vel, OMV.Vector3 size, bool isFlying) + + : base(parent_scene, localID, avName, "BSCharacter") + { + _physicsActorType = (int)ActorTypes.Agent; + RawPosition = pos; + + _flying = isFlying; + RawOrientation = OMV.Quaternion.Identity; + RawVelocity = vel; + _buoyancy = ComputeBuoyancyFromFlying(isFlying); + Friction = BSParam.AvatarStandingFriction; + Density = BSParam.AvatarDensity; + + // Old versions of ScenePresence passed only the height. If width and/or depth are zero, + // replace with the default values. + _size = size; + if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; + if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; + + // The dimensions of the physical capsule are kept in the scale. + // Physics creates a unit capsule which is scaled by the physics engine. + Scale = ComputeAvatarScale(_size); + // set _avatarVolume and _mass based on capsule size, _density and Scale + ComputeAvatarVolumeAndMass(); + + DetailLog( + "{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5},pos={6},vel={7}", + LocalID, _size, Scale, Density, _avatarVolume, RawMass, pos, vel); + + // do actual creation in taint time + PhysScene.TaintedObject(LocalID, "BSCharacter.create", delegate() + { + DetailLog("{0},BSCharacter.create,taint", LocalID); + + // New body and shape into PhysBody and PhysShape + PhysScene.Shapes.GetBodyAndShape(true, PhysScene.World, this); + + // The avatar's movement is controlled by this motor that speeds up and slows down + // the avatar seeking to reach the motor's target speed. + // This motor runs as a prestep action for the avatar so it will keep the avatar + // standing as well as moving. Destruction of the avatar will destroy the pre-step action. + m_moveActor = new BSActorAvatarMove(PhysScene, this, AvatarMoveActorName); + PhysicalActors.Add(AvatarMoveActorName, m_moveActor); + + SetPhysicalProperties(); + + IsInitialized = true; + }); + return; + } + + // called when this character is being destroyed and the resources should be released + public override void Destroy() + { + IsInitialized = false; + + base.Destroy(); + + DetailLog("{0},BSCharacter.Destroy", LocalID); + PhysScene.TaintedObject(LocalID, "BSCharacter.destroy", delegate() + { + PhysScene.Shapes.DereferenceBody(PhysBody, null /* bodyCallback */); + PhysBody.Clear(); + PhysShape.Dereference(PhysScene); + PhysShape = new BSShapeNull(); + }); + } + + private void SetPhysicalProperties() + { + PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); + + ForcePosition = RawPosition; + + // Set the velocity + if (m_moveActor != null) + m_moveActor.SetVelocityAndTarget(RawVelocity, RawVelocity, false); + + ForceVelocity = RawVelocity; + TargetVelocity = RawVelocity; + + // This will enable or disable the flying buoyancy of the avatar. + // Needs to be reset especially when an avatar is recreated after crossing a region boundry. + Flying = _flying; + + PhysScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution); + PhysScene.PE.SetMargin(PhysShape.physShapeInfo, PhysScene.Params.collisionMargin); + PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); + PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); + if (BSParam.CcdMotionThreshold > 0f) + { + PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); + PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); + } + + UpdatePhysicalMassProperties(RawMass, false); + + // Make so capsule does not fall over + PhysScene.PE.SetAngularFactorV(PhysBody, OMV.Vector3.Zero); + + // The avatar mover sets some parameters. + PhysicalActors.Refresh(); + + PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_CHARACTER_OBJECT); + + PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody); + + // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); + PhysScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION); + PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody); + + // Do this after the object has been added to the world + if (BSParam.AvatarToAvatarCollisionsByDefault) + PhysBody.collisionType = CollisionType.Avatar; + else + PhysBody.collisionType = CollisionType.PhantomToOthersAvatar; + + PhysBody.ApplyCollisionMask(PhysScene); + } + + public override void RequestPhysicsterseUpdate() + { + base.RequestPhysicsterseUpdate(); + } + + // No one calls this method so I don't know what it could possibly mean + public override bool Stopped { get { return false; } } + + public override OMV.Vector3 Size { + get + { + // Avatar capsule size is kept in the scale parameter. + return _size; + } + + set { + // This is how much the avatar size is changing. Positive means getting bigger. + // The avatar altitude must be adjusted for this change. + float heightChange = value.Z - _size.Z; + + _size = value; + // Old versions of ScenePresence passed only the height. If width and/or depth are zero, + // replace with the default values. + if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; + if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; + + Scale = ComputeAvatarScale(_size); + ComputeAvatarVolumeAndMass(); + DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", + LocalID, _size, Scale, Density, _avatarVolume, RawMass); + + PhysScene.TaintedObject(LocalID, "BSCharacter.setSize", delegate() + { + if (PhysBody.HasPhysicalBody && PhysShape.physShapeInfo.HasPhysicalShape) + { + PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); + UpdatePhysicalMassProperties(RawMass, true); + + // Adjust the avatar's position to account for the increase/decrease in size + ForcePosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, RawPosition.Z + heightChange / 2f); + + // Make sure this change appears as a property update event + PhysScene.PE.PushUpdate(PhysBody); + } + }); + + } + } + + public override PrimitiveBaseShape Shape + { + set { BaseShape = value; } + } + + public override bool Grabbed { + set { _grabbed = value; } + } + public override bool Selected { + set { _selected = value; } + } + public override bool IsSelected + { + get { return _selected; } + } + public override void CrossingFailure() { return; } + public override void link(PhysicsActor obj) { return; } + public override void delink() { return; } + + // Set motion values to zero. + // Do it to the properties so the values get set in the physics engine. + // Push the setting of the values to the viewer. + // Called at taint time! + public override void ZeroMotion(bool inTaintTime) + { + RawVelocity = OMV.Vector3.Zero; + _acceleration = OMV.Vector3.Zero; + _rotationalVelocity = OMV.Vector3.Zero; + + // Zero some other properties directly into the physics engine + PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.ZeroMotion", delegate() + { + if (PhysBody.HasPhysicalBody) + PhysScene.PE.ClearAllForces(PhysBody); + }); + } + + public override void ZeroAngularMotion(bool inTaintTime) + { + _rotationalVelocity = OMV.Vector3.Zero; + + PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.ZeroMotion", delegate() + { + if (PhysBody.HasPhysicalBody) + { + PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero); + PhysScene.PE.SetAngularVelocity(PhysBody, OMV.Vector3.Zero); + // The next also get rid of applied linear force but the linear velocity is untouched. + PhysScene.PE.ClearForces(PhysBody); + } + }); + } + + + public override void LockAngularMotion(OMV.Vector3 axis) { return; } + + public override OMV.Vector3 Position { + get { + // Don't refetch the position because this function is called a zillion times + // RawPosition = PhysicsScene.PE.GetObjectPosition(Scene.World, LocalID); + return RawPosition; + } + set { + RawPosition = value; + + PhysScene.TaintedObject(LocalID, "BSCharacter.setPosition", delegate() + { + DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, RawPosition, RawOrientation); + PositionSanityCheck(); + ForcePosition = RawPosition; + }); + } + } + public override OMV.Vector3 ForcePosition { + get { + RawPosition = PhysScene.PE.GetPosition(PhysBody); + return RawPosition; + } + set { + RawPosition = value; + if (PhysBody.HasPhysicalBody) + { + PhysScene.PE.SetTranslation(PhysBody, RawPosition, RawOrientation); + } + } + } + + + // Check that the current position is sane and, if not, modify the position to make it so. + // Check for being below terrain or on water. + // Returns 'true' of the position was made sane by some action. + private bool PositionSanityCheck() + { + bool ret = false; + + // TODO: check for out of bounds + if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) + { + // The character is out of the known/simulated area. + // Force the avatar position to be within known. ScenePresence will use the position + // plus the velocity to decide if the avatar is moving out of the region. + RawPosition = PhysScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition); + DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition); + return true; + } + + // If below the ground, move the avatar up + float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); + if (Position.Z < terrainHeight) + { + DetailLog("{0},BSCharacter.PositionSanityCheck,adjustForUnderGround,pos={1},terrain={2}", LocalID, RawPosition, terrainHeight); + RawPosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, terrainHeight + BSParam.AvatarBelowGroundUpCorrectionMeters); + ret = true; + } + if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) + { + float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(RawPosition); + if (Position.Z < waterHeight) + { + RawPosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, waterHeight); + ret = true; + } + } + + return ret; + } + + // A version of the sanity check that also makes sure a new position value is + // pushed back to the physics engine. This routine would be used by anyone + // who is not already pushing the value. + private bool PositionSanityCheck(bool inTaintTime) + { + bool ret = false; + if (PositionSanityCheck()) + { + // The new position value must be pushed into the physics engine but we can't + // just assign to "Position" because of potential call loops. + PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.PositionSanityCheck", delegate() + { + DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, RawPosition, RawOrientation); + ForcePosition = RawPosition; + }); + ret = true; + } + return ret; + } + + public override float Mass { get { return _mass; } } + + // used when we only want this prim's mass and not the linkset thing + public override float RawMass { + get {return _mass; } + } + public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) + { + OMV.Vector3 localInertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); + PhysScene.PE.SetMassProps(PhysBody, physMass, localInertia); + } + + public override OMV.Vector3 Force { + get { return RawForce; } + set { + RawForce = value; + // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force); + PhysScene.TaintedObject(LocalID, "BSCharacter.SetForce", delegate() + { + DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, RawForce); + if (PhysBody.HasPhysicalBody) + PhysScene.PE.SetObjectForce(PhysBody, RawForce); + }); + } + } + + // Avatars don't do vehicles + public override int VehicleType { get { return (int)Vehicle.TYPE_NONE; } set { return; } } + public override void VehicleFloatParam(int param, float value) { } + public override void VehicleVectorParam(int param, OMV.Vector3 value) {} + public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { } + public override void VehicleFlags(int param, bool remove) { } + + // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more + public override void SetVolumeDetect(int param) { return; } + public override bool IsVolumeDetect { get { return false; } } + + public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } } + public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } } + + // Sets the target in the motor. This starts the changing of the avatar's velocity. + public override OMV.Vector3 TargetVelocity + { + get + { + return base.m_targetVelocity; + } + set + { + DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value); + m_targetVelocity = value; + OMV.Vector3 targetVel = value; + if (_setAlwaysRun && !_flying) + targetVel *= new OMV.Vector3(BSParam.AvatarAlwaysRunFactor, BSParam.AvatarAlwaysRunFactor, 1f); + + if (m_moveActor != null) + m_moveActor.SetVelocityAndTarget(RawVelocity, targetVel, false /* inTaintTime */); + } + } + // Directly setting velocity means this is what the user really wants now. + public override OMV.Vector3 Velocity { + get { return RawVelocity; } + set { + RawVelocity = value; + OMV.Vector3 vel = RawVelocity; + + DetailLog("{0}: set Velocity = {1}", LocalID, value); + + PhysScene.TaintedObject(LocalID, "BSCharacter.setVelocity", delegate() + { + if (m_moveActor != null) + m_moveActor.SetVelocityAndTarget(vel, vel, true /* inTaintTime */); + + DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, vel); + ForceVelocity = vel; + }); + } + } + + public override OMV.Vector3 ForceVelocity { + get { return RawVelocity; } + set { + PhysScene.AssertInTaintTime("BSCharacter.ForceVelocity"); +// Util.PrintCallStack(); + DetailLog("{0}: set ForceVelocity = {1}", LocalID, value); + + RawVelocity = value; + PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity); + PhysScene.PE.Activate(PhysBody, true); + } + } + + public override OMV.Vector3 Torque { + get { return RawTorque; } + set { RawTorque = value; + } + } + + public override float CollisionScore { + get { return _collisionScore; } + set { _collisionScore = value; + } + } + public override OMV.Vector3 Acceleration { + get { return _acceleration; } + set { _acceleration = value; } + } + public override OMV.Quaternion Orientation { + get { return RawOrientation; } + set { + // Orientation is set zillions of times when an avatar is walking. It's like + // the viewer doesn't trust us. + if (RawOrientation != value) + { + RawOrientation = value; + PhysScene.TaintedObject(LocalID, "BSCharacter.setOrientation", delegate() + { + // Bullet assumes we know what we are doing when forcing orientation + // so it lets us go against all the rules and just compensates for them later. + // This forces rotation to be only around the Z axis and doesn't change any of the other axis. + // This keeps us from flipping the capsule over which the veiwer does not understand. + float oRoll, oPitch, oYaw; + RawOrientation.GetEulerAngles(out oRoll, out oPitch, out oYaw); + OMV.Quaternion trimmedOrientation = OMV.Quaternion.CreateFromEulers(0f, 0f, oYaw); + // DetailLog("{0},BSCharacter.setOrientation,taint,val={1},valDir={2},conv={3},convDir={4}", + // LocalID, RawOrientation, OMV.Vector3.UnitX * RawOrientation, + // trimmedOrientation, OMV.Vector3.UnitX * trimmedOrientation); + ForceOrientation = trimmedOrientation; + }); + } + } + } + // Go directly to Bullet to get/set the value. + public override OMV.Quaternion ForceOrientation + { + get + { + RawOrientation = PhysScene.PE.GetOrientation(PhysBody); + return RawOrientation; + } + set + { + RawOrientation = value; + if (PhysBody.HasPhysicalBody) + { + // RawPosition = PhysicsScene.PE.GetPosition(BSBody); + PhysScene.PE.SetTranslation(PhysBody, RawPosition, RawOrientation); + } + } + } + public override int PhysicsActorType { + get { return _physicsActorType; } + set { _physicsActorType = value; + } + } + public override bool IsPhysical { + get { return _isPhysical; } + set { _isPhysical = value; + } + } + public override bool IsSolid { + get { return true; } + } + public override bool IsStatic { + get { return false; } + } + public override bool IsPhysicallyActive { + get { return true; } + } + public override bool Flying { + get { return _flying; } + set { + _flying = value; + + // simulate flying by changing the effect of gravity + Buoyancy = ComputeBuoyancyFromFlying(_flying); + } + } + // Flying is implimented by changing the avatar's buoyancy. + // Would this be done better with a vehicle type? + private float ComputeBuoyancyFromFlying(bool ifFlying) { + return ifFlying ? 1f : 0f; + } + public override bool + SetAlwaysRun { + get { return _setAlwaysRun; } + set { _setAlwaysRun = value; } + } + public override bool ThrottleUpdates { + get { return _throttleUpdates; } + set { _throttleUpdates = value; } + } + public override bool FloatOnWater { + set { + _floatOnWater = value; + PhysScene.TaintedObject(LocalID, "BSCharacter.setFloatOnWater", delegate() + { + if (PhysBody.HasPhysicalBody) + { + if (_floatOnWater) + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + else + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + } + }); + } + } + public override OMV.Vector3 RotationalVelocity { + get { return _rotationalVelocity; } + set { _rotationalVelocity = value; } + } + public override OMV.Vector3 ForceRotationalVelocity { + get { return _rotationalVelocity; } + set { _rotationalVelocity = value; } + } + public override bool Kinematic { + get { return _kinematic; } + set { _kinematic = value; } + } + // neg=fall quickly, 0=1g, 1=0g, pos=float up + public override float Buoyancy { + get { return _buoyancy; } + set { _buoyancy = value; + PhysScene.TaintedObject(LocalID, "BSCharacter.setBuoyancy", delegate() + { + DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy); + ForceBuoyancy = _buoyancy; + }); + } + } + public override float ForceBuoyancy { + get { return _buoyancy; } + set { + PhysScene.AssertInTaintTime("BSCharacter.ForceBuoyancy"); + + _buoyancy = value; + DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); + // Buoyancy is faked by changing the gravity applied to the object + float grav = BSParam.Gravity * (1f - _buoyancy); + Gravity = new OMV.Vector3(0f, 0f, grav); + if (PhysBody.HasPhysicalBody) + PhysScene.PE.SetGravity(PhysBody, Gravity); + } + } + + // Used for MoveTo + public override OMV.Vector3 PIDTarget { + set { _PIDTarget = value; } + } + + public override bool PIDActive { get; set; } + + public override float PIDTau { + set { _PIDTau = value; } + } + + public override void AddForce(OMV.Vector3 force, bool pushforce) + { + // Since this force is being applied in only one step, make this a force per second. + OMV.Vector3 addForce = force / PhysScene.LastTimeStep; + AddForce(addForce, pushforce, false); + } + public override void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { + if (force.IsFinite()) + { + OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); + // DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce); + + PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.AddForce", delegate() + { + // Bullet adds this central force to the total force for this tick + // DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce); + if (PhysBody.HasPhysicalBody) + { + PhysScene.PE.ApplyCentralForce(PhysBody, addForce); + } + }); + } + else + { + m_log.WarnFormat("{0}: Got a NaN force applied to a character. LocalID={1}", LogHeader, LocalID); + return; + } + } + + public override void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { + } + public override void SetMomentum(OMV.Vector3 momentum) { + } + + private OMV.Vector3 ComputeAvatarScale(OMV.Vector3 size) + { + OMV.Vector3 newScale = size; + + // Bullet's capsule total height is the "passed height + radius * 2"; + // The base capsule is 1 unit in diameter and 2 units in height (passed radius=0.5, passed height = 1) + // The number we pass in for 'scaling' is the multiplier to get that base + // shape to be the size desired. + // So, when creating the scale for the avatar height, we take the passed height + // (size.Z) and remove the caps. + // An oddity of the Bullet capsule implementation is that it presumes the Y + // dimension is the radius of the capsule. Even though some of the code allows + // for a asymmetrical capsule, other parts of the code presume it is cylindrical. + + // Scale is multiplier of radius with one of "0.5" + + float heightAdjust = BSParam.AvatarHeightMidFudge; + if (BSParam.AvatarHeightLowFudge != 0f || BSParam.AvatarHeightHighFudge != 0f) + { + const float AVATAR_LOW = 1.1f; + const float AVATAR_MID = 1.775f; // 1.87f + const float AVATAR_HI = 2.45f; + // An avatar is between 1.1 and 2.45 meters. Midpoint is 1.775m. + float midHeightOffset = size.Z - AVATAR_MID; + if (midHeightOffset < 0f) + { + // Small avatar. Add the adjustment based on the distance from midheight + heightAdjust += ((-1f * midHeightOffset) / (AVATAR_MID - AVATAR_LOW)) * BSParam.AvatarHeightLowFudge; + } + else + { + // Large avatar. Add the adjustment based on the distance from midheight + heightAdjust += ((midHeightOffset) / (AVATAR_HI - AVATAR_MID)) * BSParam.AvatarHeightHighFudge; + } + } + if (BSParam.AvatarShape == BSShapeCollection.AvatarShapeCapsule) + { + newScale.X = size.X / 2f; + newScale.Y = size.Y / 2f; + // The total scale height is the central cylindar plus the caps on the two ends. + newScale.Z = (size.Z + (Math.Min(size.X, size.Y) * 2) + heightAdjust) / 2f; + } + else + { + newScale.Z = size.Z + heightAdjust; + } + // m_log.DebugFormat("{0} ComputeAvatarScale: size={1},adj={2},scale={3}", LogHeader, size, heightAdjust, newScale); + + // If smaller than the endcaps, just fake like we're almost that small + if (newScale.Z < 0) + newScale.Z = 0.1f; + + DetailLog("{0},BSCharacter.ComputerAvatarScale,size={1},lowF={2},midF={3},hiF={4},adj={5},newScale={6}", + LocalID, size, BSParam.AvatarHeightLowFudge, BSParam.AvatarHeightMidFudge, BSParam.AvatarHeightHighFudge, heightAdjust, newScale); + + return newScale; + } + + // set _avatarVolume and _mass based on capsule size, _density and Scale + private void ComputeAvatarVolumeAndMass() + { + _avatarVolume = (float)( + Math.PI + * Size.X / 2f + * Size.Y / 2f // the area of capsule cylinder + * Size.Z // times height of capsule cylinder + + 1.33333333f + * Math.PI + * Size.X / 2f + * Math.Min(Size.X, Size.Y) / 2 + * Size.Y / 2f // plus the volume of the capsule end caps + ); + _mass = Density * BSParam.DensityScaleFactor * _avatarVolume; + } + + // The physics engine says that properties have updated. Update same and inform + // the world that things have changed. + public override void UpdateProperties(EntityProperties entprop) + { + // Let anyone (like the actors) modify the updated properties before they are pushed into the object and the simulator. + TriggerPreUpdatePropertyAction(ref entprop); + + RawPosition = entprop.Position; + RawOrientation = entprop.Rotation; + + // Smooth velocity. OpenSimulator is VERY sensitive to changes in velocity of the avatar + // and will send agent updates to the clients if velocity changes by more than + // 0.001m/s. Bullet introduces a lot of jitter in the velocity which causes many + // extra updates. + // + // XXX: Contrary to the above comment, setting an update threshold here above 0.4 actually introduces jitter to + // avatar movement rather than removes it. The larger the threshold, the bigger the jitter. + // This is most noticeable in level flight and can be seen with + // the "show updates" option in a viewer. With an update threshold, the RawVelocity cycles between a lower + // bound and an upper bound, where the difference between the two is enough to trigger a large delta v update + // and subsequently trigger an update in ScenePresence.SendTerseUpdateToAllClients(). The cause of this cycle (feedback?) + // has not yet been identified. + // + // If there is a threshold below 0.4 or no threshold check at all (as in ODE), then RawVelocity stays constant and extra + // updates are not triggered in ScenePresence.SendTerseUpdateToAllClients(). +// if (!entprop.Velocity.ApproxEquals(RawVelocity, 0.1f)) + RawVelocity = entprop.Velocity; + + _acceleration = entprop.Acceleration; + _rotationalVelocity = entprop.RotationalVelocity; + + // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. + if (PositionSanityCheck(true)) + { + DetailLog("{0},BSCharacter.UpdateProperties,updatePosForSanity,pos={1}", LocalID, RawPosition); + entprop.Position = RawPosition; + } + + // remember the current and last set values + LastEntityProperties = CurrentEntityProperties; + CurrentEntityProperties = entprop; + + // Tell the linkset about value changes + // Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this); + + // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. + // PhysScene.PostUpdate(this); + + DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", + LocalID, RawPosition, RawOrientation, RawVelocity, _acceleration, _rotationalVelocity); + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraint.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraint.cs new file mode 100755 index 0000000..b47e9a8 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraint.cs @@ -0,0 +1,144 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public abstract class BSConstraint : IDisposable +{ + private static string LogHeader = "[BULLETSIM CONSTRAINT]"; + + protected BulletWorld m_world; + protected BSScene PhysicsScene; + protected BulletBody m_body1; + protected BulletBody m_body2; + protected BulletConstraint m_constraint; + protected bool m_enabled = false; + + public BulletBody Body1 { get { return m_body1; } } + public BulletBody Body2 { get { return m_body2; } } + public BulletConstraint Constraint { get { return m_constraint; } } + public abstract ConstraintType Type { get; } + public bool IsEnabled { get { return m_enabled; } } + + public BSConstraint(BulletWorld world) + { + m_world = world; + PhysicsScene = m_world.physicsScene; + } + + public virtual void Dispose() + { + if (m_enabled) + { + m_enabled = false; + if (m_constraint.HasPhysicalConstraint) + { + bool success = PhysicsScene.PE.DestroyConstraint(m_world, m_constraint); + m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}", + m_body1.ID, + m_body1.ID, m_body1.AddrString, + m_body2.ID, m_body2.AddrString, + success); + m_constraint.Clear(); + } + } + } + + public virtual bool SetLinearLimits(Vector3 low, Vector3 high) + { + bool ret = false; + if (m_enabled) + { + m_world.physicsScene.DetailLog("{0},BSConstraint.SetLinearLimits,taint,low={1},high={2}", m_body1.ID, low, high); + ret = PhysicsScene.PE.SetLinearLimits(m_constraint, low, high); + } + return ret; + } + + public virtual bool SetAngularLimits(Vector3 low, Vector3 high) + { + bool ret = false; + if (m_enabled) + { + m_world.physicsScene.DetailLog("{0},BSConstraint.SetAngularLimits,taint,low={1},high={2}", m_body1.ID, low, high); + ret = PhysicsScene.PE.SetAngularLimits(m_constraint, low, high); + } + return ret; + } + + public virtual bool SetSolverIterations(float cnt) + { + bool ret = false; + if (m_enabled) + { + PhysicsScene.PE.SetConstraintNumSolverIterations(m_constraint, cnt); + ret = true; + } + return ret; + } + + public virtual bool CalculateTransforms() + { + bool ret = false; + if (m_enabled) + { + // Recompute the internal transforms + PhysicsScene.PE.CalculateTransforms(m_constraint); + ret = true; + } + return ret; + } + + // Reset this constraint making sure it has all its internal structures + // recomputed and is enabled and ready to go. + public virtual bool RecomputeConstraintVariables(float mass) + { + bool ret = false; + if (m_enabled) + { + ret = CalculateTransforms(); + if (ret) + { + // Setting an object's mass to zero (making it static like when it's selected) + // automatically disables the constraints. + // If the link is enabled, be sure to set the constraint itself to enabled. + PhysicsScene.PE.SetConstraintEnable(m_constraint, BSParam.NumericBool(true)); + } + else + { + m_world.physicsScene.Logger.ErrorFormat("{0} CalculateTransforms failed. A={1}, B={2}", LogHeader, Body1.ID, Body2.ID); + } + } + return ret; + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraint6Dof.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraint6Dof.cs new file mode 100755 index 0000000..7fcb75c --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraint6Dof.cs @@ -0,0 +1,180 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public class BSConstraint6Dof : BSConstraint +{ + private static string LogHeader = "[BULLETSIM 6DOF CONSTRAINT]"; + + public override ConstraintType Type { get { return ConstraintType.D6_CONSTRAINT_TYPE; } } + + public BSConstraint6Dof(BulletWorld world, BulletBody obj1, BulletBody obj2) :base(world) + { + m_body1 = obj1; + m_body2 = obj2; + m_enabled = false; + } + + // Create a btGeneric6DofConstraint + public BSConstraint6Dof(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1, Quaternion frame1rot, + Vector3 frame2, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) + : base(world) + { + m_body1 = obj1; + m_body2 = obj2; + m_constraint = PhysicsScene.PE.Create6DofConstraint(m_world, m_body1, m_body2, + frame1, frame1rot, + frame2, frame2rot, + useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies); + m_enabled = true; + PhysicsScene.DetailLog("{0},BS6DofConstraint,create,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", + m_body1.ID, world.worldID, + obj1.ID, obj1.AddrString, obj2.ID, obj2.AddrString); + PhysicsScene.DetailLog("{0},BS6DofConstraint,create, f1Loc={1},f1Rot={2},f2Loc={3},f2Rot={4},usefA={5},disCol={6}", + m_body1.ID, frame1, frame1rot, frame2, frame2rot, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies); + } + + // 6 Dof constraint based on a midpoint between the two constrained bodies + public BSConstraint6Dof(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 joinPoint, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) + : base(world) + { + m_body1 = obj1; + m_body2 = obj2; + if (!obj1.HasPhysicalBody || !obj2.HasPhysicalBody) + { + world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", + BSScene.DetailLogZero, world.worldID, + obj1.ID, obj1.AddrString, obj2.ID, obj2.AddrString); + world.physicsScene.Logger.ErrorFormat("{0} Attempt to build 6DOF constraint with missing bodies: wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", + LogHeader, world.worldID, obj1.ID, obj1.AddrString, obj2.ID, obj2.AddrString); + m_enabled = false; + } + else + { + m_constraint = PhysicsScene.PE.Create6DofConstraintToPoint(m_world, m_body1, m_body2, + joinPoint, + useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies); + + PhysicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}", + m_body1.ID, world.worldID, m_constraint.AddrString, + obj1.ID, obj1.AddrString, obj2.ID, obj2.AddrString); + + if (!m_constraint.HasPhysicalConstraint) + { + world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}", + LogHeader, obj1.ID, obj2.ID); + m_enabled = false; + } + else + { + m_enabled = true; + } + } + } + + // A 6 Dof constraint that is fixed in the world and constrained to a on-the-fly created static object + public BSConstraint6Dof(BulletWorld world, BulletBody obj1, Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameB, bool disableCollisionsBetweenLinkedBodies) + : base(world) + { + m_body1 = obj1; + m_body2 = obj1; // Look out for confusion down the road + m_constraint = PhysicsScene.PE.Create6DofConstraintFixed(m_world, m_body1, + frameInBloc, frameInBrot, + useLinearReferenceFrameB, disableCollisionsBetweenLinkedBodies); + m_enabled = true; + PhysicsScene.DetailLog("{0},BS6DofConstraint,createFixed,wID={1},rID={2},rBody={3}", + m_body1.ID, world.worldID, obj1.ID, obj1.AddrString); + PhysicsScene.DetailLog("{0},BS6DofConstraint,createFixed, fBLoc={1},fBRot={2},usefA={3},disCol={4}", + m_body1.ID, frameInBloc, frameInBrot, useLinearReferenceFrameB, disableCollisionsBetweenLinkedBodies); + } + + public bool SetFrames(Vector3 frameA, Quaternion frameArot, Vector3 frameB, Quaternion frameBrot) + { + bool ret = false; + if (m_enabled) + { + PhysicsScene.PE.SetFrames(m_constraint, frameA, frameArot, frameB, frameBrot); + ret = true; + } + return ret; + } + + public bool SetCFMAndERP(float cfm, float erp) + { + bool ret = false; + if (m_enabled) + { + PhysicsScene.PE.SetConstraintParam(m_constraint, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL); + PhysicsScene.PE.SetConstraintParam(m_constraint, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL); + PhysicsScene.PE.SetConstraintParam(m_constraint, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL); + ret = true; + } + return ret; + } + + public bool UseFrameOffset(bool useOffset) + { + bool ret = false; + float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse; + if (m_enabled) + ret = PhysicsScene.PE.UseFrameOffset(m_constraint, onOff); + return ret; + } + + public bool TranslationalLimitMotor(bool enable, float targetVelocity, float maxMotorForce) + { + bool ret = false; + float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse; + if (m_enabled) + { + ret = PhysicsScene.PE.TranslationalLimitMotor(m_constraint, onOff, targetVelocity, maxMotorForce); + m_world.physicsScene.DetailLog("{0},BS6DOFConstraint,TransLimitMotor,enable={1},vel={2},maxForce={3}", + BSScene.DetailLogZero, enable, targetVelocity, maxMotorForce); + } + return ret; + } + + public bool SetBreakingImpulseThreshold(float threshold) + { + bool ret = false; + if (m_enabled) + ret = PhysicsScene.PE.SetBreakingImpulseThreshold(m_constraint, threshold); + return ret; + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintCollection.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintCollection.cs new file mode 100755 index 0000000..5c8d94e --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintCollection.cs @@ -0,0 +1,181 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public sealed class BSConstraintCollection : IDisposable +{ + // private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + // private static readonly string LogHeader = "[CONSTRAINT COLLECTION]"; + + delegate bool ConstraintAction(BSConstraint constrain); + + private List m_constraints; + private BulletWorld m_world; + + public BSConstraintCollection(BulletWorld world) + { + m_world = world; + m_constraints = new List(); + } + + public void Dispose() + { + this.Clear(); + } + + public void Clear() + { + lock (m_constraints) + { + foreach (BSConstraint cons in m_constraints) + { + cons.Dispose(); + } + m_constraints.Clear(); + } + } + + public bool AddConstraint(BSConstraint cons) + { + lock (m_constraints) + { + // There is only one constraint between any bodies. Remove any old just to make sure. + RemoveAndDestroyConstraint(cons.Body1, cons.Body2); + + m_constraints.Add(cons); + } + + return true; + } + + // Get the constraint between two bodies. There can be only one. + // Return 'true' if a constraint was found. + public bool TryGetConstraint(BulletBody body1, BulletBody body2, out BSConstraint returnConstraint) + { + bool found = false; + BSConstraint foundConstraint = null; + + uint lookingID1 = body1.ID; + uint lookingID2 = body2.ID; + lock (m_constraints) + { + foreach (BSConstraint constrain in m_constraints) + { + if ((constrain.Body1.ID == lookingID1 && constrain.Body2.ID == lookingID2) + || (constrain.Body1.ID == lookingID2 && constrain.Body2.ID == lookingID1)) + { + foundConstraint = constrain; + found = true; + break; + } + } + } + returnConstraint = foundConstraint; + return found; + } + + // Remove any constraint between the passed bodies. + // Presumed there is only one such constraint possible. + // Return 'true' if a constraint was found and destroyed. + public bool RemoveAndDestroyConstraint(BulletBody body1, BulletBody body2) + { + bool ret = false; + lock (m_constraints) + { + BSConstraint constrain; + if (this.TryGetConstraint(body1, body2, out constrain)) + { + // remove the constraint from our collection + ret = RemoveAndDestroyConstraint(constrain); + } + } + + return ret; + } + + // The constraint MUST exist in the collection + // Could be called if the constraint was previously removed. + // Return 'true' if the constraint was actually removed and disposed. + public bool RemoveAndDestroyConstraint(BSConstraint constrain) + { + bool removed = false; + lock (m_constraints) + { + // remove the constraint from our collection + removed = m_constraints.Remove(constrain); + } + // Dispose() is safe to call multiple times + constrain.Dispose(); + return removed; + } + + // Remove all constraints that reference the passed body. + // Return 'true' if any constraints were destroyed. + public bool RemoveAndDestroyConstraint(BulletBody body1) + { + List toRemove = new List(); + uint lookingID = body1.ID; + lock (m_constraints) + { + foreach (BSConstraint constrain in m_constraints) + { + if (constrain.Body1.ID == lookingID || constrain.Body2.ID == lookingID) + { + toRemove.Add(constrain); + } + } + foreach (BSConstraint constrain in toRemove) + { + m_constraints.Remove(constrain); + constrain.Dispose(); + } + } + return (toRemove.Count > 0); + } + + public bool RecalculateAllConstraints() + { + bool ret = false; + lock (m_constraints) + { + foreach (BSConstraint constrain in m_constraints) + { + constrain.CalculateTransforms(); + ret = true; + } + } + return ret; + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintConeTwist.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintConeTwist.cs new file mode 100755 index 0000000..7a76a9a --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintConeTwist.cs @@ -0,0 +1,54 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public sealed class BSConstraintConeTwist : BSConstraint +{ + public override ConstraintType Type { get { return ConstraintType.CONETWIST_CONSTRAINT_TYPE; } } + + public BSConstraintConeTwist(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frameInAloc, Quaternion frameInArot, + Vector3 frameInBloc, Quaternion frameInBrot, + bool disableCollisionsBetweenLinkedBodies) + : base(world) + { + m_body1 = obj1; + m_body2 = obj2; + m_constraint = PhysicsScene.PE.CreateConeTwistConstraint(world, obj1, obj2, + frameInAloc, frameInArot, frameInBloc, frameInBrot, + disableCollisionsBetweenLinkedBodies); + m_enabled = true; + } +} + +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintHinge.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintHinge.cs new file mode 100755 index 0000000..ed89f63 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintHinge.cs @@ -0,0 +1,55 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public sealed class BSConstraintHinge : BSConstraint +{ + public override ConstraintType Type { get { return ConstraintType.HINGE_CONSTRAINT_TYPE; } } + + public BSConstraintHinge(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 pivotInA, Vector3 pivotInB, + Vector3 axisInA, Vector3 axisInB, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) + : base(world) + { + m_body1 = obj1; + m_body2 = obj2; + m_constraint = PhysicsScene.PE.CreateHingeConstraint(world, obj1, obj2, + pivotInA, pivotInB, axisInA, axisInB, + useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies); + m_enabled = true; + } + +} + +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSlider.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSlider.cs new file mode 100755 index 0000000..37cfa07 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSlider.cs @@ -0,0 +1,55 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public sealed class BSConstraintSlider : BSConstraint +{ + public override ConstraintType Type { get { return ConstraintType.SLIDER_CONSTRAINT_TYPE; } } + + public BSConstraintSlider(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frameInAloc, Quaternion frameInArot, + Vector3 frameInBloc, Quaternion frameInBrot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) + : base(world) + { + m_body1 = obj1; + m_body2 = obj2; + m_constraint = PhysicsScene.PE.CreateSliderConstraint(world, obj1, obj2, + frameInAloc, frameInArot, frameInBloc, frameInBrot, + useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies); + m_enabled = true; + } + +} + +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSpring.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSpring.cs new file mode 100755 index 0000000..8e7ddff --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSpring.cs @@ -0,0 +1,103 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public sealed class BSConstraintSpring : BSConstraint6Dof +{ + public override ConstraintType Type { get { return ConstraintType.D6_SPRING_CONSTRAINT_TYPE; } } + + public BSConstraintSpring(BulletWorld world, BulletBody obj1, BulletBody obj2, + Vector3 frame1Loc, Quaternion frame1Rot, + Vector3 frame2Loc, Quaternion frame2Rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies) + :base(world, obj1, obj2) + { + m_constraint = PhysicsScene.PE.Create6DofSpringConstraint(world, obj1, obj2, + frame1Loc, frame1Rot, frame2Loc, frame2Rot, + useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies); + m_enabled = true; + + PhysicsScene.DetailLog("{0},BSConstraintSpring,create,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", + obj1.ID, world.worldID, obj1.ID, obj1.AddrString, obj2.ID, obj2.AddrString); + PhysicsScene.DetailLog("{0},BSConstraintSpring,create, f1Loc={1},f1Rot={2},f2Loc={3},f2Rot={4},usefA={5},disCol={6}", + m_body1.ID, frame1Loc, frame1Rot, frame2Loc, frame2Rot, useLinearReferenceFrameA, disableCollisionsBetweenLinkedBodies); + } + + public bool SetAxisEnable(int pIndex, bool pAxisEnable) + { + PhysicsScene.DetailLog("{0},BSConstraintSpring.SetEnable,obj1ID={1},obj2ID={2},indx={3},enable={4}", + m_body1.ID, m_body1.ID, m_body2.ID, pIndex, pAxisEnable); + PhysicsScene.PE.SpringEnable(m_constraint, pIndex, BSParam.NumericBool(pAxisEnable)); + return true; + } + + public bool SetStiffness(int pIndex, float pStiffness) + { + PhysicsScene.DetailLog("{0},BSConstraintSpring.SetStiffness,obj1ID={1},obj2ID={2},indx={3},stiff={4}", + m_body1.ID, m_body1.ID, m_body2.ID, pIndex, pStiffness); + PhysicsScene.PE.SpringSetStiffness(m_constraint, pIndex, pStiffness); + return true; + } + + public bool SetDamping(int pIndex, float pDamping) + { + PhysicsScene.DetailLog("{0},BSConstraintSpring.SetDamping,obj1ID={1},obj2ID={2},indx={3},damp={4}", + m_body1.ID, m_body1.ID, m_body2.ID, pIndex, pDamping); + PhysicsScene.PE.SpringSetDamping(m_constraint, pIndex, pDamping); + return true; + } + + public bool SetEquilibriumPoint(int pIndex, float pEqPoint) + { + PhysicsScene.DetailLog("{0},BSConstraintSpring.SetEquilibriumPoint,obj1ID={1},obj2ID={2},indx={3},eqPoint={4}", + m_body1.ID, m_body1.ID, m_body2.ID, pIndex, pEqPoint); + PhysicsScene.PE.SpringSetEquilibriumPoint(m_constraint, pIndex, pEqPoint); + return true; + } + + public bool SetEquilibriumPoint(Vector3 linearEq, Vector3 angularEq) + { + PhysicsScene.DetailLog("{0},BSConstraintSpring.SetEquilibriumPoint,obj1ID={1},obj2ID={2},linearEq={3},angularEq={4}", + m_body1.ID, m_body1.ID, m_body2.ID, linearEq, angularEq); + PhysicsScene.PE.SpringSetEquilibriumPoint(m_constraint, 0, linearEq.X); + PhysicsScene.PE.SpringSetEquilibriumPoint(m_constraint, 1, linearEq.Y); + PhysicsScene.PE.SpringSetEquilibriumPoint(m_constraint, 2, linearEq.Z); + PhysicsScene.PE.SpringSetEquilibriumPoint(m_constraint, 3, angularEq.X); + PhysicsScene.PE.SpringSetEquilibriumPoint(m_constraint, 4, angularEq.Y); + PhysicsScene.PE.SpringSetEquilibriumPoint(m_constraint, 5, angularEq.Z); + return true; + } + +} + +} \ No newline at end of file diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs b/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs new file mode 100644 index 0000000..c6d6331 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs @@ -0,0 +1,1800 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The quotations from http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial + * are Copyright (c) 2009 Linden Research, Inc and are used under their license + * of Creative Commons Attribution-Share Alike 3.0 + * (http://creativecommons.org/licenses/by-sa/3.0/). + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + public sealed class BSDynamics : BSActor + { +#pragma warning disable 414 + private static string LogHeader = "[BULLETSIM VEHICLE]"; +#pragma warning restore 414 + + // the prim this dynamic controller belongs to + private BSPrimLinkable ControllingPrim { get; set; } + + private bool m_haveRegisteredForSceneEvents; + + // mass of the vehicle fetched each time we're calles + private float m_vehicleMass; + + // Vehicle properties + public Vehicle Type { get; set; } + + // private Quaternion m_referenceFrame = Quaternion.Identity; // Axis modifier + private VehicleFlag m_flags = (VehicleFlag) 0; // Boolean settings: + // HOVER_TERRAIN_ONLY + // HOVER_GLOBAL_HEIGHT + // NO_DEFLECTION_UP + // HOVER_WATER_ONLY + // HOVER_UP_ONLY + // LIMIT_MOTOR_UP + // LIMIT_ROLL_ONLY + private Vector3 m_BlockingEndPoint = Vector3.Zero; + private Quaternion m_RollreferenceFrame = Quaternion.Identity; + private Quaternion m_referenceFrame = Quaternion.Identity; + + // Linear properties + private BSVMotor m_linearMotor = new BSVMotor("LinearMotor"); + private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time + private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center + private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL + private Vector3 m_linearFrictionTimescale = Vector3.Zero; + private float m_linearMotorDecayTimescale = 1; + private float m_linearMotorTimescale = 1; + private Vector3 m_lastLinearVelocityVector = Vector3.Zero; + private Vector3 m_lastPositionVector = Vector3.Zero; + // private bool m_LinearMotorSetLastFrame = false; + // private Vector3 m_linearMotorOffset = Vector3.Zero; + + //Angular properties + private BSVMotor m_angularMotor = new BSVMotor("AngularMotor"); + private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor + // private int m_angularMotorApply = 0; // application frame counter + private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity + private float m_angularMotorTimescale = 1; // motor angular velocity ramp up rate + private float m_angularMotorDecayTimescale = 1; // motor angular velocity decay rate + private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate + private Vector3 m_lastAngularVelocity = Vector3.Zero; + private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body + + //Deflection properties + private BSVMotor m_angularDeflectionMotor = new BSVMotor("AngularDeflection"); + private float m_angularDeflectionEfficiency = 0; + private float m_angularDeflectionTimescale = 0; + private float m_linearDeflectionEfficiency = 0; + private float m_linearDeflectionTimescale = 0; + + //Banking properties + private float m_bankingEfficiency = 0; + private float m_bankingMix = 1; + private float m_bankingTimescale = 0; + + //Hover and Buoyancy properties + private BSVMotor m_hoverMotor = new BSVMotor("Hover"); + private float m_VhoverHeight = 0f; + private float m_VhoverEfficiency = 0f; + private float m_VhoverTimescale = 0f; + private float m_VhoverTargetHeight = -1.0f; // if <0 then no hover, else its the current target height + // Modifies gravity. Slider between -1 (double-gravity) and 1 (full anti-gravity) + private float m_VehicleBuoyancy = 0f; + private Vector3 m_VehicleGravity = Vector3.Zero; // Gravity computed when buoyancy set + + //Attractor properties + private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction"); + private float m_verticalAttractionEfficiency = 1.0f; // damped + private float m_verticalAttractionCutoff = 500f; // per the documentation + // Timescale > cutoff means no vert attractor. + private float m_verticalAttractionTimescale = 510f; + + // Just some recomputed constants: +#pragma warning disable 414 + static readonly float TwoPI = ((float)Math.PI) * 2f; + static readonly float FourPI = ((float)Math.PI) * 4f; + static readonly float PIOverFour = ((float)Math.PI) / 4f; + static readonly float PIOverTwo = ((float)Math.PI) / 2f; +#pragma warning restore 414 + + public BSDynamics(BSScene myScene, BSPrim myPrim, string actorName) + : base(myScene, myPrim, actorName) + { + Type = Vehicle.TYPE_NONE; + m_haveRegisteredForSceneEvents = false; + + ControllingPrim = myPrim as BSPrimLinkable; + if (ControllingPrim == null) + { + // THIS CANNOT HAPPEN!! + } + VDetailLog("{0},Creation", ControllingPrim.LocalID); + } + + // Return 'true' if this vehicle is doing vehicle things + public bool IsActive + { + get { return (Type != Vehicle.TYPE_NONE && ControllingPrim.IsPhysicallyActive); } + } + + // Return 'true' if this a vehicle that should be sitting on the ground + public bool IsGroundVehicle + { + get { return (Type == Vehicle.TYPE_CAR || Type == Vehicle.TYPE_SLED); } + } + + #region Vehicle parameter setting + public void ProcessFloatVehicleParam(Vehicle pParam, float pValue) + { + VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", ControllingPrim.LocalID, pParam, pValue); + float clampTemp; + + switch (pParam) + { + case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY: + m_angularDeflectionEfficiency = ClampInRange(0f, pValue, 1f); + break; + case Vehicle.ANGULAR_DEFLECTION_TIMESCALE: + m_angularDeflectionTimescale = ClampInRange(0.25f, pValue, 120); + break; + case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: + m_angularMotorDecayTimescale = ClampInRange(0.25f, pValue, 120); + m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale; + break; + case Vehicle.ANGULAR_MOTOR_TIMESCALE: + m_angularMotorTimescale = ClampInRange(0.25f, pValue, 120); + m_angularMotor.TimeScale = m_angularMotorTimescale; + break; + case Vehicle.BANKING_EFFICIENCY: + m_bankingEfficiency = ClampInRange(-1f, pValue, 1f); + break; + case Vehicle.BANKING_MIX: + m_bankingMix = ClampInRange(0.01f, pValue, 1); + break; + case Vehicle.BANKING_TIMESCALE: + m_bankingTimescale = ClampInRange(0.25f, pValue, 120); + break; + case Vehicle.BUOYANCY: + m_VehicleBuoyancy = ClampInRange(-1f, pValue, 1f); + m_VehicleGravity = ControllingPrim.ComputeGravity(m_VehicleBuoyancy); + break; + case Vehicle.HOVER_EFFICIENCY: + m_VhoverEfficiency = ClampInRange(0.01f, pValue, 1f); + break; + case Vehicle.HOVER_HEIGHT: + m_VhoverHeight = ClampInRange(0f, pValue, 1000000f); + break; + case Vehicle.HOVER_TIMESCALE: + m_VhoverTimescale = ClampInRange(0.01f, pValue, 120); + break; + case Vehicle.LINEAR_DEFLECTION_EFFICIENCY: + m_linearDeflectionEfficiency = ClampInRange(0f, pValue, 1f); + break; + case Vehicle.LINEAR_DEFLECTION_TIMESCALE: + m_linearDeflectionTimescale = ClampInRange(0.01f, pValue, 120); + break; + case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: + m_linearMotorDecayTimescale = ClampInRange(0.01f, pValue, 120); + m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale; + break; + case Vehicle.LINEAR_MOTOR_TIMESCALE: + m_linearMotorTimescale = ClampInRange(0.01f, pValue, 120); + m_linearMotor.TimeScale = m_linearMotorTimescale; + break; + case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY: + m_verticalAttractionEfficiency = ClampInRange(0.1f, pValue, 1f); + m_verticalAttractionMotor.Efficiency = m_verticalAttractionEfficiency; + break; + case Vehicle.VERTICAL_ATTRACTION_TIMESCALE: + m_verticalAttractionTimescale = ClampInRange(0.01f, pValue, 120); + m_verticalAttractionMotor.TimeScale = m_verticalAttractionTimescale; + break; + + // These are vector properties but the engine lets you use a single float value to + // set all of the components to the same value + case Vehicle.ANGULAR_FRICTION_TIMESCALE: + clampTemp = ClampInRange(0.01f, pValue, 120); + m_angularFrictionTimescale = new Vector3(clampTemp, clampTemp, clampTemp); + break; + case Vehicle.ANGULAR_MOTOR_DIRECTION: + clampTemp = ClampInRange(-TwoPI, pValue, TwoPI); + m_angularMotorDirection = new Vector3(clampTemp, clampTemp, clampTemp); + m_angularMotor.Zero(); + m_angularMotor.SetTarget(m_angularMotorDirection); + break; + case Vehicle.LINEAR_FRICTION_TIMESCALE: + clampTemp = ClampInRange(0.01f, pValue, 120); + m_linearFrictionTimescale = new Vector3(clampTemp, clampTemp, clampTemp); + break; + case Vehicle.LINEAR_MOTOR_DIRECTION: + clampTemp = ClampInRange(-BSParam.MaxLinearVelocity, pValue, BSParam.MaxLinearVelocity); + m_linearMotorDirection = new Vector3(clampTemp, clampTemp, clampTemp); + m_linearMotorDirectionLASTSET = new Vector3(clampTemp, clampTemp, clampTemp); + m_linearMotor.SetTarget(m_linearMotorDirection); + break; + case Vehicle.LINEAR_MOTOR_OFFSET: + clampTemp = ClampInRange(-1000, pValue, 1000); + m_linearMotorOffset = new Vector3(clampTemp, clampTemp, clampTemp); + break; + + } + }//end ProcessFloatVehicleParam + + internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue) + { + VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", ControllingPrim.LocalID, pParam, pValue); + switch (pParam) + { + case Vehicle.ANGULAR_FRICTION_TIMESCALE: + pValue.X = ClampInRange(0.25f, pValue.X, 120); + pValue.Y = ClampInRange(0.25f, pValue.Y, 120); + pValue.Z = ClampInRange(0.25f, pValue.Z, 120); + m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); + break; + case Vehicle.ANGULAR_MOTOR_DIRECTION: + // Limit requested angular speed to 2 rps= 4 pi rads/sec + pValue.X = ClampInRange(-FourPI, pValue.X, FourPI); + pValue.Y = ClampInRange(-FourPI, pValue.Y, FourPI); + pValue.Z = ClampInRange(-FourPI, pValue.Z, FourPI); + m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); + m_angularMotor.Zero(); + m_angularMotor.SetTarget(m_angularMotorDirection); + break; + case Vehicle.LINEAR_FRICTION_TIMESCALE: + pValue.X = ClampInRange(0.25f, pValue.X, 120); + pValue.Y = ClampInRange(0.25f, pValue.Y, 120); + pValue.Z = ClampInRange(0.25f, pValue.Z, 120); + m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); + break; + case Vehicle.LINEAR_MOTOR_DIRECTION: + pValue.X = ClampInRange(-BSParam.MaxLinearVelocity, pValue.X, BSParam.MaxLinearVelocity); + pValue.Y = ClampInRange(-BSParam.MaxLinearVelocity, pValue.Y, BSParam.MaxLinearVelocity); + pValue.Z = ClampInRange(-BSParam.MaxLinearVelocity, pValue.Z, BSParam.MaxLinearVelocity); + m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); + m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z); + m_linearMotor.SetTarget(m_linearMotorDirection); + break; + case Vehicle.LINEAR_MOTOR_OFFSET: + // Not sure the correct range to limit this variable + pValue.X = ClampInRange(-1000, pValue.X, 1000); + pValue.Y = ClampInRange(-1000, pValue.Y, 1000); + pValue.Z = ClampInRange(-1000, pValue.Z, 1000); + m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z); + break; + case Vehicle.BLOCK_EXIT: + // Not sure the correct range to limit this variable + pValue.X = ClampInRange(-10000, pValue.X, 10000); + pValue.Y = ClampInRange(-10000, pValue.Y, 10000); + pValue.Z = ClampInRange(-10000, pValue.Z, 10000); + m_BlockingEndPoint = new Vector3(pValue.X, pValue.Y, pValue.Z); + break; + } + }//end ProcessVectorVehicleParam + + internal void ProcessRotationVehicleParam(Vehicle pParam, Quaternion pValue) + { + VDetailLog("{0},ProcessRotationalVehicleParam,param={1},val={2}", ControllingPrim.LocalID, pParam, pValue); + switch (pParam) + { + case Vehicle.REFERENCE_FRAME: + m_referenceFrame = pValue; + break; + case Vehicle.ROLL_FRAME: + m_RollreferenceFrame = pValue; + break; + } + }//end ProcessRotationVehicleParam + + internal void ProcessVehicleFlags(int pParam, bool remove) + { + VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", ControllingPrim.LocalID, pParam, remove); + VehicleFlag parm = (VehicleFlag)pParam; + if (pParam == -1) + m_flags = (VehicleFlag)0; + else + { + if (remove) + m_flags &= ~parm; + else + m_flags |= parm; + } + } + + public void ProcessTypeChange(Vehicle pType) + { + VDetailLog("{0},ProcessTypeChange,type={1}", ControllingPrim.LocalID, pType); + // Set Defaults For Type + Type = pType; + switch (pType) + { + case Vehicle.TYPE_NONE: + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 0; + m_linearMotorDecayTimescale = 0; + m_linearFrictionTimescale = new Vector3(0, 0, 0); + + m_angularMotorDirection = Vector3.Zero; + m_angularMotorDecayTimescale = 0; + m_angularMotorTimescale = 0; + m_angularFrictionTimescale = new Vector3(0, 0, 0); + + m_VhoverHeight = 0; + m_VhoverEfficiency = 0; + m_VhoverTimescale = 0; + m_VehicleBuoyancy = 0; + + m_linearDeflectionEfficiency = 1; + m_linearDeflectionTimescale = 1; + + m_angularDeflectionEfficiency = 0; + m_angularDeflectionTimescale = 1000; + + m_verticalAttractionEfficiency = 0; + m_verticalAttractionTimescale = 0; + + m_bankingEfficiency = 0; + m_bankingTimescale = 1000; + m_bankingMix = 1; + + m_referenceFrame = Quaternion.Identity; + m_flags = (VehicleFlag)0; + + break; + + case Vehicle.TYPE_SLED: + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 1000; + m_linearMotorDecayTimescale = 120; + m_linearFrictionTimescale = new Vector3(30, 1, 1000); + + m_angularMotorDirection = Vector3.Zero; + m_angularMotorTimescale = 1000; + m_angularMotorDecayTimescale = 120; + m_angularFrictionTimescale = new Vector3(1000, 1000, 1000); + + m_VhoverHeight = 0; + m_VhoverEfficiency = 10; // TODO: this looks wrong!! + m_VhoverTimescale = 10; + m_VehicleBuoyancy = 0; + + m_linearDeflectionEfficiency = 1; + m_linearDeflectionTimescale = 1; + + m_angularDeflectionEfficiency = 1; + m_angularDeflectionTimescale = 1000; + + m_verticalAttractionEfficiency = 0; + m_verticalAttractionTimescale = 0; + + m_bankingEfficiency = 0; + m_bankingTimescale = 10; + m_bankingMix = 1; + + m_referenceFrame = Quaternion.Identity; + m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY + | VehicleFlag.HOVER_TERRAIN_ONLY + | VehicleFlag.HOVER_GLOBAL_HEIGHT + | VehicleFlag.HOVER_UP_ONLY); + m_flags |= (VehicleFlag.NO_DEFLECTION_UP + | VehicleFlag.LIMIT_ROLL_ONLY + | VehicleFlag.LIMIT_MOTOR_UP); + + break; + case Vehicle.TYPE_CAR: + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 1; + m_linearMotorDecayTimescale = 60; + m_linearFrictionTimescale = new Vector3(100, 2, 1000); + + m_angularMotorDirection = Vector3.Zero; + m_angularMotorTimescale = 1; + m_angularMotorDecayTimescale = 0.8f; + m_angularFrictionTimescale = new Vector3(1000, 1000, 1000); + + m_VhoverHeight = 0; + m_VhoverEfficiency = 0; + m_VhoverTimescale = 1000; + m_VehicleBuoyancy = 0; + + m_linearDeflectionEfficiency = 1; + m_linearDeflectionTimescale = 2; + + m_angularDeflectionEfficiency = 0; + m_angularDeflectionTimescale = 10; + + m_verticalAttractionEfficiency = 1f; + m_verticalAttractionTimescale = 10f; + + m_bankingEfficiency = -0.2f; + m_bankingMix = 1; + m_bankingTimescale = 1; + + m_referenceFrame = Quaternion.Identity; + m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY + | VehicleFlag.HOVER_TERRAIN_ONLY + | VehicleFlag.HOVER_GLOBAL_HEIGHT); + m_flags |= (VehicleFlag.NO_DEFLECTION_UP + | VehicleFlag.LIMIT_ROLL_ONLY + | VehicleFlag.LIMIT_MOTOR_UP + | VehicleFlag.HOVER_UP_ONLY); + break; + case Vehicle.TYPE_BOAT: + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 5; + m_linearMotorDecayTimescale = 60; + m_linearFrictionTimescale = new Vector3(10, 3, 2); + + m_angularMotorDirection = Vector3.Zero; + m_angularMotorTimescale = 4; + m_angularMotorDecayTimescale = 4; + m_angularFrictionTimescale = new Vector3(10,10,10); + + m_VhoverHeight = 0; + m_VhoverEfficiency = 0.5f; + m_VhoverTimescale = 2; + m_VehicleBuoyancy = 1; + + m_linearDeflectionEfficiency = 0.5f; + m_linearDeflectionTimescale = 3; + + m_angularDeflectionEfficiency = 0.5f; + m_angularDeflectionTimescale = 5; + + m_verticalAttractionEfficiency = 0.5f; + m_verticalAttractionTimescale = 5f; + + m_bankingEfficiency = -0.3f; + m_bankingMix = 0.8f; + m_bankingTimescale = 1; + + m_referenceFrame = Quaternion.Identity; + m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY + | VehicleFlag.HOVER_GLOBAL_HEIGHT + | VehicleFlag.LIMIT_ROLL_ONLY + | VehicleFlag.HOVER_UP_ONLY); + m_flags |= (VehicleFlag.NO_DEFLECTION_UP + | VehicleFlag.LIMIT_MOTOR_UP + | VehicleFlag.HOVER_WATER_ONLY); + break; + case Vehicle.TYPE_AIRPLANE: + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 2; + m_linearMotorDecayTimescale = 60; + m_linearFrictionTimescale = new Vector3(200, 10, 5); + + m_angularMotorDirection = Vector3.Zero; + m_angularMotorTimescale = 4; + m_angularMotorDecayTimescale = 4; + m_angularFrictionTimescale = new Vector3(20, 20, 20); + + m_VhoverHeight = 0; + m_VhoverEfficiency = 0.5f; + m_VhoverTimescale = 1000; + m_VehicleBuoyancy = 0; + + m_linearDeflectionEfficiency = 0.5f; + m_linearDeflectionTimescale = 3; + + m_angularDeflectionEfficiency = 1; + m_angularDeflectionTimescale = 2; + + m_verticalAttractionEfficiency = 0.9f; + m_verticalAttractionTimescale = 2f; + + m_bankingEfficiency = 1; + m_bankingMix = 0.7f; + m_bankingTimescale = 2; + + m_referenceFrame = Quaternion.Identity; + m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY + | VehicleFlag.HOVER_TERRAIN_ONLY + | VehicleFlag.HOVER_GLOBAL_HEIGHT + | VehicleFlag.HOVER_UP_ONLY + | VehicleFlag.NO_DEFLECTION_UP + | VehicleFlag.LIMIT_MOTOR_UP); + m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY); + break; + case Vehicle.TYPE_BALLOON: + m_linearMotorDirection = Vector3.Zero; + m_linearMotorTimescale = 5; + m_linearFrictionTimescale = new Vector3(5, 5, 5); + m_linearMotorDecayTimescale = 60; + + m_angularMotorDirection = Vector3.Zero; + m_angularMotorTimescale = 6; + m_angularFrictionTimescale = new Vector3(10, 10, 10); + m_angularMotorDecayTimescale = 10; + + m_VhoverHeight = 5; + m_VhoverEfficiency = 0.8f; + m_VhoverTimescale = 10; + m_VehicleBuoyancy = 1; + + m_linearDeflectionEfficiency = 0; + m_linearDeflectionTimescale = 5; + + m_angularDeflectionEfficiency = 0; + m_angularDeflectionTimescale = 5; + + m_verticalAttractionEfficiency = 1f; + m_verticalAttractionTimescale = 100f; + + m_bankingEfficiency = 0; + m_bankingMix = 0.7f; + m_bankingTimescale = 5; + + m_referenceFrame = Quaternion.Identity; + + m_referenceFrame = Quaternion.Identity; + m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY + | VehicleFlag.HOVER_TERRAIN_ONLY + | VehicleFlag.HOVER_UP_ONLY + | VehicleFlag.NO_DEFLECTION_UP + | VehicleFlag.LIMIT_MOTOR_UP); + m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY + | VehicleFlag.HOVER_GLOBAL_HEIGHT); + break; + } + + m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale, m_linearMotorDecayTimescale, 1f); + // m_linearMotor.PhysicsScene = m_physicsScene; // DEBUG DEBUG DEBUG (enables detail logging) + + m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale, m_angularMotorDecayTimescale, 1f); + // m_angularMotor.PhysicsScene = m_physicsScene; // DEBUG DEBUG DEBUG (enables detail logging) + + /* Not implemented + m_verticalAttractionMotor = new BSVMotor("VerticalAttraction", m_verticalAttractionTimescale, + BSMotor.Infinite, BSMotor.InfiniteVector, + m_verticalAttractionEfficiency); + // Z goes away and we keep X and Y + m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging) + */ + + if (this.Type == Vehicle.TYPE_NONE) + { + UnregisterForSceneEvents(); + } + else + { + RegisterForSceneEvents(); + } + + // Update any physical parameters based on this type. + Refresh(); + } + #endregion // Vehicle parameter setting + + // BSActor.Refresh() + public override void Refresh() + { + // If asking for a refresh, reset the physical parameters before the next simulation step. + // Called whether active or not since the active state may be updated before the next step. + m_physicsScene.PostTaintObject("BSDynamics.Refresh", ControllingPrim.LocalID, delegate() + { + SetPhysicalParameters(); + }); + } + + // Some of the properties of this prim may have changed. + // Do any updating needed for a vehicle + private void SetPhysicalParameters() + { + if (IsActive) + { + // Remember the mass so we don't have to fetch it every step + m_vehicleMass = ControllingPrim.TotalMass; + + // Friction affects are handled by this vehicle code + // m_physicsScene.PE.SetFriction(ControllingPrim.PhysBody, BSParam.VehicleFriction); + // m_physicsScene.PE.SetRestitution(ControllingPrim.PhysBody, BSParam.VehicleRestitution); + ControllingPrim.Linkset.SetPhysicalFriction(BSParam.VehicleFriction); + ControllingPrim.Linkset.SetPhysicalRestitution(BSParam.VehicleRestitution); + + // Moderate angular movement introduced by Bullet. + // TODO: possibly set AngularFactor and LinearFactor for the type of vehicle. + // Maybe compute linear and angular factor and damping from params. + m_physicsScene.PE.SetAngularDamping(ControllingPrim.PhysBody, BSParam.VehicleAngularDamping); + m_physicsScene.PE.SetLinearFactor(ControllingPrim.PhysBody, BSParam.VehicleLinearFactor); + m_physicsScene.PE.SetAngularFactorV(ControllingPrim.PhysBody, BSParam.VehicleAngularFactor); + + // Vehicles report collision events so we know when it's on the ground + // m_physicsScene.PE.AddToCollisionFlags(ControllingPrim.PhysBody, CollisionFlags.BS_VEHICLE_COLLISIONS); + ControllingPrim.Linkset.AddToPhysicalCollisionFlags(CollisionFlags.BS_VEHICLE_COLLISIONS); + + // Vector3 inertia = m_physicsScene.PE.CalculateLocalInertia(ControllingPrim.PhysShape.physShapeInfo, m_vehicleMass); + // ControllingPrim.Inertia = inertia * BSParam.VehicleInertiaFactor; + // m_physicsScene.PE.SetMassProps(ControllingPrim.PhysBody, m_vehicleMass, ControllingPrim.Inertia); + // m_physicsScene.PE.UpdateInertiaTensor(ControllingPrim.PhysBody); + ControllingPrim.Linkset.ComputeAndSetLocalInertia(BSParam.VehicleInertiaFactor, m_vehicleMass); + + // Set the gravity for the vehicle depending on the buoyancy + // TODO: what should be done if prim and vehicle buoyancy differ? + m_VehicleGravity = ControllingPrim.ComputeGravity(m_VehicleBuoyancy); + // The actual vehicle gravity is set to zero in Bullet so we can do all the application of same. + // m_physicsScene.PE.SetGravity(ControllingPrim.PhysBody, Vector3.Zero); + ControllingPrim.Linkset.SetPhysicalGravity(Vector3.Zero); + + VDetailLog("{0},BSDynamics.SetPhysicalParameters,mass={1},inert={2},vehGrav={3},aDamp={4},frict={5},rest={6},lFact={7},aFact={8}", + ControllingPrim.LocalID, m_vehicleMass, ControllingPrim.Inertia, m_VehicleGravity, + BSParam.VehicleAngularDamping, BSParam.VehicleFriction, BSParam.VehicleRestitution, + BSParam.VehicleLinearFactor, BSParam.VehicleAngularFactor + ); + } + else + { + if (ControllingPrim.PhysBody.HasPhysicalBody) + m_physicsScene.PE.RemoveFromCollisionFlags(ControllingPrim.PhysBody, CollisionFlags.BS_VEHICLE_COLLISIONS); + // ControllingPrim.Linkset.RemoveFromPhysicalCollisionFlags(CollisionFlags.BS_VEHICLE_COLLISIONS); + } + } + + // BSActor.RemoveBodyDependencies + public override void RemoveDependencies() + { + Refresh(); + } + + // BSActor.Release() + public override void Dispose() + { + VDetailLog("{0},Dispose", ControllingPrim.LocalID); + UnregisterForSceneEvents(); + Type = Vehicle.TYPE_NONE; + Enabled = false; + return; + } + + private void RegisterForSceneEvents() + { + if (!m_haveRegisteredForSceneEvents) + { + m_physicsScene.BeforeStep += this.Step; + m_physicsScene.AfterStep += this.PostStep; + ControllingPrim.OnPreUpdateProperty += this.PreUpdateProperty; + m_haveRegisteredForSceneEvents = true; + } + } + + private void UnregisterForSceneEvents() + { + if (m_haveRegisteredForSceneEvents) + { + m_physicsScene.BeforeStep -= this.Step; + m_physicsScene.AfterStep -= this.PostStep; + ControllingPrim.OnPreUpdateProperty -= this.PreUpdateProperty; + m_haveRegisteredForSceneEvents = false; + } + } + + private void PreUpdateProperty(ref EntityProperties entprop) + { + // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet + // TODO: handle physics introduced by Bullet with computed vehicle physics. + if (IsActive) + { + entprop.RotationalVelocity = Vector3.Zero; + } + } + + #region Known vehicle value functions + // Vehicle physical parameters that we buffer from constant getting and setting. + // The "m_known*" values are unknown until they are fetched and the m_knownHas flag is set. + // Changing is remembered and the parameter is stored back into the physics engine only if updated. + // This does two things: 1) saves continuious calls into unmanaged code, and + // 2) signals when a physics property update must happen back to the simulator + // to update values modified for the vehicle. + private int m_knownChanged; + private int m_knownHas; + private float m_knownTerrainHeight; + private float m_knownWaterLevel; + private Vector3 m_knownPosition; + private Vector3 m_knownVelocity; + private Vector3 m_knownForce; + private Vector3 m_knownForceImpulse; + private Quaternion m_knownOrientation; + private Vector3 m_knownRotationalVelocity; + private Vector3 m_knownRotationalForce; + private Vector3 m_knownRotationalImpulse; + + private const int m_knownChangedPosition = 1 << 0; + private const int m_knownChangedVelocity = 1 << 1; + private const int m_knownChangedForce = 1 << 2; + private const int m_knownChangedForceImpulse = 1 << 3; + private const int m_knownChangedOrientation = 1 << 4; + private const int m_knownChangedRotationalVelocity = 1 << 5; + private const int m_knownChangedRotationalForce = 1 << 6; + private const int m_knownChangedRotationalImpulse = 1 << 7; + private const int m_knownChangedTerrainHeight = 1 << 8; + private const int m_knownChangedWaterLevel = 1 << 9; + + public void ForgetKnownVehicleProperties() + { + m_knownHas = 0; + m_knownChanged = 0; + } + // Push all the changed values back into the physics engine + public void PushKnownChanged() + { + if (m_knownChanged != 0) + { + if ((m_knownChanged & m_knownChangedPosition) != 0) + ControllingPrim.ForcePosition = m_knownPosition; + + if ((m_knownChanged & m_knownChangedOrientation) != 0) + ControllingPrim.ForceOrientation = m_knownOrientation; + + if ((m_knownChanged & m_knownChangedVelocity) != 0) + { + ControllingPrim.ForceVelocity = m_knownVelocity; + // Fake out Bullet by making it think the velocity is the same as last time. + // Bullet does a bunch of smoothing for changing parameters. + // Since the vehicle is demanding this setting, we override Bullet's smoothing + // by telling Bullet the value was the same last time. + // PhysicsScene.PE.SetInterpolationLinearVelocity(Prim.PhysBody, m_knownVelocity); + } + + if ((m_knownChanged & m_knownChangedForce) != 0) + ControllingPrim.AddForce((Vector3)m_knownForce, false /*pushForce*/, true /*inTaintTime*/); + + if ((m_knownChanged & m_knownChangedForceImpulse) != 0) + ControllingPrim.AddForceImpulse((Vector3)m_knownForceImpulse, false /*pushforce*/, true /*inTaintTime*/); + + if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0) + { + ControllingPrim.ForceRotationalVelocity = m_knownRotationalVelocity; + // PhysicsScene.PE.SetInterpolationAngularVelocity(Prim.PhysBody, m_knownRotationalVelocity); + } + + if ((m_knownChanged & m_knownChangedRotationalImpulse) != 0) + ControllingPrim.ApplyTorqueImpulse((Vector3)m_knownRotationalImpulse, true /*inTaintTime*/); + + if ((m_knownChanged & m_knownChangedRotationalForce) != 0) + { + ControllingPrim.AddAngularForce((Vector3)m_knownRotationalForce, false /*pushForce*/, true /*inTaintTime*/); + } + + // If we set one of the values (ie, the physics engine didn't do it) we must force + // an UpdateProperties event to send the changes up to the simulator. + m_physicsScene.PE.PushUpdate(ControllingPrim.PhysBody); + } + m_knownChanged = 0; + } + + // Since the computation of terrain height can be a little involved, this routine + // is used to fetch the height only once for each vehicle simulation step. + Vector3 lastRememberedHeightPos = new Vector3(-1, -1, -1); + private float GetTerrainHeight(Vector3 pos) + { + if ((m_knownHas & m_knownChangedTerrainHeight) == 0 || pos != lastRememberedHeightPos) + { + lastRememberedHeightPos = pos; + m_knownTerrainHeight = ControllingPrim.PhysScene.TerrainManager.GetTerrainHeightAtXYZ(pos); + m_knownHas |= m_knownChangedTerrainHeight; + } + return m_knownTerrainHeight; + } + + // Since the computation of water level can be a little involved, this routine + // is used ot fetch the level only once for each vehicle simulation step. + Vector3 lastRememberedWaterHeightPos = new Vector3(-1, -1, -1); + private float GetWaterLevel(Vector3 pos) + { + if ((m_knownHas & m_knownChangedWaterLevel) == 0 || pos != lastRememberedWaterHeightPos) + { + lastRememberedWaterHeightPos = pos; + m_knownWaterLevel = ControllingPrim.PhysScene.TerrainManager.GetWaterLevelAtXYZ(pos); + m_knownHas |= m_knownChangedWaterLevel; + } + return m_knownWaterLevel; + } + + private Vector3 VehiclePosition + { + get + { + if ((m_knownHas & m_knownChangedPosition) == 0) + { + m_knownPosition = ControllingPrim.ForcePosition; + m_knownHas |= m_knownChangedPosition; + } + return m_knownPosition; + } + set + { + m_knownPosition = value; + m_knownChanged |= m_knownChangedPosition; + m_knownHas |= m_knownChangedPosition; + } + } + + private Quaternion VehicleOrientation + { + get + { + if ((m_knownHas & m_knownChangedOrientation) == 0) + { + m_knownOrientation = ControllingPrim.ForceOrientation; + m_knownHas |= m_knownChangedOrientation; + } + return m_knownOrientation; + } + set + { + m_knownOrientation = value; + m_knownChanged |= m_knownChangedOrientation; + m_knownHas |= m_knownChangedOrientation; + } + } + + private Vector3 VehicleVelocity + { + get + { + if ((m_knownHas & m_knownChangedVelocity) == 0) + { + m_knownVelocity = ControllingPrim.ForceVelocity; + m_knownHas |= m_knownChangedVelocity; + } + return m_knownVelocity; + } + set + { + m_knownVelocity = value; + m_knownChanged |= m_knownChangedVelocity; + m_knownHas |= m_knownChangedVelocity; + } + } + + private void VehicleAddForce(Vector3 pForce) + { + if ((m_knownHas & m_knownChangedForce) == 0) + { + m_knownForce = Vector3.Zero; + m_knownHas |= m_knownChangedForce; + } + m_knownForce += pForce; + m_knownChanged |= m_knownChangedForce; + } + + private void VehicleAddForceImpulse(Vector3 pImpulse) + { + if ((m_knownHas & m_knownChangedForceImpulse) == 0) + { + m_knownForceImpulse = Vector3.Zero; + m_knownHas |= m_knownChangedForceImpulse; + } + m_knownForceImpulse += pImpulse; + m_knownChanged |= m_knownChangedForceImpulse; + } + + private Vector3 VehicleRotationalVelocity + { + get + { + if ((m_knownHas & m_knownChangedRotationalVelocity) == 0) + { + m_knownRotationalVelocity = ControllingPrim.ForceRotationalVelocity; + m_knownHas |= m_knownChangedRotationalVelocity; + } + return (Vector3)m_knownRotationalVelocity; + } + set + { + m_knownRotationalVelocity = value; + m_knownChanged |= m_knownChangedRotationalVelocity; + m_knownHas |= m_knownChangedRotationalVelocity; + } + } + private void VehicleAddAngularForce(Vector3 aForce) + { + if ((m_knownHas & m_knownChangedRotationalForce) == 0) + { + m_knownRotationalForce = Vector3.Zero; + } + m_knownRotationalForce += aForce; + m_knownChanged |= m_knownChangedRotationalForce; + m_knownHas |= m_knownChangedRotationalForce; + } + private void VehicleAddRotationalImpulse(Vector3 pImpulse) + { + if ((m_knownHas & m_knownChangedRotationalImpulse) == 0) + { + m_knownRotationalImpulse = Vector3.Zero; + m_knownHas |= m_knownChangedRotationalImpulse; + } + m_knownRotationalImpulse += pImpulse; + m_knownChanged |= m_knownChangedRotationalImpulse; + } + + // Vehicle relative forward velocity + private Vector3 VehicleForwardVelocity + { + get + { + return VehicleVelocity * Quaternion.Inverse(Quaternion.Normalize(VehicleFrameOrientation)); + } + } + + private float VehicleForwardSpeed + { + get + { + return VehicleForwardVelocity.X; + } + } + private Quaternion VehicleFrameOrientation + { + get + { + return VehicleOrientation * m_referenceFrame; + } + } + + #endregion // Known vehicle value functions + + // One step of the vehicle properties for the next 'pTimestep' seconds. + internal void Step(float pTimestep) + { + if (!IsActive) return; + + ForgetKnownVehicleProperties(); + + MoveLinear(pTimestep); + MoveAngular(pTimestep); + + LimitRotation(pTimestep); + + // remember the position so next step we can limit absolute movement effects + m_lastPositionVector = VehiclePosition; + + // If we forced the changing of some vehicle parameters, update the values and + // for the physics engine to note the changes so an UpdateProperties event will happen. + PushKnownChanged(); + + if (m_physicsScene.VehiclePhysicalLoggingEnabled) + m_physicsScene.PE.DumpRigidBody(m_physicsScene.World, ControllingPrim.PhysBody); + + VDetailLog("{0},BSDynamics.Step,done,pos={1}, force={2},velocity={3},angvel={4}", + ControllingPrim.LocalID, VehiclePosition, m_knownForce, VehicleVelocity, VehicleRotationalVelocity); + } + + // Called after the simulation step + internal void PostStep(float pTimestep) + { + if (!IsActive) return; + + if (m_physicsScene.VehiclePhysicalLoggingEnabled) + m_physicsScene.PE.DumpRigidBody(m_physicsScene.World, ControllingPrim.PhysBody); + } + + // Apply the effect of the linear motor and other linear motions (like hover and float). + private void MoveLinear(float pTimestep) + { + ComputeLinearVelocity(pTimestep); + + ComputeLinearDeflection(pTimestep); + + ComputeLinearTerrainHeightCorrection(pTimestep); + + ComputeLinearHover(pTimestep); + + ComputeLinearBlockingEndPoint(pTimestep); + + ComputeLinearMotorUp(pTimestep); + + ApplyGravity(pTimestep); + + // If not changing some axis, reduce out velocity + if ((m_flags & (VehicleFlag.NO_X | VehicleFlag.NO_Y | VehicleFlag.NO_Z)) != 0) + { + Vector3 vel = VehicleVelocity; + if ((m_flags & (VehicleFlag.NO_X)) != 0) + { + vel.X = 0; + } + if ((m_flags & (VehicleFlag.NO_Y)) != 0) + { + vel.Y = 0; + } + if ((m_flags & (VehicleFlag.NO_Z)) != 0) + { + vel.Z = 0; + } + VehicleVelocity = vel; + } + + // ================================================================== + // Clamp high or low velocities + float newVelocityLengthSq = VehicleVelocity.LengthSquared(); + if (newVelocityLengthSq > BSParam.VehicleMaxLinearVelocitySquared) + { + Vector3 origVelW = VehicleVelocity; // DEBUG DEBUG + VehicleVelocity /= VehicleVelocity.Length(); + VehicleVelocity *= BSParam.VehicleMaxLinearVelocity; + VDetailLog("{0}, MoveLinear,clampMax,origVelW={1},lenSq={2},maxVelSq={3},,newVelW={4}", + ControllingPrim.LocalID, origVelW, newVelocityLengthSq, BSParam.VehicleMaxLinearVelocitySquared, VehicleVelocity); + } + else if (newVelocityLengthSq < BSParam.VehicleMinLinearVelocitySquared) + { + Vector3 origVelW = VehicleVelocity; // DEBUG DEBUG + VDetailLog("{0}, MoveLinear,clampMin,origVelW={1},lenSq={2}", + ControllingPrim.LocalID, origVelW, newVelocityLengthSq); + VehicleVelocity = Vector3.Zero; + } + + VDetailLog("{0}, MoveLinear,done,isColl={1},newVel={2}", ControllingPrim.LocalID, ControllingPrim.HasSomeCollision, VehicleVelocity ); + + } // end MoveLinear() + + public void ComputeLinearVelocity(float pTimestep) + { + // Step the motor from the current value. Get the correction needed this step. + Vector3 origVelW = VehicleVelocity; // DEBUG + Vector3 currentVelV = VehicleForwardVelocity; + Vector3 linearMotorCorrectionV = m_linearMotor.Step(pTimestep, currentVelV); + + // Friction reduces vehicle motion based on absolute speed. Slow vehicle down by friction. + Vector3 frictionFactorV = ComputeFrictionFactor(m_linearFrictionTimescale, pTimestep); + linearMotorCorrectionV -= (currentVelV * frictionFactorV); + + // Motor is vehicle coordinates. Rotate it to world coordinates + Vector3 linearMotorVelocityW = linearMotorCorrectionV * VehicleFrameOrientation; + + // If we're a ground vehicle, don't add any upward Z movement + if ((m_flags & VehicleFlag.LIMIT_MOTOR_UP) != 0) + { + if (linearMotorVelocityW.Z > 0f) + linearMotorVelocityW.Z = 0f; + } + + // Add this correction to the velocity to make it faster/slower. + VehicleVelocity += linearMotorVelocityW; + + VDetailLog("{0}, MoveLinear,velocity,origVelW={1},velV={2},tgt={3},correctV={4},correctW={5},newVelW={6},fricFact={7}", + ControllingPrim.LocalID, origVelW, currentVelV, m_linearMotor.TargetValue, linearMotorCorrectionV, + linearMotorVelocityW, VehicleVelocity, frictionFactorV); + } + + //Given a Deflection Effiency and a Velocity, Returns a Velocity that is Partially Deflected onto the X Axis + //Clamped so that a DeflectionTimescale of less then 1 does not increase force over original velocity + private void ComputeLinearDeflection(float pTimestep) + { + Vector3 linearDeflectionV = Vector3.Zero; + Vector3 velocityV = VehicleForwardVelocity; + + if (BSParam.VehicleEnableLinearDeflection) + { + // Velocity in Y and Z dimensions is movement to the side or turning. + // Compute deflection factor from the to the side and rotational velocity + linearDeflectionV.Y = SortedClampInRange(0, (velocityV.Y * m_linearDeflectionEfficiency) / m_linearDeflectionTimescale, velocityV.Y); + linearDeflectionV.Z = SortedClampInRange(0, (velocityV.Z * m_linearDeflectionEfficiency) / m_linearDeflectionTimescale, velocityV.Z); + + // Velocity to the side and around is corrected and moved into the forward direction + linearDeflectionV.X += Math.Abs(linearDeflectionV.Y); + linearDeflectionV.X += Math.Abs(linearDeflectionV.Z); + + // Scale the deflection to the fractional simulation time + linearDeflectionV *= pTimestep; + + // Subtract the sideways and rotational velocity deflection factors while adding the correction forward + linearDeflectionV *= new Vector3(1, -1, -1); + + // Correction is vehicle relative. Convert to world coordinates. + Vector3 linearDeflectionW = linearDeflectionV * VehicleFrameOrientation; + + // Optionally, if not colliding, don't effect world downward velocity. Let falling things fall. + if (BSParam.VehicleLinearDeflectionNotCollidingNoZ && !m_controllingPrim.HasSomeCollision) + { + linearDeflectionW.Z = 0f; + } + + VehicleVelocity += linearDeflectionW; + + VDetailLog("{0}, MoveLinear,LinearDeflection,linDefEff={1},linDefTS={2},linDeflectionV={3}", + ControllingPrim.LocalID, m_linearDeflectionEfficiency, m_linearDeflectionTimescale, linearDeflectionV); + } + } + + public void ComputeLinearTerrainHeightCorrection(float pTimestep) + { + // If below the terrain, move us above the ground a little. + // TODO: Consider taking the rotated size of the object or possibly casting a ray. + if (VehiclePosition.Z < GetTerrainHeight(VehiclePosition)) + { + // Force position because applying force won't get the vehicle through the terrain + Vector3 newPosition = VehiclePosition; + newPosition.Z = GetTerrainHeight(VehiclePosition) + 1f; + VehiclePosition = newPosition; + VDetailLog("{0}, MoveLinear,terrainHeight,terrainHeight={1},pos={2}", + ControllingPrim.LocalID, GetTerrainHeight(VehiclePosition), VehiclePosition); + } + } + + public void ComputeLinearHover(float pTimestep) + { + // m_VhoverEfficiency: 0=bouncy, 1=totally damped + // m_VhoverTimescale: time to achieve height + if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0 && (m_VhoverHeight > 0) && (m_VhoverTimescale < 300)) + { + // We should hover, get the target height + if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0) + { + m_VhoverTargetHeight = GetWaterLevel(VehiclePosition) + m_VhoverHeight; + } + if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) + { + m_VhoverTargetHeight = GetTerrainHeight(VehiclePosition) + m_VhoverHeight; + } + if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0) + { + m_VhoverTargetHeight = m_VhoverHeight; + } + if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0) + { + // If body is already heigher, use its height as target height + if (VehiclePosition.Z > m_VhoverTargetHeight) + { + m_VhoverTargetHeight = VehiclePosition.Z; + + // A 'misfeature' of this flag is that if the vehicle is above it's hover height, + // the vehicle's buoyancy goes away. This is an SL bug that got used by so many + // scripts that it could not be changed. + // So, if above the height, reapply gravity if buoyancy had it turned off. + if (m_VehicleBuoyancy != 0) + { + Vector3 appliedGravity = ControllingPrim.ComputeGravity(ControllingPrim.Buoyancy) * m_vehicleMass; + VehicleAddForce(appliedGravity); + } + } + } + + if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) + { + if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f) + { + Vector3 pos = VehiclePosition; + pos.Z = m_VhoverTargetHeight; + VehiclePosition = pos; + + VDetailLog("{0}, MoveLinear,hover,pos={1},lockHoverHeight", ControllingPrim.LocalID, pos); + } + } + else + { + // Error is positive if below the target and negative if above. + Vector3 hpos = VehiclePosition; + float verticalError = m_VhoverTargetHeight - hpos.Z; + float verticalCorrection = verticalError / m_VhoverTimescale; + verticalCorrection *= m_VhoverEfficiency; + + hpos.Z += verticalCorrection; + VehiclePosition = hpos; + + // Since we are hovering, we need to do the opposite of falling -- get rid of world Z + Vector3 vel = VehicleVelocity; + vel.Z = 0f; + VehicleVelocity = vel; + + /* + float verticalCorrectionVelocity = verticalError / m_VhoverTimescale; + Vector3 verticalCorrection = new Vector3(0f, 0f, verticalCorrectionVelocity); + verticalCorrection *= m_vehicleMass; + + // TODO: implement m_VhoverEfficiency correctly + VehicleAddForceImpulse(verticalCorrection); + */ + + VDetailLog("{0}, MoveLinear,hover,pos={1},eff={2},hoverTS={3},height={4},target={5},err={6},corr={7}", + ControllingPrim.LocalID, VehiclePosition, m_VhoverEfficiency, + m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight, + verticalError, verticalCorrection); + } + } + } + + public bool ComputeLinearBlockingEndPoint(float pTimestep) + { + bool changed = false; + + Vector3 pos = VehiclePosition; + Vector3 posChange = pos - m_lastPositionVector; + if (m_BlockingEndPoint != Vector3.Zero) + { + if (pos.X >= (m_BlockingEndPoint.X - (float)1)) + { + pos.X -= posChange.X + 1; + changed = true; + } + if (pos.Y >= (m_BlockingEndPoint.Y - (float)1)) + { + pos.Y -= posChange.Y + 1; + changed = true; + } + if (pos.Z >= (m_BlockingEndPoint.Z - (float)1)) + { + pos.Z -= posChange.Z + 1; + changed = true; + } + if (pos.X <= 0) + { + pos.X += posChange.X + 1; + changed = true; + } + if (pos.Y <= 0) + { + pos.Y += posChange.Y + 1; + changed = true; + } + if (changed) + { + VehiclePosition = pos; + VDetailLog("{0}, MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}", + ControllingPrim.LocalID, m_BlockingEndPoint, posChange, pos); + } + } + return changed; + } + + // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags : + // Prevent ground vehicles from motoring into the sky. This flag has a subtle effect when + // used with conjunction with banking: the strength of the banking will decay when the + // vehicle no longer experiences collisions. The decay timescale is the same as + // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering + // when they are in mid jump. + // TODO: this code is wrong. Also, what should it do for boats (height from water)? + // This is just using the ground and a general collision check. Should really be using + // a downward raycast to find what is below. + public void ComputeLinearMotorUp(float pTimestep) + { + if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) + { + // This code tries to decide if the object is not on the ground and then pushing down + /* + float targetHeight = Type == Vehicle.TYPE_BOAT ? GetWaterLevel(VehiclePosition) : GetTerrainHeight(VehiclePosition); + distanceAboveGround = VehiclePosition.Z - targetHeight; + // Not colliding if the vehicle is off the ground + if (!Prim.HasSomeCollision) + { + // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale); + VehicleVelocity += new Vector3(0, 0, -distanceAboveGround); + } + // TODO: this calculation is wrong. From the description at + // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce + // has a decay factor. This says this force should + // be computed with a motor. + // TODO: add interaction with banking. + VDetailLog("{0}, MoveLinear,limitMotorUp,distAbove={1},colliding={2},ret={3}", + Prim.LocalID, distanceAboveGround, Prim.HasSomeCollision, ret); + */ + + // Another approach is to measure if we're going up. If going up and not colliding, + // the vehicle is in the air. Fix that by pushing down. + if (!ControllingPrim.HasSomeCollision && VehicleVelocity.Z > 0.1) + { + // Get rid of any of the velocity vector that is pushing us up. + float upVelocity = VehicleVelocity.Z; + VehicleVelocity += new Vector3(0, 0, -upVelocity); + + /* + // If we're pointed up into the air, we should nose down + Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation; + // The rotation around the Y axis is pitch up or down + if (pointingDirection.Y > 0.01f) + { + float angularCorrectionForce = -(float)Math.Asin(pointingDirection.Y); + Vector3 angularCorrectionVector = new Vector3(0f, angularCorrectionForce, 0f); + // Rotate into world coordinates and apply to vehicle + angularCorrectionVector *= VehicleOrientation; + VehicleAddAngularForce(angularCorrectionVector); + VDetailLog("{0}, MoveLinear,limitMotorUp,newVel={1},pntDir={2},corrFrc={3},aCorr={4}", + Prim.LocalID, VehicleVelocity, pointingDirection, angularCorrectionForce, angularCorrectionVector); + } + */ + VDetailLog("{0}, MoveLinear,limitMotorUp,collide={1},upVel={2},newVel={3}", + ControllingPrim.LocalID, ControllingPrim.HasSomeCollision, upVelocity, VehicleVelocity); + } + } + } + + private void ApplyGravity(float pTimeStep) + { + Vector3 appliedGravity = m_VehicleGravity * m_vehicleMass; + + // Hack to reduce downward force if the vehicle is probably sitting on the ground + if (ControllingPrim.HasSomeCollision && IsGroundVehicle) + appliedGravity *= BSParam.VehicleGroundGravityFudge; + + VehicleAddForce(appliedGravity); + + VDetailLog("{0}, MoveLinear,applyGravity,vehGrav={1},collid={2},fudge={3},mass={4},appliedForce={5}", + ControllingPrim.LocalID, m_VehicleGravity, + ControllingPrim.HasSomeCollision, BSParam.VehicleGroundGravityFudge, m_vehicleMass, appliedGravity); + } + + // ======================================================================= + // ======================================================================= + // Apply the effect of the angular motor. + // The 'contribution' is how much angular correction velocity each function wants. + // All the contributions are added together and the resulting velocity is + // set directly on the vehicle. + private void MoveAngular(float pTimestep) + { + ComputeAngularTurning(pTimestep); + + ComputeAngularVerticalAttraction(); + + ComputeAngularDeflection(); + + ComputeAngularBanking(); + + // ================================================================== + if (VehicleRotationalVelocity.ApproxEquals(Vector3.Zero, 0.0001f)) + { + // The vehicle is not adding anything angular wise. + VehicleRotationalVelocity = Vector3.Zero; + VDetailLog("{0}, MoveAngular,done,zero", ControllingPrim.LocalID); + } + else + { + VDetailLog("{0}, MoveAngular,done,nonZero,angVel={1}", ControllingPrim.LocalID, VehicleRotationalVelocity); + } + + // ================================================================== + //Offset section + if (m_linearMotorOffset != Vector3.Zero) + { + //Offset of linear velocity doesn't change the linear velocity, + // but causes a torque to be applied, for example... + // + // IIIII >>> IIIII + // IIIII >>> IIIII + // IIIII >>> IIIII + // ^ + // | Applying a force at the arrow will cause the object to move forward, but also rotate + // + // + // The torque created is the linear velocity crossed with the offset + + // TODO: this computation should be in the linear section + // because that is where we know the impulse being applied. + Vector3 torqueFromOffset = Vector3.Zero; + // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse); + if (float.IsNaN(torqueFromOffset.X)) + torqueFromOffset.X = 0; + if (float.IsNaN(torqueFromOffset.Y)) + torqueFromOffset.Y = 0; + if (float.IsNaN(torqueFromOffset.Z)) + torqueFromOffset.Z = 0; + + VehicleAddAngularForce(torqueFromOffset * m_vehicleMass); + VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", ControllingPrim.LocalID, torqueFromOffset); + } + + } + + private void ComputeAngularTurning(float pTimestep) + { + // The user wants this many radians per second angular change? + Vector3 origVehicleRotationalVelocity = VehicleRotationalVelocity; // DEBUG DEBUG + Vector3 currentAngularV = VehicleRotationalVelocity * Quaternion.Inverse(VehicleFrameOrientation); + Vector3 angularMotorContributionV = m_angularMotor.Step(pTimestep, currentAngularV); + + // ================================================================== + // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags : + // This flag prevents linear deflection parallel to world z-axis. This is useful + // for preventing ground vehicles with large linear deflection, like bumper cars, + // from climbing their linear deflection into the sky. + // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement + // TODO: This is here because this is where ODE put it but documentation says it + // is a linear effect. Where should this check go? + //if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) + // { + // angularMotorContributionV.X = 0f; + // angularMotorContributionV.Y = 0f; + // } + + // Reduce any velocity by friction. + Vector3 frictionFactorW = ComputeFrictionFactor(m_angularFrictionTimescale, pTimestep); + angularMotorContributionV -= (currentAngularV * frictionFactorW); + + Vector3 angularMotorContributionW = angularMotorContributionV * VehicleFrameOrientation; + VehicleRotationalVelocity += angularMotorContributionW; + + VDetailLog("{0}, MoveAngular,angularTurning,curAngVelV={1},origVehRotVel={2},vehRotVel={3},frictFact={4}, angContribV={5},angContribW={6}", + ControllingPrim.LocalID, currentAngularV, origVehicleRotationalVelocity, VehicleRotationalVelocity, frictionFactorW, angularMotorContributionV, angularMotorContributionW); + } + + // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial: + // Some vehicles, like boats, should always keep their up-side up. This can be done by + // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to + // the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the + // VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency, + // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An + // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an + // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay. + public void ComputeAngularVerticalAttraction() + { + + // If vertical attaction timescale is reasonable + if (BSParam.VehicleEnableAngularVerticalAttraction && m_verticalAttractionTimescale < m_verticalAttractionCutoff) + { + Vector3 vehicleUpAxis = Vector3.UnitZ * VehicleFrameOrientation; + switch (BSParam.VehicleAngularVerticalAttractionAlgorithm) + { + case 0: + { + //Another formula to try got from : + //http://answers.unity3d.com/questions/10425/how-to-stabilize-angular-motion-alignment-of-hover.html + + // Flipping what was originally a timescale into a speed variable and then multiplying it by 2 + // since only computing half the distance between the angles. + float verticalAttractionSpeed = (1 / m_verticalAttractionTimescale) * 2.0f; + + // Make a prediction of where the up axis will be when this is applied rather then where it is now as + // this makes for a smoother adjustment and less fighting between the various forces. + Vector3 predictedUp = vehicleUpAxis * Quaternion.CreateFromAxisAngle(VehicleRotationalVelocity, 0f); + + // This is only half the distance to the target so it will take 2 seconds to complete the turn. + Vector3 torqueVector = Vector3.Cross(predictedUp, Vector3.UnitZ); + + if ((m_flags & VehicleFlag.LIMIT_ROLL_ONLY) != 0) + { + Vector3 vehicleForwardAxis = Vector3.UnitX * VehicleFrameOrientation; + torqueVector = ProjectVector(torqueVector, vehicleForwardAxis); + } + + // Scale vector by our timescale since it is an acceleration it is r/s^2 or radians a timescale squared + Vector3 vertContributionV = torqueVector * verticalAttractionSpeed * verticalAttractionSpeed; + + VehicleRotationalVelocity += vertContributionV; + + VDetailLog("{0}, MoveAngular,verticalAttraction,vertAttrSpeed={1},upAxis={2},PredictedUp={3},torqueVector={4},contrib={5}", + ControllingPrim.LocalID, + verticalAttractionSpeed, + vehicleUpAxis, + predictedUp, + torqueVector, + vertContributionV); + break; + } + case 1: + { + // Possible solution derived from a discussion at: + // http://stackoverflow.com/questions/14939657/computing-vector-from-quaternion-works-computing-quaternion-from-vector-does-no + + // Create a rotation that is only the vehicle's rotation around Z + Vector3 currentEulerW = Vector3.Zero; + VehicleFrameOrientation.GetEulerAngles(out currentEulerW.X, out currentEulerW.Y, out currentEulerW.Z); + Quaternion justZOrientation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, currentEulerW.Z); + + // Create the axis that is perpendicular to the up vector and the rotated up vector. + Vector3 differenceAxisW = Vector3.Cross(Vector3.UnitZ * justZOrientation, Vector3.UnitZ * VehicleFrameOrientation); + // Compute the angle between those to vectors. + double differenceAngle = Math.Acos((double)Vector3.Dot(Vector3.UnitZ, Vector3.Normalize(Vector3.UnitZ * VehicleFrameOrientation))); + // 'differenceAngle' is the angle to rotate and 'differenceAxis' is the plane to rotate in to get the vehicle vertical + + // Reduce the change by the time period it is to change in. Timestep is handled when velocity is applied. + // TODO: add 'efficiency'. + // differenceAngle /= m_verticalAttractionTimescale; + + // Create the quaterian representing the correction angle + Quaternion correctionRotationW = Quaternion.CreateFromAxisAngle(differenceAxisW, (float)differenceAngle); + + // Turn that quaternion into Euler values to make it into velocities to apply. + Vector3 vertContributionW = Vector3.Zero; + correctionRotationW.GetEulerAngles(out vertContributionW.X, out vertContributionW.Y, out vertContributionW.Z); + vertContributionW *= -1f; + vertContributionW /= m_verticalAttractionTimescale; + + VehicleRotationalVelocity += vertContributionW; + + VDetailLog("{0}, MoveAngular,verticalAttraction,upAxis={1},diffAxis={2},diffAng={3},corrRot={4},contrib={5}", + ControllingPrim.LocalID, + vehicleUpAxis, + differenceAxisW, + differenceAngle, + correctionRotationW, + vertContributionW); + break; + } + case 2: + { + Vector3 vertContributionV = Vector3.Zero; + Vector3 origRotVelW = VehicleRotationalVelocity; // DEBUG DEBUG + + // Take a vector pointing up and convert it from world to vehicle relative coords. + Vector3 verticalError = Vector3.Normalize(Vector3.UnitZ * VehicleFrameOrientation); + + // If vertical attraction correction is needed, the vector that was pointing up (UnitZ) + // is now: + // leaning to one side: rotated around the X axis with the Y value going + // from zero (nearly straight up) to one (completely to the side)) or + // leaning front-to-back: rotated around the Y axis with the value of X being between + // zero and one. + // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees. + + // Y error means needed rotation around X axis and visa versa. + // Since the error goes from zero to one, the asin is the corresponding angle. + vertContributionV.X = (float)Math.Asin(verticalError.Y); + // (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.) + vertContributionV.Y = -(float)Math.Asin(verticalError.X); + + // If verticalError.Z is negative, the vehicle is upside down. Add additional push. + if (verticalError.Z < 0f) + { + vertContributionV.X += Math.Sign(vertContributionV.X) * PIOverFour; + // vertContribution.Y -= PIOverFour; + } + + // 'vertContrbution' is now the necessary angular correction to correct tilt in one second. + // Correction happens over a number of seconds. + Vector3 unscaledContribVerticalErrorV = vertContributionV; // DEBUG DEBUG + + // The correction happens over the user's time period + vertContributionV /= m_verticalAttractionTimescale; + + // Rotate the vehicle rotation to the world coordinates. + VehicleRotationalVelocity += (vertContributionV * VehicleFrameOrientation); + + VDetailLog("{0}, MoveAngular,verticalAttraction,,upAxis={1},origRotVW={2},vertError={3},unscaledV={4},eff={5},ts={6},vertContribV={7}", + ControllingPrim.LocalID, + vehicleUpAxis, + origRotVelW, + verticalError, + unscaledContribVerticalErrorV, + m_verticalAttractionEfficiency, + m_verticalAttractionTimescale, + vertContributionV); + break; + } + default: + { + break; + } + } + } + } + + // Angular correction to correct the direction the vehicle is pointing to be + // the direction is should want to be pointing. + // The vehicle is moving in some direction and correct its orientation to it is pointing + // in that direction. + // TODO: implement reference frame. + public void ComputeAngularDeflection() + { + + if (BSParam.VehicleEnableAngularDeflection && m_angularDeflectionEfficiency != 0 && VehicleForwardSpeed > 0.2) + { + Vector3 deflectContributionV = Vector3.Zero; + + // The direction the vehicle is moving + Vector3 movingDirection = VehicleVelocity; + movingDirection.Normalize(); + + // If the vehicle is going backward, it is still pointing forward + movingDirection *= Math.Sign(VehicleForwardSpeed); + + // The direction the vehicle is pointing + Vector3 pointingDirection = Vector3.UnitX * VehicleFrameOrientation; + //Predict where the Vehicle will be pointing after AngularVelocity change is applied. This will keep + // from overshooting and allow this correction to merge with the Vertical Attraction peacefully. + Vector3 predictedPointingDirection = pointingDirection * Quaternion.CreateFromAxisAngle(VehicleRotationalVelocity, 0f); + predictedPointingDirection.Normalize(); + + // The difference between what is and what should be. + // Vector3 deflectionError = movingDirection - predictedPointingDirection; + Vector3 deflectionError = Vector3.Cross(movingDirection, predictedPointingDirection); + + // Don't try to correct very large errors (not our job) + // if (Math.Abs(deflectionError.X) > PIOverFour) deflectionError.X = PIOverTwo * Math.Sign(deflectionError.X); + // if (Math.Abs(deflectionError.Y) > PIOverFour) deflectionError.Y = PIOverTwo * Math.Sign(deflectionError.Y); + // if (Math.Abs(deflectionError.Z) > PIOverFour) deflectionError.Z = PIOverTwo * Math.Sign(deflectionError.Z); + if (Math.Abs(deflectionError.X) > PIOverFour) deflectionError.X = 0f; + if (Math.Abs(deflectionError.Y) > PIOverFour) deflectionError.Y = 0f; + if (Math.Abs(deflectionError.Z) > PIOverFour) deflectionError.Z = 0f; + + // ret = m_angularDeflectionCorrectionMotor(1f, deflectionError); + + // Scale the correction by recovery timescale and efficiency + // Not modeling a spring so clamp the scale to no more then the arc + deflectContributionV = (-deflectionError) * ClampInRange(0, m_angularDeflectionEfficiency/m_angularDeflectionTimescale,1f); + //deflectContributionV /= m_angularDeflectionTimescale; + + VehicleRotationalVelocity += deflectContributionV; + VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}", + ControllingPrim.LocalID, movingDirection, pointingDirection, deflectionError, deflectContributionV); + VDetailLog("{0}, MoveAngular,Deflection,fwdSpd={1},defEff={2},defTS={3},PredictedPointingDir={4}", + ControllingPrim.LocalID, VehicleForwardSpeed, m_angularDeflectionEfficiency, m_angularDeflectionTimescale, predictedPointingDirection); + } + } + + // Angular change to rotate the vehicle around the Z axis when the vehicle + // is tipped around the X axis. + // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial: + // The vertical attractor feature must be enabled in order for the banking behavior to + // function. The way banking works is this: a rotation around the vehicle's roll-axis will + // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude + // of the yaw effect will be proportional to the + // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's + // velocity along its preferred axis of motion. + // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any + // positive rotation (by the right-hand rule) about the roll-axis will effect a + // (negative) torque around the yaw-axis, making it turn to the right--that is the + // vehicle will lean into the turn, which is how real airplanes and motorcycle's work. + // Negating the banking coefficient will make it so that the vehicle leans to the + // outside of the turn (not very "physical" but might allow interesting vehicles so why not?). + // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making + // banking vehicles do what you want rather than what the laws of physics allow. + // For example, consider a real motorcycle...it must be moving forward in order for + // it to turn while banking, however video-game motorcycles are often configured + // to turn in place when at a dead stop--because they are often easier to control + // that way using the limited interface of the keyboard or game controller. The + // VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic + // banking by functioning as a slider between a banking that is correspondingly + // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the + // banking effect depends only on the vehicle's rotation about its roll-axis compared + // to "dynamic" where the banking is also proportional to its velocity along its + // roll-axis. Finding the best value of the "mixture" will probably require trial and error. + // The time it takes for the banking behavior to defeat a preexisting angular velocity about the + // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to + // bank quickly then give it a banking timescale of about a second or less, otherwise you can + // make a sluggish vehicle by giving it a timescale of several seconds. + public void ComputeAngularBanking() + { + if (BSParam.VehicleEnableAngularBanking && m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff) + { + Vector3 bankingContributionV = Vector3.Zero; + + // Rotate a UnitZ vector (pointing up) to how the vehicle is oriented. + // As the vehicle rolls to the right or left, the Y value will increase from + // zero (straight up) to 1 or -1 (full tilt right or left) + Vector3 rollComponents = Vector3.UnitZ * VehicleFrameOrientation; + + // Figure out the yaw value for this much roll. + float yawAngle = m_angularMotorDirection.X * m_bankingEfficiency; + // actual error = static turn error + dynamic turn error + float mixedYawAngle =(yawAngle * (1f - m_bankingMix)) + ((yawAngle * m_bankingMix) * VehicleForwardSpeed); + + // TODO: the banking effect should not go to infinity but what to limit it to? + // And what should happen when this is being added to a user defined yaw that is already PI*4? + mixedYawAngle = ClampInRange(-FourPI, mixedYawAngle, FourPI); + + // Build the force vector to change rotation from what it is to what it should be + bankingContributionV.Z = -mixedYawAngle; + + // Don't do it all at once. Fudge because 1 second is too fast with most user defined roll as PI*4. + bankingContributionV /= m_bankingTimescale * BSParam.VehicleAngularBankingTimescaleFudge; + + VehicleRotationalVelocity += bankingContributionV; + + + VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}", + ControllingPrim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, bankingContributionV); + } + } + + // This is from previous instantiations of XXXDynamics.cs. + // Applies roll reference frame. + // TODO: is this the right way to separate the code to do this operation? + // Should this be in MoveAngular()? + internal void LimitRotation(float timestep) + { + Quaternion rotq = VehicleOrientation; + Quaternion m_rot = rotq; + if (m_RollreferenceFrame != Quaternion.Identity) + { + if (rotq.X >= m_RollreferenceFrame.X) + { + m_rot.X = rotq.X - (m_RollreferenceFrame.X / 2); + } + if (rotq.Y >= m_RollreferenceFrame.Y) + { + m_rot.Y = rotq.Y - (m_RollreferenceFrame.Y / 2); + } + if (rotq.X <= -m_RollreferenceFrame.X) + { + m_rot.X = rotq.X + (m_RollreferenceFrame.X / 2); + } + if (rotq.Y <= -m_RollreferenceFrame.Y) + { + m_rot.Y = rotq.Y + (m_RollreferenceFrame.Y / 2); + } + } + if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0) + { + m_rot.X = 0; + m_rot.Y = 0; + } + if (rotq != m_rot) + { + VehicleOrientation = m_rot; + VDetailLog("{0}, LimitRotation,done,orig={1},new={2}", ControllingPrim.LocalID, rotq, m_rot); + } + + } + + // Given a friction vector (reduction in seconds) and a timestep, return the factor to reduce + // some value by to apply this friction. + private Vector3 ComputeFrictionFactor(Vector3 friction, float pTimestep) + { + Vector3 frictionFactor = Vector3.Zero; + if (friction != BSMotor.InfiniteVector) + { + // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep; + // Individual friction components can be 'infinite' so compute each separately. + frictionFactor.X = (friction.X == BSMotor.Infinite) ? 0f : (1f / friction.X); + frictionFactor.Y = (friction.Y == BSMotor.Infinite) ? 0f : (1f / friction.Y); + frictionFactor.Z = (friction.Z == BSMotor.Infinite) ? 0f : (1f / friction.Z); + frictionFactor *= pTimestep; + } + return frictionFactor; + } + + private float SortedClampInRange(float clampa, float val, float clampb) + { + if (clampa > clampb) + { + float temp = clampa; + clampa = clampb; + clampb = temp; + } + return ClampInRange(clampa, val, clampb); + + } + + //Given a Vector and a unit vector will return the amount of the vector is on the same axis as the unit. + private Vector3 ProjectVector(Vector3 vector, Vector3 onNormal) + { + float vectorDot = Vector3.Dot(vector, onNormal); + return onNormal * vectorDot; + + } + + private float ClampInRange(float low, float val, float high) + { + return Math.Max(low, Math.Min(val, high)); + // return Utils.Clamp(val, low, high); + } + + // Invoke the detailed logger and output something if it's enabled. + private void VDetailLog(string msg, params Object[] args) + { + if (ControllingPrim.PhysScene.VehicleLoggingEnabled) + ControllingPrim.PhysScene.DetailLog(msg, args); + } + } +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSLinkset.cs b/OpenSim/Region/PhysicsModules/BulletS/BSLinkset.cs new file mode 100755 index 0000000..87eba33 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSLinkset.cs @@ -0,0 +1,503 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public abstract class BSLinkset +{ + // private static string LogHeader = "[BULLETSIM LINKSET]"; + + public enum LinksetImplementation + { + Constraint = 0, // linkset tied together with constraints + Compound = 1, // linkset tied together as a compound object + Manual = 2 // linkset tied together manually (code moves all the pieces) + } + // Create the correct type of linkset for this child + public static BSLinkset Factory(BSScene physScene, BSPrimLinkable parent) + { + BSLinkset ret = null; + + switch (parent.LinksetType) + { + case LinksetImplementation.Constraint: + ret = new BSLinksetConstraints(physScene, parent); + break; + case LinksetImplementation.Compound: + ret = new BSLinksetCompound(physScene, parent); + break; + case LinksetImplementation.Manual: + // ret = new BSLinksetManual(physScene, parent); + break; + default: + ret = new BSLinksetCompound(physScene, parent); + break; + } + if (ret == null) + { + physScene.Logger.ErrorFormat("[BULLETSIM LINKSET] Factory could not create linkset. Parent name={1}, ID={2}", parent.Name, parent.LocalID); + } + return ret; + } + + public class BSLinkInfo + { + public BSPrimLinkable member; + public BSLinkInfo(BSPrimLinkable pMember) + { + member = pMember; + } + public virtual void ResetLink() { } + public virtual void SetLinkParameters(BSConstraint constrain) { } + // Returns 'true' if physical property updates from the child should be reported to the simulator + public virtual bool ShouldUpdateChildProperties() { return false; } + } + + public LinksetImplementation LinksetImpl { get; protected set; } + + public BSPrimLinkable LinksetRoot { get; protected set; } + + protected BSScene m_physicsScene { get; private set; } + + static int m_nextLinksetID = 1; + public int LinksetID { get; private set; } + + // The children under the root in this linkset. + // protected HashSet m_children; + protected Dictionary m_children; + + // We lock the diddling of linkset classes to prevent any badness. + // This locks the modification of the instances of this class. Changes + // to the physical representation is done via the tainting mechenism. + protected object m_linksetActivityLock = new Object(); + + // We keep the prim's mass in the linkset structure since it could be dependent on other prims + public float LinksetMass { get; protected set; } + + public virtual bool LinksetIsColliding { get { return false; } } + + public OMV.Vector3 CenterOfMass + { + get { return ComputeLinksetCenterOfMass(); } + } + + public OMV.Vector3 GeometricCenter + { + get { return ComputeLinksetGeometricCenter(); } + } + + protected BSLinkset(BSScene scene, BSPrimLinkable parent) + { + // A simple linkset of one (no children) + LinksetID = m_nextLinksetID++; + // We create LOTS of linksets. + if (m_nextLinksetID <= 0) + m_nextLinksetID = 1; + m_physicsScene = scene; + LinksetRoot = parent; + m_children = new Dictionary(); + LinksetMass = parent.RawMass; + Rebuilding = false; + RebuildScheduled = false; + + parent.ClearDisplacement(); + } + + // Link to a linkset where the child knows the parent. + // Parent changing should not happen so do some sanity checking. + // We return the parent's linkset so the child can track its membership. + // Called at runtime. + public BSLinkset AddMeToLinkset(BSPrimLinkable child) + { + lock (m_linksetActivityLock) + { + // Don't add the root to its own linkset + if (!IsRoot(child)) + AddChildToLinkset(child); + LinksetMass = ComputeLinksetMass(); + } + return this; + } + + // Remove a child from a linkset. + // Returns a new linkset for the child which is a linkset of one (just the + // orphened child). + // Called at runtime. + public BSLinkset RemoveMeFromLinkset(BSPrimLinkable child, bool inTaintTime) + { + lock (m_linksetActivityLock) + { + if (IsRoot(child)) + { + // Cannot remove the root from a linkset. + return this; + } + RemoveChildFromLinkset(child, inTaintTime); + LinksetMass = ComputeLinksetMass(); + } + + // The child is down to a linkset of just itself + return BSLinkset.Factory(m_physicsScene, child); + } + + // Return 'true' if the passed object is the root object of this linkset + public bool IsRoot(BSPrimLinkable requestor) + { + return (requestor.LocalID == LinksetRoot.LocalID); + } + + public int NumberOfChildren { get { return m_children.Count; } } + + // Return 'true' if this linkset has any children (more than the root member) + public bool HasAnyChildren { get { return (m_children.Count > 0); } } + + // Return 'true' if this child is in this linkset + public bool HasChild(BSPrimLinkable child) + { + bool ret = false; + lock (m_linksetActivityLock) + { + ret = m_children.ContainsKey(child); + } + return ret; + } + + // Perform an action on each member of the linkset including root prim. + // Depends on the action on whether this should be done at taint time. + public delegate bool ForEachMemberAction(BSPrimLinkable obj); + public virtual bool ForEachMember(ForEachMemberAction action) + { + bool ret = false; + lock (m_linksetActivityLock) + { + action(LinksetRoot); + foreach (BSPrimLinkable po in m_children.Keys) + { + if (action(po)) + break; + } + } + return ret; + } + + public bool TryGetLinkInfo(BSPrimLinkable child, out BSLinkInfo foundInfo) + { + bool ret = false; + BSLinkInfo found = null; + lock (m_linksetActivityLock) + { + ret = m_children.TryGetValue(child, out found); + } + foundInfo = found; + return ret; + } + // Perform an action on each member of the linkset including root prim. + // Depends on the action on whether this should be done at taint time. + public delegate bool ForEachLinkInfoAction(BSLinkInfo obj); + public virtual bool ForEachLinkInfo(ForEachLinkInfoAction action) + { + bool ret = false; + lock (m_linksetActivityLock) + { + foreach (BSLinkInfo po in m_children.Values) + { + if (action(po)) + break; + } + } + return ret; + } + + // Check the type of the link and return 'true' if the link is flexible and the + // updates from the child should be sent to the simulator so things change. + public virtual bool ShouldReportPropertyUpdates(BSPrimLinkable child) + { + bool ret = false; + + BSLinkInfo linkInfo; + if (m_children.TryGetValue(child, out linkInfo)) + { + ret = linkInfo.ShouldUpdateChildProperties(); + } + + return ret; + } + + // Called after a simulation step to post a collision with this object. + // Return 'true' if linkset processed the collision. 'false' says the linkset didn't have + // anything to add for the collision and it should be passed through normal processing. + // Default processing for a linkset. + public virtual bool HandleCollide(uint collidingWith, BSPhysObject collidee, + OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) + { + bool ret = false; + + // prims in the same linkset cannot collide with each other + BSPrimLinkable convCollidee = collidee as BSPrimLinkable; + if (convCollidee != null && (LinksetID == convCollidee.Linkset.LinksetID)) + { + // By returning 'true', we tell the caller the collision has been 'handled' so it won't + // do anything about this collision and thus, effectivily, ignoring the collision. + ret = true; + } + else + { + // Not a collision between members of the linkset. Must be a real collision. + // So the linkset root can know if there is a collision anywhere in the linkset. + LinksetRoot.SomeCollisionSimulationStep = m_physicsScene.SimulationStep; + } + + return ret; + } + + // I am the root of a linkset and a new child is being added + // Called while LinkActivity is locked. + protected abstract void AddChildToLinkset(BSPrimLinkable child); + + // I am the root of a linkset and one of my children is being removed. + // Safe to call even if the child is not really in my linkset. + protected abstract void RemoveChildFromLinkset(BSPrimLinkable child, bool inTaintTime); + + // When physical properties are changed the linkset needs to recalculate + // its internal properties. + // May be called at runtime or taint-time. + public virtual void Refresh(BSPrimLinkable requestor) + { + LinksetMass = ComputeLinksetMass(); + } + + // Flag denoting the linkset is in the process of being rebuilt. + // Used to know not the schedule a rebuild in the middle of a rebuild. + // Because of potential update calls that could want to schedule another rebuild. + protected bool Rebuilding { get; set; } + + // Flag saying a linkset rebuild has been scheduled. + // This is turned on when the rebuild is requested and turned off when + // the rebuild is complete. Used to limit modifications to the + // linkset parameters while the linkset is in an intermediate state. + // Protected by a "lock(m_linsetActivityLock)" on the BSLinkset object + public bool RebuildScheduled { get; protected set; } + + // The object is going dynamic (physical). Do any setup necessary + // for a dynamic linkset. + // Only the state of the passed object can be modified. The rest of the linkset + // has not yet been fully constructed. + // Return 'true' if any properties updated on the passed object. + // Called at taint-time! + public abstract bool MakeDynamic(BSPrimLinkable child); + + public virtual bool AllPartsComplete + { + get { + bool ret = true; + this.ForEachMember((member) => + { + if ((!member.IsInitialized) || member.IsIncomplete || member.PrimAssetState == BSPhysObject.PrimAssetCondition.Waiting) + { + ret = false; + return true; // exit loop + } + return false; // continue loop + }); + return ret; + } + } + + // The object is going static (non-physical). Do any setup necessary + // for a static linkset. + // Return 'true' if any properties updated on the passed object. + // Called at taint-time! + public abstract bool MakeStatic(BSPrimLinkable child); + + // Called when a parameter update comes from the physics engine for any object + // of the linkset is received. + // Passed flag is update came from physics engine (true) or the user (false). + // Called at taint-time!! + public abstract void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable physObject); + + // Routine used when rebuilding the body of the root of the linkset + // Destroy all the constraints have have been made to root. + // This is called when the root body is changing. + // Returns 'true' of something was actually removed and would need restoring + // Called at taint-time!! + public abstract bool RemoveDependencies(BSPrimLinkable child); + + // ================================================================ + // Some physical setting happen to all members of the linkset + public virtual void SetPhysicalFriction(float friction) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetFriction(member.PhysBody, friction); + return false; // 'false' says to continue looping + } + ); + } + public virtual void SetPhysicalRestitution(float restitution) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetRestitution(member.PhysBody, restitution); + return false; // 'false' says to continue looping + } + ); + } + public virtual void SetPhysicalGravity(OMV.Vector3 gravity) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetGravity(member.PhysBody, gravity); + return false; // 'false' says to continue looping + } + ); + } + public virtual void ComputeAndSetLocalInertia(OMV.Vector3 inertiaFactor, float linksetMass) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + { + OMV.Vector3 inertia = m_physicsScene.PE.CalculateLocalInertia(member.PhysShape.physShapeInfo, linksetMass); + member.Inertia = inertia * inertiaFactor; + m_physicsScene.PE.SetMassProps(member.PhysBody, linksetMass, member.Inertia); + m_physicsScene.PE.UpdateInertiaTensor(member.PhysBody); + DetailLog("{0},BSLinkset.ComputeAndSetLocalInertia,m.mass={1}, inertia={2}", member.LocalID, linksetMass, member.Inertia); + + } + return false; // 'false' says to continue looping + } + ); + } + public virtual void SetPhysicalCollisionFlags(CollisionFlags collFlags) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetCollisionFlags(member.PhysBody, collFlags); + return false; // 'false' says to continue looping + } + ); + } + public virtual void AddToPhysicalCollisionFlags(CollisionFlags collFlags) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + m_physicsScene.PE.AddToCollisionFlags(member.PhysBody, collFlags); + return false; // 'false' says to continue looping + } + ); + } + public virtual void RemoveFromPhysicalCollisionFlags(CollisionFlags collFlags) + { + ForEachMember((member) => + { + if (member.PhysBody.HasPhysicalBody) + m_physicsScene.PE.RemoveFromCollisionFlags(member.PhysBody, collFlags); + return false; // 'false' says to continue looping + } + ); + } + // ================================================================ + protected virtual float ComputeLinksetMass() + { + float mass = LinksetRoot.RawMass; + if (HasAnyChildren) + { + lock (m_linksetActivityLock) + { + foreach (BSPrimLinkable bp in m_children.Keys) + { + mass += bp.RawMass; + } + } + } + return mass; + } + + // Computes linkset's center of mass in world coordinates. + protected virtual OMV.Vector3 ComputeLinksetCenterOfMass() + { + OMV.Vector3 com; + lock (m_linksetActivityLock) + { + com = LinksetRoot.Position * LinksetRoot.RawMass; + float totalMass = LinksetRoot.RawMass; + + foreach (BSPrimLinkable bp in m_children.Keys) + { + com += bp.Position * bp.RawMass; + totalMass += bp.RawMass; + } + if (totalMass != 0f) + com /= totalMass; + } + + return com; + } + + protected virtual OMV.Vector3 ComputeLinksetGeometricCenter() + { + OMV.Vector3 com; + lock (m_linksetActivityLock) + { + com = LinksetRoot.Position; + + foreach (BSPrimLinkable bp in m_children.Keys) + { + com += bp.Position; + } + com /= (m_children.Count + 1); + } + + return com; + } + + #region Extension + public virtual object Extension(string pFunct, params object[] pParams) + { + return null; + } + #endregion // Extension + + // Invoke the detailed logger and output something if it's enabled. + protected void DetailLog(string msg, params Object[] args) + { + if (m_physicsScene.PhysicsLogging.Enabled) + m_physicsScene.DetailLog(msg, args); + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSLinksetCompound.cs b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetCompound.cs new file mode 100755 index 0000000..cae9efa --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetCompound.cs @@ -0,0 +1,477 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; + +using OpenSim.Framework; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public sealed class BSLinksetCompound : BSLinkset +{ +#pragma warning disable 414 + private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]"; +#pragma warning restore 414 + + public BSLinksetCompound(BSScene scene, BSPrimLinkable parent) + : base(scene, parent) + { + LinksetImpl = LinksetImplementation.Compound; + } + + // ================================================================ + // Changing the physical property of the linkset only needs to change the root + public override void SetPhysicalFriction(float friction) + { + if (LinksetRoot.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetFriction(LinksetRoot.PhysBody, friction); + } + public override void SetPhysicalRestitution(float restitution) + { + if (LinksetRoot.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetRestitution(LinksetRoot.PhysBody, restitution); + } + public override void SetPhysicalGravity(OMV.Vector3 gravity) + { + if (LinksetRoot.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetGravity(LinksetRoot.PhysBody, gravity); + } + public override void ComputeAndSetLocalInertia(OMV.Vector3 inertiaFactor, float linksetMass) + { + OMV.Vector3 inertia = m_physicsScene.PE.CalculateLocalInertia(LinksetRoot.PhysShape.physShapeInfo, linksetMass); + LinksetRoot.Inertia = inertia * inertiaFactor; + m_physicsScene.PE.SetMassProps(LinksetRoot.PhysBody, linksetMass, LinksetRoot.Inertia); + m_physicsScene.PE.UpdateInertiaTensor(LinksetRoot.PhysBody); + } + public override void SetPhysicalCollisionFlags(CollisionFlags collFlags) + { + if (LinksetRoot.PhysBody.HasPhysicalBody) + m_physicsScene.PE.SetCollisionFlags(LinksetRoot.PhysBody, collFlags); + } + public override void AddToPhysicalCollisionFlags(CollisionFlags collFlags) + { + if (LinksetRoot.PhysBody.HasPhysicalBody) + m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, collFlags); + } + public override void RemoveFromPhysicalCollisionFlags(CollisionFlags collFlags) + { + if (LinksetRoot.PhysBody.HasPhysicalBody) + m_physicsScene.PE.RemoveFromCollisionFlags(LinksetRoot.PhysBody, collFlags); + } + // ================================================================ + + // When physical properties are changed the linkset needs to recalculate + // its internal properties. + public override void Refresh(BSPrimLinkable requestor) + { + // Something changed so do the rebuilding thing + ScheduleRebuild(requestor); + base.Refresh(requestor); + } + + // Schedule a refresh to happen after all the other taint processing. + private void ScheduleRebuild(BSPrimLinkable requestor) + { + // When rebuilding, it is possible to set properties that would normally require a rebuild. + // If already rebuilding, don't request another rebuild. + // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. + lock (m_linksetActivityLock) + { + if (!RebuildScheduled && !Rebuilding && HasAnyChildren) + { + InternalScheduleRebuild(requestor); + } + } + } + + // Must be called with m_linksetActivityLock or race conditions will haunt you. + private void InternalScheduleRebuild(BSPrimLinkable requestor) + { + DetailLog("{0},BSLinksetCompound.InternalScheduleRebuild,,rebuilding={1},hasChildren={2}", + requestor.LocalID, Rebuilding, HasAnyChildren); + RebuildScheduled = true; + m_physicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate() + { + if (HasAnyChildren) + { + if (this.AllPartsComplete) + { + RecomputeLinksetCompound(); + } + else + { + DetailLog("{0},BSLinksetCompound.InternalScheduleRebuild,,rescheduling because not all children complete", + requestor.LocalID); + InternalScheduleRebuild(requestor); + } + } + RebuildScheduled = false; + }); + } + + // The object is going dynamic (physical). Do any setup necessary for a dynamic linkset. + // Only the state of the passed object can be modified. The rest of the linkset + // has not yet been fully constructed. + // Return 'true' if any properties updated on the passed object. + // Called at taint-time! + public override bool MakeDynamic(BSPrimLinkable child) + { + bool ret = false; + DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); + if (IsRoot(child)) + { + // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly. + Refresh(LinksetRoot); + } + return ret; + } + + // The object is going static (non-physical). We do not do anything for static linksets. + // Return 'true' if any properties updated on the passed object. + // Called at taint-time! + public override bool MakeStatic(BSPrimLinkable child) + { + bool ret = false; + + DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); + child.ClearDisplacement(); + if (IsRoot(child)) + { + // Schedule a rebuild to verify that the root shape is set to the real shape. + Refresh(LinksetRoot); + } + return ret; + } + + // 'physicalUpdate' is true if these changes came directly from the physics engine. Don't need to rebuild then. + // Called at taint-time. + public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable updated) + { + if (!LinksetRoot.IsPhysicallyActive) + { + // No reason to do this physical stuff for static linksets. + DetailLog("{0},BSLinksetCompound.UpdateProperties,notPhysical", LinksetRoot.LocalID); + return; + } + + // The user moving a child around requires the rebuilding of the linkset compound shape + // One problem is this happens when a border is crossed -- the simulator implementation + // stores the position into the group which causes the move of the object + // but it also means all the child positions get updated. + // What would cause an unnecessary rebuild so we make sure the linkset is in a + // region before bothering to do a rebuild. + if (!IsRoot(updated) && m_physicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition)) + { + // If a child of the linkset is updating only the position or rotation, that can be done + // without rebuilding the linkset. + // If a handle for the child can be fetch, we update the child here. If a rebuild was + // scheduled by someone else, the rebuild will just replace this setting. + + bool updatedChild = false; + // Anything other than updating position or orientation usually means a physical update + // and that is caused by us updating the object. + if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0) + { + // Find the physical instance of the child + if (!RebuildScheduled // if rebuilding, let the rebuild do it + && !LinksetRoot.IsIncomplete // if waiting for assets or whatever, don't change + && LinksetRoot.PhysShape.HasPhysicalShape // there must be a physical shape assigned + && m_physicsScene.PE.IsCompound(LinksetRoot.PhysShape.physShapeInfo)) + { + // It is possible that the linkset is still under construction and the child is not yet + // inserted into the compound shape. A rebuild of the linkset in a pre-step action will + // build the whole thing with the new position or rotation. + // The index must be checked because Bullet references the child array but does no validity + // checking of the child index passed. + int numLinksetChildren = m_physicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape.physShapeInfo); + if (updated.LinksetChildIndex < numLinksetChildren) + { + BulletShape linksetChildShape = m_physicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex); + if (linksetChildShape.HasPhysicalShape) + { + // Found the child shape within the compound shape + m_physicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex, + updated.RawPosition - LinksetRoot.RawPosition, + updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation), + true /* shouldRecalculateLocalAabb */); + updatedChild = true; + DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1},pos={2},rot={3}", + updated.LocalID, whichUpdated, updated.RawPosition, updated.RawOrientation); + } + else // DEBUG DEBUG + { // DEBUG DEBUG + DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}", + updated.LocalID, linksetChildShape); + } // DEBUG DEBUG + } + else // DEBUG DEBUG + { // DEBUG DEBUG + // the child is not yet in the compound shape. This is non-fatal. + DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,childNotInCompoundShape,numChildren={1},index={2}", + updated.LocalID, numLinksetChildren, updated.LinksetChildIndex); + } // DEBUG DEBUG + } + else // DEBUG DEBUG + { // DEBUG DEBUG + DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noBodyOrNotCompound", updated.LocalID); + } // DEBUG DEBUG + + if (!updatedChild) + { + // If couldn't do the individual child, the linkset needs a rebuild to incorporate the new child info. + // Note: there are several ways through this code that will not update the child if + // the linkset is being rebuilt. In this case, scheduling a rebuild is a NOOP since + // there will already be a rebuild scheduled. + DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild.schedulingRebuild,whichUpdated={1}", + updated.LocalID, whichUpdated); + Refresh(updated); + } + } + } + } + + // Routine called when rebuilding the body of some member of the linkset. + // If one of the bodies is being changed, the linkset needs rebuilding. + // For instance, a linkset is built and then a mesh asset is read in and the mesh is recreated. + // Returns 'true' of something was actually removed and would need restoring + // Called at taint-time!! + public override bool RemoveDependencies(BSPrimLinkable child) + { + bool ret = false; + + DetailLog("{0},BSLinksetCompound.RemoveDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}", + child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody, IsRoot(child)); + + Refresh(child); + + return ret; + } + + // ================================================================ + + // Add a new child to the linkset. + // Called while LinkActivity is locked. + protected override void AddChildToLinkset(BSPrimLinkable child) + { + if (!HasChild(child)) + { + m_children.Add(child, new BSLinkInfo(child)); + + DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); + + // Rebuild the compound shape with the new child shape included + Refresh(child); + } + return; + } + + // Remove the specified child from the linkset. + // Safe to call even if the child is not really in the linkset. + protected override void RemoveChildFromLinkset(BSPrimLinkable child, bool inTaintTime) + { + child.ClearDisplacement(); + + if (m_children.Remove(child)) + { + DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", + child.LocalID, + LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString, + child.LocalID, child.PhysBody.AddrString); + + // Cause the child's body to be rebuilt and thus restored to normal operation + child.ForceBodyShapeRebuild(inTaintTime); + + if (!HasAnyChildren) + { + // The linkset is now empty. The root needs rebuilding. + LinksetRoot.ForceBodyShapeRebuild(inTaintTime); + } + else + { + // Rebuild the compound shape with the child removed + Refresh(LinksetRoot); + } + } + return; + } + + // Called before the simulation step to make sure the compound based linkset + // is all initialized. + // Constraint linksets are rebuilt every time. + // Note that this works for rebuilding just the root after a linkset is taken apart. + // Called at taint time!! + private bool UseBulletSimRootOffsetHack = false; // Attempt to have Bullet track the coords of root compound shape + private void RecomputeLinksetCompound() + { + try + { + Rebuilding = true; + + // No matter what is being done, force the root prim's PhysBody and PhysShape to get set + // to what they should be as if the root was not in a linkset. + // Not that bad since we only get into this routine if there are children in the linkset and + // something has been updated/changed. + // Have to do the rebuild before checking for physical because this might be a linkset + // being destructed and going non-physical. + LinksetRoot.ForceBodyShapeRebuild(true); + + // There is no reason to build all this physical stuff for a non-physical or empty linkset. + if (!LinksetRoot.IsPhysicallyActive || !HasAnyChildren) + { + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,notPhysicalOrNoChildren", LinksetRoot.LocalID); + return; // Note the 'finally' clause at the botton which will get executed. + } + + // Get a new compound shape to build the linkset shape in. + BSShape linksetShape = BSShapeCompound.GetReference(m_physicsScene); + + // Compute a displacement for each component so it is relative to the center-of-mass. + // Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass + OMV.Vector3 centerOfMassW = ComputeLinksetCenterOfMass(); + + OMV.Quaternion invRootOrientation = OMV.Quaternion.Normalize(OMV.Quaternion.Inverse(LinksetRoot.RawOrientation)); + OMV.Vector3 origRootPosition = LinksetRoot.RawPosition; + + // 'centerDisplacementV' is the vehicle relative distance from the simulator root position to the center-of-mass + OMV.Vector3 centerDisplacementV = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation; + if (UseBulletSimRootOffsetHack || !BSParam.LinksetOffsetCenterOfMass) + { + // Zero everything if center-of-mass displacement is not being done. + centerDisplacementV = OMV.Vector3.Zero; + LinksetRoot.ClearDisplacement(); + } + else + { + // The actual center-of-mass could have been set by the user. + centerDisplacementV = LinksetRoot.SetEffectiveCenterOfMassDisplacement(centerDisplacementV); + } + + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,rootPos={1},com={2},comDisp={3}", + LinksetRoot.LocalID, origRootPosition, centerOfMassW, centerDisplacementV); + + // Add the shapes of all the components of the linkset + int memberIndex = 1; + ForEachMember((cPrim) => + { + if (IsRoot(cPrim)) + { + // Root shape is always index zero. + cPrim.LinksetChildIndex = 0; + } + else + { + cPrim.LinksetChildIndex = memberIndex; + memberIndex++; + } + + // Get a reference to the shape of the child for adding of that shape to the linkset compound shape + BSShape childShape = cPrim.PhysShape.GetReference(m_physicsScene, cPrim); + + // Offset the child shape from the center-of-mass and rotate it to root relative. + OMV.Vector3 offsetPos = (cPrim.RawPosition - origRootPosition) * invRootOrientation - centerDisplacementV; + OMV.Quaternion offsetRot = OMV.Quaternion.Normalize(cPrim.RawOrientation) * invRootOrientation; + + // Add the child shape to the compound shape being built + if (childShape.physShapeInfo.HasPhysicalShape) + { + m_physicsScene.PE.AddChildShapeToCompoundShape(linksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot); + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},cShape={2},offPos={3},offRot={4}", + LinksetRoot.LocalID, cPrim.LinksetChildIndex, childShape, offsetPos, offsetRot); + + // Since we are borrowing the shape of the child, disable the origional child body + if (!IsRoot(cPrim)) + { + m_physicsScene.PE.AddToCollisionFlags(cPrim.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + m_physicsScene.PE.ForceActivationState(cPrim.PhysBody, ActivationState.DISABLE_SIMULATION); + // We don't want collisions from the old linkset children. + m_physicsScene.PE.RemoveFromCollisionFlags(cPrim.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + cPrim.PhysBody.collisionType = CollisionType.LinksetChild; + } + } + else + { + // The linkset must be in an intermediate state where all the children have not yet + // been constructed. This sometimes happens on startup when everything is getting + // built and some shapes have to wait for assets to be read in. + // Just skip this linkset for the moment and cause the shape to be rebuilt next tick. + // One problem might be that the shape is broken somehow and it never becomes completely + // available. This might cause the rebuild to happen over and over. + InternalScheduleRebuild(LinksetRoot); + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChildWithNoShape,indx={1},cShape={2},offPos={3},offRot={4}", + LinksetRoot.LocalID, cPrim.LinksetChildIndex, childShape, offsetPos, offsetRot); + // Output an annoying warning. It should only happen once but if it keeps coming out, + // the user knows there is something wrong and will report it. + m_physicsScene.Logger.WarnFormat("{0} Linkset rebuild warning. If this happens more than one or two times, please report in Mantis 7191", LogHeader); + m_physicsScene.Logger.WarnFormat("{0} pName={1}, childIdx={2}, shape={3}", + LogHeader, LinksetRoot.Name, cPrim.LinksetChildIndex, childShape); + + // This causes the loop to bail on building the rest of this linkset. + // The rebuild operation will fix it up next tick or declare the object unbuildable. + return true; + } + + return false; // 'false' says to move onto the next child in the list + }); + + // Replace the root shape with the built compound shape. + // Object removed and added to world to get collision cache rebuilt for new shape. + LinksetRoot.PhysShape.Dereference(m_physicsScene); + LinksetRoot.PhysShape = linksetShape; + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, LinksetRoot.PhysBody); + m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, linksetShape.physShapeInfo); + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, LinksetRoot.PhysBody); + DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addBody,body={1},shape={2}", + LinksetRoot.LocalID, LinksetRoot.PhysBody, linksetShape); + + // With all of the linkset packed into the root prim, it has the mass of everyone. + LinksetMass = ComputeLinksetMass(); + LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true); + + if (UseBulletSimRootOffsetHack) + { + // Enable the physical position updator to return the position and rotation of the root shape. + // This enables a feature in the C++ code to return the world coordinates of the first shape in the + // compound shape. This aleviates the need to offset the returned physical position by the + // center-of-mass offset. + // TODO: either debug this feature or remove it. + m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE); + } + } + finally + { + Rebuilding = false; + } + + // See that the Aabb surrounds the new shape + m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo); + } +} +} \ No newline at end of file diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs new file mode 100755 index 0000000..4384cdc --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs @@ -0,0 +1,854 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; + +using OpenSim.Region.OptionalModules.Scripting; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public sealed class BSLinksetConstraints : BSLinkset +{ + // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]"; + + public class BSLinkInfoConstraint : BSLinkInfo + { + public ConstraintType constraintType; + public BSConstraint constraint; + public OMV.Vector3 linearLimitLow; + public OMV.Vector3 linearLimitHigh; + public OMV.Vector3 angularLimitLow; + public OMV.Vector3 angularLimitHigh; + public bool useFrameOffset; + public bool enableTransMotor; + public float transMotorMaxVel; + public float transMotorMaxForce; + public float cfm; + public float erp; + public float solverIterations; + // + public OMV.Vector3 frameInAloc; + public OMV.Quaternion frameInArot; + public OMV.Vector3 frameInBloc; + public OMV.Quaternion frameInBrot; + public bool useLinearReferenceFrameA; + // Spring + public bool[] springAxisEnable; + public float[] springDamping; + public float[] springStiffness; + public OMV.Vector3 springLinearEquilibriumPoint; + public OMV.Vector3 springAngularEquilibriumPoint; + + public BSLinkInfoConstraint(BSPrimLinkable pMember) + : base(pMember) + { + constraint = null; + ResetLink(); + member.PhysScene.DetailLog("{0},BSLinkInfoConstraint.creation", member.LocalID); + } + + // Set all the parameters for this constraint to a fixed, non-movable constraint. + public override void ResetLink() + { + // constraintType = ConstraintType.D6_CONSTRAINT_TYPE; + constraintType = ConstraintType.BS_FIXED_CONSTRAINT_TYPE; + linearLimitLow = OMV.Vector3.Zero; + linearLimitHigh = OMV.Vector3.Zero; + angularLimitLow = OMV.Vector3.Zero; + angularLimitHigh = OMV.Vector3.Zero; + useFrameOffset = BSParam.LinkConstraintUseFrameOffset; + enableTransMotor = BSParam.LinkConstraintEnableTransMotor; + transMotorMaxVel = BSParam.LinkConstraintTransMotorMaxVel; + transMotorMaxForce = BSParam.LinkConstraintTransMotorMaxForce; + cfm = BSParam.LinkConstraintCFM; + erp = BSParam.LinkConstraintERP; + solverIterations = BSParam.LinkConstraintSolverIterations; + frameInAloc = OMV.Vector3.Zero; + frameInArot = OMV.Quaternion.Identity; + frameInBloc = OMV.Vector3.Zero; + frameInBrot = OMV.Quaternion.Identity; + useLinearReferenceFrameA = true; + springAxisEnable = new bool[6]; + springDamping = new float[6]; + springStiffness = new float[6]; + for (int ii = 0; ii < springAxisEnable.Length; ii++) + { + springAxisEnable[ii] = false; + springDamping[ii] = BSAPITemplate.SPRING_NOT_SPECIFIED; + springStiffness[ii] = BSAPITemplate.SPRING_NOT_SPECIFIED; + } + springLinearEquilibriumPoint = OMV.Vector3.Zero; + springAngularEquilibriumPoint = OMV.Vector3.Zero; + member.PhysScene.DetailLog("{0},BSLinkInfoConstraint.ResetLink", member.LocalID); + } + + // Given a constraint, apply the current constraint parameters to same. + public override void SetLinkParameters(BSConstraint constrain) + { + member.PhysScene.DetailLog("{0},BSLinkInfoConstraint.SetLinkParameters,type={1}", member.LocalID, constraintType); + switch (constraintType) + { + case ConstraintType.BS_FIXED_CONSTRAINT_TYPE: + case ConstraintType.D6_CONSTRAINT_TYPE: + BSConstraint6Dof constrain6dof = constrain as BSConstraint6Dof; + if (constrain6dof != null) + { + // NOTE: D6_SPRING_CONSTRAINT_TYPE should be updated if you change any of this code. + // zero linear and angular limits makes the objects unable to move in relation to each other + constrain6dof.SetLinearLimits(linearLimitLow, linearLimitHigh); + constrain6dof.SetAngularLimits(angularLimitLow, angularLimitHigh); + + // tweek the constraint to increase stability + constrain6dof.UseFrameOffset(useFrameOffset); + constrain6dof.TranslationalLimitMotor(enableTransMotor, transMotorMaxVel, transMotorMaxForce); + constrain6dof.SetCFMAndERP(cfm, erp); + if (solverIterations != 0f) + { + constrain6dof.SetSolverIterations(solverIterations); + } + } + break; + case ConstraintType.D6_SPRING_CONSTRAINT_TYPE: + BSConstraintSpring constrainSpring = constrain as BSConstraintSpring; + if (constrainSpring != null) + { + // zero linear and angular limits makes the objects unable to move in relation to each other + constrainSpring.SetLinearLimits(linearLimitLow, linearLimitHigh); + constrainSpring.SetAngularLimits(angularLimitLow, angularLimitHigh); + + // tweek the constraint to increase stability + constrainSpring.UseFrameOffset(useFrameOffset); + constrainSpring.TranslationalLimitMotor(enableTransMotor, transMotorMaxVel, transMotorMaxForce); + constrainSpring.SetCFMAndERP(cfm, erp); + if (solverIterations != 0f) + { + constrainSpring.SetSolverIterations(solverIterations); + } + for (int ii = 0; ii < springAxisEnable.Length; ii++) + { + constrainSpring.SetAxisEnable(ii, springAxisEnable[ii]); + if (springDamping[ii] != BSAPITemplate.SPRING_NOT_SPECIFIED) + constrainSpring.SetDamping(ii, springDamping[ii]); + if (springStiffness[ii] != BSAPITemplate.SPRING_NOT_SPECIFIED) + constrainSpring.SetStiffness(ii, springStiffness[ii]); + } + constrainSpring.CalculateTransforms(); + + if (springLinearEquilibriumPoint != OMV.Vector3.Zero) + constrainSpring.SetEquilibriumPoint(springLinearEquilibriumPoint, springAngularEquilibriumPoint); + else + constrainSpring.SetEquilibriumPoint(BSAPITemplate.SPRING_NOT_SPECIFIED, BSAPITemplate.SPRING_NOT_SPECIFIED); + } + break; + default: + break; + } + } + + // Return 'true' if the property updates from the physics engine should be reported + // to the simulator. + // If the constraint is fixed, we don't need to report as the simulator and viewer will + // report the right things. + public override bool ShouldUpdateChildProperties() + { + bool ret = true; + if (constraintType == ConstraintType.BS_FIXED_CONSTRAINT_TYPE) + ret = false; + + return ret; + } + } + + public BSLinksetConstraints(BSScene scene, BSPrimLinkable parent) : base(scene, parent) + { + LinksetImpl = LinksetImplementation.Constraint; + } + + private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINT]"; + + // When physical properties are changed the linkset needs to recalculate + // its internal properties. + // This is queued in the 'post taint' queue so the + // refresh will happen once after all the other taints are applied. + public override void Refresh(BSPrimLinkable requestor) + { + ScheduleRebuild(requestor); + base.Refresh(requestor); + + } + + private void ScheduleRebuild(BSPrimLinkable requestor) + { + DetailLog("{0},BSLinksetConstraint.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}", + requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren)); + + // When rebuilding, it is possible to set properties that would normally require a rebuild. + // If already rebuilding, don't request another rebuild. + // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. + lock (this) + { + if (!RebuildScheduled) + { + if (!Rebuilding && HasAnyChildren) + { + RebuildScheduled = true; + // Queue to happen after all the other taint processing + m_physicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate() + { + if (HasAnyChildren) + { + // Constraints that have not been changed are not rebuild but make sure + // the constraint of the requestor is rebuilt. + PhysicallyUnlinkAChildFromRoot(LinksetRoot, requestor); + // Rebuild the linkset and all its constraints. + RecomputeLinksetConstraints(); + } + RebuildScheduled = false; + }); + } + } + } + } + + // The object is going dynamic (physical). Do any setup necessary + // for a dynamic linkset. + // Only the state of the passed object can be modified. The rest of the linkset + // has not yet been fully constructed. + // Return 'true' if any properties updated on the passed object. + // Called at taint-time! + public override bool MakeDynamic(BSPrimLinkable child) + { + bool ret = false; + DetailLog("{0},BSLinksetConstraints.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); + if (IsRoot(child)) + { + // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly. + Refresh(LinksetRoot); + } + return ret; + } + + // The object is going static (non-physical). Do any setup necessary for a static linkset. + // Return 'true' if any properties updated on the passed object. + // This doesn't normally happen -- OpenSim removes the objects from the physical + // world if it is a static linkset. + // Called at taint-time! + public override bool MakeStatic(BSPrimLinkable child) + { + bool ret = false; + + DetailLog("{0},BSLinksetConstraint.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); + child.ClearDisplacement(); + if (IsRoot(child)) + { + // Schedule a rebuild to verify that the root shape is set to the real shape. + Refresh(LinksetRoot); + } + return ret; + } + + // Called at taint-time!! + public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable pObj) + { + // Nothing to do for constraints on property updates + } + + // Routine called when rebuilding the body of some member of the linkset. + // Destroy all the constraints have have been made to root and set + // up to rebuild the constraints before the next simulation step. + // Returns 'true' of something was actually removed and would need restoring + // Called at taint-time!! + public override bool RemoveDependencies(BSPrimLinkable child) + { + bool ret = false; + + DetailLog("{0},BSLinksetConstraint.RemoveDependencies,removeChildrenForRoot,rID={1},rBody={2}", + child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString); + + lock (m_linksetActivityLock) + { + // Just undo all the constraints for this linkset. Rebuild at the end of the step. + ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot); + // Cause the constraints, et al to be rebuilt before the next simulation step. + Refresh(LinksetRoot); + } + return ret; + } + + // ================================================================ + + // Add a new child to the linkset. + // Called while LinkActivity is locked. + protected override void AddChildToLinkset(BSPrimLinkable child) + { + if (!HasChild(child)) + { + m_children.Add(child, new BSLinkInfoConstraint(child)); + + DetailLog("{0},BSLinksetConstraints.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); + + // Cause constraints and assorted properties to be recomputed before the next simulation step. + Refresh(LinksetRoot); + } + return; + } + + // Remove the specified child from the linkset. + // Safe to call even if the child is not really in my linkset. + protected override void RemoveChildFromLinkset(BSPrimLinkable child, bool inTaintTime) + { + if (m_children.Remove(child)) + { + BSPrimLinkable rootx = LinksetRoot; // capture the root and body as of now + BSPrimLinkable childx = child; + + DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", + childx.LocalID, + rootx.LocalID, rootx.PhysBody.AddrString, + childx.LocalID, childx.PhysBody.AddrString); + + m_physicsScene.TaintedObject(inTaintTime, childx.LocalID, "BSLinksetConstraints.RemoveChildFromLinkset", delegate() + { + PhysicallyUnlinkAChildFromRoot(rootx, childx); + }); + // See that the linkset parameters are recomputed at the end of the taint time. + Refresh(LinksetRoot); + } + else + { + // Non-fatal occurance. + // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader); + } + return; + } + + // Create a constraint between me (root of linkset) and the passed prim (the child). + // Called at taint time! + private void PhysicallyLinkAChildToRoot(BSPrimLinkable rootPrim, BSPrimLinkable childPrim) + { + // Don't build the constraint when asked. Put it off until just before the simulation step. + Refresh(rootPrim); + } + + // Create a static constraint between the two passed objects + private BSConstraint BuildConstraint(BSPrimLinkable rootPrim, BSLinkInfo li) + { + BSLinkInfoConstraint linkInfo = li as BSLinkInfoConstraint; + if (linkInfo == null) + return null; + + // Zero motion for children so they don't interpolate + li.member.ZeroMotion(true); + + BSConstraint constrain = null; + + switch (linkInfo.constraintType) + { + case ConstraintType.BS_FIXED_CONSTRAINT_TYPE: + case ConstraintType.D6_CONSTRAINT_TYPE: + // Relative position normalized to the root prim + // Essentually a vector pointing from center of rootPrim to center of li.member + OMV.Vector3 childRelativePosition = linkInfo.member.Position - rootPrim.Position; + + // real world coordinate of midpoint between the two objects + OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); + + DetailLog("{0},BSLinksetConstraint.BuildConstraint,6Dof,rBody={1},cBody={2},rLoc={3},cLoc={4},midLoc={5}", + rootPrim.LocalID, rootPrim.PhysBody, linkInfo.member.PhysBody, + rootPrim.Position, linkInfo.member.Position, midPoint); + + // create a constraint that allows no freedom of movement between the two objects + // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 + + constrain = new BSConstraint6Dof( + m_physicsScene.World, rootPrim.PhysBody, linkInfo.member.PhysBody, midPoint, true, true ); + + /* NOTE: below is an attempt to build constraint with full frame computation, etc. + * Using the midpoint is easier since it lets the Bullet code manipulate the transforms + * of the objects. + * Code left for future programmers. + // ================================================================================== + // relative position normalized to the root prim + OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation); + OMV.Vector3 childRelativePosition = (liConstraint.member.Position - rootPrim.Position) * invThisOrientation; + + // relative rotation of the child to the parent + OMV.Quaternion childRelativeRotation = invThisOrientation * liConstraint.member.Orientation; + OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation); + + DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, liConstraint.member.LocalID); + constrain = new BS6DofConstraint( + PhysicsScene.World, rootPrim.Body, liConstraint.member.Body, + OMV.Vector3.Zero, + OMV.Quaternion.Inverse(rootPrim.Orientation), + OMV.Vector3.Zero, + OMV.Quaternion.Inverse(liConstraint.member.Orientation), + true, + true + ); + // ================================================================================== + */ + + break; + case ConstraintType.D6_SPRING_CONSTRAINT_TYPE: + constrain = new BSConstraintSpring(m_physicsScene.World, rootPrim.PhysBody, linkInfo.member.PhysBody, + linkInfo.frameInAloc, linkInfo.frameInArot, linkInfo.frameInBloc, linkInfo.frameInBrot, + linkInfo.useLinearReferenceFrameA, + true /*disableCollisionsBetweenLinkedBodies*/); + DetailLog("{0},BSLinksetConstraint.BuildConstraint,spring,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6}", + rootPrim.LocalID, + rootPrim.LocalID, rootPrim.PhysBody.AddrString, + linkInfo.member.LocalID, linkInfo.member.PhysBody.AddrString, + rootPrim.Position, linkInfo.member.Position); + + break; + default: + break; + } + + linkInfo.SetLinkParameters(constrain); + + m_physicsScene.Constraints.AddConstraint(constrain); + + return constrain; + } + + // Remove linkage between the linkset root and a particular child + // The root and child bodies are passed in because we need to remove the constraint between + // the bodies that were present at unlink time. + // Called at taint time! + private bool PhysicallyUnlinkAChildFromRoot(BSPrimLinkable rootPrim, BSPrimLinkable childPrim) + { + bool ret = false; + DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", + rootPrim.LocalID, + rootPrim.LocalID, rootPrim.PhysBody.AddrString, + childPrim.LocalID, childPrim.PhysBody.AddrString); + + // If asked to unlink root from root, just remove all the constraints + if (rootPrim == childPrim || childPrim == LinksetRoot) + { + PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot); + ret = true; + } + else + { + // Find the constraint for this link and get rid of it from the overall collection and from my list + if (m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody)) + { + // Make the child refresh its location + m_physicsScene.PE.PushUpdate(childPrim.PhysBody); + ret = true; + } + } + + return ret; + } + + // Remove linkage between myself and any possible children I might have. + // Returns 'true' of any constraints were destroyed. + // Called at taint time! + private bool PhysicallyUnlinkAllChildrenFromRoot(BSPrimLinkable rootPrim) + { + DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); + + return m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody); + } + + // Call each of the constraints that make up this linkset and recompute the + // various transforms and variables. Create constraints of not created yet. + // Called before the simulation step to make sure the constraint based linkset + // is all initialized. + // Called at taint time!! + private void RecomputeLinksetConstraints() + { + float linksetMass = LinksetMass; + LinksetRoot.UpdatePhysicalMassProperties(linksetMass, true); + + DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}", + LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString, linksetMass); + + try + { + Rebuilding = true; + + // There is no reason to build all this physical stuff for a non-physical linkset. + if (!LinksetRoot.IsPhysicallyActive || !HasAnyChildren) + { + DetailLog("{0},BSLinksetConstraint.RecomputeLinksetCompound,notPhysicalOrNoChildren", LinksetRoot.LocalID); + return; // Note the 'finally' clause at the botton which will get executed. + } + + ForEachLinkInfo((li) => + { + // A child in the linkset physically shows the mass of the whole linkset. + // This allows Bullet to apply enough force on the child to move the whole linkset. + // (Also do the mass stuff before recomputing the constraint so mass is not zero.) + li.member.UpdatePhysicalMassProperties(linksetMass, true); + + BSConstraint constrain; + if (!m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, li.member.PhysBody, out constrain)) + { + // If constraint doesn't exist yet, create it. + constrain = BuildConstraint(LinksetRoot, li); + } + li.SetLinkParameters(constrain); + constrain.RecomputeConstraintVariables(linksetMass); + + // PhysicsScene.PE.DumpConstraint(PhysicsScene.World, constrain.Constraint); // DEBUG DEBUG + return false; // 'false' says to keep processing other members + }); + } + finally + { + Rebuilding = false; + } + } + + #region Extension + public override object Extension(string pFunct, params object[] pParams) + { + object ret = null; + switch (pFunct) + { + // pParams = [ BSPhysObject root, BSPhysObject child, integer linkType ] + case ExtendedPhysics.PhysFunctChangeLinkType: + if (pParams.Length > 2) + { + int requestedType = (int)pParams[2]; + DetailLog("{0},BSLinksetConstraint.ChangeLinkType,requestedType={1}", LinksetRoot.LocalID, requestedType); + if (requestedType == (int)ConstraintType.BS_FIXED_CONSTRAINT_TYPE + || requestedType == (int)ConstraintType.D6_CONSTRAINT_TYPE + || requestedType == (int)ConstraintType.D6_SPRING_CONSTRAINT_TYPE + || requestedType == (int)ConstraintType.HINGE_CONSTRAINT_TYPE + || requestedType == (int)ConstraintType.CONETWIST_CONSTRAINT_TYPE + || requestedType == (int)ConstraintType.SLIDER_CONSTRAINT_TYPE) + { + BSPrimLinkable child = pParams[1] as BSPrimLinkable; + if (child != null) + { + DetailLog("{0},BSLinksetConstraint.ChangeLinkType,rootID={1},childID={2},type={3}", + LinksetRoot.LocalID, LinksetRoot.LocalID, child.LocalID, requestedType); + m_physicsScene.TaintedObject(child.LocalID, "BSLinksetConstraint.PhysFunctChangeLinkType", delegate() + { + // Pick up all the constraints currently created. + RemoveDependencies(child); + + BSLinkInfo linkInfo = null; + if (TryGetLinkInfo(child, out linkInfo)) + { + BSLinkInfoConstraint linkInfoC = linkInfo as BSLinkInfoConstraint; + if (linkInfoC != null) + { + linkInfoC.constraintType = (ConstraintType)requestedType; + ret = (object)true; + DetailLog("{0},BSLinksetConstraint.ChangeLinkType,link={1},type={2}", + linkInfo.member.LocalID, linkInfo.member.LocalID, linkInfoC.constraintType); + } + else + { + DetailLog("{0},BSLinksetConstraint.ChangeLinkType,linkInfoNotConstraint,childID={1}", LinksetRoot.LocalID, child.LocalID); + } + } + else + { + DetailLog("{0},BSLinksetConstraint.ChangeLinkType,noLinkInfoForChild,childID={1}", LinksetRoot.LocalID, child.LocalID); + } + // Cause the whole linkset to be rebuilt in post-taint time. + Refresh(child); + }); + } + else + { + DetailLog("{0},BSLinksetConstraint.SetLinkType,childNotBSPrimLinkable", LinksetRoot.LocalID); + } + } + else + { + DetailLog("{0},BSLinksetConstraint.SetLinkType,illegalRequestedType,reqested={1},spring={2}", + LinksetRoot.LocalID, requestedType, ((int)ConstraintType.D6_SPRING_CONSTRAINT_TYPE)); + } + } + break; + // pParams = [ BSPhysObject root, BSPhysObject child ] + case ExtendedPhysics.PhysFunctGetLinkType: + if (pParams.Length > 0) + { + BSPrimLinkable child = pParams[1] as BSPrimLinkable; + if (child != null) + { + BSLinkInfo linkInfo = null; + if (TryGetLinkInfo(child, out linkInfo)) + { + BSLinkInfoConstraint linkInfoC = linkInfo as BSLinkInfoConstraint; + if (linkInfoC != null) + { + ret = (object)(int)linkInfoC.constraintType; + DetailLog("{0},BSLinksetConstraint.GetLinkType,link={1},type={2}", + linkInfo.member.LocalID, linkInfo.member.LocalID, linkInfoC.constraintType); + + } + } + } + } + break; + // pParams = [ BSPhysObject root, BSPhysObject child, int op, object opParams, int op, object opParams, ... ] + case ExtendedPhysics.PhysFunctChangeLinkParams: + // There should be two parameters: the childActor and a list of parameters to set + if (pParams.Length > 2) + { + BSPrimLinkable child = pParams[1] as BSPrimLinkable; + BSLinkInfo baseLinkInfo = null; + if (TryGetLinkInfo(child, out baseLinkInfo)) + { + BSLinkInfoConstraint linkInfo = baseLinkInfo as BSLinkInfoConstraint; + if (linkInfo != null) + { + int valueInt; + float valueFloat; + bool valueBool; + OMV.Vector3 valueVector; + OMV.Vector3 valueVector2; + OMV.Quaternion valueQuaternion; + int axisLow, axisHigh; + + int opIndex = 2; + while (opIndex < pParams.Length) + { + int thisOp = 0; + string errMsg = ""; + try + { + thisOp = (int)pParams[opIndex]; + DetailLog("{0},BSLinksetConstraint.ChangeLinkParams2,op={1},val={2}", + linkInfo.member.LocalID, thisOp, pParams[opIndex + 1]); + switch (thisOp) + { + case ExtendedPhysics.PHYS_PARAM_LINK_TYPE: + valueInt = (int)pParams[opIndex + 1]; + ConstraintType valueType = (ConstraintType)valueInt; + if (valueType == ConstraintType.BS_FIXED_CONSTRAINT_TYPE + || valueType == ConstraintType.D6_CONSTRAINT_TYPE + || valueType == ConstraintType.D6_SPRING_CONSTRAINT_TYPE + || valueType == ConstraintType.HINGE_CONSTRAINT_TYPE + || valueType == ConstraintType.CONETWIST_CONSTRAINT_TYPE + || valueType == ConstraintType.SLIDER_CONSTRAINT_TYPE) + { + linkInfo.constraintType = valueType; + } + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_FRAMEINA_LOC: + errMsg = "PHYS_PARAM_FRAMEINA_LOC takes one parameter of type vector"; + valueVector = (OMV.Vector3)pParams[opIndex + 1]; + linkInfo.frameInAloc = valueVector; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_FRAMEINA_ROT: + errMsg = "PHYS_PARAM_FRAMEINA_ROT takes one parameter of type rotation"; + valueQuaternion = (OMV.Quaternion)pParams[opIndex + 1]; + linkInfo.frameInArot = valueQuaternion; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_FRAMEINB_LOC: + errMsg = "PHYS_PARAM_FRAMEINB_LOC takes one parameter of type vector"; + valueVector = (OMV.Vector3)pParams[opIndex + 1]; + linkInfo.frameInBloc = valueVector; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_FRAMEINB_ROT: + errMsg = "PHYS_PARAM_FRAMEINB_ROT takes one parameter of type rotation"; + valueQuaternion = (OMV.Quaternion)pParams[opIndex + 1]; + linkInfo.frameInBrot = valueQuaternion; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_LINEAR_LIMIT_LOW: + errMsg = "PHYS_PARAM_LINEAR_LIMIT_LOW takes one parameter of type vector"; + valueVector = (OMV.Vector3)pParams[opIndex + 1]; + linkInfo.linearLimitLow = valueVector; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_LINEAR_LIMIT_HIGH: + errMsg = "PHYS_PARAM_LINEAR_LIMIT_HIGH takes one parameter of type vector"; + valueVector = (OMV.Vector3)pParams[opIndex + 1]; + linkInfo.linearLimitHigh = valueVector; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_ANGULAR_LIMIT_LOW: + errMsg = "PHYS_PARAM_ANGULAR_LIMIT_LOW takes one parameter of type vector"; + valueVector = (OMV.Vector3)pParams[opIndex + 1]; + linkInfo.angularLimitLow = valueVector; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_ANGULAR_LIMIT_HIGH: + errMsg = "PHYS_PARAM_ANGULAR_LIMIT_HIGH takes one parameter of type vector"; + valueVector = (OMV.Vector3)pParams[opIndex + 1]; + linkInfo.angularLimitHigh = valueVector; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_USE_FRAME_OFFSET: + errMsg = "PHYS_PARAM_USE_FRAME_OFFSET takes one parameter of type integer (bool)"; + valueBool = ((int)pParams[opIndex + 1]) != 0; + linkInfo.useFrameOffset = valueBool; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_ENABLE_TRANSMOTOR: + errMsg = "PHYS_PARAM_ENABLE_TRANSMOTOR takes one parameter of type integer (bool)"; + valueBool = ((int)pParams[opIndex + 1]) != 0; + linkInfo.enableTransMotor = valueBool; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_TRANSMOTOR_MAXVEL: + errMsg = "PHYS_PARAM_TRANSMOTOR_MAXVEL takes one parameter of type float"; + valueFloat = (float)pParams[opIndex + 1]; + linkInfo.transMotorMaxVel = valueFloat; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_TRANSMOTOR_MAXFORCE: + errMsg = "PHYS_PARAM_TRANSMOTOR_MAXFORCE takes one parameter of type float"; + valueFloat = (float)pParams[opIndex + 1]; + linkInfo.transMotorMaxForce = valueFloat; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_CFM: + errMsg = "PHYS_PARAM_CFM takes one parameter of type float"; + valueFloat = (float)pParams[opIndex + 1]; + linkInfo.cfm = valueFloat; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_ERP: + errMsg = "PHYS_PARAM_ERP takes one parameter of type float"; + valueFloat = (float)pParams[opIndex + 1]; + linkInfo.erp = valueFloat; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_SOLVER_ITERATIONS: + errMsg = "PHYS_PARAM_SOLVER_ITERATIONS takes one parameter of type float"; + valueFloat = (float)pParams[opIndex + 1]; + linkInfo.solverIterations = valueFloat; + opIndex += 2; + break; + case ExtendedPhysics.PHYS_PARAM_SPRING_AXIS_ENABLE: + errMsg = "PHYS_PARAM_SPRING_AXIS_ENABLE takes two parameters of types integer and integer (bool)"; + valueInt = (int)pParams[opIndex + 1]; + valueBool = ((int)pParams[opIndex + 2]) != 0; + GetAxisRange(valueInt, out axisLow, out axisHigh); + for (int ii = axisLow; ii <= axisHigh; ii++) + linkInfo.springAxisEnable[ii] = valueBool; + opIndex += 3; + break; + case ExtendedPhysics.PHYS_PARAM_SPRING_DAMPING: + errMsg = "PHYS_PARAM_SPRING_DAMPING takes two parameters of types integer and float"; + valueInt = (int)pParams[opIndex + 1]; + valueFloat = (float)pParams[opIndex + 2]; + GetAxisRange(valueInt, out axisLow, out axisHigh); + for (int ii = axisLow; ii <= axisHigh; ii++) + linkInfo.springDamping[ii] = valueFloat; + opIndex += 3; + break; + case ExtendedPhysics.PHYS_PARAM_SPRING_STIFFNESS: + errMsg = "PHYS_PARAM_SPRING_STIFFNESS takes two parameters of types integer and float"; + valueInt = (int)pParams[opIndex + 1]; + valueFloat = (float)pParams[opIndex + 2]; + GetAxisRange(valueInt, out axisLow, out axisHigh); + for (int ii = axisLow; ii <= axisHigh; ii++) + linkInfo.springStiffness[ii] = valueFloat; + opIndex += 3; + break; + case ExtendedPhysics.PHYS_PARAM_SPRING_EQUILIBRIUM_POINT: + errMsg = "PHYS_PARAM_SPRING_EQUILIBRIUM_POINT takes two parameters of type vector"; + valueVector = (OMV.Vector3)pParams[opIndex + 1]; + valueVector2 = (OMV.Vector3)pParams[opIndex + 2]; + linkInfo.springLinearEquilibriumPoint = valueVector; + linkInfo.springAngularEquilibriumPoint = valueVector2; + opIndex += 3; + break; + case ExtendedPhysics.PHYS_PARAM_USE_LINEAR_FRAMEA: + errMsg = "PHYS_PARAM_USE_LINEAR_FRAMEA takes one parameter of type integer (bool)"; + valueBool = ((int)pParams[opIndex + 1]) != 0; + linkInfo.useLinearReferenceFrameA = valueBool; + opIndex += 2; + break; + default: + break; + } + } + catch (InvalidCastException e) + { + m_physicsScene.Logger.WarnFormat("{0} value of wrong type in physSetLinksetParams: {1}, err={2}", + LogHeader, errMsg, e); + } + catch (Exception e) + { + m_physicsScene.Logger.WarnFormat("{0} bad parameters in physSetLinksetParams: {1}", LogHeader, e); + } + } + } + // Something changed so a rebuild is in order + Refresh(child); + } + } + break; + default: + ret = base.Extension(pFunct, pParams); + break; + } + return ret; + } + + // Bullet constraints keep some limit parameters for each linear and angular axis. + // Setting same is easier if there is an easy way to see all or types. + // This routine returns the array limits for the set of axis. + private void GetAxisRange(int rangeSpec, out int low, out int high) + { + switch (rangeSpec) + { + case ExtendedPhysics.PHYS_AXIS_LINEAR_ALL: + low = 0; + high = 2; + break; + case ExtendedPhysics.PHYS_AXIS_ANGULAR_ALL: + low = 3; + high = 5; + break; + case ExtendedPhysics.PHYS_AXIS_ALL: + low = 0; + high = 5; + break; + default: + low = high = rangeSpec; + break; + } + return; + } + #endregion // Extension + +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSMaterials.cs b/OpenSim/Region/PhysicsModules/BulletS/BSMaterials.cs new file mode 100755 index 0000000..ee77d6e --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSMaterials.cs @@ -0,0 +1,203 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using Nini.Config; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public struct MaterialAttributes +{ + // Material type values that correspond with definitions for LSL + public enum Material : int + { + Stone = 0, + Metal, + Glass, + Wood, + Flesh, + Plastic, + Rubber, + Light, + // Hereafter are BulletSim additions + Avatar, + NumberOfTypes // the count of types in the enum. + } + + // Names must be in the order of the above enum. + // These names must coorespond to the lower case field names in the MaterialAttributes + // structure as reflection is used to select the field to put the value in. + public static readonly string[] MaterialAttribs = { "Density", "Friction", "Restitution"}; + + public MaterialAttributes(string t, float d, float f, float r) + { + type = t; + density = d; + friction = f; + restitution = r; + } + public string type; + public float density; + public float friction; + public float restitution; +} + +public static class BSMaterials +{ + // Attributes for each material type + private static readonly MaterialAttributes[] Attributes; + + // Map of material name to material type code + public static readonly Dictionary MaterialMap; + + static BSMaterials() + { + // Attribute sets for both the non-physical and physical instances of materials. + Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2]; + + // Map of name to type code. + MaterialMap = new Dictionary(); + MaterialMap.Add("Stone", MaterialAttributes.Material.Stone); + MaterialMap.Add("Metal", MaterialAttributes.Material.Metal); + MaterialMap.Add("Glass", MaterialAttributes.Material.Glass); + MaterialMap.Add("Wood", MaterialAttributes.Material.Wood); + MaterialMap.Add("Flesh", MaterialAttributes.Material.Flesh); + MaterialMap.Add("Plastic", MaterialAttributes.Material.Plastic); + MaterialMap.Add("Rubber", MaterialAttributes.Material.Rubber); + MaterialMap.Add("Light", MaterialAttributes.Material.Light); + MaterialMap.Add("Avatar", MaterialAttributes.Material.Avatar); + } + + // This is where all the default material attributes are defined. + public static void InitializeFromDefaults(ConfigurationParameters parms) + { + // Values from http://wiki.secondlife.com/wiki/PRIM_MATERIAL + float dDensity = parms.defaultDensity; + float dFriction = parms.defaultFriction; + float dRestitution = parms.defaultRestitution; + Attributes[(int)MaterialAttributes.Material.Stone] = + new MaterialAttributes("stone",dDensity, 0.8f, 0.4f); + Attributes[(int)MaterialAttributes.Material.Metal] = + new MaterialAttributes("metal",dDensity, 0.3f, 0.4f); + Attributes[(int)MaterialAttributes.Material.Glass] = + new MaterialAttributes("glass",dDensity, 0.2f, 0.7f); + Attributes[(int)MaterialAttributes.Material.Wood] = + new MaterialAttributes("wood",dDensity, 0.6f, 0.5f); + Attributes[(int)MaterialAttributes.Material.Flesh] = + new MaterialAttributes("flesh",dDensity, 0.9f, 0.3f); + Attributes[(int)MaterialAttributes.Material.Plastic] = + new MaterialAttributes("plastic",dDensity, 0.4f, 0.7f); + Attributes[(int)MaterialAttributes.Material.Rubber] = + new MaterialAttributes("rubber",dDensity, 0.9f, 0.9f); + Attributes[(int)MaterialAttributes.Material.Light] = + new MaterialAttributes("light",dDensity, dFriction, dRestitution); + Attributes[(int)MaterialAttributes.Material.Avatar] = + new MaterialAttributes("avatar",3.5f, 0.2f, 0f); + + Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("stonePhysical",dDensity, 0.8f, 0.4f); + Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("metalPhysical",dDensity, 0.3f, 0.4f); + Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("glassPhysical",dDensity, 0.2f, 0.7f); + Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("woodPhysical",dDensity, 0.6f, 0.5f); + Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("fleshPhysical",dDensity, 0.9f, 0.3f); + Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("plasticPhysical",dDensity, 0.4f, 0.7f); + Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("rubberPhysical",dDensity, 0.9f, 0.9f); + Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("lightPhysical",dDensity, dFriction, dRestitution); + Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] = + new MaterialAttributes("avatarPhysical",3.5f, 0.2f, 0f); + } + + // Under the [BulletSim] section, one can change the individual material + // attribute values. The format of the configuration parameter is: + // ["Physical"] = floatValue + // For instance: + // [BulletSim] + // StoneFriction = 0.2 + // FleshRestitutionPhysical = 0.8 + // Materials can have different parameters for their static and + // physical instantiations. When setting the non-physical value, + // both values are changed. Setting the physical value only changes + // the physical value. + public static void InitializefromParameters(IConfig pConfig) + { + foreach (KeyValuePair kvp in MaterialMap) + { + string matName = kvp.Key; + foreach (string attribName in MaterialAttributes.MaterialAttribs) + { + string paramName = matName + attribName; + if (pConfig.Contains(paramName)) + { + float paramValue = pConfig.GetFloat(paramName); + SetAttributeValue((int)kvp.Value, attribName, paramValue); + // set the physical value also + SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue); + } + paramName += "Physical"; + if (pConfig.Contains(paramName)) + { + float paramValue = pConfig.GetFloat(paramName); + SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue); + } + } + } + } + + // Use reflection to set the value in the attribute structure. + private static void SetAttributeValue(int matType, string attribName, float val) + { + // Get the current attribute values for this material + MaterialAttributes thisAttrib = Attributes[matType]; + // Find the field for the passed attribute name (eg, find field named 'friction') + FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower()); + if (fieldInfo != null) + { + fieldInfo.SetValue(thisAttrib, val); + // Copy new attributes back to array -- since MaterialAttributes is 'struct', passed by value, not reference. + Attributes[matType] = thisAttrib; + } + } + + // Given a material type, return a structure of attributes. + public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical) + { + int ind = (int)type; + if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes; + return Attributes[ind]; + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSMotors.cs b/OpenSim/Region/PhysicsModules/BulletS/BSMotors.cs new file mode 100755 index 0000000..7693195 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSMotors.cs @@ -0,0 +1,451 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public abstract class BSMotor +{ + // Timescales and other things can be turned off by setting them to 'infinite'. + public const float Infinite = 12345.6f; + public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite); + + public BSMotor(string useName) + { + UseName = useName; + PhysicsScene = null; + Enabled = true; + } + public virtual bool Enabled { get; set; } + public virtual void Reset() { } + public virtual void Zero() { } + public virtual void GenerateTestOutput(float timeStep) { } + + // A name passed at motor creation for easily identifyable debugging messages. + public string UseName { get; private set; } + + // Used only for outputting debug information. Might not be set so check for null. + public BSScene PhysicsScene { get; set; } + protected void MDetailLog(string msg, params Object[] parms) + { + if (PhysicsScene != null) + { + PhysicsScene.DetailLog(msg, parms); + } + } +} + +// Motor which moves CurrentValue to TargetValue over TimeScale seconds. +// The TargetValue decays in TargetValueDecayTimeScale. +// This motor will "zero itself" over time in that the targetValue will +// decay to zero and the currentValue will follow it to that zero. +// The overall effect is for the returned correction value to go from large +// values to small and eventually zero values. +// TimeScale and TargetDelayTimeScale may be 'infinite' which means no decay. + +// For instance, if something is moving at speed X and the desired speed is Y, +// CurrentValue is X and TargetValue is Y. As the motor is stepped, new +// values of CurrentValue are returned that approach the TargetValue. +// The feature of decaying TargetValue is so vehicles will eventually +// come to a stop rather than run forever. This can be disabled by +// setting TargetValueDecayTimescale to 'infinite'. +// The change from CurrentValue to TargetValue is linear over TimeScale seconds. +public class BSVMotor : BSMotor +{ + // public Vector3 FrameOfReference { get; set; } + // public Vector3 Offset { get; set; } + + public virtual float TimeScale { get; set; } + public virtual float TargetValueDecayTimeScale { get; set; } + public virtual float Efficiency { get; set; } + + public virtual float ErrorZeroThreshold { get; set; } + + public virtual Vector3 TargetValue { get; protected set; } + public virtual Vector3 CurrentValue { get; protected set; } + public virtual Vector3 LastError { get; protected set; } + + public virtual bool ErrorIsZero() + { + return ErrorIsZero(LastError); + } + public virtual bool ErrorIsZero(Vector3 err) + { + return (err == Vector3.Zero || err.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)); + } + + public BSVMotor(string useName) + : base(useName) + { + TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite; + Efficiency = 1f; + CurrentValue = TargetValue = Vector3.Zero; + ErrorZeroThreshold = 0.001f; + } + public BSVMotor(string useName, float timeScale, float decayTimeScale, float efficiency) + : this(useName) + { + TimeScale = timeScale; + TargetValueDecayTimeScale = decayTimeScale; + Efficiency = efficiency; + CurrentValue = TargetValue = Vector3.Zero; + } + public void SetCurrent(Vector3 current) + { + CurrentValue = current; + } + public void SetTarget(Vector3 target) + { + TargetValue = target; + } + public override void Zero() + { + base.Zero(); + CurrentValue = TargetValue = Vector3.Zero; + } + + // Compute the next step and return the new current value. + // Returns the correction needed to move 'current' to 'target'. + public virtual Vector3 Step(float timeStep) + { + if (!Enabled) return TargetValue; + + Vector3 origTarget = TargetValue; // DEBUG + Vector3 origCurrVal = CurrentValue; // DEBUG + + Vector3 correction = Vector3.Zero; + Vector3 error = TargetValue - CurrentValue; + if (!ErrorIsZero(error)) + { + correction = StepError(timeStep, error); + + CurrentValue += correction; + + // The desired value reduces to zero which also reduces the difference with current. + // If the decay time is infinite, don't decay at all. + float decayFactor = 0f; + if (TargetValueDecayTimeScale != BSMotor.Infinite) + { + decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep; + TargetValue *= (1f - decayFactor); + } + + MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}", + BSScene.DetailLogZero, UseName, origCurrVal, origTarget, + timeStep, error, correction); + MDetailLog("{0}, BSVMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},tgt={4},curr={5}", + BSScene.DetailLogZero, UseName, TargetValueDecayTimeScale, decayFactor, TargetValue, CurrentValue); + } + else + { + // Difference between what we have and target is small. Motor is done. + if (TargetValue.ApproxEquals(Vector3.Zero, ErrorZeroThreshold)) + { + // The target can step down to nearly zero but not get there. If close to zero + // it is really zero. + TargetValue = Vector3.Zero; + } + CurrentValue = TargetValue; + MDetailLog("{0}, BSVMotor.Step,zero,{1},origTgt={2},origCurr={3},currTgt={4},currCurr={5}", + BSScene.DetailLogZero, UseName, origCurrVal, origTarget, TargetValue, CurrentValue); + } + LastError = error; + + return correction; + } + // version of step that sets the current value before doing the step + public virtual Vector3 Step(float timeStep, Vector3 current) + { + CurrentValue = current; + return Step(timeStep); + } + // Given and error, computer a correction for this step. + // Simple scaling of the error by the timestep. + public virtual Vector3 StepError(float timeStep, Vector3 error) + { + if (!Enabled) return Vector3.Zero; + + Vector3 returnCorrection = Vector3.Zero; + if (!ErrorIsZero(error)) + { + // correction = error / secondsItShouldTakeToCorrect + Vector3 correctionAmount; + if (TimeScale == 0f || TimeScale == BSMotor.Infinite) + correctionAmount = error * timeStep; + else + correctionAmount = error / TimeScale * timeStep; + + returnCorrection = correctionAmount; + MDetailLog("{0}, BSVMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}", + BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount); + } + return returnCorrection; + } + + // The user sets all the parameters and calls this which outputs values until error is zero. + public override void GenerateTestOutput(float timeStep) + { + // maximum number of outputs to generate. + int maxOutput = 50; + MDetailLog("{0},BSVMotor.Test,{1},===================================== BEGIN Test Output", BSScene.DetailLogZero, UseName); + MDetailLog("{0},BSVMotor.Test,{1},timeScale={2},targDlyTS={3},eff={4},curr={5},tgt={6}", + BSScene.DetailLogZero, UseName, + TimeScale, TargetValueDecayTimeScale, Efficiency, + CurrentValue, TargetValue); + + LastError = BSMotor.InfiniteVector; + while (maxOutput-- > 0 && !ErrorIsZero()) + { + Vector3 lastStep = Step(timeStep); + MDetailLog("{0},BSVMotor.Test,{1},cur={2},tgt={3},lastError={4},lastStep={5}", + BSScene.DetailLogZero, UseName, CurrentValue, TargetValue, LastError, lastStep); + } + MDetailLog("{0},BSVMotor.Test,{1},===================================== END Test Output", BSScene.DetailLogZero, UseName); + + + } + + public override string ToString() + { + return String.Format("<{0},curr={1},targ={2},lastErr={3},decayTS={4}>", + UseName, CurrentValue, TargetValue, LastError, TargetValueDecayTimeScale); + } +} + +// ============================================================================ +// ============================================================================ +public class BSFMotor : BSMotor +{ + public virtual float TimeScale { get; set; } + public virtual float TargetValueDecayTimeScale { get; set; } + public virtual float Efficiency { get; set; } + + public virtual float ErrorZeroThreshold { get; set; } + + public virtual float TargetValue { get; protected set; } + public virtual float CurrentValue { get; protected set; } + public virtual float LastError { get; protected set; } + + public virtual bool ErrorIsZero() + { + return ErrorIsZero(LastError); + } + public virtual bool ErrorIsZero(float err) + { + return (err >= -ErrorZeroThreshold && err <= ErrorZeroThreshold); + } + + public BSFMotor(string useName, float timeScale, float decayTimescale, float efficiency) + : base(useName) + { + TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite; + Efficiency = 1f; + CurrentValue = TargetValue = 0f; + ErrorZeroThreshold = 0.01f; + } + public void SetCurrent(float current) + { + CurrentValue = current; + } + public void SetTarget(float target) + { + TargetValue = target; + } + public override void Zero() + { + base.Zero(); + CurrentValue = TargetValue = 0f; + } + + public virtual float Step(float timeStep) + { + if (!Enabled) return TargetValue; + + float origTarget = TargetValue; // DEBUG + float origCurrVal = CurrentValue; // DEBUG + + float correction = 0f; + float error = TargetValue - CurrentValue; + if (!ErrorIsZero(error)) + { + correction = StepError(timeStep, error); + + CurrentValue += correction; + + // The desired value reduces to zero which also reduces the difference with current. + // If the decay time is infinite, don't decay at all. + float decayFactor = 0f; + if (TargetValueDecayTimeScale != BSMotor.Infinite) + { + decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep; + TargetValue *= (1f - decayFactor); + } + + MDetailLog("{0}, BSFMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},err={5},corr={6}", + BSScene.DetailLogZero, UseName, origCurrVal, origTarget, + timeStep, error, correction); + MDetailLog("{0}, BSFMotor.Step,nonZero,{1},tgtDecayTS={2},decayFact={3},tgt={4},curr={5}", + BSScene.DetailLogZero, UseName, TargetValueDecayTimeScale, decayFactor, TargetValue, CurrentValue); + } + else + { + // Difference between what we have and target is small. Motor is done. + if (Util.InRange(TargetValue, -ErrorZeroThreshold, ErrorZeroThreshold)) + { + // The target can step down to nearly zero but not get there. If close to zero + // it is really zero. + TargetValue = 0f; + } + CurrentValue = TargetValue; + MDetailLog("{0}, BSFMotor.Step,zero,{1},origTgt={2},origCurr={3},ret={4}", + BSScene.DetailLogZero, UseName, origCurrVal, origTarget, CurrentValue); + } + LastError = error; + + return CurrentValue; + } + + public virtual float StepError(float timeStep, float error) + { + if (!Enabled) return 0f; + + float returnCorrection = 0f; + if (!ErrorIsZero(error)) + { + // correction = error / secondsItShouldTakeToCorrect + float correctionAmount; + if (TimeScale == 0f || TimeScale == BSMotor.Infinite) + correctionAmount = error * timeStep; + else + correctionAmount = error / TimeScale * timeStep; + + returnCorrection = correctionAmount; + MDetailLog("{0}, BSFMotor.Step,nonZero,{1},timeStep={2},timeScale={3},err={4},corr={5}", + BSScene.DetailLogZero, UseName, timeStep, TimeScale, error, correctionAmount); + } + return returnCorrection; + } + + public override string ToString() + { + return String.Format("<{0},curr={1},targ={2},lastErr={3},decayTS={4}>", + UseName, CurrentValue, TargetValue, LastError, TargetValueDecayTimeScale); + } + +} + +// ============================================================================ +// ============================================================================ +// Proportional, Integral, Derivitive ("PID") Motor +// Good description at http://www.answers.com/topic/pid-controller . Includes processes for choosing p, i and d factors. +public class BSPIDVMotor : BSVMotor +{ + // Larger makes more overshoot, smaller means converge quicker. Range of 0.1 to 10. + public Vector3 proportionFactor { get; set; } + public Vector3 integralFactor { get; set; } + public Vector3 derivFactor { get; set; } + + // The factors are vectors for the three dimensions. This is the proportional of each + // that is applied. This could be multiplied through the actual factors but it + // is sometimes easier to manipulate the factors and their mix separately. + public Vector3 FactorMix; + + // Arbritrary factor range. + // EfficiencyHigh means move quickly to the correct number. EfficiencyLow means might over correct. + public float EfficiencyHigh = 0.4f; + public float EfficiencyLow = 4.0f; + + // Running integration of the error + Vector3 RunningIntegration { get; set; } + + public BSPIDVMotor(string useName) + : base(useName) + { + proportionFactor = new Vector3(1.00f, 1.00f, 1.00f); + integralFactor = new Vector3(1.00f, 1.00f, 1.00f); + derivFactor = new Vector3(1.00f, 1.00f, 1.00f); + FactorMix = new Vector3(0.5f, 0.25f, 0.25f); + RunningIntegration = Vector3.Zero; + LastError = Vector3.Zero; + } + + public override void Zero() + { + base.Zero(); + } + + public override float Efficiency + { + get { return base.Efficiency; } + set + { + base.Efficiency = Util.Clamp(value, 0f, 1f); + + // Compute factors based on efficiency. + // If efficiency is high (1f), use a factor value that moves the error value to zero with little overshoot. + // If efficiency is low (0f), use a factor value that overcorrects. + // TODO: might want to vary contribution of different factor depending on efficiency. + // float factor = ((1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow) / 3f; + float factor = (1f - this.Efficiency) * EfficiencyHigh + EfficiencyLow; + + proportionFactor = new Vector3(factor, factor, factor); + integralFactor = new Vector3(factor, factor, factor); + derivFactor = new Vector3(factor, factor, factor); + + MDetailLog("{0}, BSPIDVMotor.setEfficiency,eff={1},factor={2}", BSScene.DetailLogZero, Efficiency, factor); + } + } + + // Advance the PID computation on this error. + public override Vector3 StepError(float timeStep, Vector3 error) + { + if (!Enabled) return Vector3.Zero; + + // Add up the error so we can integrate over the accumulated errors + RunningIntegration += error * timeStep; + + // A simple derivitive is the rate of change from the last error. + Vector3 derivitive = (error - LastError) * timeStep; + + // Correction = (proportionOfPresentError + accumulationOfPastError + rateOfChangeOfError) + Vector3 ret = error / TimeScale * timeStep * proportionFactor * FactorMix.X + + RunningIntegration / TimeScale * integralFactor * FactorMix.Y + + derivitive / TimeScale * derivFactor * FactorMix.Z + ; + + MDetailLog("{0}, BSPIDVMotor.step,ts={1},err={2},lerr={3},runnInt={4},deriv={5},ret={6}", + BSScene.DetailLogZero, timeStep, error, LastError, RunningIntegration, derivitive, ret); + + return ret; + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs b/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs new file mode 100755 index 0000000..6d46fe6 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs @@ -0,0 +1,927 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +using OpenSim.Region.Physics.Manager; + +using OpenMetaverse; +using Nini.Config; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public static class BSParam +{ + private static string LogHeader = "[BULLETSIM PARAMETERS]"; + + // Tuning notes: + // From: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6575 + // Contact points can be added even if the distance is positive. The constraint solver can deal with + // contacts with positive distances as well as negative (penetration). Contact points are discarded + // if the distance exceeds a certain threshold. + // Bullet has a contact processing threshold and a contact breaking threshold. + // If the distance is larger than the contact breaking threshold, it will be removed after one frame. + // If the distance is larger than the contact processing threshold, the constraint solver will ignore it. + + // This is separate/independent from the collision margin. The collision margin increases the object a bit + // to improve collision detection performance and accuracy. + // =================== + // From: + + /// + /// Set whether physics is active or not. + /// + /// + /// Can be enabled and disabled to start and stop physics. + /// + public static bool Active { get; private set; } + + public static bool UseSeparatePhysicsThread { get; private set; } + public static float PhysicsTimeStep { get; private set; } + + // Level of Detail values kept as float because that's what the Meshmerizer wants + public static float MeshLOD { get; private set; } + public static float MeshCircularLOD { get; private set; } + public static float MeshMegaPrimLOD { get; private set; } + public static float MeshMegaPrimThreshold { get; private set; } + public static float SculptLOD { get; private set; } + + public static int CrossingFailuresBeforeOutOfBounds { get; private set; } + public static float UpdateVelocityChangeThreshold { get; private set; } + + public static float MinimumObjectMass { get; private set; } + public static float MaximumObjectMass { get; private set; } + public static float MaxLinearVelocity { get; private set; } + public static float MaxLinearVelocitySquared { get; private set; } + public static float MaxAngularVelocity { get; private set; } + public static float MaxAngularVelocitySquared { get; private set; } + public static float MaxAddForceMagnitude { get; private set; } + public static float MaxAddForceMagnitudeSquared { get; private set; } + public static float DensityScaleFactor { get; private set; } + + public static float LinearDamping { get; private set; } + public static float AngularDamping { get; private set; } + public static float DeactivationTime { get; private set; } + public static float LinearSleepingThreshold { get; private set; } + public static float AngularSleepingThreshold { get; private set; } + public static float CcdMotionThreshold { get; private set; } + public static float CcdSweptSphereRadius { get; private set; } + public static float ContactProcessingThreshold { get; private set; } + + public static bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed + public static bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes + public static bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects + public static bool ShouldRemoveZeroWidthTriangles { get; private set; } + public static bool ShouldUseBulletHACD { get; set; } + public static bool ShouldUseSingleConvexHullForPrims { get; set; } + public static bool ShouldUseGImpactShapeForPrims { get; set; } + public static bool ShouldUseAssetHulls { get; set; } + + public static float TerrainImplementation { get; set; } + public static int TerrainMeshMagnification { get; private set; } + public static float TerrainGroundPlane { get; private set; } + public static float TerrainFriction { get; private set; } + public static float TerrainHitFraction { get; private set; } + public static float TerrainRestitution { get; private set; } + public static float TerrainContactProcessingThreshold { get; private set; } + public static float TerrainCollisionMargin { get; private set; } + + public static float DefaultFriction { get; private set; } + public static float DefaultDensity { get; private set; } + public static float DefaultRestitution { get; private set; } + public static float CollisionMargin { get; private set; } + public static float Gravity { get; private set; } + + // Physics Engine operation + public static float MaxPersistantManifoldPoolSize { get; private set; } + public static float MaxCollisionAlgorithmPoolSize { get; private set; } + public static bool ShouldDisableContactPoolDynamicAllocation { get; private set; } + public static bool ShouldForceUpdateAllAabbs { get; private set; } + public static bool ShouldRandomizeSolverOrder { get; private set; } + public static bool ShouldSplitSimulationIslands { get; private set; } + public static bool ShouldEnableFrictionCaching { get; private set; } + public static float NumberOfSolverIterations { get; private set; } + public static bool UseSingleSidedMeshes { get; private set; } + public static float GlobalContactBreakingThreshold { get; private set; } + public static float PhysicsUnmanLoggingFrames { get; private set; } + + // Avatar parameters + public static bool AvatarToAvatarCollisionsByDefault { get; private set; } + public static float AvatarFriction { get; private set; } + public static float AvatarStandingFriction { get; private set; } + public static float AvatarAlwaysRunFactor { get; private set; } + public static float AvatarDensity { get; private set; } + public static float AvatarRestitution { get; private set; } + public static int AvatarShape { get; private set; } + public static float AvatarCapsuleWidth { get; private set; } + public static float AvatarCapsuleDepth { get; private set; } + public static float AvatarCapsuleHeight { get; private set; } + public static float AvatarHeightLowFudge { get; private set; } + public static float AvatarHeightMidFudge { get; private set; } + public static float AvatarHeightHighFudge { get; private set; } + public static float AvatarFlyingGroundMargin { get; private set; } + public static float AvatarFlyingGroundUpForce { get; private set; } + public static float AvatarTerminalVelocity { get; private set; } + public static float AvatarContactProcessingThreshold { get; private set; } + public static float AvatarStopZeroThreshold { get; private set; } + public static int AvatarJumpFrames { get; private set; } + public static float AvatarBelowGroundUpCorrectionMeters { get; private set; } + public static float AvatarStepHeight { get; private set; } + public static float AvatarStepAngle { get; private set; } + public static float AvatarStepGroundFudge { get; private set; } + public static float AvatarStepApproachFactor { get; private set; } + public static float AvatarStepForceFactor { get; private set; } + public static float AvatarStepUpCorrectionFactor { get; private set; } + public static int AvatarStepSmoothingSteps { get; private set; } + + // Vehicle parameters + public static float VehicleMaxLinearVelocity { get; private set; } + public static float VehicleMaxLinearVelocitySquared { get; private set; } + public static float VehicleMinLinearVelocity { get; private set; } + public static float VehicleMinLinearVelocitySquared { get; private set; } + public static float VehicleMaxAngularVelocity { get; private set; } + public static float VehicleMaxAngularVelocitySq { get; private set; } + public static float VehicleAngularDamping { get; private set; } + public static float VehicleFriction { get; private set; } + public static float VehicleRestitution { get; private set; } + public static Vector3 VehicleLinearFactor { get; private set; } + public static Vector3 VehicleAngularFactor { get; private set; } + public static Vector3 VehicleInertiaFactor { get; private set; } + public static float VehicleGroundGravityFudge { get; private set; } + public static float VehicleAngularBankingTimescaleFudge { get; private set; } + public static bool VehicleEnableLinearDeflection { get; private set; } + public static bool VehicleLinearDeflectionNotCollidingNoZ { get; private set; } + public static bool VehicleEnableAngularVerticalAttraction { get; private set; } + public static int VehicleAngularVerticalAttractionAlgorithm { get; private set; } + public static bool VehicleEnableAngularDeflection { get; private set; } + public static bool VehicleEnableAngularBanking { get; private set; } + + // Convex Hulls + // Parameters for convex hull routine that ships with Bullet + public static int CSHullMaxDepthSplit { get; private set; } + public static int CSHullMaxDepthSplitForSimpleShapes { get; private set; } + public static float CSHullConcavityThresholdPercent { get; private set; } + public static float CSHullVolumeConservationThresholdPercent { get; private set; } + public static int CSHullMaxVertices { get; private set; } + public static float CSHullMaxSkinWidth { get; private set; } + public static float BHullMaxVerticesPerHull { get; private set; } // 100 + public static float BHullMinClusters { get; private set; } // 2 + public static float BHullCompacityWeight { get; private set; } // 0.1 + public static float BHullVolumeWeight { get; private set; } // 0.0 + public static float BHullConcavity { get; private set; } // 100 + public static bool BHullAddExtraDistPoints { get; private set; } // false + public static bool BHullAddNeighboursDistPoints { get; private set; } // false + public static bool BHullAddFacesPoints { get; private set; } // false + public static bool BHullShouldAdjustCollisionMargin { get; private set; } // false + public static float WhichHACD { get; private set; } // zero if Bullet HACD, non-zero says VHACD + // Parameters for VHACD 2.0: http://code.google.com/p/v-hacd + // To enable, set both ShouldUseBulletHACD=true and WhichHACD=1 + // http://kmamou.blogspot.ca/2014/12/v-hacd-20-parameters-description.html + public static float VHACDresolution { get; private set; } // 100,000 max number of voxels generated during voxelization stage + public static float VHACDdepth { get; private set; } // 20 max number of clipping stages + public static float VHACDconcavity { get; private set; } // 0.0025 maximum concavity + public static float VHACDplaneDownsampling { get; private set; } // 4 granularity of search for best clipping plane + public static float VHACDconvexHullDownsampling { get; private set; } // 4 precision of hull gen process + public static float VHACDalpha { get; private set; } // 0.05 bias toward clipping along symmetry planes + public static float VHACDbeta { get; private set; } // 0.05 bias toward clipping along revolution axis + public static float VHACDgamma { get; private set; } // 0.00125 max concavity when merging + public static float VHACDpca { get; private set; } // 0 on/off normalizing mesh before decomp + public static float VHACDmode { get; private set; } // 0 0:voxel based, 1: tetrahedron based + public static float VHACDmaxNumVerticesPerCH { get; private set; } // 64 max triangles per convex hull + public static float VHACDminVolumePerCH { get; private set; } // 0.0001 sampling of generated convex hulls + + // Linkset implementation parameters + public static float LinksetImplementation { get; private set; } + public static bool LinksetOffsetCenterOfMass { get; private set; } + public static bool LinkConstraintUseFrameOffset { get; private set; } + public static bool LinkConstraintEnableTransMotor { get; private set; } + public static float LinkConstraintTransMotorMaxVel { get; private set; } + public static float LinkConstraintTransMotorMaxForce { get; private set; } + public static float LinkConstraintERP { get; private set; } + public static float LinkConstraintCFM { get; private set; } + public static float LinkConstraintSolverIterations { get; private set; } + + public static float PID_D { get; private set; } // derivative + public static float PID_P { get; private set; } // proportional + + // Various constants that come from that other virtual world that shall not be named. + public const float MinGravityZ = -1f; + public const float MaxGravityZ = 28f; + public const float MinFriction = 0f; + public const float MaxFriction = 255f; + public const float MinDensity = 0.01f; + public const float MaxDensity = 22587f; + public const float MinRestitution = 0f; + public const float MaxRestitution = 1f; + + // ===================================================================================== + // ===================================================================================== + + // Base parameter definition that gets and sets parameter values via a string + public abstract class ParameterDefnBase + { + public string name; // string name of the parameter + public string desc; // a short description of what the parameter means + public ParameterDefnBase(string pName, string pDesc) + { + name = pName; + desc = pDesc; + } + // Set the parameter value to the default + public abstract void AssignDefault(BSScene s); + // Get the value as a string + public abstract string GetValue(BSScene s); + // Set the value to this string value + public abstract void SetValue(BSScene s, string valAsString); + // set the value on a particular object (usually sets in physics engine) + public abstract void SetOnObject(BSScene s, BSPhysObject obj); + public abstract bool HasSetOnObject { get; } + } + + // Specific parameter definition for a parameter of a specific type. + public delegate T PGetValue(BSScene s); + public delegate void PSetValue(BSScene s, T val); + public delegate void PSetOnObject(BSScene scene, BSPhysObject obj); + public sealed class ParameterDefn : ParameterDefnBase + { + private T defaultValue; + private PSetValue setter; + private PGetValue getter; + private PSetOnObject objectSet; + public ParameterDefn(string pName, string pDesc, T pDefault, PGetValue pGetter, PSetValue pSetter) + : base(pName, pDesc) + { + defaultValue = pDefault; + setter = pSetter; + getter = pGetter; + objectSet = null; + } + public ParameterDefn(string pName, string pDesc, T pDefault, PGetValue pGetter, PSetValue pSetter, PSetOnObject pObjSetter) + : base(pName, pDesc) + { + defaultValue = pDefault; + setter = pSetter; + getter = pGetter; + objectSet = pObjSetter; + } + // Simple parameter variable where property name is the same as the INI file name + // and the value is only a simple get and set. + public ParameterDefn(string pName, string pDesc, T pDefault) + : base(pName, pDesc) + { + defaultValue = pDefault; + setter = (s, v) => { SetValueByName(s, name, v); }; + getter = (s) => { return GetValueByName(s, name); }; + objectSet = null; + } + // Use reflection to find the property named 'pName' in BSParam and assign 'val' to same. + private void SetValueByName(BSScene s, string pName, T val) + { + PropertyInfo prop = typeof(BSParam).GetProperty(pName, BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); + if (prop == null) + { + // This should only be output when someone adds a new INI parameter and misspells the name. + s.Logger.ErrorFormat("{0} SetValueByName: did not find '{1}'. Verify specified property name is the same as the given INI parameters name.", LogHeader, pName); + } + else + { + prop.SetValue(null, val, null); + } + } + // Use reflection to find the property named 'pName' in BSParam and return the value in same. + private T GetValueByName(BSScene s, string pName) + { + PropertyInfo prop = typeof(BSParam).GetProperty(pName, BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); + if (prop == null) + { + // This should only be output when someone adds a new INI parameter and misspells the name. + s.Logger.ErrorFormat("{0} GetValueByName: did not find '{1}'. Verify specified property name is the same as the given INI parameter name.", LogHeader, pName); + } + return (T)prop.GetValue(null, null); + } + public override void AssignDefault(BSScene s) + { + setter(s, defaultValue); + } + public override string GetValue(BSScene s) + { + return getter(s).ToString(); + } + public override void SetValue(BSScene s, string valAsString) + { + // Get the generic type of the setter + Type genericType = setter.GetType().GetGenericArguments()[0]; + // Find the 'Parse' method on that type + System.Reflection.MethodInfo parser = null; + try + { + parser = genericType.GetMethod("Parse", new Type[] { typeof(String) } ); + } + catch (Exception e) + { + s.Logger.ErrorFormat("{0} Exception getting parser for type '{1}': {2}", LogHeader, genericType, e); + parser = null; + } + if (parser != null) + { + // Parse the input string + try + { + T setValue = (T)parser.Invoke(genericType, new Object[] { valAsString }); + // Store the parsed value + setter(s, setValue); + // s.Logger.DebugFormat("{0} Parameter {1} = {2}", LogHeader, name, setValue); + } + catch + { + s.Logger.ErrorFormat("{0} Failed parsing parameter value '{1}' as type '{2}'", LogHeader, valAsString, genericType); + } + } + else + { + s.Logger.ErrorFormat("{0} Could not find parameter parser for type '{1}'", LogHeader, genericType); + } + } + public override bool HasSetOnObject + { + get { return objectSet != null; } + } + public override void SetOnObject(BSScene s, BSPhysObject obj) + { + if (objectSet != null) + objectSet(s, obj); + } + } + + // List of all of the externally visible parameters. + // For each parameter, this table maps a text name to getter and setters. + // To add a new externally referencable/settable parameter, add the paramter storage + // location somewhere in the program and make an entry in this table with the + // getters and setters. + // It is easiest to find an existing definition and copy it. + // + // A ParameterDefn() takes the following parameters: + // -- the text name of the parameter. This is used for console input and ini file. + // -- a short text description of the parameter. This shows up in the console listing. + // -- a default value + // -- a delegate for getting the value + // -- a delegate for setting the value + // -- an optional delegate to update the value in the world. Most often used to + // push the new value to an in-world object. + // + // The single letter parameters for the delegates are: + // s = BSScene + // o = BSPhysObject + // v = value (appropriate type) + private static ParameterDefnBase[] ParameterDefinitions = + { + new ParameterDefn("Active", "If 'true', false then physics is not active", + false ), + new ParameterDefn("UseSeparatePhysicsThread", "If 'true', the physics engine runs independent from the simulator heartbeat", + false ), + new ParameterDefn("PhysicsTimeStep", "If separate thread, seconds to simulate each interval", + 0.089f ), + + new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", + true, + (s) => { return ShouldMeshSculptedPrim; }, + (s,v) => { ShouldMeshSculptedPrim = v; } ), + new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", + false, + (s) => { return ShouldForceSimplePrimMeshing; }, + (s,v) => { ShouldForceSimplePrimMeshing = v; } ), + new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects", + true, + (s) => { return ShouldUseHullsForPhysicalObjects; }, + (s,v) => { ShouldUseHullsForPhysicalObjects = v; } ), + new ParameterDefn("ShouldRemoveZeroWidthTriangles", "If true, remove degenerate triangles from meshes", + true ), + new ParameterDefn("ShouldUseBulletHACD", "If true, use the Bullet version of HACD", + false ), + new ParameterDefn("ShouldUseSingleConvexHullForPrims", "If true, use a single convex hull shape for physical prims", + true ), + new ParameterDefn("ShouldUseGImpactShapeForPrims", "If true, use a GImpact shape for prims with cuts and twists", + false ), + new ParameterDefn("ShouldUseAssetHulls", "If true, use hull if specified in the mesh asset info", + true ), + + new ParameterDefn("CrossingFailuresBeforeOutOfBounds", "How forgiving we are about getting into adjactent regions", + 5 ), + new ParameterDefn("UpdateVelocityChangeThreshold", "Change in updated velocity required before reporting change to simulator", + 0.1f ), + + new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", + 32f, + (s) => { return MeshLOD; }, + (s,v) => { MeshLOD = v; } ), + new ParameterDefn("MeshLevelOfDetailCircular", "Level of detail for prims with circular cuts or shapes", + 32f, + (s) => { return MeshCircularLOD; }, + (s,v) => { MeshCircularLOD = v; } ), + new ParameterDefn("MeshLevelOfDetailMegaPrimThreshold", "Size (in meters) of a mesh before using MeshMegaPrimLOD", + 10f, + (s) => { return MeshMegaPrimThreshold; }, + (s,v) => { MeshMegaPrimThreshold = v; } ), + new ParameterDefn("MeshLevelOfDetailMegaPrim", "Level of detail to render meshes larger than threshold meters", + 32f, + (s) => { return MeshMegaPrimLOD; }, + (s,v) => { MeshMegaPrimLOD = v; } ), + new ParameterDefn("SculptLevelOfDetail", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)", + 32f, + (s) => { return SculptLOD; }, + (s,v) => { SculptLOD = v; } ), + + new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps", + 10, + (s) => { return s.m_maxSubSteps; }, + (s,v) => { s.m_maxSubSteps = (int)v; } ), + new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)", + 1f / 60f, + (s) => { return s.m_fixedTimeStep; }, + (s,v) => { s.m_fixedTimeStep = v; } ), + new ParameterDefn("NominalFrameRate", "The base frame rate we claim", + 55f, + (s) => { return s.NominalFrameRate; }, + (s,v) => { s.NominalFrameRate = (int)v; } ), + new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame", + 2048, + (s) => { return s.m_maxCollisionsPerFrame; }, + (s,v) => { s.m_maxCollisionsPerFrame = (int)v; } ), + new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame", + 8000, + (s) => { return s.m_maxUpdatesPerFrame; }, + (s,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), + + new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)", + 0.0001f, + (s) => { return MinimumObjectMass; }, + (s,v) => { MinimumObjectMass = v; } ), + new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", + 10000.01f, + (s) => { return MaximumObjectMass; }, + (s,v) => { MaximumObjectMass = v; } ), + new ParameterDefn("MaxLinearVelocity", "Maximum velocity magnitude that can be assigned to an object", + 1000.0f, + (s) => { return MaxLinearVelocity; }, + (s,v) => { MaxLinearVelocity = v; MaxLinearVelocitySquared = v * v; } ), + new ParameterDefn("MaxAngularVelocity", "Maximum rotational velocity magnitude that can be assigned to an object", + 1000.0f, + (s) => { return MaxAngularVelocity; }, + (s,v) => { MaxAngularVelocity = v; MaxAngularVelocitySquared = v * v; } ), + // LL documentation says thie number should be 20f for llApplyImpulse and 200f for llRezObject + new ParameterDefn("MaxAddForceMagnitude", "Maximum force that can be applied by llApplyImpulse (SL says 20f)", + 20000.0f, + (s) => { return MaxAddForceMagnitude; }, + (s,v) => { MaxAddForceMagnitude = v; MaxAddForceMagnitudeSquared = v * v; } ), + // Density is passed around as 100kg/m3. This scales that to 1kg/m3. + // Reduce by power of 100 because Bullet doesn't seem to handle objects with large mass very well + new ParameterDefn("DensityScaleFactor", "Conversion for simulator/viewer density (100kg/m3) to physical density (1kg/m3)", + 0.01f ), + + new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", + 2200f ), + new ParameterDefn("PID_P", "Parameteric factor for motion smoothing", + 900f ), + + new ParameterDefn("DefaultFriction", "Friction factor used on new objects", + 0.2f, + (s) => { return DefaultFriction; }, + (s,v) => { DefaultFriction = v; s.UnmanagedParams[0].defaultFriction = v; } ), + // For historical reasons, the viewer and simulator multiply the density by 100 + new ParameterDefn("DefaultDensity", "Density for new objects" , + 1000.0006836f, // Aluminum g/cm3 * 100 + (s) => { return DefaultDensity; }, + (s,v) => { DefaultDensity = v; s.UnmanagedParams[0].defaultDensity = v; } ), + new ParameterDefn("DefaultRestitution", "Bouncyness of an object" , + 0f, + (s) => { return DefaultRestitution; }, + (s,v) => { DefaultRestitution = v; s.UnmanagedParams[0].defaultRestitution = v; } ), + new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", + 0.04f, + (s) => { return CollisionMargin; }, + (s,v) => { CollisionMargin = v; s.UnmanagedParams[0].collisionMargin = v; } ), + new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)", + -9.80665f, + (s) => { return Gravity; }, + (s,v) => { Gravity = v; s.UnmanagedParams[0].gravity = v; }, + (s,o) => { s.PE.SetGravity(o.PhysBody, new Vector3(0f,0f,Gravity)); } ), + + + new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", + 0f, + (s) => { return LinearDamping; }, + (s,v) => { LinearDamping = v; }, + (s,o) => { s.PE.SetDamping(o.PhysBody, LinearDamping, AngularDamping); } ), + new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", + 0f, + (s) => { return AngularDamping; }, + (s,v) => { AngularDamping = v; }, + (s,o) => { s.PE.SetDamping(o.PhysBody, LinearDamping, AngularDamping); } ), + new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", + 0.2f, + (s) => { return DeactivationTime; }, + (s,v) => { DeactivationTime = v; }, + (s,o) => { s.PE.SetDeactivationTime(o.PhysBody, DeactivationTime); } ), + new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", + 0.8f, + (s) => { return LinearSleepingThreshold; }, + (s,v) => { LinearSleepingThreshold = v;}, + (s,o) => { s.PE.SetSleepingThresholds(o.PhysBody, LinearSleepingThreshold, AngularSleepingThreshold); } ), + new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", + 1.0f, + (s) => { return AngularSleepingThreshold; }, + (s,v) => { AngularSleepingThreshold = v;}, + (s,o) => { s.PE.SetSleepingThresholds(o.PhysBody, LinearSleepingThreshold, AngularSleepingThreshold); } ), + new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , + 0.0f, // set to zero to disable + (s) => { return CcdMotionThreshold; }, + (s,v) => { CcdMotionThreshold = v;}, + (s,o) => { s.PE.SetCcdMotionThreshold(o.PhysBody, CcdMotionThreshold); } ), + new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , + 0.2f, + (s) => { return CcdSweptSphereRadius; }, + (s,v) => { CcdSweptSphereRadius = v;}, + (s,o) => { s.PE.SetCcdSweptSphereRadius(o.PhysBody, CcdSweptSphereRadius); } ), + new ParameterDefn("ContactProcessingThreshold", "Distance above which contacts can be discarded (0 means no discard)" , + 0.0f, + (s) => { return ContactProcessingThreshold; }, + (s,v) => { ContactProcessingThreshold = v;}, + (s,o) => { s.PE.SetContactProcessingThreshold(o.PhysBody, ContactProcessingThreshold); } ), + + new ParameterDefn("TerrainImplementation", "Type of shape to use for terrain (0=heightmap, 1=mesh)", + (float)BSTerrainPhys.TerrainImplementation.Heightmap ), + new ParameterDefn("TerrainMeshMagnification", "Number of times the 256x256 heightmap is multiplied to create the terrain mesh" , + 2 ), + new ParameterDefn("TerrainGroundPlane", "Altitude of ground plane used to keep things from falling to infinity" , + -500.0f ), + new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , + 0.3f ), + new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , + 0.8f ), + new ParameterDefn("TerrainRestitution", "Bouncyness" , + 0f ), + new ParameterDefn("TerrainContactProcessingThreshold", "Distance from terrain to stop processing collisions" , + 0.0f ), + new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" , + 0.04f ), + + new ParameterDefn("AvatarToAvatarCollisionsByDefault", "Should avatars collide with other avatars by default?", + true), + new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", + 0.2f ), + new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", + 0.95f ), + new ParameterDefn("AvatarAlwaysRunFactor", "Speed multiplier if avatar is set to always run", + 1.3f ), + // For historical reasons, density is reported * 100 + new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation. Scaled times 100.", + 3500f) , // 3.5 * 100 + new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.", + 0f ), + new ParameterDefn("AvatarShape", "Code for avatar physical shape: 0:capsule, 1:cube, 2:ovoid, 2:mesh", + BSShapeCollection.AvatarShapeCube ) , + new ParameterDefn("AvatarCapsuleWidth", "The distance between the sides of the avatar capsule", + 0.6f ) , + new ParameterDefn("AvatarCapsuleDepth", "The distance between the front and back of the avatar capsule", + 0.45f ), + new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", + 1.5f ), + new ParameterDefn("AvatarHeightLowFudge", "A fudge factor to make small avatars stand on the ground", + 0f ), + new ParameterDefn("AvatarHeightMidFudge", "A fudge distance to adjust average sized avatars to be standing on ground", + 0f ), + new ParameterDefn("AvatarHeightHighFudge", "A fudge factor to make tall avatars stand on the ground", + 0f ), + new ParameterDefn("AvatarFlyingGroundMargin", "Meters avatar is kept above the ground when flying", + 5f ), + new ParameterDefn("AvatarFlyingGroundUpForce", "Upward force applied to the avatar to keep it at flying ground margin", + 2.0f ), + new ParameterDefn("AvatarTerminalVelocity", "Terminal Velocity of falling avatar", + -54.0f ), + new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", + 0.1f ), + new ParameterDefn("AvatarStopZeroThreshold", "Movement velocity below which avatar is assumed to be stopped", + 0.1f ), + new ParameterDefn("AvatarBelowGroundUpCorrectionMeters", "Meters to move avatar up if it seems to be below ground", + 1.0f ), + new ParameterDefn("AvatarJumpFrames", "Number of frames to allow jump forces. Changes jump height.", + 4 ), + new ParameterDefn("AvatarStepHeight", "Height of a step obstacle to consider step correction", + 0.999f ) , + new ParameterDefn("AvatarStepAngle", "The angle (in radians) for a vertical surface to be considered a step", + 0.3f ) , + new ParameterDefn("AvatarStepGroundFudge", "Fudge factor subtracted from avatar base when comparing collision height", + 0.1f ) , + new ParameterDefn("AvatarStepApproachFactor", "Factor to control angle of approach to step (0=straight on)", + 2f ), + new ParameterDefn("AvatarStepForceFactor", "Controls the amount of force up applied to step up onto a step", + 0f ), + new ParameterDefn("AvatarStepUpCorrectionFactor", "Multiplied by height of step collision to create up movement at step", + 0.8f ), + new ParameterDefn("AvatarStepSmoothingSteps", "Number of frames after a step collision that we continue walking up stairs", + 1 ), + + new ParameterDefn("VehicleMaxLinearVelocity", "Maximum velocity magnitude that can be assigned to a vehicle", + 1000.0f, + (s) => { return (float)VehicleMaxLinearVelocity; }, + (s,v) => { VehicleMaxLinearVelocity = v; VehicleMaxLinearVelocitySquared = v * v; } ), + new ParameterDefn("VehicleMinLinearVelocity", "Maximum velocity magnitude that can be assigned to a vehicle", + 0.001f, + (s) => { return (float)VehicleMinLinearVelocity; }, + (s,v) => { VehicleMinLinearVelocity = v; VehicleMinLinearVelocitySquared = v * v; } ), + new ParameterDefn("VehicleMaxAngularVelocity", "Maximum rotational velocity magnitude that can be assigned to a vehicle", + 12.0f, + (s) => { return (float)VehicleMaxAngularVelocity; }, + (s,v) => { VehicleMaxAngularVelocity = v; VehicleMaxAngularVelocitySq = v * v; } ), + new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)", + 0.0f ), + new ParameterDefn("VehicleLinearFactor", "Fraction of physical linear changes applied to vehicle (<0,0,0> to <1,1,1>)", + new Vector3(1f, 1f, 1f) ), + new ParameterDefn("VehicleAngularFactor", "Fraction of physical angular changes applied to vehicle (<0,0,0> to <1,1,1>)", + new Vector3(1f, 1f, 1f) ), + new ParameterDefn("VehicleInertiaFactor", "Fraction of physical inertia applied (<0,0,0> to <1,1,1>)", + new Vector3(1f, 1f, 1f) ), + new ParameterDefn("VehicleFriction", "Friction of vehicle on the ground (0.0 - 1.0)", + 0.0f ), + new ParameterDefn("VehicleRestitution", "Bouncyness factor for vehicles (0.0 - 1.0)", + 0.0f ), + new ParameterDefn("VehicleGroundGravityFudge", "Factor to multiply gravity if a ground vehicle is probably on the ground (0.0 - 1.0)", + 0.2f ), + new ParameterDefn("VehicleAngularBankingTimescaleFudge", "Factor to multiple angular banking timescale. Tune to increase realism.", + 60.0f ), + new ParameterDefn("VehicleEnableLinearDeflection", "Turn on/off vehicle linear deflection effect", + true ), + new ParameterDefn("VehicleLinearDeflectionNotCollidingNoZ", "Turn on/off linear deflection Z effect on non-colliding vehicles", + true ), + new ParameterDefn("VehicleEnableAngularVerticalAttraction", "Turn on/off vehicle angular vertical attraction effect", + true ), + new ParameterDefn("VehicleAngularVerticalAttractionAlgorithm", "Select vertical attraction algo. You need to look at the source.", + 0 ), + new ParameterDefn("VehicleEnableAngularDeflection", "Turn on/off vehicle angular deflection effect", + true ), + new ParameterDefn("VehicleEnableAngularBanking", "Turn on/off vehicle angular banking effect", + true ), + + new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", + 0f, + (s) => { return MaxPersistantManifoldPoolSize; }, + (s,v) => { MaxPersistantManifoldPoolSize = v; s.UnmanagedParams[0].maxPersistantManifoldPoolSize = v; } ), + new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)", + 0f, + (s) => { return MaxCollisionAlgorithmPoolSize; }, + (s,v) => { MaxCollisionAlgorithmPoolSize = v; s.UnmanagedParams[0].maxCollisionAlgorithmPoolSize = v; } ), + new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count", + false, + (s) => { return ShouldDisableContactPoolDynamicAllocation; }, + (s,v) => { ShouldDisableContactPoolDynamicAllocation = v; + s.UnmanagedParams[0].shouldDisableContactPoolDynamicAllocation = NumericBool(v); } ), + new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step", + false, + (s) => { return ShouldForceUpdateAllAabbs; }, + (s,v) => { ShouldForceUpdateAllAabbs = v; s.UnmanagedParams[0].shouldForceUpdateAllAabbs = NumericBool(v); } ), + new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", + true, + (s) => { return ShouldRandomizeSolverOrder; }, + (s,v) => { ShouldRandomizeSolverOrder = v; s.UnmanagedParams[0].shouldRandomizeSolverOrder = NumericBool(v); } ), + new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", + true, + (s) => { return ShouldSplitSimulationIslands; }, + (s,v) => { ShouldSplitSimulationIslands = v; s.UnmanagedParams[0].shouldSplitSimulationIslands = NumericBool(v); } ), + new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching", + true, + (s) => { return ShouldEnableFrictionCaching; }, + (s,v) => { ShouldEnableFrictionCaching = v; s.UnmanagedParams[0].shouldEnableFrictionCaching = NumericBool(v); } ), + new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)", + 0f, // zero says use Bullet default + (s) => { return NumberOfSolverIterations; }, + (s,v) => { NumberOfSolverIterations = v; s.UnmanagedParams[0].numberOfSolverIterations = v; } ), + new ParameterDefn("UseSingleSidedMeshes", "Whether to compute collisions based on single sided meshes.", + true, + (s) => { return UseSingleSidedMeshes; }, + (s,v) => { UseSingleSidedMeshes = v; s.UnmanagedParams[0].useSingleSidedMeshes = NumericBool(v); } ), + new ParameterDefn("GlobalContactBreakingThreshold", "Amount of shape radius before breaking a collision contact (0 says Bullet default (0.2))", + 0f, + (s) => { return GlobalContactBreakingThreshold; }, + (s,v) => { GlobalContactBreakingThreshold = v; s.UnmanagedParams[0].globalContactBreakingThreshold = v; } ), + new ParameterDefn("PhysicsUnmanLoggingFrames", "If non-zero, frames between output of detailed unmanaged physics statistics", + 0f, + (s) => { return PhysicsUnmanLoggingFrames; }, + (s,v) => { PhysicsUnmanLoggingFrames = v; s.UnmanagedParams[0].physicsLoggingFrames = v; } ), + + new ParameterDefn("CSHullMaxDepthSplit", "CS impl: max depth to split for hull. 1-10 but > 7 is iffy", + 7 ), + new ParameterDefn("CSHullMaxDepthSplitForSimpleShapes", "CS impl: max depth setting for simple prim shapes", + 2 ), + new ParameterDefn("CSHullConcavityThresholdPercent", "CS impl: concavity threshold percent (0-20)", + 5f ), + new ParameterDefn("CSHullVolumeConservationThresholdPercent", "percent volume conservation to collapse hulls (0-30)", + 5f ), + new ParameterDefn("CSHullMaxVertices", "CS impl: maximum number of vertices in output hulls. Keep < 50.", + 32 ), + new ParameterDefn("CSHullMaxSkinWidth", "CS impl: skin width to apply to output hulls.", + 0f ), + + new ParameterDefn("BHullMaxVerticesPerHull", "Bullet impl: max number of vertices per created hull", + 200f ), + new ParameterDefn("BHullMinClusters", "Bullet impl: minimum number of hulls to create per mesh", + 10f ), + new ParameterDefn("BHullCompacityWeight", "Bullet impl: weight factor for how compact to make hulls", + 20f ), + new ParameterDefn("BHullVolumeWeight", "Bullet impl: weight factor for volume in created hull", + 0.1f ), + new ParameterDefn("BHullConcavity", "Bullet impl: weight factor for how convex a created hull can be", + 10f ), + new ParameterDefn("BHullAddExtraDistPoints", "Bullet impl: whether to add extra vertices for long distance vectors", + true ), + new ParameterDefn("BHullAddNeighboursDistPoints", "Bullet impl: whether to add extra vertices between neighbor hulls", + true ), + new ParameterDefn("BHullAddFacesPoints", "Bullet impl: whether to add extra vertices to break up hull faces", + true ), + new ParameterDefn("BHullShouldAdjustCollisionMargin", "Bullet impl: whether to shrink resulting hulls to account for collision margin", + false ), + + new ParameterDefn("WhichHACD", "zero if Bullet HACD, non-zero says VHACD", + 0f ), + new ParameterDefn("VHACDresolution", "max number of voxels generated during voxelization stage", + 100000f ), + new ParameterDefn("VHACDdepth", "max number of clipping stages", + 20f ), + new ParameterDefn("VHACDconcavity", "maximum concavity", + 0.0025f ), + new ParameterDefn("VHACDplaneDownsampling", "granularity of search for best clipping plane", + 4f ), + new ParameterDefn("VHACDconvexHullDownsampling", "precision of hull gen process", + 4f ), + new ParameterDefn("VHACDalpha", "bias toward clipping along symmetry planes", + 0.05f ), + new ParameterDefn("VHACDbeta", "bias toward clipping along revolution axis", + 0.05f ), + new ParameterDefn("VHACDgamma", "max concavity when merging", + 0.00125f ), + new ParameterDefn("VHACDpca", "on/off normalizing mesh before decomp", + 0f ), + new ParameterDefn("VHACDmode", "0:voxel based, 1: tetrahedron based", + 0f ), + new ParameterDefn("VHACDmaxNumVerticesPerCH", "max triangles per convex hull", + 64f ), + new ParameterDefn("VHACDminVolumePerCH", "sampling of generated convex hulls", + 0.0001f ), + + new ParameterDefn("LinksetImplementation", "Type of linkset implementation (0=Constraint, 1=Compound, 2=Manual)", + (float)BSLinkset.LinksetImplementation.Compound ), + new ParameterDefn("LinksetOffsetCenterOfMass", "If 'true', compute linkset center-of-mass and offset linkset position to account for same", + true ), + new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", + false ), + new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints", + true ), + new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints", + 5.0f ), + new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints", + 0.1f ), + new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1", + 0.1f ), + new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", + 0.1f ), + new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)", + 40 ), + + new ParameterDefn("PhysicsMetricFrames", "Frames between outputting detailed phys metrics. (0 is off)", + 0, + (s) => { return s.PhysicsMetricDumpFrames; }, + (s,v) => { s.PhysicsMetricDumpFrames = v; } ), + new ParameterDefn("ResetBroadphasePool", "Setting this is any value resets the broadphase collision pool", + 0f, + (s) => { return 0f; }, + (s,v) => { BSParam.ResetBroadphasePoolTainted(s, v, false /* inTaintTime */); } ), + new ParameterDefn("ResetConstraintSolver", "Setting this is any value resets the constraint solver", + 0f, + (s) => { return 0f; }, + (s,v) => { BSParam.ResetConstraintSolverTainted(s, v); } ), + }; + + // Convert a boolean to our numeric true and false values + public static float NumericBool(bool b) + { + return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse); + } + + // Convert numeric true and false values to a boolean + public static bool BoolNumeric(float b) + { + return (b == ConfigurationParameters.numericTrue ? true : false); + } + + // Search through the parameter definitions and return the matching + // ParameterDefn structure. + // Case does not matter as names are compared after converting to lower case. + // Returns 'false' if the parameter is not found. + internal static bool TryGetParameter(string paramName, out ParameterDefnBase defn) + { + bool ret = false; + ParameterDefnBase foundDefn = null; + string pName = paramName.ToLower(); + + foreach (ParameterDefnBase parm in ParameterDefinitions) + { + if (pName == parm.name.ToLower()) + { + foundDefn = parm; + ret = true; + break; + } + } + defn = foundDefn; + return ret; + } + + // Pass through the settable parameters and set the default values + internal static void SetParameterDefaultValues(BSScene physicsScene) + { + foreach (ParameterDefnBase parm in ParameterDefinitions) + { + parm.AssignDefault(physicsScene); + } + } + + // Get user set values out of the ini file. + internal static void SetParameterConfigurationValues(BSScene physicsScene, IConfig cfg) + { + foreach (ParameterDefnBase parm in ParameterDefinitions) + { + parm.SetValue(physicsScene, cfg.GetString(parm.name, parm.GetValue(physicsScene))); + } + } + + internal static PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1]; + + // This creates an array in the correct format for returning the list of + // parameters. This is used by the 'list' option of the 'physics' command. + internal static void BuildParameterTable() + { + if (SettableParameters.Length < ParameterDefinitions.Length) + { + List entries = new List(); + for (int ii = 0; ii < ParameterDefinitions.Length; ii++) + { + ParameterDefnBase pd = ParameterDefinitions[ii]; + entries.Add(new PhysParameterEntry(pd.name, pd.desc)); + } + + // make the list alphabetical for ease of finding anything + entries.Sort((ppe1, ppe2) => { return ppe1.name.CompareTo(ppe2.name); }); + + SettableParameters = entries.ToArray(); + } + } + + // ===================================================================== + // ===================================================================== + // There are parameters that, when set, cause things to happen in the physics engine. + // This causes the broadphase collision cache to be cleared. + private static void ResetBroadphasePoolTainted(BSScene pPhysScene, float v, bool inTaintTime) + { + BSScene physScene = pPhysScene; + physScene.TaintedObject(inTaintTime, "BSParam.ResetBroadphasePoolTainted", delegate() + { + physScene.PE.ResetBroadphasePool(physScene.World); + }); + } + + // This causes the constraint solver cache to be cleared and reset. + private static void ResetConstraintSolverTainted(BSScene pPhysScene, float v) + { + BSScene physScene = pPhysScene; + physScene.TaintedObject(BSScene.DetailLogZero, "BSParam.ResetConstraintSolver", delegate() + { + physScene.PE.ResetConstraintSolver(physScene.World); + }); + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs new file mode 100755 index 0000000..90da7a6 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs @@ -0,0 +1,620 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; + +using OMV = OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +/* + * Class to wrap all objects. + * The rest of BulletSim doesn't need to keep checking for avatars or prims + * unless the difference is significant. + * + * Variables in the physicsl objects are in three forms: + * VariableName: used by the simulator and performs taint operations, etc + * RawVariableName: direct reference to the BulletSim storage for the variable value + * ForceVariableName: direct reference (store and fetch) to the value in the physics engine. + * The last one should only be referenced in taint-time. + */ + +/* + * As of 20121221, the following are the call sequences (going down) for different script physical functions: + * llApplyImpulse llApplyRotImpulse llSetTorque llSetForce + * SOP.ApplyImpulse SOP.ApplyAngularImpulse SOP.SetAngularImpulse SOP.SetForce + * SOG.ApplyImpulse SOG.ApplyAngularImpulse SOG.SetAngularImpulse + * PA.AddForce PA.AddAngularForce PA.Torque = v PA.Force = v + * BS.ApplyCentralForce BS.ApplyTorque + */ + +// Flags used to denote which properties updates when making UpdateProperties calls to linksets, etc. +public enum UpdatedProperties : uint +{ + Position = 1 << 0, + Orientation = 1 << 1, + Velocity = 1 << 2, + Acceleration = 1 << 3, + RotationalVelocity = 1 << 4, + EntPropUpdates = Position | Orientation | Velocity | Acceleration | RotationalVelocity, +} +public abstract class BSPhysObject : PhysicsActor +{ + protected BSPhysObject() + { + } + protected BSPhysObject(BSScene parentScene, uint localID, string name, string typeName) + { + IsInitialized = false; + + PhysScene = parentScene; + LocalID = localID; + PhysObjectName = name; + Name = name; // PhysicsActor also has the name of the object. Someday consolidate. + TypeName = typeName; + + // Oddity if object is destroyed and recreated very quickly it could still have the old body. + if (!PhysBody.HasPhysicalBody) + PhysBody = new BulletBody(localID); + + // Clean out anything that might be in the physical actor list. + // Again, a workaround for destroying and recreating an object very quickly. + PhysicalActors.Dispose(); + + UserSetCenterOfMassDisplacement = null; + + PrimAssetState = PrimAssetCondition.Unknown; + + // Initialize variables kept in base. + // Beware that these cause taints to be queued whch can cause race conditions on startup. + GravModifier = 1.0f; + Gravity = new OMV.Vector3(0f, 0f, BSParam.Gravity); + HoverActive = false; + + // Default material type. Also sets Friction, Restitution and Density. + SetMaterial((int)MaterialAttributes.Material.Wood); + + CollisionsLastTickStep = -1; + + SubscribedEventsMs = 0; + // Crazy values that will never be true + CollidingStep = BSScene.NotASimulationStep; + CollidingGroundStep = BSScene.NotASimulationStep; + CollisionAccumulation = BSScene.NotASimulationStep; + ColliderIsMoving = false; + CollisionScore = 0; + + // All axis free. + LockedLinearAxis = LockedAxisFree; + LockedAngularAxis = LockedAxisFree; + } + + // Tell the object to clean up. + public virtual void Destroy() + { + PhysicalActors.Enable(false); + PhysScene.TaintedObject(LocalID, "BSPhysObject.Destroy", delegate() + { + PhysicalActors.Dispose(); + }); + } + + public BSScene PhysScene { get; protected set; } + // public override uint LocalID { get; set; } // Use the LocalID definition in PhysicsActor + public string PhysObjectName { get; protected set; } + public string TypeName { get; protected set; } + + // Set to 'true' when the object is completely initialized. + // This mostly prevents property updates and collisions until the object is completely here. + public bool IsInitialized { get; protected set; } + + // Set to 'true' if an object (mesh/linkset/sculpty) is not completely constructed. + // This test is used to prevent some updates to the object when it only partially exists. + // There are several reasons and object might be incomplete: + // Its underlying mesh/sculpty is an asset which must be fetched from the asset store + // It is a linkset who is being added to or removed from + // It is changing state (static to physical, for instance) which requires rebuilding + // This is a computed value based on the underlying physical object construction + abstract public bool IsIncomplete { get; } + + // Return the object mass without calculating it or having side effects + public abstract float RawMass { get; } + // Set the raw mass but also update physical mass properties (inertia, ...) + // 'inWorld' true if the object has already been added to the dynamic world. + public abstract void UpdatePhysicalMassProperties(float mass, bool inWorld); + + // The gravity being applied to the object. A function of default grav, GravityModifier and Buoyancy. + public virtual OMV.Vector3 Gravity { get; set; } + // The last value calculated for the prim's inertia + public OMV.Vector3 Inertia { get; set; } + + // Reference to the physical body (btCollisionObject) of this object + public BulletBody PhysBody = new BulletBody(0); + // Reference to the physical shape (btCollisionShape) of this object + public BSShape PhysShape = new BSShapeNull(); + + // The physical representation of the prim might require an asset fetch. + // The asset state is first 'Unknown' then 'Waiting' then either 'Failed' or 'Fetched'. + public enum PrimAssetCondition + { + Unknown, Waiting, FailedAssetFetch, FailedMeshing, Fetched + } + public PrimAssetCondition PrimAssetState { get; set; } + public virtual bool AssetFailed() + { + return ( (this.PrimAssetState == PrimAssetCondition.FailedAssetFetch) + || (this.PrimAssetState == PrimAssetCondition.FailedMeshing) ); + } + + // The objects base shape information. Null if not a prim type shape. + public PrimitiveBaseShape BaseShape { get; protected set; } + + // When the physical properties are updated, an EntityProperty holds the update values. + // Keep the current and last EntityProperties to enable computation of differences + // between the current update and the previous values. + public EntityProperties CurrentEntityProperties { get; set; } + public EntityProperties LastEntityProperties { get; set; } + + public virtual OMV.Vector3 Scale { get; set; } + + // It can be confusing for an actor to know if it should move or update an object + // depeneding on the setting of 'selected', 'physical, ... + // This flag is the true test -- if true, the object is being acted on in the physical world + public abstract bool IsPhysicallyActive { get; } + + // Detailed state of the object. + public abstract bool IsSolid { get; } + public abstract bool IsStatic { get; } + public abstract bool IsSelected { get; } + public abstract bool IsVolumeDetect { get; } + + // Materialness + public MaterialAttributes.Material Material { get; private set; } + public override void SetMaterial(int material) + { + Material = (MaterialAttributes.Material)material; + + // Setting the material sets the material attributes also. + // TODO: decide if this is necessary -- the simulator does this. + MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false); + Friction = matAttrib.friction; + Restitution = matAttrib.restitution; + Density = matAttrib.density; + // DetailLog("{0},{1}.SetMaterial,Mat={2},frict={3},rest={4},den={5}", LocalID, TypeName, Material, Friction, Restitution, Density); + } + + public override float Density + { + get + { + return base.Density; + } + set + { + DetailLog("{0},BSPhysObject.Density,set,den={1}", LocalID, value); + base.Density = value; + } + } + + // Stop all physical motion. + public abstract void ZeroMotion(bool inTaintTime); + public abstract void ZeroAngularMotion(bool inTaintTime); + + // Update the physical location and motion of the object. Called with data from Bullet. + public abstract void UpdateProperties(EntityProperties entprop); + + public virtual OMV.Vector3 RawPosition { get; set; } + public abstract OMV.Vector3 ForcePosition { get; set; } + + public virtual OMV.Quaternion RawOrientation { get; set; } + public abstract OMV.Quaternion ForceOrientation { get; set; } + + public virtual OMV.Vector3 RawVelocity { get; set; } + public abstract OMV.Vector3 ForceVelocity { get; set; } + + public OMV.Vector3 RawForce { get; set; } + public OMV.Vector3 RawTorque { get; set; } + public override void AddAngularForce(OMV.Vector3 force, bool pushforce) + { + AddAngularForce(force, pushforce, false); + } + public abstract void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime); + public abstract void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime); + + public abstract OMV.Vector3 ForceRotationalVelocity { get; set; } + + public abstract float ForceBuoyancy { get; set; } + + public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; } + + public override bool PIDActive + { + get { return MoveToTargetActive; } + set { MoveToTargetActive = value; } + } + + public override OMV.Vector3 PIDTarget { set { MoveToTargetTarget = value; } } + public override float PIDTau { set { MoveToTargetTau = value; } } + + public bool MoveToTargetActive { get; set; } + public OMV.Vector3 MoveToTargetTarget { get; set; } + public float MoveToTargetTau { get; set; } + + // Used for llSetHoverHeight and maybe vehicle height. Hover Height will override MoveTo target's Z + public override bool PIDHoverActive { set { HoverActive = value; } } + public override float PIDHoverHeight { set { HoverHeight = value; } } + public override PIDHoverType PIDHoverType { set { HoverType = value; } } + public override float PIDHoverTau { set { HoverTau = value; } } + + public bool HoverActive { get; set; } + public float HoverHeight { get; set; } + public PIDHoverType HoverType { get; set; } + public float HoverTau { get; set; } + + // For RotLookAt + public override OMV.Quaternion APIDTarget { set { return; } } + public override bool APIDActive { set { return; } } + public override float APIDStrength { set { return; } } + public override float APIDDamping { set { return; } } + + // The current velocity forward + public virtual float ForwardSpeed + { + get + { + OMV.Vector3 characterOrientedVelocity = RawVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation)); + return characterOrientedVelocity.X; + } + } + // The forward speed we are trying to achieve (TargetVelocity) + public virtual float TargetVelocitySpeed + { + get + { + OMV.Vector3 characterOrientedVelocity = TargetVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation)); + return characterOrientedVelocity.X; + } + } + + // The user can optionally set the center of mass. The user's setting will override any + // computed center-of-mass (like in linksets). + // Note this is a displacement from the root's coordinates. Zero means use the root prim as center-of-mass. + public OMV.Vector3? UserSetCenterOfMassDisplacement { get; set; } + + public OMV.Vector3 LockedLinearAxis; // zero means locked. one means free. + public OMV.Vector3 LockedAngularAxis; // zero means locked. one means free. + public const float FreeAxis = 1f; + public const float LockedAxis = 0f; + public readonly OMV.Vector3 LockedAxisFree = new OMV.Vector3(FreeAxis, FreeAxis, FreeAxis); // All axis are free + + // If an axis is locked (flagged above) then the limits of that axis are specified here. + // Linear axis limits are relative to the object's starting coordinates. + // Angular limits are limited to -PI to +PI + public OMV.Vector3 LockedLinearAxisLow; + public OMV.Vector3 LockedLinearAxisHigh; + public OMV.Vector3 LockedAngularAxisLow; + public OMV.Vector3 LockedAngularAxisHigh; + + // Enable physical actions. Bullet will keep sleeping non-moving physical objects so + // they need waking up when parameters are changed. + // Called in taint-time!! + public void ActivateIfPhysical(bool forceIt) + { + if (PhysBody.HasPhysicalBody) + { + if (IsPhysical) + { + // Physical objects might need activating + PhysScene.PE.Activate(PhysBody, forceIt); + } + else + { + // Clear the collision cache since we've changed some properties. + PhysScene.PE.ClearCollisionProxyCache(PhysScene.World, PhysBody); + } + } + } + + // 'actors' act on the physical object to change or constrain its motion. These can range from + // hovering to complex vehicle motion. + // May be called at non-taint time as this just adds the actor to the action list and the real + // work is done during the simulation step. + // Note that, if the actor is already in the list and we are disabling same, the actor is just left + // in the list disabled. + public delegate BSActor CreateActor(); + public void EnableActor(bool enableActor, string actorName, CreateActor creator) + { + lock (PhysicalActors) + { + BSActor theActor; + if (PhysicalActors.TryGetActor(actorName, out theActor)) + { + // The actor already exists so just turn it on or off + DetailLog("{0},BSPhysObject.EnableActor,enablingExistingActor,name={1},enable={2}", LocalID, actorName, enableActor); + theActor.Enabled = enableActor; + } + else + { + // The actor does not exist. If it should, create it. + if (enableActor) + { + DetailLog("{0},BSPhysObject.EnableActor,creatingActor,name={1}", LocalID, actorName); + theActor = creator(); + PhysicalActors.Add(actorName, theActor); + theActor.Enabled = true; + } + else + { + DetailLog("{0},BSPhysObject.EnableActor,notCreatingActorSinceNotEnabled,name={1}", LocalID, actorName); + } + } + } + } + + #region Collisions + + // Requested number of milliseconds between collision events. Zero means disabled. + protected int SubscribedEventsMs { get; set; } + // Given subscription, the time that a collision may be passed up + protected int NextCollisionOkTime { get; set; } + // The simulation step that last had a collision + protected long CollidingStep { get; set; } + // The simulation step that last had a collision with the ground + protected long CollidingGroundStep { get; set; } + // The simulation step that last collided with an object + protected long CollidingObjectStep { get; set; } + // The collision flags we think are set in Bullet + protected CollisionFlags CurrentCollisionFlags { get; set; } + // On a collision, check the collider and remember if the last collider was moving + // Used to modify the standing of avatars (avatars on stationary things stand still) + public bool ColliderIsMoving; + // 'true' if the last collider was a volume detect object + public bool ColliderIsVolumeDetect; + // Used by BSCharacter to manage standing (and not slipping) + public bool IsStationary; + + // Count of collisions for this object + protected long CollisionAccumulation { get; set; } + + public override bool IsColliding { + get { return (CollidingStep == PhysScene.SimulationStep); } + set { + if (value) + CollidingStep = PhysScene.SimulationStep; + else + CollidingStep = BSScene.NotASimulationStep; + } + } + // Complex objects (like linksets) need to know if there is a collision on any part of + // their shape. 'IsColliding' has an existing definition of reporting a collision on + // only this specific prim or component of linksets. + // 'HasSomeCollision' is defined as reporting if there is a collision on any part of + // the complex body that this prim is the root of. + public virtual bool HasSomeCollision + { + get { return IsColliding; } + set { IsColliding = value; } + } + public override bool CollidingGround { + get { return (CollidingGroundStep == PhysScene.SimulationStep); } + set + { + if (value) + CollidingGroundStep = PhysScene.SimulationStep; + else + CollidingGroundStep = BSScene.NotASimulationStep; + } + } + public override bool CollidingObj { + get { return (CollidingObjectStep == PhysScene.SimulationStep); } + set { + if (value) + CollidingObjectStep = PhysScene.SimulationStep; + else + CollidingObjectStep = BSScene.NotASimulationStep; + } + } + + // The collisions that have been collected for the next collision reporting (throttled by subscription) + protected CollisionEventUpdate CollisionCollection = new CollisionEventUpdate(); + // This is the collision collection last reported to the Simulator. + public CollisionEventUpdate CollisionsLastReported = new CollisionEventUpdate(); + // Remember the collisions recorded in the last tick for fancy collision checking + // (like a BSCharacter walking up stairs). + public CollisionEventUpdate CollisionsLastTick = new CollisionEventUpdate(); + private long CollisionsLastTickStep = -1; + + // The simulation step is telling this object about a collision. + // Return 'true' if a collision was processed and should be sent up. + // Return 'false' if this object is not enabled/subscribed/appropriate for or has already seen this collision. + // Called at taint time from within the Step() function + public delegate bool CollideCall(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth); + public virtual bool Collide(uint collidingWith, BSPhysObject collidee, + OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) + { + bool ret = false; + + // The following lines make IsColliding(), CollidingGround() and CollidingObj work + CollidingStep = PhysScene.SimulationStep; + if (collidingWith <= PhysScene.TerrainManager.HighestTerrainID) + { + CollidingGroundStep = PhysScene.SimulationStep; + } + else + { + CollidingObjectStep = PhysScene.SimulationStep; + } + + CollisionAccumulation++; + + // For movement tests, remember if we are colliding with an object that is moving. + ColliderIsMoving = collidee != null ? (collidee.RawVelocity != OMV.Vector3.Zero) : false; + ColliderIsVolumeDetect = collidee != null ? (collidee.IsVolumeDetect) : false; + + // Make a collection of the collisions that happened the last simulation tick. + // This is different than the collection created for sending up to the simulator as it is cleared every tick. + if (CollisionsLastTickStep != PhysScene.SimulationStep) + { + CollisionsLastTick = new CollisionEventUpdate(); + CollisionsLastTickStep = PhysScene.SimulationStep; + } + CollisionsLastTick.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); + + // If someone has subscribed for collision events log the collision so it will be reported up + if (SubscribedEvents()) { + lock (PhysScene.CollisionLock) + { + CollisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); + } + DetailLog("{0},{1}.Collision.AddCollider,call,with={2},point={3},normal={4},depth={5},colliderMoving={6}", + LocalID, TypeName, collidingWith, contactPoint, contactNormal, pentrationDepth, ColliderIsMoving); + + ret = true; + } + return ret; + } + + // Send the collected collisions into the simulator. + // Called at taint time from within the Step() function thus no locking problems + // with CollisionCollection and ObjectsWithNoMoreCollisions. + // Called with BSScene.CollisionLock locked to protect the collision lists. + // Return 'true' if there were some actual collisions passed up + public virtual bool SendCollisions() + { + bool ret = true; + + // If no collisions this call but there were collisions last call, force the collision + // event to be happen right now so quick collision_end. + bool force = (CollisionCollection.Count == 0 && CollisionsLastReported.Count != 0); + + // throttle the collisions to the number of milliseconds specified in the subscription + if (force || (PhysScene.SimulationNowTime >= NextCollisionOkTime)) + { + NextCollisionOkTime = PhysScene.SimulationNowTime + SubscribedEventsMs; + + // We are called if we previously had collisions. If there are no collisions + // this time, send up one last empty event so OpenSim can sense collision end. + if (CollisionCollection.Count == 0) + { + // If I have no collisions this time, remove me from the list of objects with collisions. + ret = false; + } + + DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count); + base.SendCollisionUpdate(CollisionCollection); + + // Remember the collisions from this tick for some collision specific processing. + CollisionsLastReported = CollisionCollection; + + // The CollisionCollection instance is passed around in the simulator. + // Make sure we don't have a handle to that one and that a new one is used for next time. + // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here, + // a race condition is created for the other users of this instance. + CollisionCollection = new CollisionEventUpdate(); + } + return ret; + } + + // Subscribe for collision events. + // Parameter is the millisecond rate the caller wishes collision events to occur. + public override void SubscribeEvents(int ms) { + // DetailLog("{0},{1}.SubscribeEvents,subscribing,ms={2}", LocalID, TypeName, ms); + SubscribedEventsMs = ms; + if (ms > 0) + { + // make sure first collision happens + NextCollisionOkTime = Util.EnvironmentTickCountSubtract(SubscribedEventsMs); + + PhysScene.TaintedObject(LocalID, TypeName+".SubscribeEvents", delegate() + { + if (PhysBody.HasPhysicalBody) + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + }); + } + else + { + // Subscribing for zero or less is the same as unsubscribing + UnSubscribeEvents(); + } + } + public override void UnSubscribeEvents() { + // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName); + SubscribedEventsMs = 0; + PhysScene.TaintedObject(LocalID, TypeName+".UnSubscribeEvents", delegate() + { + // Make sure there is a body there because sometimes destruction happens in an un-ideal order. + if (PhysBody.HasPhysicalBody) + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + }); + } + // Return 'true' if the simulator wants collision events + public override bool SubscribedEvents() { + return (SubscribedEventsMs > 0); + } + // Because 'CollisionScore' is called many times while sorting, it should not be recomputed + // each time called. So this is built to be light weight for each collision and to do + // all the processing when the user asks for the info. + public void ComputeCollisionScore() + { + // Scale the collision count by the time since the last collision. + // The "+1" prevents dividing by zero. + long timeAgo = PhysScene.SimulationStep - CollidingStep + 1; + CollisionScore = CollisionAccumulation / timeAgo; + } + public override float CollisionScore { get; set; } + + #endregion // Collisions + + #region Per Simulation Step actions + + public BSActorCollection PhysicalActors = new BSActorCollection(); + + // When an update to the physical properties happens, this event is fired to let + // different actors to modify the update before it is passed around + public delegate void PreUpdatePropertyAction(ref EntityProperties entprop); + public event PreUpdatePropertyAction OnPreUpdateProperty; + protected void TriggerPreUpdatePropertyAction(ref EntityProperties entprop) + { + PreUpdatePropertyAction actions = OnPreUpdateProperty; + if (actions != null) + actions(ref entprop); + } + + #endregion // Per Simulation Step actions + + // High performance detailed logging routine used by the physical objects. + protected void DetailLog(string msg, params Object[] args) + { + if (PhysScene.PhysicsLogging.Enabled) + PhysScene.DetailLog(msg, args); + } + +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs new file mode 100644 index 0000000..9442854 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs @@ -0,0 +1,76 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + /// + /// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim. + /// This module interfaces to an unmanaged C++ library which makes the + /// actual calls into the Bullet physics engine. + /// The unmanaged library is found in opensim-libs::trunk/unmanaged/BulletSim/. + /// The unmanaged library is compiled and linked statically with Bullet + /// to create BulletSim.dll and libBulletSim.so (for both 32 and 64 bit). + /// +public class BSPlugin : IPhysicsPlugin +{ + //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private BSScene _mScene; + + public BSPlugin() + { + } + + public bool Init() + { + return true; + } + + public PhysicsScene GetScene(String sceneIdentifier) + { + if (_mScene == null) + { + _mScene = new BSScene(GetName(), sceneIdentifier); + } + return (_mScene); + } + + public string GetName() + { + return ("BulletSim"); + } + + public void Dispose() + { + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs new file mode 100644 index 0000000..a00991f --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs @@ -0,0 +1,1896 @@ +/* + * 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.Reflection; +using System.Collections.Generic; +using System.Xml; +using log4net; +using OMV = OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Physics.ConvexDecompositionDotNet; +using OpenSim.Region.OptionalModules.Scripting; // for ExtendedPhysics + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + + [Serializable] +public class BSPrim : BSPhysObject +{ + protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[BULLETS PRIM]"; + + // _size is what the user passed. Scale is what we pass to the physics engine with the mesh. + private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user + + private bool _grabbed; + private bool _isSelected; + private bool _isVolumeDetect; + + private float _mass; // the mass of this object + private OMV.Vector3 _acceleration; + private int _physicsActorType; + private bool _isPhysical; + private bool _flying; + private bool _setAlwaysRun; + private bool _throttleUpdates; + private bool _floatOnWater; + private OMV.Vector3 _rotationalVelocity; + private bool _kinematic; + private float _buoyancy; + + private int CrossingFailures { get; set; } + + // Keep a handle to the vehicle actor so it is easy to set parameters on same. + public const string VehicleActorName = "BasicVehicle"; + + // Parameters for the hover actor + public const string HoverActorName = "BSPrim.HoverActor"; + // Parameters for the axis lock actor + public const String LockedAxisActorName = "BSPrim.LockedAxis"; + // Parameters for the move to target actor + public const string MoveToTargetActorName = "BSPrim.MoveToTargetActor"; + // Parameters for the setForce and setTorque actors + public const string SetForceActorName = "BSPrim.SetForceActor"; + public const string SetTorqueActorName = "BSPrim.SetTorqueActor"; + + public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, + OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) + : base(parent_scene, localID, primName, "BSPrim") + { + // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID); + _physicsActorType = (int)ActorTypes.Prim; + RawPosition = pos; + _size = size; + Scale = size; // prims are the size the user wants them to be (different for BSCharactes). + RawOrientation = rotation; + _buoyancy = 0f; + RawVelocity = OMV.Vector3.Zero; + _rotationalVelocity = OMV.Vector3.Zero; + BaseShape = pbs; + _isPhysical = pisPhysical; + _isVolumeDetect = false; + + _mass = CalculateMass(); + + DetailLog("{0},BSPrim.constructor,pbs={1}", LocalID, BSScene.PrimitiveBaseShapeToString(pbs)); + // DetailLog("{0},BSPrim.constructor,call", LocalID); + // do the actual object creation at taint time + PhysScene.TaintedObject(LocalID, "BSPrim.create", delegate() + { + // Make sure the object is being created with some sanity. + ExtremeSanityCheck(true /* inTaintTime */); + + CreateGeomAndObject(true); + + CurrentCollisionFlags = PhysScene.PE.GetCollisionFlags(PhysBody); + + IsInitialized = true; + }); + } + + // called when this prim is being destroyed and we should free all the resources + public override void Destroy() + { + // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); + IsInitialized = false; + + base.Destroy(); + + // Undo any vehicle properties + this.VehicleType = (int)Vehicle.TYPE_NONE; + + PhysScene.TaintedObject(LocalID, "BSPrim.Destroy", delegate() + { + DetailLog("{0},BSPrim.Destroy,taint,", LocalID); + // If there are physical body and shape, release my use of same. + PhysScene.Shapes.DereferenceBody(PhysBody, null); + PhysBody.Clear(); + PhysShape.Dereference(PhysScene); + PhysShape = new BSShapeNull(); + }); + } + + // No one uses this property. + public override bool Stopped { + get { return false; } + } + + public override bool IsIncomplete { + get { + return ShapeRebuildScheduled; + } + } + + // 'true' if this object's shape is in need of a rebuild and a rebuild has been queued. + // The prim is still available but its underlying shape will change soon. + // This is protected by a 'lock(this)'. + public bool ShapeRebuildScheduled { get; protected set; } + + public override OMV.Vector3 Size { + get { return _size; } + set { + // We presume the scale and size are the same. If scale must be changed for + // the physical shape, that is done when the geometry is built. + _size = value; + Scale = _size; + ForceBodyShapeRebuild(false); + } + } + + public override PrimitiveBaseShape Shape { + set { + BaseShape = value; + DetailLog("{0},BSPrim.changeShape,pbs={1}", LocalID, BSScene.PrimitiveBaseShapeToString(BaseShape)); + PrimAssetState = PrimAssetCondition.Unknown; + ForceBodyShapeRebuild(false); + } + } + // Cause the body and shape of the prim to be rebuilt if necessary. + // If there are no changes required, this is quick and does not make changes to the prim. + // If rebuilding is necessary (like changing from static to physical), that will happen. + // The 'ShapeRebuildScheduled' tells any checker that the body/shape may change shortly. + // The return parameter is not used by anyone. + public override bool ForceBodyShapeRebuild(bool inTaintTime) + { + if (inTaintTime) + { + // If called in taint time, do the operation immediately + _mass = CalculateMass(); // changing the shape changes the mass + CreateGeomAndObject(true); + } + else + { + lock (this) + { + // If a rebuild is not already in the queue + if (!ShapeRebuildScheduled) + { + // Remember that a rebuild is queued -- this is used to flag an incomplete object + ShapeRebuildScheduled = true; + PhysScene.TaintedObject(LocalID, "BSPrim.ForceBodyShapeRebuild", delegate() + { + _mass = CalculateMass(); // changing the shape changes the mass + CreateGeomAndObject(true); + ShapeRebuildScheduled = false; + }); + } + } + } + return true; + } + public override bool Grabbed { + set { _grabbed = value; + } + } + public override bool Selected { + set + { + if (value != _isSelected) + { + _isSelected = value; + PhysScene.TaintedObject(LocalID, "BSPrim.setSelected", delegate() + { + DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); + SetObjectDynamic(false); + }); + } + } + } + public override bool IsSelected + { + get { return _isSelected; } + } + + public override void CrossingFailure() + { + CrossingFailures++; + if (CrossingFailures > BSParam.CrossingFailuresBeforeOutOfBounds) + { + base.RaiseOutOfBounds(RawPosition); + } + else if (CrossingFailures == BSParam.CrossingFailuresBeforeOutOfBounds) + { + m_log.WarnFormat("{0} Too many crossing failures for {1}", LogHeader, Name); + } + return; + } + + // link me to the specified parent + public override void link(PhysicsActor obj) { + } + + // delink me from my linkset + public override void delink() { + } + + // Set motion values to zero. + // Do it to the properties so the values get set in the physics engine. + // Push the setting of the values to the viewer. + // Called at taint time! + public override void ZeroMotion(bool inTaintTime) + { + RawVelocity = OMV.Vector3.Zero; + _acceleration = OMV.Vector3.Zero; + _rotationalVelocity = OMV.Vector3.Zero; + + // Zero some other properties in the physics engine + PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.ZeroMotion", delegate() + { + if (PhysBody.HasPhysicalBody) + PhysScene.PE.ClearAllForces(PhysBody); + }); + } + public override void ZeroAngularMotion(bool inTaintTime) + { + _rotationalVelocity = OMV.Vector3.Zero; + // Zero some other properties in the physics engine + PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.ZeroMotion", delegate() + { + // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity); + if (PhysBody.HasPhysicalBody) + { + PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); + PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); + } + }); + } + + public override void LockAngularMotion(OMV.Vector3 axis) + { + DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); + + ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR, 0f, 0f); + if (axis.X != 1) + { + ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_X, 0f, 0f); + } + if (axis.Y != 1) + { + ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Y, 0f, 0f); + } + if (axis.Z != 1) + { + ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Z, 0f, 0f); + } + + InitializeAxisActor(); + + return; + } + + public override OMV.Vector3 Position { + get { + // don't do the GetObjectPosition for root elements because this function is called a zillion times. + // RawPosition = ForcePosition; + return RawPosition; + } + set { + // If the position must be forced into the physics engine, use ForcePosition. + // All positions are given in world positions. + if (RawPosition == value) + { + DetailLog("{0},BSPrim.setPosition,call,positionNotChanging,pos={1},orient={2}", LocalID, RawPosition, RawOrientation); + return; + } + RawPosition = value; + PositionSanityCheck(false); + + PhysScene.TaintedObject(LocalID, "BSPrim.setPosition", delegate() + { + DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, RawPosition, RawOrientation); + ForcePosition = RawPosition; + }); + } + } + + // NOTE: overloaded by BSPrimDisplaced to handle offset for center-of-gravity. + public override OMV.Vector3 ForcePosition { + get { + RawPosition = PhysScene.PE.GetPosition(PhysBody); + return RawPosition; + } + set { + RawPosition = value; + if (PhysBody.HasPhysicalBody) + { + PhysScene.PE.SetTranslation(PhysBody, RawPosition, RawOrientation); + ActivateIfPhysical(false); + } + } + } + + // Check that the current position is sane and, if not, modify the position to make it so. + // Check for being below terrain and being out of bounds. + // Returns 'true' of the position was made sane by some action. + private bool PositionSanityCheck(bool inTaintTime) + { + bool ret = false; + + // We don't care where non-physical items are placed + if (!IsPhysicallyActive) + return ret; + + if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) + { + // The physical object is out of the known/simulated area. + // Upper levels of code will handle the transition to other areas so, for + // the time, we just ignore the position. + return ret; + } + + float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); + OMV.Vector3 upForce = OMV.Vector3.Zero; + float approxSize = Math.Max(Size.X, Math.Max(Size.Y, Size.Z)); + if ((RawPosition.Z + approxSize / 2f) < terrainHeight) + { + DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, RawPosition, terrainHeight); + float targetHeight = terrainHeight + (Size.Z / 2f); + // If the object is below ground it just has to be moved up because pushing will + // not get it through the terrain + RawPosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, targetHeight); + if (inTaintTime) + { + ForcePosition = RawPosition; + } + // If we are throwing the object around, zero its other forces + ZeroMotion(inTaintTime); + ret = true; + } + + if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) + { + float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(RawPosition); + // TODO: a floating motor so object will bob in the water + if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f) + { + // Upforce proportional to the distance away from the water. Correct the error in 1 sec. + upForce.Z = (waterHeight - RawPosition.Z) * 1f; + + // Apply upforce and overcome gravity. + OMV.Vector3 correctionForce = upForce - PhysScene.DefaultGravity; + DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, RawPosition, upForce, correctionForce); + AddForce(correctionForce, false, inTaintTime); + ret = true; + } + } + + return ret; + } + + // Occasionally things will fly off and really get lost. + // Find the wanderers and bring them back. + // Return 'true' if some parameter need some sanity. + private bool ExtremeSanityCheck(bool inTaintTime) + { + bool ret = false; + + int wayOverThere = -1000; + int wayOutThere = 10000; + // There have been instances of objects getting thrown way out of bounds and crashing + // the border crossing code. + if ( RawPosition.X < wayOverThere || RawPosition.X > wayOutThere + || RawPosition.Y < wayOverThere || RawPosition.X > wayOutThere + || RawPosition.Z < wayOverThere || RawPosition.X > wayOutThere) + { + RawPosition = new OMV.Vector3(10, 10, 50); + ZeroMotion(inTaintTime); + ret = true; + } + if (RawVelocity.LengthSquared() > BSParam.MaxLinearVelocitySquared) + { + RawVelocity = Util.ClampV(RawVelocity, BSParam.MaxLinearVelocity); + ret = true; + } + if (_rotationalVelocity.LengthSquared() > BSParam.MaxAngularVelocitySquared) + { + _rotationalVelocity = Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity); + ret = true; + } + + return ret; + } + + // Return the effective mass of the object. + // The definition of this call is to return the mass of the prim. + // If the simulator cares about the mass of the linkset, it will sum it itself. + public override float Mass + { + get { return _mass; } + } + // TotalMass returns the mass of the large object the prim may be in (overridden by linkset code) + public virtual float TotalMass + { + get { return _mass; } + } + // used when we only want this prim's mass and not the linkset thing + public override float RawMass { + get { return _mass; } + } + // Set the physical mass to the passed mass. + // Note that this does not change _mass! + public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) + { + if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape) + { + if (IsStatic) + { + PhysScene.PE.SetGravity(PhysBody, PhysScene.DefaultGravity); + Inertia = OMV.Vector3.Zero; + PhysScene.PE.SetMassProps(PhysBody, 0f, Inertia); + PhysScene.PE.UpdateInertiaTensor(PhysBody); + } + else + { + if (inWorld) + { + // Changing interesting properties doesn't change proxy and collision cache + // information. The Bullet solution is to re-add the object to the world + // after parameters are changed. + PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); + } + + // The computation of mass props requires gravity to be set on the object. + Gravity = ComputeGravity(Buoyancy); + PhysScene.PE.SetGravity(PhysBody, Gravity); + + // OMV.Vector3 currentScale = PhysScene.PE.GetLocalScaling(PhysShape.physShapeInfo); // DEBUG DEBUG + // DetailLog("{0},BSPrim.UpdateMassProperties,currentScale{1},shape={2}", LocalID, currentScale, PhysShape.physShapeInfo); // DEBUG DEBUG + + Inertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); + PhysScene.PE.SetMassProps(PhysBody, physMass, Inertia); + PhysScene.PE.UpdateInertiaTensor(PhysBody); + + DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2},grav={3},inWorld={4}", + LocalID, physMass, Inertia, Gravity, inWorld); + + if (inWorld) + { + AddObjectToPhysicalWorld(); + } + } + } + } + + // Return what gravity should be set to this very moment + public OMV.Vector3 ComputeGravity(float buoyancy) + { + OMV.Vector3 ret = PhysScene.DefaultGravity; + + if (!IsStatic) + { + ret *= (1f - buoyancy); + ret *= GravModifier; + } + + return ret; + } + + // Is this used? + public override OMV.Vector3 CenterOfMass + { + get { return RawPosition; } + } + + // Is this used? + public override OMV.Vector3 GeometricCenter + { + get { return RawPosition; } + } + + public override OMV.Vector3 Force { + get { return RawForce; } + set { + RawForce = value; + EnableActor(RawForce != OMV.Vector3.Zero, SetForceActorName, delegate() + { + return new BSActorSetForce(PhysScene, this, SetForceActorName); + }); + + // Call update so actor Refresh() is called to start things off + PhysScene.TaintedObject(LocalID, "BSPrim.setForce", delegate() + { + UpdatePhysicalParameters(); + }); + } + } + + // Find and return a handle to the current vehicle actor. + // Return 'null' if there is no vehicle actor. + public BSDynamics GetVehicleActor(bool createIfNone) + { + BSDynamics ret = null; + BSActor actor; + if (PhysicalActors.TryGetActor(VehicleActorName, out actor)) + { + ret = actor as BSDynamics; + } + else + { + if (createIfNone) + { + ret = new BSDynamics(PhysScene, this, VehicleActorName); + PhysicalActors.Add(ret.ActorName, ret); + } + } + return ret; + } + + public override int VehicleType { + get { + int ret = (int)Vehicle.TYPE_NONE; + BSDynamics vehicleActor = GetVehicleActor(false /* createIfNone */); + if (vehicleActor != null) + ret = (int)vehicleActor.Type; + return ret; + } + set { + Vehicle type = (Vehicle)value; + + PhysScene.TaintedObject(LocalID, "setVehicleType", delegate() + { + // Some vehicle scripts change vehicle type on the fly as an easy way to + // change all the parameters. Like a plane changing to CAR when on the + // ground. In this case, don't want to zero motion. + // ZeroMotion(true /* inTaintTime */); + if (type == Vehicle.TYPE_NONE) + { + // Vehicle type is 'none' so get rid of any actor that may have been allocated. + BSDynamics vehicleActor = GetVehicleActor(false /* createIfNone */); + if (vehicleActor != null) + { + PhysicalActors.RemoveAndRelease(vehicleActor.ActorName); + } + } + else + { + // Vehicle type is not 'none' so create an actor and set it running. + BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); + if (vehicleActor != null) + { + vehicleActor.ProcessTypeChange(type); + ActivateIfPhysical(false); + } + } + }); + } + } + public override void VehicleFloatParam(int param, float value) + { + PhysScene.TaintedObject(LocalID, "BSPrim.VehicleFloatParam", delegate() + { + BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); + if (vehicleActor != null) + { + vehicleActor.ProcessFloatVehicleParam((Vehicle)param, value); + ActivateIfPhysical(false); + } + }); + } + public override void VehicleVectorParam(int param, OMV.Vector3 value) + { + PhysScene.TaintedObject(LocalID, "BSPrim.VehicleVectorParam", delegate() + { + BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); + if (vehicleActor != null) + { + vehicleActor.ProcessVectorVehicleParam((Vehicle)param, value); + ActivateIfPhysical(false); + } + }); + } + public override void VehicleRotationParam(int param, OMV.Quaternion rotation) + { + PhysScene.TaintedObject(LocalID, "BSPrim.VehicleRotationParam", delegate() + { + BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); + if (vehicleActor != null) + { + vehicleActor.ProcessRotationVehicleParam((Vehicle)param, rotation); + ActivateIfPhysical(false); + } + }); + } + public override void VehicleFlags(int param, bool remove) + { + PhysScene.TaintedObject(LocalID, "BSPrim.VehicleFlags", delegate() + { + BSDynamics vehicleActor = GetVehicleActor(true /* createIfNone */); + if (vehicleActor != null) + { + vehicleActor.ProcessVehicleFlags(param, remove); + } + }); + } + + // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more + public override void SetVolumeDetect(int param) { + bool newValue = (param != 0); + if (_isVolumeDetect != newValue) + { + _isVolumeDetect = newValue; + PhysScene.TaintedObject(LocalID, "BSPrim.SetVolumeDetect", delegate() + { + // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect); + SetObjectDynamic(true); + }); + } + return; + } + public override bool IsVolumeDetect + { + get { return _isVolumeDetect; } + } + public override void SetMaterial(int material) + { + base.SetMaterial(material); + PhysScene.TaintedObject(LocalID, "BSPrim.SetMaterial", delegate() + { + UpdatePhysicalParameters(); + }); + } + public override float Friction + { + get { return base.Friction; } + set + { + if (base.Friction != value) + { + base.Friction = value; + PhysScene.TaintedObject(LocalID, "BSPrim.setFriction", delegate() + { + UpdatePhysicalParameters(); + }); + } + } + } + public override float Restitution + { + get { return base.Restitution; } + set + { + if (base.Restitution != value) + { + base.Restitution = value; + PhysScene.TaintedObject(LocalID, "BSPrim.setRestitution", delegate() + { + UpdatePhysicalParameters(); + }); + } + } + } + // The simulator/viewer keep density as 100kg/m3. + // Remember to use BSParam.DensityScaleFactor to create the physical density. + public override float Density + { + get { return base.Density; } + set + { + if (base.Density != value) + { + base.Density = value; + PhysScene.TaintedObject(LocalID, "BSPrim.setDensity", delegate() + { + UpdatePhysicalParameters(); + }); + } + } + } + public override float GravModifier + { + get { return base.GravModifier; } + set + { + if (base.GravModifier != value) + { + base.GravModifier = value; + PhysScene.TaintedObject(LocalID, "BSPrim.setGravityModifier", delegate() + { + UpdatePhysicalParameters(); + }); + } + } + } + public override OMV.Vector3 Velocity { + get { return RawVelocity; } + set { + RawVelocity = value; + PhysScene.TaintedObject(LocalID, "BSPrim.setVelocity", delegate() + { + // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, RawVelocity); + ForceVelocity = RawVelocity; + }); + } + } + public override OMV.Vector3 ForceVelocity { + get { return RawVelocity; } + set { + PhysScene.AssertInTaintTime("BSPrim.ForceVelocity"); + + RawVelocity = Util.ClampV(value, BSParam.MaxLinearVelocity); + if (PhysBody.HasPhysicalBody) + { + DetailLog("{0},BSPrim.ForceVelocity,taint,vel={1}", LocalID, RawVelocity); + PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity); + ActivateIfPhysical(false); + } + } + } + public override OMV.Vector3 Torque { + get { return RawTorque; } + set { + RawTorque = value; + EnableActor(RawTorque != OMV.Vector3.Zero, SetTorqueActorName, delegate() + { + return new BSActorSetTorque(PhysScene, this, SetTorqueActorName); + }); + DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, RawTorque); + + // Call update so actor Refresh() is called to start things off + PhysScene.TaintedObject(LocalID, "BSPrim.setTorque", delegate() + { + UpdatePhysicalParameters(); + }); + } + } + public override OMV.Vector3 Acceleration { + get { return _acceleration; } + set { _acceleration = value; } + } + + public override OMV.Quaternion Orientation { + get { + return RawOrientation; + } + set { + if (RawOrientation == value) + return; + RawOrientation = value; + + PhysScene.TaintedObject(LocalID, "BSPrim.setOrientation", delegate() + { + ForceOrientation = RawOrientation; + }); + } + } + // Go directly to Bullet to get/set the value. + public override OMV.Quaternion ForceOrientation + { + get + { + RawOrientation = PhysScene.PE.GetOrientation(PhysBody); + return RawOrientation; + } + set + { + RawOrientation = value; + if (PhysBody.HasPhysicalBody) + PhysScene.PE.SetTranslation(PhysBody, RawPosition, RawOrientation); + } + } + public override int PhysicsActorType { + get { return _physicsActorType; } + set { _physicsActorType = value; } + } + public override bool IsPhysical { + get { return _isPhysical; } + set { + if (_isPhysical != value) + { + _isPhysical = value; + PhysScene.TaintedObject(LocalID, "BSPrim.setIsPhysical", delegate() + { + DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); + SetObjectDynamic(true); + // whether phys-to-static or static-to-phys, the object is not moving. + ZeroMotion(true); + + }); + } + } + } + + // An object is static (does not move) if selected or not physical + public override bool IsStatic + { + get { return _isSelected || !IsPhysical; } + } + + // An object is solid if it's not phantom and if it's not doing VolumeDetect + public override bool IsSolid + { + get { return !IsPhantom && !_isVolumeDetect; } + } + + // The object is moving and is actively being dynamic in the physical world + public override bool IsPhysicallyActive + { + get { return !_isSelected && IsPhysical; } + } + + // Make gravity work if the object is physical and not selected + // Called at taint-time!! + private void SetObjectDynamic(bool forceRebuild) + { + // Recreate the physical object if necessary + CreateGeomAndObject(forceRebuild); + } + + // Convert the simulator's physical properties into settings on BulletSim objects. + // There are four flags we're interested in: + // IsStatic: Object does not move, otherwise the object has mass and moves + // isSolid: other objects bounce off of this object + // isVolumeDetect: other objects pass through but can generate collisions + // collisionEvents: whether this object returns collision events + // NOTE: overloaded by BSPrimLinkable to also update linkset physical parameters. + public virtual void UpdatePhysicalParameters() + { + if (!PhysBody.HasPhysicalBody) + { + // This would only happen if updates are called for during initialization when the body is not set up yet. + // DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,calledWithNoPhysBody", LocalID); + return; + } + + // Mangling all the physical properties requires the object not be in the physical world. + // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found). + PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); + + // Set up the object physicalness (does gravity and collisions move this object) + MakeDynamic(IsStatic); + + // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters) + PhysicalActors.Refresh(); + + // Arrange for collision events if the simulator wants them + EnableCollisions(SubscribedEvents()); + + // Make solid or not (do things bounce off or pass through this object). + MakeSolid(IsSolid); + + AddObjectToPhysicalWorld(); + + // Rebuild its shape + PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody); + + DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},cType={6},body={7},shape={8}", + LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), + CurrentCollisionFlags, PhysBody.collisionType, PhysBody, PhysShape); + } + + // "Making dynamic" means changing to and from static. + // When static, gravity does not effect the object and it is fixed in space. + // When dynamic, the object can fall and be pushed by others. + // This is independent of its 'solidness' which controls what passes through + // this object and what interacts with it. + protected virtual void MakeDynamic(bool makeStatic) + { + if (makeStatic) + { + // Become a Bullet 'static' object type + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); + // Stop all movement + ZeroMotion(true); + + // Set various physical properties so other object interact properly + PhysScene.PE.SetFriction(PhysBody, Friction); + PhysScene.PE.SetRestitution(PhysBody, Restitution); + PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); + + // Mass is zero which disables a bunch of physics stuff in Bullet + UpdatePhysicalMassProperties(0f, false); + // Set collision detection parameters + if (BSParam.CcdMotionThreshold > 0f) + { + PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); + PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); + } + + // The activation state is 'disabled' so Bullet will not try to act on it. + // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_SIMULATION); + // Start it out sleeping and physical actions could wake it up. + PhysScene.PE.ForceActivationState(PhysBody, ActivationState.ISLAND_SLEEPING); + + // This collides like a static object + PhysBody.collisionType = CollisionType.Static; + } + else + { + // Not a Bullet static object + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); + + // Set various physical properties so other object interact properly + PhysScene.PE.SetFriction(PhysBody, Friction); + PhysScene.PE.SetRestitution(PhysBody, Restitution); + // DetailLog("{0},BSPrim.MakeDynamic,frict={1},rest={2}", LocalID, Friction, Restitution); + + // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 + // Since this can be called multiple times, only zero forces when becoming physical + // PhysicsScene.PE.ClearAllForces(BSBody); + + // For good measure, make sure the transform is set through to the motion state + ForcePosition = RawPosition; + ForceVelocity = RawVelocity; + ForceRotationalVelocity = _rotationalVelocity; + + // A dynamic object has mass + UpdatePhysicalMassProperties(RawMass, false); + + // Set collision detection parameters + if (BSParam.CcdMotionThreshold > 0f) + { + PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); + PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); + } + + // Various values for simulation limits + PhysScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping); + PhysScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime); + PhysScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); + PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); + + // This collides like an object. + PhysBody.collisionType = CollisionType.Dynamic; + + // Force activation of the object so Bullet will act on it. + // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. + PhysScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); + } + } + + // "Making solid" means that other object will not pass through this object. + // To make transparent, we create a Bullet ghost object. + // Note: This expects to be called from the UpdatePhysicalParameters() routine as + // the functions after this one set up the state of a possibly newly created collision body. + private void MakeSolid(bool makeSolid) + { + CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysScene.PE.GetBodyType(PhysBody); + if (makeSolid) + { + // Verify the previous code created the correct shape for this type of thing. + if ((bodyType & CollisionObjectTypes.CO_RIGID_BODY) == 0) + { + m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType); + } + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + } + else + { + if ((bodyType & CollisionObjectTypes.CO_GHOST_OBJECT) == 0) + { + m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); + } + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); + + // Change collision info from a static object to a ghosty collision object + PhysBody.collisionType = CollisionType.VolumeDetect; + } + } + + // Turn on or off the flag controlling whether collision events are returned to the simulator. + private void EnableCollisions(bool wantsCollisionEvents) + { + if (wantsCollisionEvents) + { + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + } + else + { + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); + } + } + + // Add me to the physical world. + // Object MUST NOT already be in the world. + // This routine exists because some assorted properties get mangled by adding to the world. + internal void AddObjectToPhysicalWorld() + { + if (PhysBody.HasPhysicalBody) + { + PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody); + } + else + { + m_log.ErrorFormat("{0} Attempt to add physical object without body. id={1}", LogHeader, LocalID); + DetailLog("{0},BSPrim.AddObjectToPhysicalWorld,addObjectWithoutBody,cType={1}", LocalID, PhysBody.collisionType); + } + } + + // prims don't fly + public override bool Flying { + get { return _flying; } + set { + _flying = value; + } + } + public override bool SetAlwaysRun { + get { return _setAlwaysRun; } + set { _setAlwaysRun = value; } + } + public override bool ThrottleUpdates { + get { return _throttleUpdates; } + set { _throttleUpdates = value; } + } + public bool IsPhantom { + get { + // SceneObjectPart removes phantom objects from the physics scene + // so, although we could implement touching and such, we never + // are invoked as a phantom object + return false; + } + } + public override bool FloatOnWater { + set { + _floatOnWater = value; + PhysScene.TaintedObject(LocalID, "BSPrim.setFloatOnWater", delegate() + { + if (_floatOnWater) + CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + else + CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); + }); + } + } + public override OMV.Vector3 RotationalVelocity { + get { + return _rotationalVelocity; + } + set { + _rotationalVelocity = value; + Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity); + // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); + PhysScene.TaintedObject(LocalID, "BSPrim.setRotationalVelocity", delegate() + { + ForceRotationalVelocity = _rotationalVelocity; + }); + } + } + public override OMV.Vector3 ForceRotationalVelocity { + get { + return _rotationalVelocity; + } + set { + _rotationalVelocity = Util.ClampV(value, BSParam.MaxAngularVelocity); + if (PhysBody.HasPhysicalBody) + { + DetailLog("{0},BSPrim.ForceRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); + PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); + // PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); + ActivateIfPhysical(false); + } + } + } + public override bool Kinematic { + get { return _kinematic; } + set { _kinematic = value; + // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic); + } + } + public override float Buoyancy { + get { return _buoyancy; } + set { + _buoyancy = value; + PhysScene.TaintedObject(LocalID, "BSPrim.setBuoyancy", delegate() + { + ForceBuoyancy = _buoyancy; + }); + } + } + public override float ForceBuoyancy { + get { return _buoyancy; } + set { + _buoyancy = value; + // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); + // Force the recalculation of the various inertia,etc variables in the object + UpdatePhysicalMassProperties(RawMass, true); + DetailLog("{0},BSPrim.ForceBuoyancy,buoy={1},mass={2},grav={3}", LocalID, _buoyancy, RawMass, Gravity); + ActivateIfPhysical(false); + } + } + + public override bool PIDActive + { + get + { + return MoveToTargetActive; + } + + set + { + MoveToTargetActive = value; + + EnableActor(MoveToTargetActive, MoveToTargetActorName, delegate() + { + return new BSActorMoveToTarget(PhysScene, this, MoveToTargetActorName); + }); + + // Call update so actor Refresh() is called to start things off + PhysScene.TaintedObject(LocalID, "BSPrim.PIDActive", delegate() + { + UpdatePhysicalParameters(); + }); + } + } + + public override OMV.Vector3 PIDTarget + { + set + { + base.PIDTarget = value; + BSActor actor; + if (PhysicalActors.TryGetActor(MoveToTargetActorName, out actor)) + { + // if the actor exists, tell it to refresh its values. + actor.Refresh(); + } + + } + } + // Used for llSetHoverHeight and maybe vehicle height + // Hover Height will override MoveTo target's Z + public override bool PIDHoverActive { + set { + base.HoverActive = value; + EnableActor(HoverActive, HoverActorName, delegate() + { + return new BSActorHover(PhysScene, this, HoverActorName); + }); + + // Call update so actor Refresh() is called to start things off + PhysScene.TaintedObject(LocalID, "BSPrim.PIDHoverActive", delegate() + { + UpdatePhysicalParameters(); + }); + } + } + + public override void AddForce(OMV.Vector3 force, bool pushforce) { + // Per documentation, max force is limited. + OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); + + // Since this force is being applied in only one step, make this a force per second. + addForce /= PhysScene.LastTimeStep; + AddForce(addForce, pushforce, false /* inTaintTime */); + } + + // Applying a force just adds this to the total force on the object. + // This added force will only last the next simulation tick. + public override void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { + // for an object, doesn't matter if force is a pushforce or not + if (IsPhysicallyActive) + { + if (force.IsFinite()) + { + // DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce); + + OMV.Vector3 addForce = force; + PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.AddForce", delegate() + { + // Bullet adds this central force to the total force for this tick. + // Deep down in Bullet: + // linearVelocity += totalForce / mass * timeStep; + DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce); + if (PhysBody.HasPhysicalBody) + { + PhysScene.PE.ApplyCentralForce(PhysBody, addForce); + ActivateIfPhysical(false); + } + }); + } + else + { + m_log.WarnFormat("{0}: AddForce: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); + return; + } + } + } + + public void AddForceImpulse(OMV.Vector3 impulse, bool pushforce, bool inTaintTime) { + // for an object, doesn't matter if force is a pushforce or not + if (!IsPhysicallyActive) + { + if (impulse.IsFinite()) + { + OMV.Vector3 addImpulse = Util.ClampV(impulse, BSParam.MaxAddForceMagnitude); + // DetailLog("{0},BSPrim.addForceImpulse,call,impulse={1}", LocalID, impulse); + + PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.AddImpulse", delegate() + { + // Bullet adds this impulse immediately to the velocity + DetailLog("{0},BSPrim.addForceImpulse,taint,impulseforce={1}", LocalID, addImpulse); + if (PhysBody.HasPhysicalBody) + { + PhysScene.PE.ApplyCentralImpulse(PhysBody, addImpulse); + ActivateIfPhysical(false); + } + }); + } + else + { + m_log.WarnFormat("{0}: AddForceImpulse: Got a NaN impulse applied to a prim. LocalID={1}", LogHeader, LocalID); + return; + } + } + } + + // BSPhysObject.AddAngularForce() + public override void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) + { + if (force.IsFinite()) + { + OMV.Vector3 angForce = force; + PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.AddAngularForce", delegate() + { + if (PhysBody.HasPhysicalBody) + { + DetailLog("{0},BSPrim.AddAngularForce,taint,angForce={1}", LocalID, angForce); + PhysScene.PE.ApplyTorque(PhysBody, angForce); + ActivateIfPhysical(false); + } + }); + } + else + { + m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); + return; + } + } + + // A torque impulse. + // ApplyTorqueImpulse adds torque directly to the angularVelocity. + // AddAngularForce accumulates the force and applied it to the angular velocity all at once. + // Computed as: angularVelocity += impulse * inertia; + public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) + { + OMV.Vector3 applyImpulse = impulse; + PhysScene.TaintedObject(inTaintTime, LocalID, "BSPrim.ApplyTorqueImpulse", delegate() + { + if (PhysBody.HasPhysicalBody) + { + PhysScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse); + ActivateIfPhysical(false); + } + }); + } + + public override void SetMomentum(OMV.Vector3 momentum) { + // DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum); + } + #region Mass Calculation + + private float CalculateMass() + { + float volume = _size.X * _size.Y * _size.Z; // default + float tmp; + + float returnMass = 0; + float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f; + float hollowVolume = hollowAmount * hollowAmount; + + switch (BaseShape.ProfileShape) + { + case ProfileShape.Square: + // default box + + if (BaseShape.PathCurve == (byte)Extrusion.Straight) + { + if (hollowAmount > 0.0) + { + switch (BaseShape.HollowShape) + { + case HollowShape.Square: + case HollowShape.Same: + break; + + case HollowShape.Circle: + + hollowVolume *= 0.78539816339f; + break; + + case HollowShape.Triangle: + + hollowVolume *= (0.5f * .5f); + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + + else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) + { + //a tube + + volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX); + tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY); + volume -= volume*tmp*tmp; + + if (hollowAmount > 0.0) + { + hollowVolume *= hollowAmount; + + switch (BaseShape.HollowShape) + { + case HollowShape.Square: + case HollowShape.Same: + break; + + case HollowShape.Circle: + hollowVolume *= 0.78539816339f;; + break; + + case HollowShape.Triangle: + hollowVolume *= 0.5f * 0.5f; + break; + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + + break; + + case ProfileShape.Circle: + + if (BaseShape.PathCurve == (byte)Extrusion.Straight) + { + volume *= 0.78539816339f; // elipse base + + if (hollowAmount > 0.0) + { + switch (BaseShape.HollowShape) + { + case HollowShape.Same: + case HollowShape.Circle: + break; + + case HollowShape.Square: + hollowVolume *= 0.5f * 2.5984480504799f; + break; + + case HollowShape.Triangle: + hollowVolume *= .5f * 1.27323954473516f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + + else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) + { + volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX); + tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY); + volume *= (1.0f - tmp * tmp); + + if (hollowAmount > 0.0) + { + + // calculate the hollow volume by it's shape compared to the prim shape + hollowVolume *= hollowAmount; + + switch (BaseShape.HollowShape) + { + case HollowShape.Same: + case HollowShape.Circle: + break; + + case HollowShape.Square: + hollowVolume *= 0.5f * 2.5984480504799f; + break; + + case HollowShape.Triangle: + hollowVolume *= .5f * 1.27323954473516f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + break; + + case ProfileShape.HalfCircle: + if (BaseShape.PathCurve == (byte)Extrusion.Curve1) + { + volume *= 0.52359877559829887307710723054658f; + } + break; + + case ProfileShape.EquilateralTriangle: + + if (BaseShape.PathCurve == (byte)Extrusion.Straight) + { + volume *= 0.32475953f; + + if (hollowAmount > 0.0) + { + + // calculate the hollow volume by it's shape compared to the prim shape + switch (BaseShape.HollowShape) + { + case HollowShape.Same: + case HollowShape.Triangle: + hollowVolume *= .25f; + break; + + case HollowShape.Square: + hollowVolume *= 0.499849f * 3.07920140172638f; + break; + + case HollowShape.Circle: + // Hollow shape is a perfect cyllinder in respect to the cube's scale + // Cyllinder hollow volume calculation + + hollowVolume *= 0.1963495f * 3.07920140172638f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + else if (BaseShape.PathCurve == (byte)Extrusion.Curve1) + { + volume *= 0.32475953f; + volume *= 0.01f * (float)(200 - BaseShape.PathScaleX); + tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY); + volume *= (1.0f - tmp * tmp); + + if (hollowAmount > 0.0) + { + + hollowVolume *= hollowAmount; + + switch (BaseShape.HollowShape) + { + case HollowShape.Same: + case HollowShape.Triangle: + hollowVolume *= .25f; + break; + + case HollowShape.Square: + hollowVolume *= 0.499849f * 3.07920140172638f; + break; + + case HollowShape.Circle: + + hollowVolume *= 0.1963495f * 3.07920140172638f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + break; + + default: + break; + } + + + + float taperX1; + float taperY1; + float taperX; + float taperY; + float pathBegin; + float pathEnd; + float profileBegin; + float profileEnd; + + if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible) + { + taperX1 = BaseShape.PathScaleX * 0.01f; + if (taperX1 > 1.0f) + taperX1 = 2.0f - taperX1; + taperX = 1.0f - taperX1; + + taperY1 = BaseShape.PathScaleY * 0.01f; + if (taperY1 > 1.0f) + taperY1 = 2.0f - taperY1; + taperY = 1.0f - taperY1; + } + else + { + taperX = BaseShape.PathTaperX * 0.01f; + if (taperX < 0.0f) + taperX = -taperX; + taperX1 = 1.0f - taperX; + + taperY = BaseShape.PathTaperY * 0.01f; + if (taperY < 0.0f) + taperY = -taperY; + taperY1 = 1.0f - taperY; + + } + + + volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY); + + pathBegin = (float)BaseShape.PathBegin * 2.0e-5f; + pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f; + volume *= (pathEnd - pathBegin); + + // this is crude aproximation + profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f; + profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f; + volume *= (profileEnd - profileBegin); + + returnMass = Density * BSParam.DensityScaleFactor * volume; + + returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass); + // DetailLog("{0},BSPrim.CalculateMass,den={1},vol={2},mass={3}", LocalID, Density, volume, returnMass); + DetailLog("{0},BSPrim.CalculateMass,den={1},vol={2},mass={3},pathB={4},pathE={5},profB={6},profE={7},siz={8}", + LocalID, Density, volume, returnMass, pathBegin, pathEnd, profileBegin, profileEnd, _size); + + return returnMass; + }// end CalculateMass + #endregion Mass Calculation + + // Rebuild the geometry and object. + // This is called when the shape changes so we need to recreate the mesh/hull. + // Called at taint-time!!! + public void CreateGeomAndObject(bool forceRebuild) + { + // Create the correct physical representation for this type of object. + // Updates base.PhysBody and base.PhysShape with the new information. + // Ignore 'forceRebuild'. 'GetBodyAndShape' makes the right choices and changes of necessary. + PhysScene.Shapes.GetBodyAndShape(false /*forceRebuild */, PhysScene.World, this, delegate(BulletBody pBody, BulletShape pShape) + { + // Called if the current prim body is about to be destroyed. + // Remove all the physical dependencies on the old body. + // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...) + // Note: this virtual function is overloaded by BSPrimLinkable to remove linkset constraints. + RemoveDependencies(); + }); + + // Make sure the properties are set on the new object + UpdatePhysicalParameters(); + return; + } + + // Called at taint-time + protected virtual void RemoveDependencies() + { + PhysicalActors.RemoveDependencies(); + } + + #region Extension + public override object Extension(string pFunct, params object[] pParams) + { + DetailLog("{0} BSPrim.Extension,op={1}", LocalID, pFunct); + object ret = null; + switch (pFunct) + { + case ExtendedPhysics.PhysFunctAxisLockLimits: + ret = SetAxisLockLimitsExtension(pParams); + break; + default: + ret = base.Extension(pFunct, pParams); + break; + } + return ret; + } + + private void InitializeAxisActor() + { + EnableActor(LockedAngularAxis != LockedAxisFree || LockedLinearAxis != LockedAxisFree, + LockedAxisActorName, delegate() + { + return new BSActorLockAxis(PhysScene, this, LockedAxisActorName); + }); + + // Update parameters so the new actor's Refresh() action is called at the right time. + PhysScene.TaintedObject(LocalID, "BSPrim.LockAxis", delegate() + { + UpdatePhysicalParameters(); + }); + } + + // Passed an array of an array of parameters, set the axis locking. + // This expects an int (PHYS_AXIS_*) followed by none or two limit floats + // followed by another int and floats, etc. + private object SetAxisLockLimitsExtension(object[] pParams) + { + DetailLog("{0} SetAxisLockLimitsExtension. parmlen={1}", LocalID, pParams.GetLength(0)); + object ret = null; + try + { + if (pParams.GetLength(0) > 1) + { + int index = 2; + while (index < pParams.GetLength(0)) + { + var funct = pParams[index]; + DetailLog("{0} SetAxisLockLimitsExtension. op={1}, index={2}", LocalID, funct, index); + if (funct is Int32 || funct is Int64) + { + switch ((int)funct) + { + // Those that take no parameters + case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR: + case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_X: + case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_Y: + case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_Z: + case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR: + case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_X: + case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Y: + case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Z: + case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR: + case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_X: + case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_Y: + case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_Z: + case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR: + case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_X: + case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_Y: + case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_Z: + case ExtendedPhysics.PHYS_AXIS_UNLOCK: + ApplyAxisLimits((int)funct, 0f, 0f); + index += 1; + break; + // Those that take two parameters (the limits) + case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_X: + case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_Y: + case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_Z: + case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_X: + case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_Y: + case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_Z: + ApplyAxisLimits((int)funct, (float)pParams[index + 1], (float)pParams[index + 2]); + index += 3; + break; + default: + m_log.WarnFormat("{0} SetSxisLockLimitsExtension. Unknown op={1}", LogHeader, funct); + index += 1; + break; + } + } + } + InitializeAxisActor(); + ret = (object)index; + } + } + catch (Exception e) + { + m_log.WarnFormat("{0} SetSxisLockLimitsExtension exception in object {1}: {2}", LogHeader, this.Name, e); + ret = null; + } + return ret; // not implemented yet + } + + // Set the locking parameters. + // If an axis is locked, the limits for the axis are set to zero, + // If the axis is being constrained, the high and low value are passed and set. + // When done here, LockedXXXAxis flags are set and LockedXXXAxixLow/High are set to the range. + protected void ApplyAxisLimits(int funct, float low, float high) + { + DetailLog("{0} ApplyAxisLimits. op={1}, low={2}, high={3}", LocalID, funct, low, high); + float linearMax = 23000f; + float angularMax = (float)Math.PI; + + switch (funct) + { + case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR: + this.LockedLinearAxis = new OMV.Vector3(LockedAxis, LockedAxis, LockedAxis); + this.LockedLinearAxisLow = OMV.Vector3.Zero; + this.LockedLinearAxisHigh = OMV.Vector3.Zero; + break; + case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_X: + this.LockedLinearAxis.X = LockedAxis; + this.LockedLinearAxisLow.X = 0f; + this.LockedLinearAxisHigh.X = 0f; + break; + case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_X: + this.LockedLinearAxis.X = LockedAxis; + this.LockedLinearAxisLow.X = Util.Clip(low, -linearMax, linearMax); + this.LockedLinearAxisHigh.X = Util.Clip(high, -linearMax, linearMax); + break; + case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_Y: + this.LockedLinearAxis.Y = LockedAxis; + this.LockedLinearAxisLow.Y = 0f; + this.LockedLinearAxisHigh.Y = 0f; + break; + case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_Y: + this.LockedLinearAxis.Y = LockedAxis; + this.LockedLinearAxisLow.Y = Util.Clip(low, -linearMax, linearMax); + this.LockedLinearAxisHigh.Y = Util.Clip(high, -linearMax, linearMax); + break; + case ExtendedPhysics.PHYS_AXIS_LOCK_LINEAR_Z: + this.LockedLinearAxis.Z = LockedAxis; + this.LockedLinearAxisLow.Z = 0f; + this.LockedLinearAxisHigh.Z = 0f; + break; + case ExtendedPhysics.PHYS_AXIS_LIMIT_LINEAR_Z: + this.LockedLinearAxis.Z = LockedAxis; + this.LockedLinearAxisLow.Z = Util.Clip(low, -linearMax, linearMax); + this.LockedLinearAxisHigh.Z = Util.Clip(high, -linearMax, linearMax); + break; + case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR: + this.LockedAngularAxis = new OMV.Vector3(LockedAxis, LockedAxis, LockedAxis); + this.LockedAngularAxisLow = OMV.Vector3.Zero; + this.LockedAngularAxisHigh = OMV.Vector3.Zero; + break; + case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_X: + this.LockedAngularAxis.X = LockedAxis; + this.LockedAngularAxisLow.X = 0; + this.LockedAngularAxisHigh.X = 0; + break; + case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_X: + this.LockedAngularAxis.X = LockedAxis; + this.LockedAngularAxisLow.X = Util.Clip(low, -angularMax, angularMax); + this.LockedAngularAxisHigh.X = Util.Clip(high, -angularMax, angularMax); + break; + case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Y: + this.LockedAngularAxis.Y = LockedAxis; + this.LockedAngularAxisLow.Y = 0; + this.LockedAngularAxisHigh.Y = 0; + break; + case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_Y: + this.LockedAngularAxis.Y = LockedAxis; + this.LockedAngularAxisLow.Y = Util.Clip(low, -angularMax, angularMax); + this.LockedAngularAxisHigh.Y = Util.Clip(high, -angularMax, angularMax); + break; + case ExtendedPhysics.PHYS_AXIS_LOCK_ANGULAR_Z: + this.LockedAngularAxis.Z = LockedAxis; + this.LockedAngularAxisLow.Z = 0; + this.LockedAngularAxisHigh.Z = 0; + break; + case ExtendedPhysics.PHYS_AXIS_LIMIT_ANGULAR_Z: + this.LockedAngularAxis.Z = LockedAxis; + this.LockedAngularAxisLow.Z = Util.Clip(low, -angularMax, angularMax); + this.LockedAngularAxisHigh.Z = Util.Clip(high, -angularMax, angularMax); + break; + case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR: + this.LockedLinearAxis = LockedAxisFree; + this.LockedLinearAxisLow = new OMV.Vector3(-linearMax, -linearMax, -linearMax); + this.LockedLinearAxisHigh = new OMV.Vector3(linearMax, linearMax, linearMax); + break; + case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_X: + this.LockedLinearAxis.X = FreeAxis; + this.LockedLinearAxisLow.X = -linearMax; + this.LockedLinearAxisHigh.X = linearMax; + break; + case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_Y: + this.LockedLinearAxis.Y = FreeAxis; + this.LockedLinearAxisLow.Y = -linearMax; + this.LockedLinearAxisHigh.Y = linearMax; + break; + case ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR_Z: + this.LockedLinearAxis.Z = FreeAxis; + this.LockedLinearAxisLow.Z = -linearMax; + this.LockedLinearAxisHigh.Z = linearMax; + break; + case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR: + this.LockedAngularAxis = LockedAxisFree; + this.LockedAngularAxisLow = new OMV.Vector3(-angularMax, -angularMax, -angularMax); + this.LockedAngularAxisHigh = new OMV.Vector3(angularMax, angularMax, angularMax); + break; + case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_X: + this.LockedAngularAxis.X = FreeAxis; + this.LockedAngularAxisLow.X = -angularMax; + this.LockedAngularAxisHigh.X = angularMax; + break; + case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_Y: + this.LockedAngularAxis.Y = FreeAxis; + this.LockedAngularAxisLow.Y = -angularMax; + this.LockedAngularAxisHigh.Y = angularMax; + break; + case ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR_Z: + this.LockedAngularAxis.Z = FreeAxis; + this.LockedAngularAxisLow.Z = -angularMax; + this.LockedAngularAxisHigh.Z = angularMax; + break; + case ExtendedPhysics.PHYS_AXIS_UNLOCK: + ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_UNLOCK_LINEAR, 0f, 0f); + ApplyAxisLimits(ExtendedPhysics.PHYS_AXIS_UNLOCK_ANGULAR, 0f, 0f); + break; + default: + break; + } + return; + } + #endregion // Extension + + // The physics engine says that properties have updated. Update same and inform + // the world that things have changed. + // NOTE: BSPrim.UpdateProperties is overloaded by BSPrimLinkable which modifies updates from root and children prims. + // NOTE: BSPrim.UpdateProperties is overloaded by BSPrimDisplaced which handles mapping physical position to simulator position. + public override void UpdateProperties(EntityProperties entprop) + { + // Let anyone (like the actors) modify the updated properties before they are pushed into the object and the simulator. + TriggerPreUpdatePropertyAction(ref entprop); + + // DetailLog("{0},BSPrim.UpdateProperties,entry,entprop={1}", LocalID, entprop); // DEBUG DEBUG + + // Assign directly to the local variables so the normal set actions do not happen + RawPosition = entprop.Position; + RawOrientation = entprop.Rotation; + // DEBUG DEBUG DEBUG -- smooth velocity changes a bit. The simulator seems to be + // very sensitive to velocity changes. + if (entprop.Velocity == OMV.Vector3.Zero || !entprop.Velocity.ApproxEquals(RawVelocity, BSParam.UpdateVelocityChangeThreshold)) + RawVelocity = entprop.Velocity; + _acceleration = entprop.Acceleration; + _rotationalVelocity = entprop.RotationalVelocity; + + // DetailLog("{0},BSPrim.UpdateProperties,afterAssign,entprop={1}", LocalID, entprop); // DEBUG DEBUG + + // The sanity check can change the velocity and/or position. + if (PositionSanityCheck(true /* inTaintTime */ )) + { + entprop.Position = RawPosition; + entprop.Velocity = RawVelocity; + entprop.RotationalVelocity = _rotationalVelocity; + entprop.Acceleration = _acceleration; + } + + OMV.Vector3 direction = OMV.Vector3.UnitX * RawOrientation; // DEBUG DEBUG DEBUG + DetailLog("{0},BSPrim.UpdateProperties,call,entProp={1},dir={2}", LocalID, entprop, direction); + + // remember the current and last set values + LastEntityProperties = CurrentEntityProperties; + CurrentEntityProperties = entprop; + + PhysScene.PostUpdate(this); + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs new file mode 100755 index 0000000..2eb1440 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs @@ -0,0 +1,182 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSPrimDisplaced : BSPrim +{ + // The purpose of this subclass is to do any mapping between what the simulator thinks + // the prim position and orientation is and what the physical position/orientation. + // This difference happens because Bullet assumes the center-of-mass is the <0,0,0> + // of the prim/linkset. The simulator, on the other hand, tracks the location of + // the prim/linkset by the location of the root prim. So, if center-of-mass is anywhere + // but the origin of the root prim, the physical origin is displaced from the simulator origin. + // + // This routine works by capturing ForcePosition and + // adjusting the simulator values (being set) into the physical values. + // The conversion is also done in the opposite direction (physical origin -> simulator origin). + // + // The updateParameter call is also captured and the values from the physics engine + // are converted into simulator origin values before being passed to the base + // class. + + // PositionDisplacement is the vehicle relative distance from the root prim position to the center-of-mass. + public virtual OMV.Vector3 PositionDisplacement { get; set; } + + public BSPrimDisplaced(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, + OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) + : base(localID, primName, parent_scene, pos, size, rotation, pbs, pisPhysical) + { + ClearDisplacement(); + } + + // Clears any center-of-mass displacement introduced by linksets, etc. + // Does not clear the displacement set by the user. + public void ClearDisplacement() + { + if (UserSetCenterOfMassDisplacement.HasValue) + PositionDisplacement = (OMV.Vector3)UserSetCenterOfMassDisplacement; + else + PositionDisplacement = OMV.Vector3.Zero; + } + + // Set this sets and computes the displacement from the passed prim to the center-of-mass. + // A user set value for center-of-mass overrides whatever might be passed in here. + // The displacement is in local coordinates (relative to root prim in linkset oriented coordinates). + // Returns the relative offset from the root position to the center-of-mass. + // Called at taint time. + public virtual Vector3 SetEffectiveCenterOfMassDisplacement(Vector3 centerOfMassDisplacement) + { + PhysScene.AssertInTaintTime("BSPrimDisplaced.SetEffectiveCenterOfMassDisplacement"); + Vector3 comDisp; + if (UserSetCenterOfMassDisplacement.HasValue) + comDisp = (OMV.Vector3)UserSetCenterOfMassDisplacement; + else + comDisp = centerOfMassDisplacement; + + // Eliminate any jitter caused be very slight differences in masses and positions + if (comDisp.ApproxEquals(Vector3.Zero, 0.01f) ) + comDisp = Vector3.Zero; + + DetailLog("{0},BSPrimDisplaced.SetEffectiveCenterOfMassDisplacement,userSet={1},comDisp={2}", + LocalID, UserSetCenterOfMassDisplacement.HasValue, comDisp); + if ( !comDisp.ApproxEquals(PositionDisplacement, 0.01f) ) + { + // Displacement setting is changing. + // The relationship between the physical object and simulated object must be aligned. + PositionDisplacement = comDisp; + this.ForcePosition = RawPosition; + } + + return PositionDisplacement; + } + + // 'ForcePosition' is the one way to set the physical position of the body in the physics engine. + // Displace the simulator idea of position (center of root prim) to the physical position. + public override Vector3 ForcePosition + { + get { + OMV.Vector3 physPosition = PhysScene.PE.GetPosition(PhysBody); + if (PositionDisplacement != OMV.Vector3.Zero) + { + // If there is some displacement, return the physical position (center-of-mass) + // location minus the displacement to give the center of the root prim. + OMV.Vector3 displacement = PositionDisplacement * ForceOrientation; + DetailLog("{0},BSPrimDisplaced.ForcePosition,get,physPos={1},disp={2},simPos={3}", + LocalID, physPosition, displacement, physPosition - displacement); + physPosition -= displacement; + } + RawPosition = physPosition; + return physPosition; + } + set + { + if (PositionDisplacement != OMV.Vector3.Zero) + { + // This value is the simulator's idea of where the prim is: the center of the root prim + RawPosition = value; + + // Move the passed root prim postion to the center-of-mass position and set in the physics engine. + OMV.Vector3 displacement = PositionDisplacement * RawOrientation; + OMV.Vector3 displacedPos = RawPosition + displacement; + DetailLog("{0},BSPrimDisplaced.ForcePosition,set,simPos={1},disp={2},physPos={3}", + LocalID, RawPosition, displacement, displacedPos); + if (PhysBody.HasPhysicalBody) + { + PhysScene.PE.SetTranslation(PhysBody, displacedPos, RawOrientation); + ActivateIfPhysical(false); + } + } + else + { + base.ForcePosition = value; + } + } + } + + // These are also overridden by BSPrimLinkable if the prim can be part of a linkset + public override OMV.Vector3 CenterOfMass + { + get { return RawPosition; } + } + + public override OMV.Vector3 GeometricCenter + { + get { return RawPosition; } + } + + public override void UpdateProperties(EntityProperties entprop) + { + // Undo any center-of-mass displacement that might have been done. + if (PositionDisplacement != OMV.Vector3.Zero) + { + // The origional shape was offset from 'zero' by PositionDisplacement. + // These physical location must be back converted to be centered around the displaced + // root shape. + + // Move the returned center-of-mass location to the root prim location. + OMV.Vector3 displacement = PositionDisplacement * entprop.Rotation; + OMV.Vector3 displacedPos = entprop.Position - displacement; + DetailLog("{0},BSPrimDisplaced.UpdateProperties,physPos={1},disp={2},simPos={3}", + LocalID, entprop.Position, displacement, displacedPos); + entprop.Position = displacedPos; + } + + base.UpdateProperties(entprop); + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs new file mode 100755 index 0000000..430d645 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs @@ -0,0 +1,350 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Region.OptionalModules.Scripting; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSPrimLinkable : BSPrimDisplaced +{ + // The purpose of this subclass is to add linkset functionality to the prim. This overrides + // operations necessary for keeping the linkset created and, additionally, this + // calls the linkset implementation for its creation and management. + +#pragma warning disable 414 + private static readonly string LogHeader = "[BULLETS PRIMLINKABLE]"; +#pragma warning restore 414 + + // This adds the overrides for link() and delink() so the prim is linkable. + + public BSLinkset Linkset { get; set; } + // The index of this child prim. + public int LinksetChildIndex { get; set; } + + public BSLinkset.LinksetImplementation LinksetType { get; set; } + + public BSPrimLinkable(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, + OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) + : base(localID, primName, parent_scene, pos, size, rotation, pbs, pisPhysical) + { + // Default linkset implementation for this prim + LinksetType = (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation; + + Linkset = BSLinkset.Factory(PhysScene, this); + + Linkset.Refresh(this); + } + + public override void Destroy() + { + Linkset = Linkset.RemoveMeFromLinkset(this, false /* inTaintTime */); + base.Destroy(); + } + + public override void link(Manager.PhysicsActor obj) + { + BSPrimLinkable parent = obj as BSPrimLinkable; + if (parent != null) + { + BSPhysObject parentBefore = Linkset.LinksetRoot; // DEBUG + int childrenBefore = Linkset.NumberOfChildren; // DEBUG + + Linkset = parent.Linkset.AddMeToLinkset(this); + + DetailLog("{0},BSPrimLinkable.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}", + LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); + } + return; + } + + public override void delink() + { + // TODO: decide if this parent checking needs to happen at taint time + // Race condition here: if link() and delink() in same simulation tick, the delink will not happen + + BSPhysObject parentBefore = Linkset.LinksetRoot; // DEBUG + int childrenBefore = Linkset.NumberOfChildren; // DEBUG + + Linkset = Linkset.RemoveMeFromLinkset(this, false /* inTaintTime*/); + + DetailLog("{0},BSPrimLinkable.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ", + LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); + return; + } + + // When simulator changes position, this might be moving a child of the linkset. + public override OMV.Vector3 Position + { + get { return base.Position; } + set + { + base.Position = value; + PhysScene.TaintedObject(LocalID, "BSPrimLinkable.setPosition", delegate() + { + Linkset.UpdateProperties(UpdatedProperties.Position, this); + }); + } + } + + // When simulator changes orientation, this might be moving a child of the linkset. + public override OMV.Quaternion Orientation + { + get { return base.Orientation; } + set + { + base.Orientation = value; + PhysScene.TaintedObject(LocalID, "BSPrimLinkable.setOrientation", delegate() + { + Linkset.UpdateProperties(UpdatedProperties.Orientation, this); + }); + } + } + + public override float TotalMass + { + get { return Linkset.LinksetMass; } + } + + public override OMV.Vector3 CenterOfMass + { + get { return Linkset.CenterOfMass; } + } + + public override OMV.Vector3 GeometricCenter + { + get { return Linkset.GeometricCenter; } + } + + // Refresh the linkset structure and parameters when the prim's physical parameters are changed. + public override void UpdatePhysicalParameters() + { + base.UpdatePhysicalParameters(); + // Recompute any linkset parameters. + // When going from non-physical to physical, this re-enables the constraints that + // had been automatically disabled when the mass was set to zero. + // For compound based linksets, this enables and disables interactions of the children. + if (Linkset != null) // null can happen during initialization + Linkset.Refresh(this); + } + + // When the prim is made dynamic or static, the linkset needs to change. + protected override void MakeDynamic(bool makeStatic) + { + base.MakeDynamic(makeStatic); + if (Linkset != null) // null can happen during initialization + { + if (makeStatic) + Linkset.MakeStatic(this); + else + Linkset.MakeDynamic(this); + } + } + + // Body is being taken apart. Remove physical dependencies and schedule a rebuild. + protected override void RemoveDependencies() + { + Linkset.RemoveDependencies(this); + base.RemoveDependencies(); + } + + // Called after a simulation step for the changes in physical object properties. + // Do any filtering/modification needed for linksets. + public override void UpdateProperties(EntityProperties entprop) + { + if (Linkset.IsRoot(this) || Linkset.ShouldReportPropertyUpdates(this)) + { + // Properties are only updated for the roots of a linkset. + // TODO: this will have to change when linksets are articulated. + base.UpdateProperties(entprop); + } + /* + else + { + // For debugging, report the movement of children + DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}", + LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, + entprop.Acceleration, entprop.RotationalVelocity); + } + */ + // The linkset might like to know about changing locations + Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this); + } + + // Called after a simulation step to post a collision with this object. + // This returns 'true' if the collision has been queued and the SendCollisions call must + // be made at the end of the simulation step. + public override bool Collide(uint collidingWith, BSPhysObject collidee, + OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) + { + bool ret = false; + // Ask the linkset if it wants to handle the collision + if (!Linkset.HandleCollide(collidingWith, collidee, contactPoint, contactNormal, pentrationDepth)) + { + // The linkset didn't handle it so pass the collision through normal processing + ret = base.Collide(collidingWith, collidee, contactPoint, contactNormal, pentrationDepth); + } + return ret; + } + + // A linkset reports any collision on any part of the linkset. + public long SomeCollisionSimulationStep = 0; + public override bool HasSomeCollision + { + get + { + return (SomeCollisionSimulationStep == PhysScene.SimulationStep) || base.IsColliding; + } + set + { + if (value) + SomeCollisionSimulationStep = PhysScene.SimulationStep; + else + SomeCollisionSimulationStep = 0; + + base.HasSomeCollision = value; + } + } + + // Convert the existing linkset of this prim into a new type. + public bool ConvertLinkset(BSLinkset.LinksetImplementation newType) + { + bool ret = false; + if (LinksetType != newType) + { + DetailLog("{0},BSPrimLinkable.ConvertLinkset,oldT={1},newT={2}", LocalID, LinksetType, newType); + + // Set the implementation type first so the call to BSLinkset.Factory gets the new type. + this.LinksetType = newType; + + BSLinkset oldLinkset = this.Linkset; + BSLinkset newLinkset = BSLinkset.Factory(PhysScene, this); + + this.Linkset = newLinkset; + + // Pick up any physical dependencies this linkset might have in the physics engine. + oldLinkset.RemoveDependencies(this); + + // Create a list of the children (mainly because can't interate through a list that's changing) + List children = new List(); + oldLinkset.ForEachMember((child) => + { + if (!oldLinkset.IsRoot(child)) + children.Add(child); + return false; // 'false' says to continue to next member + }); + + // Remove the children from the old linkset and add to the new (will be a new instance from the factory) + foreach (BSPrimLinkable child in children) + { + oldLinkset.RemoveMeFromLinkset(child, true /*inTaintTime*/); + } + foreach (BSPrimLinkable child in children) + { + newLinkset.AddMeToLinkset(child); + child.Linkset = newLinkset; + } + + // Force the shape and linkset to get reconstructed + newLinkset.Refresh(this); + this.ForceBodyShapeRebuild(true /* inTaintTime */); + } + return ret; + } + + #region Extension + public override object Extension(string pFunct, params object[] pParams) + { + DetailLog("{0} BSPrimLinkable.Extension,op={1},nParam={2}", LocalID, pFunct, pParams.Length); + object ret = null; + switch (pFunct) + { + // physGetLinksetType(); + // pParams = [ BSPhysObject root, null ] + case ExtendedPhysics.PhysFunctGetLinksetType: + { + ret = (object)LinksetType; + DetailLog("{0},BSPrimLinkable.Extension.physGetLinksetType,type={1}", LocalID, ret); + break; + } + // physSetLinksetType(type); + // pParams = [ BSPhysObject root, null, integer type ] + case ExtendedPhysics.PhysFunctSetLinksetType: + { + if (pParams.Length > 2) + { + BSLinkset.LinksetImplementation linksetType = (BSLinkset.LinksetImplementation)pParams[2]; + if (Linkset.IsRoot(this)) + { + PhysScene.TaintedObject(LocalID, "BSPrim.PhysFunctSetLinksetType", delegate() + { + // Cause the linkset type to change + DetailLog("{0},BSPrimLinkable.Extension.physSetLinksetType, oldType={1},newType={2}", + LocalID, Linkset.LinksetImpl, linksetType); + ConvertLinkset(linksetType); + }); + } + ret = (object)(int)linksetType; + } + break; + } + // physChangeLinkType(linknum, typeCode); + // pParams = [ BSPhysObject root, BSPhysObject child, integer linkType ] + case ExtendedPhysics.PhysFunctChangeLinkType: + { + ret = Linkset.Extension(pFunct, pParams); + break; + } + // physGetLinkType(linknum); + // pParams = [ BSPhysObject root, BSPhysObject child ] + case ExtendedPhysics.PhysFunctGetLinkType: + { + ret = Linkset.Extension(pFunct, pParams); + break; + } + // physChangeLinkParams(linknum, [code, value, code, value, ...]); + // pParams = [ BSPhysObject root, BSPhysObject child, object[] [ string op, object opParam, string op, object opParam, ... ] ] + case ExtendedPhysics.PhysFunctChangeLinkParams: + { + ret = Linkset.Extension(pFunct, pParams); + break; + } + default: + ret = base.Extension(pFunct, pParams); + break; + } + return ret; + } + #endregion // Extension +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs new file mode 100644 index 0000000..8a19944 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs @@ -0,0 +1,1281 @@ +/* + * 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.Runtime.InteropServices; +using System.Text; +using System.Threading; +using OpenSim.Framework; +using OpenSim.Framework.Monitoring; +using OpenSim.Region.Framework; +using OpenSim.Region.CoreModules; +using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging; +using OpenSim.Region.Physics.Manager; +using Nini.Config; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public sealed class BSScene : PhysicsScene, IPhysicsParameters +{ + internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + internal static readonly string LogHeader = "[BULLETS SCENE]"; + + // The name of the region we're working for. + public string RegionName { get; private set; } + + public string BulletSimVersion = "?"; + + // The handle to the underlying managed or unmanaged version of Bullet being used. + public string BulletEngineName { get; private set; } + public BSAPITemplate PE; + + // If the physics engine is running on a separate thread + public Thread m_physicsThread; + + public Dictionary PhysObjects; + public BSShapeCollection Shapes; + + // Keeping track of the objects with collisions so we can report begin and end of a collision + public HashSet ObjectsWithCollisions = new HashSet(); + public HashSet ObjectsWithNoMoreCollisions = new HashSet(); + + // All the collision processing is protected with this lock object + public Object CollisionLock = new Object(); + + // Properties are updated here + public Object UpdateLock = new Object(); + public HashSet ObjectsWithUpdates = new HashSet(); + + // Keep track of all the avatars so we can send them a collision event + // every tick so OpenSim will update its animation. + private HashSet AvatarsInScene = new HashSet(); + private Object AvatarsInSceneLock = new Object(); + + // let my minuions use my logger + public ILog Logger { get { return m_log; } } + + public IMesher mesher; + public uint WorldID { get; private set; } + public BulletWorld World { get; private set; } + + // All the constraints that have been allocated in this instance. + public BSConstraintCollection Constraints { get; private set; } + + // Simulation parameters + //internal float m_physicsStepTime; // if running independently, the interval simulated by default + + internal int m_maxSubSteps; + internal float m_fixedTimeStep; + + internal float m_simulatedTime; // the time simulated previously. Used for physics framerate calc. + + internal long m_simulationStep = 0; // The current simulation step. + public long SimulationStep { get { return m_simulationStep; } } + // A number to use for SimulationStep that is probably not any step value + // Used by the collision code (which remembers the step when a collision happens) to remember not any simulation step. + public static long NotASimulationStep = -1234; + + internal float LastTimeStep { get; private set; } // The simulation time from the last invocation of Simulate() + + internal float NominalFrameRate { get; set; } // Parameterized ideal frame rate that simulation is scaled to + + // Physical objects can register for prestep or poststep events + public delegate void PreStepAction(float timeStep); + public delegate void PostStepAction(float timeStep); + public event PreStepAction BeforeStep; + public event PostStepAction AfterStep; + + // A value of the time 'now' so all the collision and update routines do not have to get their own + // Set to 'now' just before all the prims and actors are called for collisions and updates + public int SimulationNowTime { get; private set; } + + // True if initialized and ready to do simulation steps + private bool m_initialized = false; + + // Flag which is true when processing taints. + // Not guaranteed to be correct all the time (don't depend on this) but good for debugging. + public bool InTaintTime { get; private set; } + + // Pinned memory used to pass step information between managed and unmanaged + internal int m_maxCollisionsPerFrame; + internal CollisionDesc[] m_collisionArray; + + internal int m_maxUpdatesPerFrame; + internal EntityProperties[] m_updateArray; + + /// + /// Used to control physics simulation timing if Bullet is running on its own thread. + /// + private ManualResetEvent m_updateWaitEvent; + + public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero + public const uint GROUNDPLANE_ID = 1; + public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here + + public float SimpleWaterLevel { get; set; } + public BSTerrainManager TerrainManager { get; private set; } + + public ConfigurationParameters Params + { + get { return UnmanagedParams[0]; } + } + public Vector3 DefaultGravity + { + get { return new Vector3(0f, 0f, Params.gravity); } + } + // Just the Z value of the gravity + public float DefaultGravityZ + { + get { return Params.gravity; } + } + + // When functions in the unmanaged code must be called, it is only + // done at a known time just before the simulation step. The taint + // system saves all these function calls and executes them in + // order before the simulation. + public delegate void TaintCallback(); + private struct TaintCallbackEntry + { + public String originator; + public String ident; + public TaintCallback callback; + public TaintCallbackEntry(string pIdent, TaintCallback pCallBack) + { + originator = BSScene.DetailLogZero; + ident = pIdent; + callback = pCallBack; + } + public TaintCallbackEntry(string pOrigin, string pIdent, TaintCallback pCallBack) + { + originator = pOrigin; + ident = pIdent; + callback = pCallBack; + } + } + private Object _taintLock = new Object(); // lock for using the next object + private List _taintOperations; + private Dictionary _postTaintOperations; + private List _postStepOperations; + + // A pointer to an instance if this structure is passed to the C++ code + // Used to pass basic configuration values to the unmanaged code. + internal ConfigurationParameters[] UnmanagedParams; + + // Sometimes you just have to log everything. + public Logging.LogWriter PhysicsLogging; + private bool m_physicsLoggingEnabled; + private string m_physicsLoggingDir; + private string m_physicsLoggingPrefix; + private int m_physicsLoggingFileMinutes; + private bool m_physicsLoggingDoFlush; + private bool m_physicsPhysicalDumpEnabled; + public int PhysicsMetricDumpFrames { get; set; } + // 'true' of the vehicle code is to log lots of details + public bool VehicleLoggingEnabled { get; private set; } + public bool VehiclePhysicalLoggingEnabled { get; private set; } + + #region Construction and Initialization + public BSScene(string engineType, string identifier) + { + m_initialized = false; + + // The name of the region we're working for is passed to us. Keep for identification. + RegionName = identifier; + + // Set identifying variables in the PhysicsScene interface. + EngineType = engineType; + Name = EngineType + "/" + RegionName; + } + + // Old version of initialization that assumes legacy sized regions (256x256) + public override void Initialise(IMesher meshmerizer, IConfigSource config) + { + m_log.ErrorFormat("{0} WARNING WARNING WARNING! BulletSim initialized without region extent specification. Terrain will be messed up."); + Vector3 regionExtent = new Vector3( Constants.RegionSize, Constants.RegionSize, Constants.RegionSize); + Initialise(meshmerizer, config, regionExtent); + + } + + public override void Initialise(IMesher meshmerizer, IConfigSource config, Vector3 regionExtent) + { + mesher = meshmerizer; + _taintOperations = new List(); + _postTaintOperations = new Dictionary(); + _postStepOperations = new List(); + PhysObjects = new Dictionary(); + Shapes = new BSShapeCollection(this); + + m_simulatedTime = 0f; + LastTimeStep = 0.1f; + + // Allocate pinned memory to pass parameters. + UnmanagedParams = new ConfigurationParameters[1]; + + // Set default values for physics parameters plus any overrides from the ini file + GetInitialParameterValues(config); + + // Force some parameters to values depending on other configurations + // Only use heightmap terrain implementation if terrain larger than legacy size + if ((uint)regionExtent.X > Constants.RegionSize || (uint)regionExtent.Y > Constants.RegionSize) + { + m_log.WarnFormat("{0} Forcing terrain implementation to heightmap for large region", LogHeader); + BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap; + } + + // Get the connection to the physics engine (could be native or one of many DLLs) + PE = SelectUnderlyingBulletEngine(BulletEngineName); + + // Enable very detailed logging. + // By creating an empty logger when not logging, the log message invocation code + // can be left in and every call doesn't have to check for null. + if (m_physicsLoggingEnabled) + { + PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes, m_physicsLoggingDoFlush); + PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output its own error messages. + } + else + { + PhysicsLogging = new Logging.LogWriter(); + } + + // Allocate memory for returning of the updates and collisions from the physics engine + m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame]; + m_updateArray = new EntityProperties[m_maxUpdatesPerFrame]; + + // The bounding box for the simulated world. The origin is 0,0,0 unless we're + // a child in a mega-region. + // Bullet actually doesn't care about the extents of the simulated + // area. It tracks active objects no matter where they are. + Vector3 worldExtent = regionExtent; + + World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray); + + Constraints = new BSConstraintCollection(World); + + TerrainManager = new BSTerrainManager(this, worldExtent); + TerrainManager.CreateInitialGroundPlaneAndTerrain(); + + // Put some informational messages into the log file. + m_log.InfoFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); + + InTaintTime = false; + m_initialized = true; + + // If the physics engine runs on its own thread, start same. + if (BSParam.UseSeparatePhysicsThread) + { + // The physics simulation should happen independently of the heartbeat loop + m_physicsThread + = WorkManager.StartThread( + BulletSPluginPhysicsThread, + string.Format("{0} ({1})", BulletEngineName, RegionName), + ThreadPriority.Normal, + true, + true); + } + } + + // All default parameter values are set here. There should be no values set in the + // variable definitions. + private void GetInitialParameterValues(IConfigSource config) + { + ConfigurationParameters parms = new ConfigurationParameters(); + UnmanagedParams[0] = parms; + + BSParam.SetParameterDefaultValues(this); + + if (config != null) + { + // If there are specifications in the ini file, use those values + IConfig pConfig = config.Configs["BulletSim"]; + if (pConfig != null) + { + BSParam.SetParameterConfigurationValues(this, pConfig); + + // There are two Bullet implementations to choose from + BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged"); + + // Very detailed logging for physics debugging + // TODO: the boolean values can be moved to the normal parameter processing. + m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); + m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); + m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); + m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); + m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false); + m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false); + // Very detailed logging for vehicle debugging + VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); + VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false); + + // Do any replacements in the parameters + m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); + } + else + { + // Nothing in the configuration INI file so assume unmanaged and other defaults. + BulletEngineName = "BulletUnmanaged"; + m_physicsLoggingEnabled = false; + VehicleLoggingEnabled = false; + } + + // The material characteristics. + BSMaterials.InitializeFromDefaults(Params); + if (pConfig != null) + { + // Let the user add new and interesting material property values. + BSMaterials.InitializefromParameters(pConfig); + } + } + } + + // A helper function that handles a true/false parameter and returns the proper float number encoding + float ParamBoolean(IConfig config, string parmName, float deflt) + { + float ret = deflt; + if (config.Contains(parmName)) + { + ret = ConfigurationParameters.numericFalse; + if (config.GetBoolean(parmName, false)) + { + ret = ConfigurationParameters.numericTrue; + } + } + return ret; + } + + // Select the connection to the actual Bullet implementation. + // The main engine selection is the engineName up to the first hypen. + // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name + // is passed to the engine to do its special selection, etc. + private BSAPITemplate SelectUnderlyingBulletEngine(string engineName) + { + // For the moment, do a simple switch statement. + // Someday do fancyness with looking up the interfaces in the assembly. + BSAPITemplate ret = null; + + string selectionName = engineName.ToLower(); + int hyphenIndex = engineName.IndexOf("-"); + if (hyphenIndex > 0) + selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1); + + switch (selectionName) + { + case "bullet": + case "bulletunmanaged": + ret = new BSAPIUnman(engineName, this); + break; + case "bulletxna": + ret = new BSAPIXNA(engineName, this); + // Disable some features that are not implemented in BulletXNA + m_log.InfoFormat("{0} Disabling some physics features not implemented by BulletXNA", LogHeader); + m_log.InfoFormat("{0} Disabling ShouldUseBulletHACD", LogHeader); + BSParam.ShouldUseBulletHACD = false; + m_log.InfoFormat("{0} Disabling ShouldUseSingleConvexHullForPrims", LogHeader); + BSParam.ShouldUseSingleConvexHullForPrims = false; + m_log.InfoFormat("{0} Disabling ShouldUseGImpactShapeForPrims", LogHeader); + BSParam.ShouldUseGImpactShapeForPrims = false; + m_log.InfoFormat("{0} Setting terrain implimentation to Heightmap", LogHeader); + BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap; + break; + } + + if (ret == null) + { + m_log.ErrorFormat("{0} COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*' or 'BulletXNA-*'", LogHeader); + } + else + { + m_log.InfoFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion); + } + + return ret; + } + + public override void Dispose() + { + // m_log.DebugFormat("{0}: Dispose()", LogHeader); + + // make sure no stepping happens while we're deleting stuff + m_initialized = false; + + lock (PhysObjects) + { + foreach (KeyValuePair kvp in PhysObjects) + { + kvp.Value.Destroy(); + } + PhysObjects.Clear(); + } + + // Now that the prims are all cleaned up, there should be no constraints left + if (Constraints != null) + { + Constraints.Dispose(); + Constraints = null; + } + + if (Shapes != null) + { + Shapes.Dispose(); + Shapes = null; + } + + if (TerrainManager != null) + { + TerrainManager.ReleaseGroundPlaneAndTerrain(); + TerrainManager.Dispose(); + TerrainManager = null; + } + + // Anything left in the unmanaged code should be cleaned out + PE.Shutdown(World); + + // Not logging any more + PhysicsLogging.Close(); + } + #endregion // Construction and Initialization + + #region Prim and Avatar addition and removal + + public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying) + { + m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); + return null; + } + + public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying) + { + // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName); + + if (!m_initialized) return null; + + BSCharacter actor = new BSCharacter(localID, avName, this, position, velocity, size, isFlying); + lock (PhysObjects) + PhysObjects.Add(localID, actor); + + // TODO: Remove kludge someday. + // We must generate a collision for avatars whether they collide or not. + // This is required by OpenSim to update avatar animations, etc. + lock (AvatarsInSceneLock) + AvatarsInScene.Add(actor); + + return actor; + } + + public override void RemoveAvatar(PhysicsActor actor) + { + // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader); + + if (!m_initialized) return; + + BSCharacter bsactor = actor as BSCharacter; + if (bsactor != null) + { + try + { + lock (PhysObjects) + PhysObjects.Remove(bsactor.LocalID); + // Remove kludge someday + lock (AvatarsInSceneLock) + AvatarsInScene.Remove(bsactor); + } + catch (Exception e) + { + m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e); + } + bsactor.Destroy(); + // bsactor.dispose(); + } + else + { + m_log.ErrorFormat("{0}: Requested to remove avatar that is not a BSCharacter. ID={1}, type={2}", + LogHeader, actor.LocalID, actor.GetType().Name); + } + } + + public override void RemovePrim(PhysicsActor prim) + { + if (!m_initialized) return; + + BSPhysObject bsprim = prim as BSPhysObject; + if (bsprim != null) + { + DetailLog("{0},RemovePrim,call", bsprim.LocalID); + // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID); + try + { + lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID); + } + catch (Exception e) + { + m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e); + } + bsprim.Destroy(); + // bsprim.dispose(); + } + else + { + m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader); + } + } + + public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, + Vector3 size, Quaternion rotation, bool isPhysical, uint localID) + { + // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName); + + if (!m_initialized) return null; + + // DetailLog("{0},BSScene.AddPrimShape,call", localID); + + BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical); + lock (PhysObjects) PhysObjects.Add(localID, prim); + return prim; + } + + // This is a call from the simulator saying that some physical property has been updated. + // The BulletSim driver senses the changing of relevant properties so this taint + // information call is not needed. + public override void AddPhysicsActorTaint(PhysicsActor prim) { } + + #endregion // Prim and Avatar addition and removal + + #region Simulation + + // Call from the simulator to send physics information to the simulator objects. + // This pushes all the collision and property update events into the objects in + // the simulator and, since it is on the heartbeat thread, there is an implicit + // locking of those data structures from other heartbeat events. + // If the physics engine is running on a separate thread, the update information + // will be in the ObjectsWithCollions and ObjectsWithUpdates structures. + public override float Simulate(float timeStep) + { + if (!BSParam.UseSeparatePhysicsThread) + { + DoPhysicsStep(timeStep); + } + return SendUpdatesToSimulator(timeStep); + } + + // Call the physics engine to do one 'timeStep' and collect collisions and updates + // into ObjectsWithCollisions and ObjectsWithUpdates data structures. + private void DoPhysicsStep(float timeStep) + { + // prevent simulation until we've been initialized + if (!m_initialized) return; + + LastTimeStep = timeStep; + + int updatedEntityCount = 0; + int collidersCount = 0; + + int beforeTime = Util.EnvironmentTickCount(); + int simTime = 0; + + int numTaints = _taintOperations.Count; + InTaintTime = true; // Only used for debugging so locking is not necessary. + + // update the prim states while we know the physics engine is not busy + ProcessTaints(); + + // Some of the physical objects requre individual, pre-step calls + // (vehicles and avatar movement, in particular) + TriggerPreStepEvent(timeStep); + + // the prestep actions might have added taints + numTaints += _taintOperations.Count; + ProcessTaints(); + + InTaintTime = false; // Only used for debugging so locking is not necessary. + + // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. + // Only enable this in a limited test world with few objects. + if (m_physicsPhysicalDumpEnabled) + PE.DumpAllInfo(World); + + // step the physical world one interval + m_simulationStep++; + int numSubSteps = 0; + try + { + numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); + + } + catch (Exception e) + { + m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}", + LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e); + DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}", + DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); + updatedEntityCount = 0; + collidersCount = 0; + } + + // Make the physics engine dump useful statistics periodically + if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) + PE.DumpPhysicsStatistics(World); + + // Get a value for 'now' so all the collision and update routines don't have to get their own. + SimulationNowTime = Util.EnvironmentTickCount(); + + // Send collision information to the colliding objects. The objects decide if the collision + // is 'real' (like linksets don't collide with themselves) and the individual objects + // know if the simulator has subscribed to collisions. + lock (CollisionLock) + { + if (collidersCount > 0) + { + lock (PhysObjects) + { + for (int ii = 0; ii < collidersCount; ii++) + { + uint cA = m_collisionArray[ii].aID; + uint cB = m_collisionArray[ii].bID; + Vector3 point = m_collisionArray[ii].point; + Vector3 normal = m_collisionArray[ii].normal; + float penetration = m_collisionArray[ii].penetration; + SendCollision(cA, cB, point, normal, penetration); + SendCollision(cB, cA, point, -normal, penetration); + } + } + } + } + + // If any of the objects had updated properties, tell the managed objects about the update + // and remember that there was a change so it will be passed to the simulator. + lock (UpdateLock) + { + if (updatedEntityCount > 0) + { + lock (PhysObjects) + { + for (int ii = 0; ii < updatedEntityCount; ii++) + { + EntityProperties entprop = m_updateArray[ii]; + BSPhysObject pobj; + if (PhysObjects.TryGetValue(entprop.ID, out pobj)) + { + if (pobj.IsInitialized) + pobj.UpdateProperties(entprop); + } + } + } + } + } + + // Some actors want to know when the simulation step is complete. + TriggerPostStepEvent(timeStep); + + simTime = Util.EnvironmentTickCountSubtract(beforeTime); + if (PhysicsLogging.Enabled) + { + DetailLog("{0},DoPhysicsStep,complete,frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}", + DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, + updatedEntityCount, collidersCount, ObjectsWithCollisions.Count); + } + + // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. + // Only enable this in a limited test world with few objects. + if (m_physicsPhysicalDumpEnabled) + PE.DumpAllInfo(World); + + // The physics engine returns the number of milliseconds it simulated this call. + // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. + // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55). + m_simulatedTime += (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate; + } + + // Called by a BSPhysObject to note that it has changed properties and this information + // should be passed up to the simulator at the proper time. + // Note: this is called by the BSPhysObject from invocation via DoPhysicsStep() above so + // this is is under UpdateLock. + public void PostUpdate(BSPhysObject updatee) + { + lock (UpdateLock) + { + ObjectsWithUpdates.Add(updatee); + } + } + + // The simulator thinks it is physics time so return all the collisions and position + // updates that were collected in actual physics simulation. + private float SendUpdatesToSimulator(float timeStep) + { + if (!m_initialized) return 5.0f; + + DetailLog("{0},SendUpdatesToSimulator,collisions={1},updates={2},simedTime={3}", + BSScene.DetailLogZero, ObjectsWithCollisions.Count, ObjectsWithUpdates.Count, m_simulatedTime); + // Push the collisions into the simulator. + lock (CollisionLock) + { + if (ObjectsWithCollisions.Count > 0) + { + foreach (BSPhysObject bsp in ObjectsWithCollisions) + if (!bsp.SendCollisions()) + { + // If the object is done colliding, see that it's removed from the colliding list + ObjectsWithNoMoreCollisions.Add(bsp); + } + } + + // This is a kludge to get avatar movement updates. + // The simulator expects collisions for avatars even if there are have been no collisions. + // The event updates avatar animations and stuff. + // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. + // Note that we get a copy of the list to search because SendCollision() can take a while. + HashSet tempAvatarsInScene; + lock (AvatarsInSceneLock) + { + tempAvatarsInScene = new HashSet(AvatarsInScene); + } + foreach (BSPhysObject actor in tempAvatarsInScene) + { + if (!ObjectsWithCollisions.Contains(actor)) // don't call avatars twice + actor.SendCollisions(); + } + tempAvatarsInScene = null; + + // Objects that are done colliding are removed from the ObjectsWithCollisions list. + // Not done above because it is inside an iteration of ObjectWithCollisions. + // This complex collision processing is required to create an empty collision + // event call after all real collisions have happened on an object. This allows + // the simulator to generate the 'collision end' event. + if (ObjectsWithNoMoreCollisions.Count > 0) + { + foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) + ObjectsWithCollisions.Remove(po); + ObjectsWithNoMoreCollisions.Clear(); + } + } + + // Call the simulator for each object that has physics property updates. + HashSet updatedObjects = null; + lock (UpdateLock) + { + if (ObjectsWithUpdates.Count > 0) + { + updatedObjects = ObjectsWithUpdates; + ObjectsWithUpdates = new HashSet(); + } + } + if (updatedObjects != null) + { + foreach (BSPhysObject obj in updatedObjects) + { + obj.RequestPhysicsterseUpdate(); + } + updatedObjects.Clear(); + } + + // Return the framerate simulated to give the above returned results. + // (Race condition here but this is just bookkeeping so rare mistakes do not merit a lock). + float simTime = m_simulatedTime; + m_simulatedTime = 0f; + return simTime; + } + + // Something has collided + private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration) + { + if (localID <= TerrainManager.HighestTerrainID) + { + return; // don't send collisions to the terrain + } + + BSPhysObject collider; + // NOTE that PhysObjects was locked before the call to SendCollision(). + if (!PhysObjects.TryGetValue(localID, out collider)) + { + // If the object that is colliding cannot be found, just ignore the collision. + DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith); + return; + } + + // Note: the terrain is not in the physical object list so 'collidee' can be null when Collide() is called. + BSPhysObject collidee = null; + PhysObjects.TryGetValue(collidingWith, out collidee); + + // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); + + if (collider.IsInitialized) + { + if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) + { + // If a collision was 'good', remember to send it to the simulator + lock (CollisionLock) + { + ObjectsWithCollisions.Add(collider); + } + } + } + + return; + } + + public void BulletSPluginPhysicsThread() + { + Thread.CurrentThread.Priority = ThreadPriority.Highest; + m_updateWaitEvent = new ManualResetEvent(false); + + while (m_initialized) + { + int beginSimulationRealtimeMS = Util.EnvironmentTickCount(); + + if (BSParam.Active) + DoPhysicsStep(BSParam.PhysicsTimeStep); + + int simulationRealtimeMS = Util.EnvironmentTickCountSubtract(beginSimulationRealtimeMS); + int simulationTimeVsRealtimeDifferenceMS = ((int)(BSParam.PhysicsTimeStep*1000f)) - simulationRealtimeMS; + + if (simulationTimeVsRealtimeDifferenceMS > 0) + { + // The simulation of the time interval took less than realtime. + // Do a wait for the rest of realtime. + m_updateWaitEvent.WaitOne(simulationTimeVsRealtimeDifferenceMS); + //Thread.Sleep(simulationTimeVsRealtimeDifferenceMS); + } + else + { + // The simulation took longer than realtime. + // Do some scaling of simulation time. + // TODO. + DetailLog("{0},BulletSPluginPhysicsThread,longerThanRealtime={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS); + } + + Watchdog.UpdateThread(); + } + + Watchdog.RemoveThread(); + } + + #endregion // Simulation + + public override void GetResults() { } + + #region Terrain + + public override void SetTerrain(float[] heightMap) { + TerrainManager.SetTerrain(heightMap); + } + + public override void SetWaterLevel(float baseheight) + { + SimpleWaterLevel = baseheight; + } + + public override void DeleteTerrain() + { + // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); + } + + // Although no one seems to check this, I do support combining. + public override bool SupportsCombining() + { + return TerrainManager.SupportsCombining(); + } + // This call says I am a child to region zero in a mega-region. 'pScene' is that + // of region zero, 'offset' is my offset from regions zero's origin, and + // 'extents' is the largest XY that is handled in my region. + public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents) + { + TerrainManager.Combine(pScene, offset, extents); + } + + // Unhook all the combining that I know about. + public override void UnCombine(PhysicsScene pScene) + { + TerrainManager.UnCombine(pScene); + } + + #endregion // Terrain + + public override Dictionary GetTopColliders() + { + Dictionary topColliders; + + lock (PhysObjects) + { + foreach (KeyValuePair kvp in PhysObjects) + { + kvp.Value.ComputeCollisionScore(); + } + + List orderedPrims = new List(PhysObjects.Values); + orderedPrims.OrderByDescending(p => p.CollisionScore); + topColliders = orderedPrims.Take(25).ToDictionary(p => p.LocalID, p => p.CollisionScore); + } + + return topColliders; + } + + public override bool IsThreaded { get { return false; } } + + #region Extensions + public override object Extension(string pFunct, params object[] pParams) + { + DetailLog("{0} BSScene.Extension,op={1}", DetailLogZero, pFunct); + return base.Extension(pFunct, pParams); + } + #endregion // Extensions + + public static string PrimitiveBaseShapeToString(PrimitiveBaseShape pbs) + { + float pathShearX = pbs.PathShearX < 128 ? (float)pbs.PathShearX * 0.01f : (float)(pbs.PathShearX - 256) * 0.01f; + float pathShearY = pbs.PathShearY < 128 ? (float)pbs.PathShearY * 0.01f : (float)(pbs.PathShearY - 256) * 0.01f; + float pathBegin = (float)pbs.PathBegin * 2.0e-5f; + float pathEnd = 1.0f - (float)pbs.PathEnd * 2.0e-5f; + float pathScaleX = (float)(200 - pbs.PathScaleX) * 0.01f; + float pathScaleY = (float)(200 - pbs.PathScaleY) * 0.01f; + float pathTaperX = pbs.PathTaperX * 0.01f; + float pathTaperY = pbs.PathTaperY * 0.01f; + + float profileBegin = (float)pbs.ProfileBegin * 2.0e-5f; + float profileEnd = 1.0f - (float)pbs.ProfileEnd * 2.0e-5f; + float profileHollow = (float)pbs.ProfileHollow * 2.0e-5f; + if (profileHollow > 0.95f) + profileHollow = 0.95f; + + StringBuilder buff = new StringBuilder(); + buff.Append("shape="); + buff.Append(((ProfileShape)pbs.ProfileShape).ToString()); + buff.Append(","); + buff.Append("hollow="); + buff.Append(((HollowShape)pbs.HollowShape).ToString()); + buff.Append(","); + buff.Append("pathCurve="); + buff.Append(((Extrusion)pbs.PathCurve).ToString()); + buff.Append(","); + buff.Append("profCurve="); + buff.Append(((Extrusion)pbs.ProfileCurve).ToString()); + buff.Append(","); + buff.Append("profHollow="); + buff.Append(profileHollow.ToString()); + buff.Append(","); + buff.Append("pathBegEnd="); + buff.Append(pathBegin.ToString()); + buff.Append("/"); + buff.Append(pathEnd.ToString()); + buff.Append(","); + buff.Append("profileBegEnd="); + buff.Append(profileBegin.ToString()); + buff.Append("/"); + buff.Append(profileEnd.ToString()); + buff.Append(","); + buff.Append("scaleXY="); + buff.Append(pathScaleX.ToString()); + buff.Append("/"); + buff.Append(pathScaleY.ToString()); + buff.Append(","); + buff.Append("shearXY="); + buff.Append(pathShearX.ToString()); + buff.Append("/"); + buff.Append(pathShearY.ToString()); + buff.Append(","); + buff.Append("taperXY="); + buff.Append(pbs.PathTaperX.ToString()); + buff.Append("/"); + buff.Append(pbs.PathTaperY.ToString()); + buff.Append(","); + buff.Append("skew="); + buff.Append(pbs.PathSkew.ToString()); + buff.Append(","); + buff.Append("twist/Beg="); + buff.Append(pbs.PathTwist.ToString()); + buff.Append("/"); + buff.Append(pbs.PathTwistBegin.ToString()); + + return buff.ToString(); + } + + #region Taints + // The simulation execution order is: + // Simulate() + // DoOneTimeTaints + // TriggerPreStepEvent + // DoOneTimeTaints + // Step() + // ProcessAndSendToSimulatorCollisions + // ProcessAndSendToSimulatorPropertyUpdates + // TriggerPostStepEvent + + // Calls to the PhysicsActors can't directly call into the physics engine + // because it might be busy. We delay changes to a known time. + // We rely on C#'s closure to save and restore the context for the delegate. + public void TaintedObject(string pOriginator, string pIdent, TaintCallback pCallback) + { + TaintedObject(false /*inTaintTime*/, pOriginator, pIdent, pCallback); + } + public void TaintedObject(uint pOriginator, String pIdent, TaintCallback pCallback) + { + TaintedObject(false /*inTaintTime*/, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); + } + public void TaintedObject(bool inTaintTime, String pIdent, TaintCallback pCallback) + { + TaintedObject(inTaintTime, BSScene.DetailLogZero, pIdent, pCallback); + } + public void TaintedObject(bool inTaintTime, uint pOriginator, String pIdent, TaintCallback pCallback) + { + TaintedObject(inTaintTime, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); + } + // Sometimes a potentially tainted operation can be used in and out of taint time. + // This routine executes the command immediately if in taint-time otherwise it is queued. + public void TaintedObject(bool inTaintTime, string pOriginator, string pIdent, TaintCallback pCallback) + { + if (!m_initialized) return; + + if (inTaintTime) + pCallback(); + else + { + lock (_taintLock) + { + _taintOperations.Add(new TaintCallbackEntry(pOriginator, pIdent, pCallback)); + } + } + } + + private void TriggerPreStepEvent(float timeStep) + { + PreStepAction actions = BeforeStep; + if (actions != null) + actions(timeStep); + + } + + private void TriggerPostStepEvent(float timeStep) + { + PostStepAction actions = AfterStep; + if (actions != null) + actions(timeStep); + + } + + // When someone tries to change a property on a BSPrim or BSCharacter, the object queues + // a callback into itself to do the actual property change. That callback is called + // here just before the physics engine is called to step the simulation. + public void ProcessTaints() + { + ProcessRegularTaints(); + ProcessPostTaintTaints(); + } + + private void ProcessRegularTaints() + { + if (m_initialized && _taintOperations.Count > 0) // save allocating new list if there is nothing to process + { + // swizzle a new list into the list location so we can process what's there + List oldList; + lock (_taintLock) + { + oldList = _taintOperations; + _taintOperations = new List(); + } + + foreach (TaintCallbackEntry tcbe in oldList) + { + try + { + DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", tcbe.originator, tcbe.ident); // DEBUG DEBUG DEBUG + tcbe.callback(); + } + catch (Exception e) + { + m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e); + } + } + oldList.Clear(); + } + } + + // Schedule an update to happen after all the regular taints are processed. + // Note that new requests for the same operation ("ident") for the same object ("ID") + // will replace any previous operation by the same object. + public void PostTaintObject(String ident, uint ID, TaintCallback callback) + { + string IDAsString = ID.ToString(); + string uniqueIdent = ident + "-" + IDAsString; + lock (_taintLock) + { + _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(IDAsString, uniqueIdent, callback); + } + + return; + } + + // Taints that happen after the normal taint processing but before the simulation step. + private void ProcessPostTaintTaints() + { + if (m_initialized && _postTaintOperations.Count > 0) + { + Dictionary oldList; + lock (_taintLock) + { + oldList = _postTaintOperations; + _postTaintOperations = new Dictionary(); + } + + foreach (KeyValuePair kvp in oldList) + { + try + { + DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG + kvp.Value.callback(); + } + catch (Exception e) + { + m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e); + } + } + oldList.Clear(); + } + } + + // Only used for debugging. Does not change state of anything so locking is not necessary. + public bool AssertInTaintTime(string whereFrom) + { + if (!InTaintTime) + { + DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); + m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); + // Util.PrintCallStack(DetailLog); + } + return InTaintTime; + } + + #endregion // Taints + + #region IPhysicsParameters + // Get the list of parameters this physics engine supports + public PhysParameterEntry[] GetParameterList() + { + BSParam.BuildParameterTable(); + return BSParam.SettableParameters; + } + + // Set parameter on a specific or all instances. + // Return 'false' if not able to set the parameter. + // Setting the value in the m_params block will change the value the physics engine + // will use the next time since it's pinned and shared memory. + // Some of the values require calling into the physics engine to get the new + // value activated ('terrainFriction' for instance). + public bool SetPhysicsParameter(string parm, string val, uint localID) + { + bool ret = false; + + BSParam.ParameterDefnBase theParam; + if (BSParam.TryGetParameter(parm, out theParam)) + { + // Set the value in the C# code + theParam.SetValue(this, val); + + // Optionally set the parameter in the unmanaged code + if (theParam.HasSetOnObject) + { + // update all the localIDs specified + // If the local ID is APPLY_TO_NONE, just change the default value + // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs + // If the localID is a specific object, apply the parameter change to only that object + List objectIDs = new List(); + switch (localID) + { + case PhysParameterEntry.APPLY_TO_NONE: + // This will cause a call into the physical world if some operation is specified (SetOnObject). + objectIDs.Add(TERRAIN_ID); + TaintedUpdateParameter(parm, objectIDs, val); + break; + case PhysParameterEntry.APPLY_TO_ALL: + lock (PhysObjects) objectIDs = new List(PhysObjects.Keys); + TaintedUpdateParameter(parm, objectIDs, val); + break; + default: + // setting only one localID + objectIDs.Add(localID); + TaintedUpdateParameter(parm, objectIDs, val); + break; + } + } + + ret = true; + } + return ret; + } + + // schedule the actual updating of the paramter to when the phys engine is not busy + private void TaintedUpdateParameter(string parm, List lIDs, string val) + { + string xval = val; + List xlIDs = lIDs; + string xparm = parm; + TaintedObject(DetailLogZero, "BSScene.UpdateParameterSet", delegate() { + BSParam.ParameterDefnBase thisParam; + if (BSParam.TryGetParameter(xparm, out thisParam)) + { + if (thisParam.HasSetOnObject) + { + foreach (uint lID in xlIDs) + { + BSPhysObject theObject = null; + if (PhysObjects.TryGetValue(lID, out theObject)) + thisParam.SetOnObject(this, theObject); + } + } + } + }); + } + + // Get parameter. + // Return 'false' if not able to get the parameter. + public bool GetPhysicsParameter(string parm, out string value) + { + string val = String.Empty; + bool ret = false; + BSParam.ParameterDefnBase theParam; + if (BSParam.TryGetParameter(parm, out theParam)) + { + val = theParam.GetValue(this); + ret = true; + } + value = val; + return ret; + } + + #endregion IPhysicsParameters + + // Invoke the detailed logger and output something if it's enabled. + public void DetailLog(string msg, params Object[] args) + { + PhysicsLogging.Write(msg, args); + } + // Used to fill in the LocalID when there isn't one. It's the correct number of characters. + public const string DetailLogZero = "0000000000"; + +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs b/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs new file mode 100755 index 0000000..d1de844 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs @@ -0,0 +1,425 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; +using OMV = OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Physics.ConvexDecompositionDotNet; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public sealed class BSShapeCollection : IDisposable +{ +#pragma warning disable 414 + private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; +#pragma warning restore 414 + + private BSScene m_physicsScene { get; set; } + + private Object m_collectionActivityLock = new Object(); + + private bool DDetail = false; + + public BSShapeCollection(BSScene physScene) + { + m_physicsScene = physScene; + // Set the next to 'true' for very detailed shape update detailed logging (detailed details?) + // While detailed debugging is still active, this is better than commenting out all the + // DetailLog statements. When debugging slows down, this and the protected logging + // statements can be commented/removed. + DDetail = true; + } + + public void Dispose() + { + // TODO!!!!!!!!! + } + + // Callbacks called just before either the body or shape is destroyed. + // Mostly used for changing bodies out from under Linksets. + // Useful for other cases where parameters need saving. + // Passing 'null' says no callback. + public delegate void PhysicalDestructionCallback(BulletBody pBody, BulletShape pShape); + + // Called to update/change the body and shape for an object. + // The object has some shape and body on it. Here we decide if that is the correct shape + // for the current state of the object (static/dynamic/...). + // If bodyCallback is not null, it is called if either the body or the shape are changed + // so dependencies (like constraints) can be removed before the physical object is dereferenced. + // Return 'true' if either the body or the shape changed. + // Called at taint-time. + public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, PhysicalDestructionCallback bodyCallback) + { + m_physicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape"); + + bool ret = false; + + // This lock could probably be pushed down lower but building shouldn't take long + lock (m_collectionActivityLock) + { + // Do we have the correct geometry for this type of object? + // Updates prim.BSShape with information/pointers to shape. + // Returns 'true' of BSShape is changed to a new shape. + bool newGeom = CreateGeom(forceRebuild, prim, bodyCallback); + // If we had to select a new shape geometry for the object, + // rebuild the body around it. + // Updates prim.BSBody with information/pointers to requested body + // Returns 'true' if BSBody was changed. + bool newBody = CreateBody((newGeom || forceRebuild), prim, m_physicsScene.World, bodyCallback); + ret = newGeom || newBody; + } + DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}", + prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape); + + return ret; + } + + public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim) + { + return GetBodyAndShape(forceRebuild, sim, prim, null); + } + + // If the existing prim's shape is to be replaced, remove the tie to the existing shape + // before replacing it. + private void DereferenceExistingShape(BSPhysObject prim, PhysicalDestructionCallback shapeCallback) + { + if (prim.PhysShape.HasPhysicalShape) + { + if (shapeCallback != null) + shapeCallback(prim.PhysBody, prim.PhysShape.physShapeInfo); + prim.PhysShape.Dereference(m_physicsScene); + } + prim.PhysShape = new BSShapeNull(); + } + + // Create the geometry information in Bullet for later use. + // The objects needs a hull if it's physical otherwise a mesh is enough. + // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls, + // shared geometries will be used. If the parameters of the existing shape are the same + // as this request, the shape is not rebuilt. + // Info in prim.BSShape is updated to the new shape. + // Returns 'true' if the geometry was rebuilt. + // Called at taint-time! + public const int AvatarShapeCapsule = 0; + public const int AvatarShapeCube = 1; + public const int AvatarShapeOvoid = 2; + public const int AvatarShapeMesh = 3; + private bool CreateGeom(bool forceRebuild, BSPhysObject prim, PhysicalDestructionCallback shapeCallback) + { + bool ret = false; + bool haveShape = false; + bool nativeShapePossible = true; + PrimitiveBaseShape pbs = prim.BaseShape; + + // Kludge to create the capsule for the avatar. + // TDOD: Remove/redo this when BSShapeAvatar is working!! + BSCharacter theChar = prim as BSCharacter; + if (theChar != null) + { + DereferenceExistingShape(prim, shapeCallback); + switch (BSParam.AvatarShape) + { + case AvatarShapeCapsule: + prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, + BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE); + ret = true; + haveShape = true; + break; + case AvatarShapeCube: + prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, + BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_CAPSULE); + ret = true; + haveShape = true; + break; + case AvatarShapeOvoid: + // Saddly, Bullet doesn't scale spheres so this doesn't work as an avatar shape + prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, + BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_CAPSULE); + ret = true; + haveShape = true; + break; + case AvatarShapeMesh: + break; + default: + break; + } + } + + // If the prim attributes are simple, this could be a simple Bullet native shape + // Native shapes work whether to object is static or physical. + if (!haveShape + && nativeShapePossible + && pbs != null + && PrimHasNoCuts(pbs) + && ( !pbs.SculptEntry || (pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) ) + ) + { + // Get the scale of any existing shape so we can see if the new shape is same native type and same size. + OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero; + if (prim.PhysShape.HasPhysicalShape) + scaleOfExistingShape = m_physicsScene.PE.GetLocalScaling(prim.PhysShape.physShapeInfo); + + if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}", + prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.physShapeInfo.shapeType); + + // It doesn't look like Bullet scales native spheres so make sure the scales are all equal + if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) + && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) + { + haveShape = true; + if (forceRebuild + || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_SPHERE + ) + { + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, + BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_SPHERE); + ret = true; + } + if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},rebuilt={2},shape={3}", + prim.LocalID, forceRebuild, ret, prim.PhysShape); + } + // If we didn't make a sphere, maybe a box will work. + if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) + { + haveShape = true; + if (forceRebuild + || prim.Scale != scaleOfExistingShape + || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_BOX + ) + { + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, + BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); + ret = true; + } + if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},rebuilt={2},shape={3}", + prim.LocalID, forceRebuild, ret, prim.PhysShape); + } + } + + // If a simple shape is not happening, create a mesh and possibly a hull. + if (!haveShape && pbs != null) + { + ret = CreateGeomMeshOrHull(prim, shapeCallback); + } + + return ret; + } + + // return 'true' if this shape description does not include any cutting or twisting. + public static bool PrimHasNoCuts(PrimitiveBaseShape pbs) + { + return pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 + && pbs.ProfileHollow == 0 + && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 + && pbs.PathBegin == 0 && pbs.PathEnd == 0 + && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 + && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 + && pbs.PathShearX == 0 && pbs.PathShearY == 0; + } + + // return 'true' if the prim's shape was changed. + private bool CreateGeomMeshOrHull(BSPhysObject prim, PhysicalDestructionCallback shapeCallback) + { + + bool ret = false; + // Note that if it's a native shape, the check for physical/non-physical is not + // made. Native shapes work in either case. + if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) + { + // Use a simple, single mesh convex hull shape if the object is simple enough + BSShape potentialHull = null; + + PrimitiveBaseShape pbs = prim.BaseShape; + // Use a simple, one section convex shape for prims that are probably convex (no cuts or twists) + if (BSParam.ShouldUseSingleConvexHullForPrims + && pbs != null + && !pbs.SculptEntry + && PrimHasNoCuts(pbs) + ) + { + potentialHull = BSShapeConvexHull.GetReference(m_physicsScene, false /* forceRebuild */, prim); + } + // Use the GImpact shape if it is a prim that has some concaveness + if (potentialHull == null + && BSParam.ShouldUseGImpactShapeForPrims + && pbs != null + && !pbs.SculptEntry + ) + { + potentialHull = BSShapeGImpact.GetReference(m_physicsScene, false /* forceRebuild */, prim); + } + // If not any of the simple cases, just make a hull + if (potentialHull == null) + { + potentialHull = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim); + } + + // If the current shape is not what is on the prim at the moment, time to change. + if (!prim.PhysShape.HasPhysicalShape + || potentialHull.ShapeType != prim.PhysShape.ShapeType + || potentialHull.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) + { + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = potentialHull; + ret = true; + } + else + { + // The current shape on the prim is the correct one. We don't need the potential reference. + potentialHull.Dereference(m_physicsScene); + } + if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1}", prim.LocalID, prim.PhysShape); + } + else + { + // Non-physical objects should be just meshes. + BSShape potentialMesh = BSShapeMesh.GetReference(m_physicsScene, false /*forceRebuild*/, prim); + // If the current shape is not what is on the prim at the moment, time to change. + if (!prim.PhysShape.HasPhysicalShape + || potentialMesh.ShapeType != prim.PhysShape.ShapeType + || potentialMesh.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) + { + DereferenceExistingShape(prim, shapeCallback); + prim.PhysShape = potentialMesh; + ret = true; + } + else + { + // We don't need this reference to the mesh that is already being using. + potentialMesh.Dereference(m_physicsScene); + } + if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1}", prim.LocalID, prim.PhysShape); + } + return ret; + } + + // Track another user of a body. + // We presume the caller has allocated the body. + // Bodies only have one user so the body is just put into the world if not already there. + private void ReferenceBody(BulletBody body) + { + lock (m_collectionActivityLock) + { + if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body); + if (!m_physicsScene.PE.IsInWorld(m_physicsScene.World, body)) + { + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, body); + if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); + } + } + } + + // Release the usage of a body. + // Called when releasing use of a BSBody. BSShape is handled separately. + // Called in taint time. + public void DereferenceBody(BulletBody body, PhysicalDestructionCallback bodyCallback ) + { + if (!body.HasPhysicalBody) + return; + + m_physicsScene.AssertInTaintTime("BSShapeCollection.DereferenceBody"); + + lock (m_collectionActivityLock) + { + if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body); + // If the caller needs to know the old body is going away, pass the event up. + if (bodyCallback != null) + bodyCallback(body, null); + + // Removing an object not in the world is a NOOP + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, body); + + // Zero any reference to the shape so it is not freed when the body is deleted. + m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, body, null); + + m_physicsScene.PE.DestroyObject(m_physicsScene.World, body); + } + } + + // Create a body object in Bullet. + // Updates prim.BSBody with the information about the new body if one is created. + // Returns 'true' if an object was actually created. + // Called at taint-time. + private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, PhysicalDestructionCallback bodyCallback) + { + bool ret = false; + + // the mesh, hull or native shape must have already been created in Bullet + bool mustRebuild = !prim.PhysBody.HasPhysicalBody; + + // If there is an existing body, verify it's of an acceptable type. + // If not a solid object, body is a GhostObject. Otherwise a RigidBody. + if (!mustRebuild) + { + CollisionObjectTypes bodyType = (CollisionObjectTypes)m_physicsScene.PE.GetBodyType(prim.PhysBody); + if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY + || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT) + { + // If the collisionObject is not the correct type for solidness, rebuild what's there + mustRebuild = true; + if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,forceRebuildBecauseChangingBodyType,bodyType={1}", prim.LocalID, bodyType); + } + } + + if (mustRebuild || forceRebuild) + { + // Free any old body + DereferenceBody(prim.PhysBody, bodyCallback); + + BulletBody aBody; + if (prim.IsSolid) + { + aBody = m_physicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation); + if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,rigid,body={1}", prim.LocalID, aBody); + } + else + { + aBody = m_physicsScene.PE.CreateGhostFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation); + if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,body={1}", prim.LocalID, aBody); + } + + ReferenceBody(aBody); + + prim.PhysBody = aBody; + + ret = true; + } + + return ret; + } + + private void DetailLog(string msg, params Object[] args) + { + if (m_physicsScene.PhysicsLogging.Enabled) + m_physicsScene.DetailLog(msg, args); + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs new file mode 100755 index 0000000..86d86cb --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs @@ -0,0 +1,1463 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Physics.Meshing; +using OpenSim.Region.Physics.ConvexDecompositionDotNet; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +// Information class that holds stats for the shape. Which values mean +// something depends on the type of shape. +// This information is used for debugging and stats and is not used +// for operational things. +public class ShapeInfoInfo +{ + public int Vertices { get; set; } + private int m_hullCount; + private int[] m_verticesPerHull; + public ShapeInfoInfo() + { + Vertices = 0; + m_hullCount = 0; + m_verticesPerHull = null; + } + public int HullCount + { + set + { + m_hullCount = value; + m_verticesPerHull = new int[m_hullCount]; + Array.Clear(m_verticesPerHull, 0, m_hullCount); + } + get { return m_hullCount; } + } + public void SetVerticesPerHull(int hullNum, int vertices) + { + if (m_verticesPerHull != null && hullNum < m_verticesPerHull.Length) + { + m_verticesPerHull[hullNum] = vertices; + } + } + public int GetVerticesPerHull(int hullNum) + { + if (m_verticesPerHull != null && hullNum < m_verticesPerHull.Length) + { + return m_verticesPerHull[hullNum]; + } + return 0; + } + public override string ToString() + { + StringBuilder buff = new StringBuilder(); + // buff.Append("ShapeInfo=<"); + buff.Append("<"); + if (Vertices > 0) + { + buff.Append("verts="); + buff.Append(Vertices.ToString()); + } + + if (Vertices > 0 && HullCount > 0) buff.Append(","); + + if (HullCount > 0) + { + buff.Append("nHulls="); + buff.Append(HullCount.ToString()); + buff.Append(","); + buff.Append("hullVerts="); + for (int ii = 0; ii < HullCount; ii++) + { + if (ii != 0) buff.Append(","); + buff.Append(GetVerticesPerHull(ii).ToString()); + } + } + buff.Append(">"); + return buff.ToString(); + } +} + +public abstract class BSShape +{ + private static string LogHeader = "[BULLETSIM SHAPE]"; + + public int referenceCount { get; set; } + public DateTime lastReferenced { get; set; } + public BulletShape physShapeInfo { get; set; } + public ShapeInfoInfo shapeInfo { get; private set; } + + public BSShape() + { + referenceCount = 1; + lastReferenced = DateTime.Now; + physShapeInfo = new BulletShape(); + shapeInfo = new ShapeInfoInfo(); + } + public BSShape(BulletShape pShape) + { + referenceCount = 1; + lastReferenced = DateTime.Now; + physShapeInfo = pShape; + shapeInfo = new ShapeInfoInfo(); + } + + // Get another reference to this shape. + public abstract BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim); + + // Called when this shape is being used again. + // Used internally. External callers should call instance.GetReference() to properly copy/reference + // the shape. + protected virtual void IncrementReference() + { + referenceCount++; + lastReferenced = DateTime.Now; + } + + // Called when this shape is done being used. + protected virtual void DecrementReference() + { + referenceCount--; + lastReferenced = DateTime.Now; + } + + // Release the use of a physical shape. + public abstract void Dereference(BSScene physicsScene); + + // Return 'true' if there is an allocated physics physical shape under this class instance. + public virtual bool HasPhysicalShape + { + get + { + if (physShapeInfo != null) + return physShapeInfo.HasPhysicalShape; + return false; + } + } + public virtual BSPhysicsShapeType ShapeType + { + get + { + BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; + if (physShapeInfo != null && physShapeInfo.HasPhysicalShape) + ret = physShapeInfo.shapeType; + return ret; + } + } + + // Returns a string for debugging that uniquily identifies the memory used by this instance + public virtual string AddrString + { + get + { + if (physShapeInfo != null) + return physShapeInfo.AddrString; + return "unknown"; + } + } + + public override string ToString() + { + StringBuilder buff = new StringBuilder(); + if (physShapeInfo == null) + { + buff.Append(""); + return buff.ToString(); + } + + #region Common shape routines + // Create a hash of all the shape parameters to be used as a key for this particular shape. + public static System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod) + { + // level of detail based on size and type of the object + float lod = BSParam.MeshLOD; + if (pbs.SculptEntry) + lod = BSParam.SculptLOD; + + // Mega prims usually get more detail because one can interact with shape approximations at this size. + float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z)); + if (maxAxis > BSParam.MeshMegaPrimThreshold) + lod = BSParam.MeshMegaPrimLOD; + + retLod = lod; + return pbs.GetMeshKey(size, lod); + } + + // The creation of a mesh or hull can fail if an underlying asset is not available. + // There are two cases: 1) the asset is not in the cache and it needs to be fetched; + // and 2) the asset cannot be converted (like failed decompression of JPEG2000s). + // The first case causes the asset to be fetched. The second case requires + // us to not loop forever. + // Called after creating a physical mesh or hull. If the physical shape was created, + // just return. + public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim) + { + // If the shape was successfully created, nothing more to do + if (newShape.HasPhysicalShape) + return newShape; + + // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been + // fetched but we end up here again, the meshing of the asset must have failed. + // Prevent trying to keep fetching the mesh by declaring failure. + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) + { + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedMeshing; + physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. prim={1}, texture={2}", + LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,setFailed,prim={1},tex={2}", + prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); + } + else + { + // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset + if (prim.BaseShape.SculptEntry + && !prim.AssetFailed() + && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting + && prim.BaseShape.SculptTexture != OMV.UUID.Zero + ) + { + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAsset,objNam={1},tex={2}", + prim.LocalID, prim.PhysObjectName, prim.BaseShape.SculptTexture); + // Multiple requestors will know we're waiting for this asset + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; + + BSPhysObject xprim = prim; + RequestAssetDelegate assetProvider = physicsScene.RequestAssetMethod; + if (assetProvider != null) + { + BSPhysObject yprim = xprim; // probably not necessary, but, just in case. + assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) + { + // physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,assetProviderCallback", xprim.LocalID); + bool assetFound = false; + string mismatchIDs = String.Empty; // DEBUG DEBUG + if (asset != null && yprim.BaseShape.SculptEntry) + { + if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) + { + yprim.BaseShape.SculptData = asset.Data; + // This will cause the prim to see that the filler shape is not the right + // one and try again to build the object. + // No race condition with the normal shape setting since the rebuild is at taint time. + yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; + yprim.ForceBodyShapeRebuild(false /* inTaintTime */); + assetFound = true; + } + else + { + mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; + } + } + if (!assetFound) + { + yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch; + } + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,fetchAssetCallback,found={1},isSculpt={2},ids={3}", + yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); + }); + } + else + { + xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch; + physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", + LogHeader, physicsScene.Name); + } + } + else + { + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedAssetFetch) + { + physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. prim={1}, texture={2}", + LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailed,prim={1},tex={2}", + prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); + } + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedMeshing) + { + physicsScene.Logger.WarnFormat("{0} Mesh asset would not mesh. prim={1}, texture={2}", + LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,wasFailedMeshing,prim={1},tex={2}", + prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture); + } + } + } + + // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object. + BSShape fillShape = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); + physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,boxTempShape", prim.LocalID); + + return fillShape.physShapeInfo; + } + + public static String UsefulPrimInfo(BSScene pScene, BSPhysObject prim) + { + StringBuilder buff = new StringBuilder(prim.PhysObjectName); + buff.Append("/pos="); + buff.Append(prim.RawPosition.ToString()); + if (pScene != null) + { + buff.Append("/rgn="); + buff.Append(pScene.Name); + } + return buff.ToString(); + } + + #endregion // Common shape routines +} + +// ============================================================================================================ +public class BSShapeNull : BSShape +{ + public BSShapeNull() : base() + { + } + public static BSShape GetReference() { return new BSShapeNull(); } + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) { return new BSShapeNull(); } + public override void Dereference(BSScene physicsScene) { /* The magic of garbage collection will make this go away */ } +} + +// ============================================================================================================ +// BSShapeNative is a wrapper for a Bullet 'native' shape -- cube and sphere. +// They are odd in that they don't allocate meshes but are computated/procedural. +// This means allocation and freeing is different than meshes. +public class BSShapeNative : BSShape +{ + private static string LogHeader = "[BULLETSIM SHAPE NATIVE]"; + public BSShapeNative(BulletShape pShape) : base(pShape) + { + } + + public static BSShape GetReference(BSScene physicsScene, BSPhysObject prim, + BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) + { + // Native shapes are not shared and are always built anew. + return new BSShapeNative(CreatePhysicalNativeShape(physicsScene, prim, shapeType, shapeKey)); + } + + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) + { + // Native shapes are not shared so we return a new shape. + BSShape ret = null; + lock (physShapeInfo) + { + ret = new BSShapeNative(CreatePhysicalNativeShape(pPhysicsScene, pPrim, + physShapeInfo.shapeType, (FixedShapeKey)physShapeInfo.shapeKey)); + } + return ret; + } + + // Make this reference to the physical shape go away since native shapes are not shared. + public override void Dereference(BSScene physicsScene) + { + // Native shapes are not tracked and are released immediately + lock (physShapeInfo) + { + if (physShapeInfo.HasPhysicalShape) + { + physicsScene.DetailLog("{0},BSShapeNative.Dereference,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this); + physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); + } + physShapeInfo.Clear(); + // Garbage collection will free up this instance. + } + } + + private static BulletShape CreatePhysicalNativeShape(BSScene physicsScene, BSPhysObject prim, + BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) + { + BulletShape newShape; + + ShapeData nativeShapeData = new ShapeData(); + nativeShapeData.Type = shapeType; + nativeShapeData.ID = prim.LocalID; + nativeShapeData.Scale = prim.Scale; + nativeShapeData.Size = prim.Scale; + nativeShapeData.MeshKey = (ulong)shapeKey; + nativeShapeData.HullKey = (ulong)shapeKey; + + if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE) + { + newShape = physicsScene.PE.BuildCapsuleShape(physicsScene.World, 1f, 1f, prim.Scale); + physicsScene.DetailLog("{0},BSShapeNative,capsule,scale={1}", prim.LocalID, prim.Scale); + } + else + { + newShape = physicsScene.PE.BuildNativeShape(physicsScene.World, nativeShapeData); + } + if (!newShape.HasPhysicalShape) + { + physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", + LogHeader, prim.LocalID, shapeType); + } + newShape.shapeType = shapeType; + newShape.isNativeShape = true; + newShape.shapeKey = (UInt64)shapeKey; + return newShape; + } + +} + +// ============================================================================================================ +// BSShapeMesh is a simple mesh. +public class BSShapeMesh : BSShape +{ + private static string LogHeader = "[BULLETSIM SHAPE MESH]"; + public static Dictionary Meshes = new Dictionary(); + + public BSShapeMesh(BulletShape pShape) : base(pShape) + { + } + public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) + { + float lod; + System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + + BSShapeMesh retMesh = null; + lock (Meshes) + { + if (Meshes.TryGetValue(newMeshKey, out retMesh)) + { + // The mesh has already been created. Return a new reference to same. + retMesh.IncrementReference(); + } + else + { + retMesh = new BSShapeMesh(new BulletShape()); + // An instance of this mesh has not been created. Build and remember same. + BulletShape newShape = retMesh.CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); + + // Check to see if mesh was created (might require an asset). + newShape = VerifyMeshCreated(physicsScene, newShape, prim); + if (!newShape.isNativeShape || prim.AssetFailed() ) + { + // If a mesh was what was created, remember the built shape for later sharing. + // Also note that if meshing failed we put it in the mesh list as there is nothing else to do about the mesh. + Meshes.Add(newMeshKey, retMesh); + } + + retMesh.physShapeInfo = newShape; + } + } + physicsScene.DetailLog("{0},BSShapeMesh,getReference,mesh={1},size={2},lod={3}", prim.LocalID, retMesh, prim.Size, lod); + return retMesh; + } + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) + { + BSShape ret = null; + // If the underlying shape is native, the actual shape has not been build (waiting for asset) + // and we must create a copy of the native shape since they are never shared. + if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape) + { + // TODO: decide when the native shapes should be freed. Check in Dereference? + ret = BSShapeNative.GetReference(pPhysicsScene, pPrim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); + } + else + { + // Another reference to this shape is just counted. + IncrementReference(); + ret = this; + } + return ret; + } + public override void Dereference(BSScene physicsScene) + { + lock (Meshes) + { + this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeMesh.Dereference,shape={1}", BSScene.DetailLogZero, this); + // TODO: schedule aging and destruction of unused meshes. + } + } + // Loop through all the known meshes and return the description based on the physical address. + public static bool TryGetMeshByPtr(BulletShape pShape, out BSShapeMesh outMesh) + { + bool ret = false; + BSShapeMesh foundDesc = null; + lock (Meshes) + { + foreach (BSShapeMesh sm in Meshes.Values) + { + if (sm.physShapeInfo.ReferenceSame(pShape)) + { + foundDesc = sm; + ret = true; + break; + } + + } + } + outMesh = foundDesc; + return ret; + } + + public delegate BulletShape CreateShapeCall(BulletWorld world, int indicesCount, int[] indices, int verticesCount, float[] vertices ); + private BulletShape CreatePhysicalMesh(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, + PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) + { + return BSShapeMesh.CreatePhysicalMeshShape(physicsScene, prim, newMeshKey, pbs, size, lod, + (w, iC, i, vC, v) => + { + shapeInfo.Vertices = vC; + return physicsScene.PE.CreateMeshShape(w, iC, i, vC, v); + }); + } + + // Code that uses the mesher to create the index/vertices info for a trimesh shape. + // This is used by the passed 'makeShape' call to create the Bullet mesh shape. + // The actual build call is passed so this logic can be used by several of the shapes that use a + // simple mesh as their base shape. + public static BulletShape CreatePhysicalMeshShape(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, + PrimitiveBaseShape pbs, OMV.Vector3 size, float lod, CreateShapeCall makeShape) + { + BulletShape newShape = new BulletShape(); + + IMesh meshData = null; + lock (physicsScene.mesher) + { + meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, + false, // say it is not physical so a bounding box is not built + false // do not cache the mesh and do not use previously built versions + ); + } + + if (meshData != null) + { + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) + { + // Release the fetched asset data once it has been used. + pbs.SculptData = new byte[0]; + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; + } + + int[] indices = meshData.getIndexListAsInt(); + int realIndicesIndex = indices.Length; + float[] verticesAsFloats = meshData.getVertexListAsFloat(); + + if (BSParam.ShouldRemoveZeroWidthTriangles) + { + // Remove degenerate triangles. These are triangles with two of the vertices + // are the same. This is complicated by the problem that vertices are not + // made unique in sculpties so we have to compare the values in the vertex. + realIndicesIndex = 0; + for (int tri = 0; tri < indices.Length; tri += 3) + { + // Compute displacements into vertex array for each vertex of the triangle + int v1 = indices[tri + 0] * 3; + int v2 = indices[tri + 1] * 3; + int v3 = indices[tri + 2] * 3; + // Check to see if any two of the vertices are the same + if (!( ( verticesAsFloats[v1 + 0] == verticesAsFloats[v2 + 0] + && verticesAsFloats[v1 + 1] == verticesAsFloats[v2 + 1] + && verticesAsFloats[v1 + 2] == verticesAsFloats[v2 + 2]) + || ( verticesAsFloats[v2 + 0] == verticesAsFloats[v3 + 0] + && verticesAsFloats[v2 + 1] == verticesAsFloats[v3 + 1] + && verticesAsFloats[v2 + 2] == verticesAsFloats[v3 + 2]) + || ( verticesAsFloats[v1 + 0] == verticesAsFloats[v3 + 0] + && verticesAsFloats[v1 + 1] == verticesAsFloats[v3 + 1] + && verticesAsFloats[v1 + 2] == verticesAsFloats[v3 + 2]) ) + ) + { + // None of the vertices of the triangles are the same. This is a good triangle; + indices[realIndicesIndex + 0] = indices[tri + 0]; + indices[realIndicesIndex + 1] = indices[tri + 1]; + indices[realIndicesIndex + 2] = indices[tri + 2]; + realIndicesIndex += 3; + } + } + } + physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,key={1},origTri={2},realTri={3},numVerts={4}", + BSScene.DetailLogZero, newMeshKey.ToString("X"), indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); + + if (realIndicesIndex != 0) + { + newShape = makeShape(physicsScene.World, realIndicesIndex, indices, verticesAsFloats.Length / 3, verticesAsFloats); + } + else + { + // Force the asset condition to 'failed' so we won't try to keep fetching and processing this mesh. + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedMeshing; + physicsScene.Logger.DebugFormat("{0} All mesh triangles degenerate. Prim={1}", LogHeader, UsefulPrimInfo(physicsScene, prim) ); + physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,allDegenerate,key={1}", prim.LocalID, newMeshKey); + } + } + newShape.shapeKey = newMeshKey; + + return newShape; + } +} + +// ============================================================================================================ +// BSShapeHull is a physical shape representation htat is made up of many convex hulls. +// The convex hulls are either supplied with the asset or are approximated by one of the +// convex hull creation routines (in OpenSim or in Bullet). +public class BSShapeHull : BSShape +{ +#pragma warning disable 414 + private static string LogHeader = "[BULLETSIM SHAPE HULL]"; +#pragma warning restore 414 + + public static Dictionary Hulls = new Dictionary(); + + + public BSShapeHull(BulletShape pShape) : base(pShape) + { + } + public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) + { + float lod; + System.UInt64 newHullKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + + BSShapeHull retHull = null; + lock (Hulls) + { + if (Hulls.TryGetValue(newHullKey, out retHull)) + { + // The mesh has already been created. Return a new reference to same. + retHull.IncrementReference(); + } + else + { + retHull = new BSShapeHull(new BulletShape()); + // An instance of this mesh has not been created. Build and remember same. + BulletShape newShape = retHull.CreatePhysicalHull(physicsScene, prim, newHullKey, prim.BaseShape, prim.Size, lod); + + // Check to see if hull was created (might require an asset). + newShape = VerifyMeshCreated(physicsScene, newShape, prim); + if (!newShape.isNativeShape || prim.AssetFailed()) + { + // If a mesh was what was created, remember the built shape for later sharing. + Hulls.Add(newHullKey, retHull); + } + retHull.physShapeInfo = newShape; + } + } + physicsScene.DetailLog("{0},BSShapeHull,getReference,hull={1},size={2},lod={3}", prim.LocalID, retHull, prim.Size, lod); + return retHull; + } + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) + { + BSShape ret = null; + // If the underlying shape is native, the actual shape has not been build (waiting for asset) + // and we must create a copy of the native shape since they are never shared. + if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape) + { + // TODO: decide when the native shapes should be freed. Check in Dereference? + ret = BSShapeNative.GetReference(pPhysicsScene, pPrim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); + } + else + { + // Another reference to this shape is just counted. + IncrementReference(); + ret = this; + } + return ret; + } + public override void Dereference(BSScene physicsScene) + { + lock (Hulls) + { + this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeHull.Dereference,shape={1}", BSScene.DetailLogZero, this); + // TODO: schedule aging and destruction of unused meshes. + } + } + + List m_hulls; + private BulletShape CreatePhysicalHull(BSScene physicsScene, BSPhysObject prim, System.UInt64 newHullKey, + PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) + { + BulletShape newShape = new BulletShape(); + + IMesh meshData = null; + List> allHulls = null; + lock (physicsScene.mesher) + { + // Pass true for physicalness as this prevents the creation of bounding box which is not needed + meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */); + + // If we should use the asset's hull info, fetch it out of the locked mesher + if (meshData != null && BSParam.ShouldUseAssetHulls) + { + Meshmerizer realMesher = physicsScene.mesher as Meshmerizer; + if (realMesher != null) + { + allHulls = realMesher.GetConvexHulls(size); + } + if (allHulls == null) + { + physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,assetHulls,noAssetHull", prim.LocalID); + } + } + } + + // If there is hull data in the mesh asset, build the hull from that + if (allHulls != null && BSParam.ShouldUseAssetHulls) + { + int hullCount = allHulls.Count; + shapeInfo.HullCount = hullCount; + int totalVertices = 1; // include one for the count of the hulls + // Using the structure described for HACD hulls, create the memory sturcture + // to pass the hull data to the creater. + foreach (List hullVerts in allHulls) + { + totalVertices += 4; // add four for the vertex count and centroid + totalVertices += hullVerts.Count * 3; // one vertex is three dimensions + } + float[] convHulls = new float[totalVertices]; + + convHulls[0] = (float)hullCount; + int jj = 1; + int hullIndex = 0; + foreach (List hullVerts in allHulls) + { + convHulls[jj + 0] = hullVerts.Count; + convHulls[jj + 1] = 0f; // centroid x,y,z + convHulls[jj + 2] = 0f; + convHulls[jj + 3] = 0f; + jj += 4; + foreach (OMV.Vector3 oneVert in hullVerts) + { + convHulls[jj + 0] = oneVert.X; + convHulls[jj + 1] = oneVert.Y; + convHulls[jj + 2] = oneVert.Z; + jj += 3; + } + shapeInfo.SetVerticesPerHull(hullIndex, hullVerts.Count); + hullIndex++; + } + + // create the hull data structure in Bullet + newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls); + + physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,assetHulls,hulls={1},totVert={2},shape={3}", + prim.LocalID, hullCount, totalVertices, newShape); + } + + // If no hull specified in the asset and we should use Bullet's HACD approximation... + if (!newShape.HasPhysicalShape && BSParam.ShouldUseBulletHACD) + { + // Build the hull shape from an existing mesh shape. + // The mesh should have already been created in Bullet. + physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,bulletHACD,entry", prim.LocalID); + BSShape meshShape = BSShapeMesh.GetReference(physicsScene, true, prim); + + if (meshShape.physShapeInfo.HasPhysicalShape) + { + HACDParams parms = new HACDParams(); + parms.maxVerticesPerHull = BSParam.BHullMaxVerticesPerHull; + parms.minClusters = BSParam.BHullMinClusters; + parms.compacityWeight = BSParam.BHullCompacityWeight; + parms.volumeWeight = BSParam.BHullVolumeWeight; + parms.concavity = BSParam.BHullConcavity; + parms.addExtraDistPoints = BSParam.NumericBool(BSParam.BHullAddExtraDistPoints); + parms.addNeighboursDistPoints = BSParam.NumericBool(BSParam.BHullAddNeighboursDistPoints); + parms.addFacesPoints = BSParam.NumericBool(BSParam.BHullAddFacesPoints); + parms.shouldAdjustCollisionMargin = BSParam.NumericBool(BSParam.BHullShouldAdjustCollisionMargin); + parms.whichHACD = 0; // Use the HACD routine that comes with Bullet + + physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape); + newShape = physicsScene.PE.BuildHullShapeFromMesh(physicsScene.World, meshShape.physShapeInfo, parms); + physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,shape={1}", prim.LocalID, newShape); + + // Now done with the mesh shape. + shapeInfo.HullCount = 1; + BSShapeMesh maybeMesh = meshShape as BSShapeMesh; + if (maybeMesh != null) + shapeInfo.SetVerticesPerHull(0, maybeMesh.shapeInfo.Vertices); + meshShape.Dereference(physicsScene); + } + physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,bulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); + } + + // If no other hull specifications, use our HACD hull approximation. + if (!newShape.HasPhysicalShape && meshData != null) + { + if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) + { + // Release the fetched asset data once it has been used. + pbs.SculptData = new byte[0]; + prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; + } + + int[] indices = meshData.getIndexListAsInt(); + List vertices = meshData.getVertexList(); + + //format conversion from IMesh format to DecompDesc format + List convIndices = new List(); + List convVertices = new List(); + for (int ii = 0; ii < indices.GetLength(0); ii++) + { + convIndices.Add(indices[ii]); + } + foreach (OMV.Vector3 vv in vertices) + { + convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); + } + + uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit; + if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes) + { + // Simple primitive shapes we know are convex so they are better implemented with + // fewer hulls. + // Check for simple shape (prim without cuts) and reduce split parameter if so. + if (BSShapeCollection.PrimHasNoCuts(pbs)) + { + maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes; + } + } + + // setup and do convex hull conversion + m_hulls = new List(); + DecompDesc dcomp = new DecompDesc(); + dcomp.mIndices = convIndices; + dcomp.mVertices = convVertices; + dcomp.mDepth = maxDepthSplit; + dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent; + dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent; + dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices; + dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth; + ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); + // create the hull into the _hulls variable + convexBuilder.process(dcomp); + + physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", + BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count); + + // Convert the vertices and indices for passing to unmanaged. + // The hull information is passed as a large floating point array. + // The format is: + // convHulls[0] = number of hulls + // convHulls[1] = number of vertices in first hull + // convHulls[2] = hull centroid X coordinate + // convHulls[3] = hull centroid Y coordinate + // convHulls[4] = hull centroid Z coordinate + // convHulls[5] = first hull vertex X + // convHulls[6] = first hull vertex Y + // convHulls[7] = first hull vertex Z + // convHulls[8] = second hull vertex X + // ... + // convHulls[n] = number of vertices in second hull + // convHulls[n+1] = second hull centroid X coordinate + // ... + // + // TODO: is is very inefficient. Someday change the convex hull generator to return + // data structures that do not need to be converted in order to pass to Bullet. + // And maybe put the values directly into pinned memory rather than marshaling. + int hullCount = m_hulls.Count; + int totalVertices = 1; // include one for the count of the hulls + foreach (ConvexResult cr in m_hulls) + { + totalVertices += 4; // add four for the vertex count and centroid + totalVertices += cr.HullIndices.Count * 3; // we pass just triangles + } + float[] convHulls = new float[totalVertices]; + + convHulls[0] = (float)hullCount; + int jj = 1; + foreach (ConvexResult cr in m_hulls) + { + // copy vertices for index access + float3[] verts = new float3[cr.HullVertices.Count]; + int kk = 0; + foreach (float3 ff in cr.HullVertices) + { + verts[kk++] = ff; + } + + // add to the array one hull's worth of data + convHulls[jj++] = cr.HullIndices.Count; + convHulls[jj++] = 0f; // centroid x,y,z + convHulls[jj++] = 0f; + convHulls[jj++] = 0f; + foreach (int ind in cr.HullIndices) + { + convHulls[jj++] = verts[ind].x; + convHulls[jj++] = verts[ind].y; + convHulls[jj++] = verts[ind].z; + } + } + // create the hull data structure in Bullet + newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls); + } + newShape.shapeKey = newHullKey; + return newShape; + } + // Callback from convex hull creater with a newly created hull. + // Just add it to our collection of hulls for this shape. + private void HullReturn(ConvexResult result) + { + m_hulls.Add(result); + return; + } + // Loop through all the known hulls and return the description based on the physical address. + public static bool TryGetHullByPtr(BulletShape pShape, out BSShapeHull outHull) + { + bool ret = false; + BSShapeHull foundDesc = null; + lock (Hulls) + { + foreach (BSShapeHull sh in Hulls.Values) + { + if (sh.physShapeInfo.ReferenceSame(pShape)) + { + foundDesc = sh; + ret = true; + break; + } + + } + } + outHull = foundDesc; + return ret; + } +} + +// ============================================================================================================ +// BSShapeCompound is a wrapper for the Bullet compound shape which is built from multiple, separate +// meshes. Used by BulletSim for complex shapes like linksets. +public class BSShapeCompound : BSShape +{ + private static string LogHeader = "[BULLETSIM SHAPE COMPOUND]"; + public static Dictionary CompoundShapes = new Dictionary(); + + public BSShapeCompound(BulletShape pShape) : base(pShape) + { + } + public static BSShape GetReference(BSScene physicsScene) + { + // Base compound shapes are not shared so this returns a raw shape. + // A built compound shape can be reused in linksets. + BSShapeCompound ret = new BSShapeCompound(CreatePhysicalCompoundShape(physicsScene)); + CompoundShapes.Add(ret.AddrString, ret); + return ret; + } + public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim) + { + // Calling this reference means we want another handle to an existing compound shape + // (usually linksets) so return this copy. + IncrementReference(); + return this; + } + // Dereferencing a compound shape releases the hold on all the child shapes. + public override void Dereference(BSScene physicsScene) + { + lock (physShapeInfo) + { + this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeCompound.Dereference,shape={1}", BSScene.DetailLogZero, this); + if (referenceCount <= 0) + { + if (!physicsScene.PE.IsCompound(physShapeInfo)) + { + // Failed the sanity check!! + physicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", + LogHeader, physShapeInfo.shapeType, physShapeInfo.AddrString); + physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", + BSScene.DetailLogZero, physShapeInfo.shapeType, physShapeInfo.AddrString); + return; + } + + int numChildren = physicsScene.PE.GetNumberOfCompoundChildren(physShapeInfo); + physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", + BSScene.DetailLogZero, physShapeInfo, numChildren); + + // Loop through all the children dereferencing each. + for (int ii = numChildren - 1; ii >= 0; ii--) + { + BulletShape childShape = physicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(physShapeInfo, ii); + DereferenceAnonCollisionShape(physicsScene, childShape); + } + + lock (CompoundShapes) + CompoundShapes.Remove(physShapeInfo.AddrString); + physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); + } + } + } + public static bool TryGetCompoundByPtr(BulletShape pShape, out BSShapeCompound outCompound) + { + lock (CompoundShapes) + { + string addr = pShape.AddrString; + return CompoundShapes.TryGetValue(addr, out outCompound); + } + } + private static BulletShape CreatePhysicalCompoundShape(BSScene physicsScene) + { + BulletShape cShape = physicsScene.PE.CreateCompoundShape(physicsScene.World, false); + return cShape; + } + // Sometimes we have a pointer to a collision shape but don't know what type it is. + // Figure out type and call the correct dereference routine. + // Called at taint-time. + private void DereferenceAnonCollisionShape(BSScene physicsScene, BulletShape pShape) + { + // TODO: figure a better way to go through all the shape types and find a possible instance. + physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,shape={1}", + BSScene.DetailLogZero, pShape); + BSShapeMesh meshDesc; + if (BSShapeMesh.TryGetMeshByPtr(pShape, out meshDesc)) + { + meshDesc.Dereference(physicsScene); + // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundMesh,shape={1}", BSScene.DetailLogZero, pShape); + } + else + { + BSShapeHull hullDesc; + if (BSShapeHull.TryGetHullByPtr(pShape, out hullDesc)) + { + hullDesc.Dereference(physicsScene); + // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundHull,shape={1}", BSScene.DetailLogZero, pShape); + } + else + { + BSShapeConvexHull chullDesc; + if (BSShapeConvexHull.TryGetConvexHullByPtr(pShape, out chullDesc)) + { + chullDesc.Dereference(physicsScene); + // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundConvexHull,shape={1}", BSScene.DetailLogZero, pShape); + } + else + { + BSShapeGImpact gImpactDesc; + if (BSShapeGImpact.TryGetGImpactByPtr(pShape, out gImpactDesc)) + { + gImpactDesc.Dereference(physicsScene); + // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundgImpact,shape={1}", BSScene.DetailLogZero, pShape); + } + else + { + // Didn't find it in the lists of specific types. It could be compound. + BSShapeCompound compoundDesc; + if (BSShapeCompound.TryGetCompoundByPtr(pShape, out compoundDesc)) + { + compoundDesc.Dereference(physicsScene); + // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,recursiveCompoundShape,shape={1}", BSScene.DetailLogZero, pShape); + } + else + { + // If none of the above, maybe it is a simple native shape. + if (physicsScene.PE.IsNativeShape(pShape)) + { + // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,assumingNative,shape={1}", BSScene.DetailLogZero, pShape); + BSShapeNative nativeShape = new BSShapeNative(pShape); + nativeShape.Dereference(physicsScene); + } + else + { + physicsScene.Logger.WarnFormat("{0} DereferenceAnonCollisionShape. Did not find shape. {1}", + LogHeader, pShape); + } + } + } + } + } + } + } +} + +// ============================================================================================================ +// BSShapeConvexHull is a wrapper for a Bullet single convex hull. A BSShapeHull contains multiple convex +// hull shapes. This is used for simple prims that are convex and thus can be made into a simple +// collision shape (a single hull). More complex physical shapes will be BSShapeHull's. +public class BSShapeConvexHull : BSShape +{ +#pragma warning disable 414 + private static string LogHeader = "[BULLETSIM SHAPE CONVEX HULL]"; +#pragma warning restore 414 + + public static Dictionary ConvexHulls = new Dictionary(); + + public BSShapeConvexHull(BulletShape pShape) : base(pShape) + { + } + public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) + { + float lod; + System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + + physicsScene.DetailLog("{0},BSShapeConvexHull,getReference,newKey={1},size={2},lod={3}", + prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod); + + BSShapeConvexHull retConvexHull = null; + lock (ConvexHulls) + { + if (ConvexHulls.TryGetValue(newMeshKey, out retConvexHull)) + { + // The mesh has already been created. Return a new reference to same. + retConvexHull.IncrementReference(); + } + else + { + retConvexHull = new BSShapeConvexHull(new BulletShape()); + BulletShape convexShape = null; + + // Get a handle to a mesh to build the hull from + BSShape baseMesh = BSShapeMesh.GetReference(physicsScene, false /* forceRebuild */, prim); + if (baseMesh.physShapeInfo.isNativeShape) + { + // We get here if the mesh was not creatable. Could be waiting for an asset from the disk. + // In the short term, we return the native shape and a later ForceBodyShapeRebuild should + // get back to this code with a buildable mesh. + // TODO: not sure the temp native shape is freed when the mesh is rebuilt. When does this get freed? + convexShape = baseMesh.physShapeInfo; + } + else + { + convexShape = physicsScene.PE.BuildConvexHullShapeFromMesh(physicsScene.World, baseMesh.physShapeInfo); + convexShape.shapeKey = newMeshKey; + ConvexHulls.Add(convexShape.shapeKey, retConvexHull); + physicsScene.DetailLog("{0},BSShapeConvexHull.GetReference,addingNewlyCreatedShape,shape={1}", + BSScene.DetailLogZero, convexShape); + } + + // Done with the base mesh + baseMesh.Dereference(physicsScene); + + retConvexHull.physShapeInfo = convexShape; + } + } + return retConvexHull; + } + public override BSShape GetReference(BSScene physicsScene, BSPhysObject prim) + { + // Calling this reference means we want another handle to an existing shape + // (usually linksets) so return this copy. + IncrementReference(); + return this; + } + // Dereferencing a compound shape releases the hold on all the child shapes. + public override void Dereference(BSScene physicsScene) + { + lock (ConvexHulls) + { + this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeConvexHull.Dereference,shape={1}", BSScene.DetailLogZero, this); + // TODO: schedule aging and destruction of unused meshes. + } + } + // Loop through all the known hulls and return the description based on the physical address. + public static bool TryGetConvexHullByPtr(BulletShape pShape, out BSShapeConvexHull outHull) + { + bool ret = false; + BSShapeConvexHull foundDesc = null; + lock (ConvexHulls) + { + foreach (BSShapeConvexHull sh in ConvexHulls.Values) + { + if (sh.physShapeInfo.ReferenceSame(pShape)) + { + foundDesc = sh; + ret = true; + break; + } + + } + } + outHull = foundDesc; + return ret; + } +} +// ============================================================================================================ +// BSShapeGImpact is a wrapper for the Bullet GImpact shape which is a collision mesh shape that +// can handle concave as well as convex shapes. Much slower computationally but creates smoother +// shapes than multiple convex hull approximations. +public class BSShapeGImpact : BSShape +{ +#pragma warning disable 414 + private static string LogHeader = "[BULLETSIM SHAPE GIMPACT]"; +#pragma warning restore 414 + + public static Dictionary GImpacts = new Dictionary(); + + public BSShapeGImpact(BulletShape pShape) : base(pShape) + { + } + public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) + { + float lod; + System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); + + physicsScene.DetailLog("{0},BSShapeGImpact,getReference,newKey={1},size={2},lod={3}", + prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod); + + BSShapeGImpact retGImpact = null; + lock (GImpacts) + { + if (GImpacts.TryGetValue(newMeshKey, out retGImpact)) + { + // The mesh has already been created. Return a new reference to same. + retGImpact.IncrementReference(); + } + else + { + retGImpact = new BSShapeGImpact(new BulletShape()); + BulletShape newShape = retGImpact.CreatePhysicalGImpact(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); + + // Check to see if mesh was created (might require an asset). + newShape = VerifyMeshCreated(physicsScene, newShape, prim); + newShape.shapeKey = newMeshKey; + if (!newShape.isNativeShape || prim.AssetFailed()) + { + // If a mesh was what was created, remember the built shape for later sharing. + // Also note that if meshing failed we put it in the mesh list as there is nothing + // else to do about the mesh. + GImpacts.Add(newMeshKey, retGImpact); + } + + retGImpact.physShapeInfo = newShape; + } + } + return retGImpact; + } + + private BulletShape CreatePhysicalGImpact(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, + PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) + { + return BSShapeMesh.CreatePhysicalMeshShape(physicsScene, prim, newMeshKey, pbs, size, lod, + (w, iC, i, vC, v) => + { + shapeInfo.Vertices = vC; + return physicsScene.PE.CreateGImpactShape(w, iC, i, vC, v); + }); + } + + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) + { + BSShape ret = null; + // If the underlying shape is native, the actual shape has not been build (waiting for asset) + // and we must create a copy of the native shape since they are never shared. + if (physShapeInfo.HasPhysicalShape && physShapeInfo.isNativeShape) + { + // TODO: decide when the native shapes should be freed. Check in Dereference? + ret = BSShapeNative.GetReference(pPhysicsScene, pPrim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); + } + else + { + // Another reference to this shape is just counted. + IncrementReference(); + ret = this; + } + return ret; + } + // Dereferencing a compound shape releases the hold on all the child shapes. + public override void Dereference(BSScene physicsScene) + { + lock (GImpacts) + { + this.DecrementReference(); + physicsScene.DetailLog("{0},BSShapeGImpact.Dereference,shape={1}", BSScene.DetailLogZero, this); + // TODO: schedule aging and destruction of unused meshes. + } + } + // Loop through all the known hulls and return the description based on the physical address. + public static bool TryGetGImpactByPtr(BulletShape pShape, out BSShapeGImpact outHull) + { + bool ret = false; + BSShapeGImpact foundDesc = null; + lock (GImpacts) + { + foreach (BSShapeGImpact sh in GImpacts.Values) + { + if (sh.physShapeInfo.ReferenceSame(pShape)) + { + foundDesc = sh; + ret = true; + break; + } + + } + } + outHull = foundDesc; + return ret; + } +} + +// ============================================================================================================ +// BSShapeAvatar is a specialized mesh shape for avatars. +public class BSShapeAvatar : BSShape +{ +#pragma warning disable 414 + private static string LogHeader = "[BULLETSIM SHAPE AVATAR]"; +#pragma warning restore 414 + + public BSShapeAvatar() + : base() + { + } + public static BSShape GetReference(BSPhysObject prim) + { + return new BSShapeNull(); + } + public override BSShape GetReference(BSScene pPhysicsScene, BSPhysObject pPrim) + { + return new BSShapeNull(); + } + public override void Dereference(BSScene physicsScene) { } + + // From the front: + // A---A + // / \ + // B-------B + // / \ +Z + // C-----------C | + // \ / -Y --+-- +Y + // \ / | + // \ / -Z + // D-----D + // \ / + // E-E + + // From the top A and E are just lines. + // B, C and D are hexagons: + // + // C1--C2 +X + // / \ | + // C0 C3 -Y --+-- +Y + // \ / | + // C5--C4 -X + + // Zero goes directly through the middle so the offsets are from that middle axis + // and up and down from a middle horizon (A and E are the same distance from the zero). + // The height, width and depth is one. All scaling is done by the simulator. + + // Z component -- how far the level is from the middle zero + private const float Aup = 0.5f; + private const float Bup = 0.4f; + private const float Cup = 0.3f; + private const float Dup = -0.4f; + private const float Eup = -0.5f; + + // Y component -- distance from center to x0 and x3 + private const float Awid = 0.25f; + private const float Bwid = 0.3f; + private const float Cwid = 0.5f; + private const float Dwid = 0.3f; + private const float Ewid = 0.2f; + + // Y component -- distance from center to x1, x2, x4 and x5 + private const float Afwid = 0.0f; + private const float Bfwid = 0.2f; + private const float Cfwid = 0.4f; + private const float Dfwid = 0.2f; + private const float Efwid = 0.0f; + + // X component -- distance from zero to the front or back of a level + private const float Adep = 0f; + private const float Bdep = 0.3f; + private const float Cdep = 0.5f; + private const float Ddep = 0.2f; + private const float Edep = 0f; + + private OMV.Vector3[] avatarVertices = { + new OMV.Vector3( 0.0f, -Awid, Aup), // A0 + new OMV.Vector3( 0.0f, +Awid, Aup), // A3 + + new OMV.Vector3( 0.0f, -Bwid, Bup), // B0 + new OMV.Vector3(+Bdep, -Bfwid, Bup), // B1 + new OMV.Vector3(+Bdep, +Bfwid, Bup), // B2 + new OMV.Vector3( 0.0f, +Bwid, Bup), // B3 + new OMV.Vector3(-Bdep, +Bfwid, Bup), // B4 + new OMV.Vector3(-Bdep, -Bfwid, Bup), // B5 + + new OMV.Vector3( 0.0f, -Cwid, Cup), // C0 + new OMV.Vector3(+Cdep, -Cfwid, Cup), // C1 + new OMV.Vector3(+Cdep, +Cfwid, Cup), // C2 + new OMV.Vector3( 0.0f, +Cwid, Cup), // C3 + new OMV.Vector3(-Cdep, +Cfwid, Cup), // C4 + new OMV.Vector3(-Cdep, -Cfwid, Cup), // C5 + + new OMV.Vector3( 0.0f, -Dwid, Dup), // D0 + new OMV.Vector3(+Ddep, -Dfwid, Dup), // D1 + new OMV.Vector3(+Ddep, +Dfwid, Dup), // D2 + new OMV.Vector3( 0.0f, +Dwid, Dup), // D3 + new OMV.Vector3(-Ddep, +Dfwid, Dup), // D4 + new OMV.Vector3(-Ddep, -Dfwid, Dup), // D5 + + new OMV.Vector3( 0.0f, -Ewid, Eup), // E0 + new OMV.Vector3( 0.0f, +Ewid, Eup), // E3 + }; + + // Offsets of the vertices in the vertices array + private enum Ind : int + { + A0, A3, + B0, B1, B2, B3, B4, B5, + C0, C1, C2, C3, C4, C5, + D0, D1, D2, D3, D4, D5, + E0, E3 + } + + // Comments specify trianges and quads in clockwise direction + private Ind[] avatarIndices = { + Ind.A0, Ind.B0, Ind.B1, // A0,B0,B1 + Ind.A0, Ind.B1, Ind.B2, Ind.B2, Ind.A3, Ind.A0, // A0,B1,B2,A3 + Ind.A3, Ind.B2, Ind.B3, // A3,B2,B3 + Ind.A3, Ind.B3, Ind.B4, // A3,B3,B4 + Ind.A3, Ind.B4, Ind.B5, Ind.B5, Ind.A0, Ind.A3, // A3,B4,B5,A0 + Ind.A0, Ind.B5, Ind.B0, // A0,B5,B0 + + Ind.B0, Ind.C0, Ind.C1, Ind.C1, Ind.B1, Ind.B0, // B0,C0,C1,B1 + Ind.B1, Ind.C1, Ind.C2, Ind.C2, Ind.B2, Ind.B1, // B1,C1,C2,B2 + Ind.B2, Ind.C2, Ind.C3, Ind.C3, Ind.B3, Ind.B2, // B2,C2,C3,B3 + Ind.B3, Ind.C3, Ind.C4, Ind.C4, Ind.B4, Ind.B3, // B3,C3,C4,B4 + Ind.B4, Ind.C4, Ind.C5, Ind.C5, Ind.B5, Ind.B4, // B4,C4,C5,B5 + Ind.B5, Ind.C5, Ind.C0, Ind.C0, Ind.B0, Ind.B5, // B5,C5,C0,B0 + + Ind.C0, Ind.D0, Ind.D1, Ind.D1, Ind.C1, Ind.C0, // C0,D0,D1,C1 + Ind.C1, Ind.D1, Ind.D2, Ind.D2, Ind.C2, Ind.C1, // C1,D1,D2,C2 + Ind.C2, Ind.D2, Ind.D3, Ind.D3, Ind.C3, Ind.C2, // C2,D2,D3,C3 + Ind.C3, Ind.D3, Ind.D4, Ind.D4, Ind.C4, Ind.C3, // C3,D3,D4,C4 + Ind.C4, Ind.D4, Ind.D5, Ind.D5, Ind.C5, Ind.C4, // C4,D4,D5,C5 + Ind.C5, Ind.D5, Ind.D0, Ind.D0, Ind.C0, Ind.C5, // C5,D5,D0,C0 + + Ind.E0, Ind.D0, Ind.D1, // E0,D0,D1 + Ind.E0, Ind.D1, Ind.D2, Ind.D2, Ind.E3, Ind.E0, // E0,D1,D2,E3 + Ind.E3, Ind.D2, Ind.D3, // E3,D2,D3 + Ind.E3, Ind.D3, Ind.D4, // E3,D3,D4 + Ind.E3, Ind.D4, Ind.D5, Ind.D5, Ind.E0, Ind.E3, // E3,D4,D5,E0 + Ind.E0, Ind.D5, Ind.D0, // E0,D5,D0 + + }; + +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs new file mode 100755 index 0000000..d70b2fb --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs @@ -0,0 +1,170 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Region.Framework; +using OpenSim.Region.CoreModules; +using OpenSim.Region.Physics.Manager; + +using Nini.Config; +using log4net; + +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public sealed class BSTerrainHeightmap : BSTerrainPhys +{ + static string LogHeader = "[BULLETSIM TERRAIN HEIGHTMAP]"; + + BulletHMapInfo m_mapInfo = null; + + // Constructor to build a default, flat heightmap terrain. + public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize) + : base(physicsScene, regionBase, id) + { + Vector3 minTerrainCoords = new Vector3(0f, 0f, BSTerrainManager.HEIGHT_INITIALIZATION - BSTerrainManager.HEIGHT_EQUAL_FUDGE); + Vector3 maxTerrainCoords = new Vector3(regionSize.X, regionSize.Y, BSTerrainManager.HEIGHT_INITIALIZATION); + int totalHeights = (int)maxTerrainCoords.X * (int)maxTerrainCoords.Y; + float[] initialMap = new float[totalHeights]; + for (int ii = 0; ii < totalHeights; ii++) + { + initialMap[ii] = BSTerrainManager.HEIGHT_INITIALIZATION; + } + m_mapInfo = new BulletHMapInfo(id, initialMap, regionSize.X, regionSize.Y); + m_mapInfo.minCoords = minTerrainCoords; + m_mapInfo.maxCoords = maxTerrainCoords; + m_mapInfo.terrainRegionBase = TerrainBase; + // Don't have to free any previous since we just got here. + BuildHeightmapTerrain(); + } + + // This minCoords and maxCoords passed in give the size of the terrain (min and max Z + // are the high and low points of the heightmap). + public BSTerrainHeightmap(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, + Vector3 minCoords, Vector3 maxCoords) + : base(physicsScene, regionBase, id) + { + m_mapInfo = new BulletHMapInfo(id, initialMap, maxCoords.X - minCoords.X, maxCoords.Y - minCoords.Y); + m_mapInfo.minCoords = minCoords; + m_mapInfo.maxCoords = maxCoords; + m_mapInfo.minZ = minCoords.Z; + m_mapInfo.maxZ = maxCoords.Z; + m_mapInfo.terrainRegionBase = TerrainBase; + + // Don't have to free any previous since we just got here. + BuildHeightmapTerrain(); + } + + public override void Dispose() + { + ReleaseHeightMapTerrain(); + } + + // Using the information in m_mapInfo, create the physical representation of the heightmap. + private void BuildHeightmapTerrain() + { + // Create the terrain shape from the mapInfo + m_mapInfo.terrainShape = m_physicsScene.PE.CreateTerrainShape( m_mapInfo.ID, + new Vector3(m_mapInfo.sizeX, m_mapInfo.sizeY, 0), m_mapInfo.minZ, m_mapInfo.maxZ, + m_mapInfo.heightMap, 1f, BSParam.TerrainCollisionMargin); + + + // The terrain object initial position is at the center of the object + Vector3 centerPos; + centerPos.X = m_mapInfo.minCoords.X + (m_mapInfo.sizeX / 2f); + centerPos.Y = m_mapInfo.minCoords.Y + (m_mapInfo.sizeY / 2f); + centerPos.Z = m_mapInfo.minZ + ((m_mapInfo.maxZ - m_mapInfo.minZ) / 2f); + + m_mapInfo.terrainBody = m_physicsScene.PE.CreateBodyWithDefaultMotionState(m_mapInfo.terrainShape, + m_mapInfo.ID, centerPos, Quaternion.Identity); + + // Set current terrain attributes + m_physicsScene.PE.SetFriction(m_mapInfo.terrainBody, BSParam.TerrainFriction); + m_physicsScene.PE.SetHitFraction(m_mapInfo.terrainBody, BSParam.TerrainHitFraction); + m_physicsScene.PE.SetRestitution(m_mapInfo.terrainBody, BSParam.TerrainRestitution); + m_physicsScene.PE.SetCollisionFlags(m_mapInfo.terrainBody, CollisionFlags.CF_STATIC_OBJECT); + + m_mapInfo.terrainBody.collisionType = CollisionType.Terrain; + + // Return the new terrain to the world of physical objects + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_mapInfo.terrainBody); + + // redo its bounding box now that it is in the world + m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_mapInfo.terrainBody); + + // Make it so the terrain will not move or be considered for movement. + m_physicsScene.PE.ForceActivationState(m_mapInfo.terrainBody, ActivationState.DISABLE_SIMULATION); + + return; + } + + // If there is information in m_mapInfo pointing to physical structures, release same. + private void ReleaseHeightMapTerrain() + { + if (m_mapInfo != null) + { + if (m_mapInfo.terrainBody.HasPhysicalBody) + { + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_mapInfo.terrainBody); + // Frees both the body and the shape. + m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_mapInfo.terrainBody); + } + } + m_mapInfo = null; + } + + // The passed position is relative to the base of the region. + public override float GetTerrainHeightAtXYZ(Vector3 pos) + { + float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; + + int mapIndex = (int)pos.Y * (int)m_mapInfo.sizeY + (int)pos.X; + try + { + ret = m_mapInfo.heightMap[mapIndex]; + } + catch + { + // Sometimes they give us wonky values of X and Y. Give a warning and return something. + m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", + LogHeader, m_mapInfo.terrainRegionBase, pos); + ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; + } + return ret; + } + + // The passed position is relative to the base of the region. + public override float GetWaterLevelAtXYZ(Vector3 pos) + { + return m_physicsScene.SimpleWaterLevel; + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs new file mode 100755 index 0000000..50f917a --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs @@ -0,0 +1,585 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Region.Framework; +using OpenSim.Region.CoreModules; +using OpenSim.Region.Physics.Manager; + +using Nini.Config; +using log4net; + +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +// The physical implementation of the terrain is wrapped in this class. +public abstract class BSTerrainPhys : IDisposable +{ + public enum TerrainImplementation + { + Heightmap = 0, + Mesh = 1 + } + + protected BSScene m_physicsScene { get; private set; } + // Base of the region in world coordinates. Coordinates inside the region are relative to this. + public Vector3 TerrainBase { get; private set; } + public uint ID { get; private set; } + + public BSTerrainPhys(BSScene physicsScene, Vector3 regionBase, uint id) + { + m_physicsScene = physicsScene; + TerrainBase = regionBase; + ID = id; + } + public abstract void Dispose(); + public abstract float GetTerrainHeightAtXYZ(Vector3 pos); + public abstract float GetWaterLevelAtXYZ(Vector3 pos); +} + +// ========================================================================================== +public sealed class BSTerrainManager : IDisposable +{ + static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; + + // These height values are fractional so the odd values will be + // noticable when debugging. + public const float HEIGHT_INITIALIZATION = 24.987f; + public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f; + public const float HEIGHT_GETHEIGHT_RET = 24.765f; + public const float WATER_HEIGHT_GETHEIGHT_RET = 19.998f; + + // If the min and max height are equal, we reduce the min by this + // amount to make sure that a bounding box is built for the terrain. + public const float HEIGHT_EQUAL_FUDGE = 0.2f; + + // Until the whole simulator is changed to pass us the region size, we rely on constants. + public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); + + // The scene that I am part of + private BSScene m_physicsScene { get; set; } + + // The ground plane created to keep thing from falling to infinity. + private BulletBody m_groundPlane; + + // If doing mega-regions, if we're region zero we will be managing multiple + // region terrains since region zero does the physics for the whole mega-region. + private Dictionary m_terrains; + + // Flags used to know when to recalculate the height. + private bool m_terrainModified = false; + + // If we are doing mega-regions, terrains are added from TERRAIN_ID to m_terrainCount. + // This is incremented before assigning to new region so it is the last ID allocated. + private uint m_terrainCount = BSScene.CHILDTERRAIN_ID - 1; + public uint HighestTerrainID { get {return m_terrainCount; } } + + // If doing mega-regions, this holds our offset from region zero of + // the mega-regions. "parentScene" points to the PhysicsScene of region zero. + private Vector3 m_worldOffset; + // If the parent region (region 0), this is the extent of the combined regions + // relative to the origin of region zero + private Vector3 m_worldMax; + private PhysicsScene MegaRegionParentPhysicsScene { get; set; } + + public BSTerrainManager(BSScene physicsScene, Vector3 regionSize) + { + m_physicsScene = physicsScene; + DefaultRegionSize = regionSize; + + m_terrains = new Dictionary(); + + // Assume one region of default size + m_worldOffset = Vector3.Zero; + m_worldMax = new Vector3(DefaultRegionSize); + MegaRegionParentPhysicsScene = null; + } + + public void Dispose() + { + ReleaseGroundPlaneAndTerrain(); + } + + // Create the initial instance of terrain and the underlying ground plane. + // This is called from the initialization routine so we presume it is + // safe to call Bullet in real time. We hope no one is moving prims around yet. + public void CreateInitialGroundPlaneAndTerrain() + { + DetailLog("{0},BSTerrainManager.CreateInitialGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, m_physicsScene.RegionName); + // The ground plane is here to catch things that are trying to drop to negative infinity + BulletShape groundPlaneShape = m_physicsScene.PE.CreateGroundPlaneShape(BSScene.GROUNDPLANE_ID, 1f, BSParam.TerrainCollisionMargin); + Vector3 groundPlaneAltitude = new Vector3(0f, 0f, BSParam.TerrainGroundPlane); + m_groundPlane = m_physicsScene.PE.CreateBodyWithDefaultMotionState(groundPlaneShape, + BSScene.GROUNDPLANE_ID, groundPlaneAltitude, Quaternion.Identity); + + // Everything collides with the ground plane. + m_groundPlane.collisionType = CollisionType.Groundplane; + + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_groundPlane); + m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_groundPlane); + + // Ground plane does not move + m_physicsScene.PE.ForceActivationState(m_groundPlane, ActivationState.DISABLE_SIMULATION); + + BSTerrainPhys initialTerrain = new BSTerrainHeightmap(m_physicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize); + lock (m_terrains) + { + // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain. + m_terrains.Add(Vector3.Zero, initialTerrain); + } + } + + // Release all the terrain structures we might have allocated + public void ReleaseGroundPlaneAndTerrain() + { + DetailLog("{0},BSTerrainManager.ReleaseGroundPlaneAndTerrain,region={1}", BSScene.DetailLogZero, m_physicsScene.RegionName); + if (m_groundPlane.HasPhysicalBody) + { + if (m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_groundPlane)) + { + m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_groundPlane); + } + m_groundPlane.Clear(); + } + + ReleaseTerrain(); + } + + // Release all the terrain we have allocated + public void ReleaseTerrain() + { + lock (m_terrains) + { + foreach (KeyValuePair kvp in m_terrains) + { + kvp.Value.Dispose(); + } + m_terrains.Clear(); + } + } + + // The simulator wants to set a new heightmap for the terrain. + public void SetTerrain(float[] heightMap) { + float[] localHeightMap = heightMap; + // If there are multiple requests for changes to the same terrain between ticks, + // only do that last one. + m_physicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate() + { + if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null) + { + // If a child of a mega-region, we shouldn't have any terrain allocated for us + ReleaseGroundPlaneAndTerrain(); + // If doing the mega-prim stuff and we are the child of the zero region, + // the terrain is added to our parent + if (MegaRegionParentPhysicsScene is BSScene) + { + DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}", BSScene.DetailLogZero, m_worldOffset, m_worldMax); + ((BSScene)MegaRegionParentPhysicsScene).TerrainManager.AddMegaRegionChildTerrain( + BSScene.CHILDTERRAIN_ID, localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize); + } + } + else + { + // If not doing the mega-prim thing, just change the terrain + DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero); + + UpdateTerrain(BSScene.TERRAIN_ID, localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize); + } + }); + } + + // Another region is calling this region and passing a terrain. + // A region that is not the mega-region root will pass its terrain to the root region so the root region + // physics engine will have all the terrains. + private void AddMegaRegionChildTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords) + { + // Since we are called by another region's thread, the action must be rescheduled onto our processing thread. + m_physicsScene.PostTaintObject("TerrainManager.AddMegaRegionChild" + minCoords.ToString(), id, delegate() + { + UpdateTerrain(id, heightMap, minCoords, maxCoords); + }); + } + + // If called for terrain has has not been previously allocated, a new terrain will be built + // based on the passed information. The 'id' should be either the terrain id or + // BSScene.CHILDTERRAIN_ID. If the latter, a new child terrain ID will be allocated and used. + // The latter feature is for creating child terrains for mega-regions. + // If there is an existing terrain body, a new + // terrain shape is created and added to the body. + // This call is most often used to update the heightMap and parameters of the terrain. + // (The above does suggest that some simplification/refactoring is in order.) + // Called during taint-time. + private void UpdateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords) + { + // Find high and low points of passed heightmap. + // The min and max passed in is usually the area objects can be in (maximum + // object height, for instance). The terrain wants the bounding box for the + // terrain so replace passed min and max Z with the actual terrain min/max Z. + float minZ = float.MaxValue; + float maxZ = float.MinValue; + foreach (float height in heightMap) + { + if (height < minZ) minZ = height; + if (height > maxZ) maxZ = height; + } + if (minZ == maxZ) + { + // If min and max are the same, reduce min a little bit so a good bounding box is created. + minZ -= BSTerrainManager.HEIGHT_EQUAL_FUDGE; + } + minCoords.Z = minZ; + maxCoords.Z = maxZ; + + DetailLog("{0},BSTerrainManager.UpdateTerrain,call,id={1},minC={2},maxC={3}", + BSScene.DetailLogZero, id, minCoords, maxCoords); + + Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f); + + lock (m_terrains) + { + BSTerrainPhys terrainPhys; + if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys)) + { + // There is already a terrain in this spot. Free the old and build the new. + DetailLog("{0},BSTerrainManager.UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}", + BSScene.DetailLogZero, id, terrainRegionBase, minCoords, maxCoords); + + // Remove old terrain from the collection + m_terrains.Remove(terrainRegionBase); + // Release any physical memory it may be using. + terrainPhys.Dispose(); + + if (MegaRegionParentPhysicsScene == null) + { + // This terrain is not part of the mega-region scheme. Create vanilla terrain. + BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords); + m_terrains.Add(terrainRegionBase, newTerrainPhys); + + m_terrainModified = true; + } + else + { + // It's possible that Combine() was called after this code was queued. + // If we are a child of combined regions, we don't create any terrain for us. + DetailLog("{0},BSTerrainManager.UpdateTerrain:AmACombineChild,taint", BSScene.DetailLogZero); + + // Get rid of any terrain that may have been allocated for us. + ReleaseGroundPlaneAndTerrain(); + + // I hate doing this, but just bail + return; + } + } + else + { + // We don't know about this terrain so either we are creating a new terrain or + // our mega-prim child is giving us a new terrain to add to the phys world + + // if this is a child terrain, calculate a unique terrain id + uint newTerrainID = id; + if (newTerrainID >= BSScene.CHILDTERRAIN_ID) + newTerrainID = ++m_terrainCount; + + DetailLog("{0},BSTerrainManager.UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}", + BSScene.DetailLogZero, newTerrainID, minCoords, maxCoords); + BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords); + m_terrains.Add(terrainRegionBase, newTerrainPhys); + + m_terrainModified = true; + } + } + } + + // TODO: redo terrain implementation selection to allow other base types than heightMap. + private BSTerrainPhys BuildPhysicalTerrain(Vector3 terrainRegionBase, uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords) + { + m_physicsScene.Logger.DebugFormat("{0} Terrain for {1}/{2} created with {3}", + LogHeader, m_physicsScene.RegionName, terrainRegionBase, + (BSTerrainPhys.TerrainImplementation)BSParam.TerrainImplementation); + BSTerrainPhys newTerrainPhys = null; + switch ((int)BSParam.TerrainImplementation) + { + case (int)BSTerrainPhys.TerrainImplementation.Heightmap: + newTerrainPhys = new BSTerrainHeightmap(m_physicsScene, terrainRegionBase, id, + heightMap, minCoords, maxCoords); + break; + case (int)BSTerrainPhys.TerrainImplementation.Mesh: + newTerrainPhys = new BSTerrainMesh(m_physicsScene, terrainRegionBase, id, + heightMap, minCoords, maxCoords); + break; + default: + m_physicsScene.Logger.ErrorFormat("{0} Bad terrain implementation specified. Type={1}/{2},Region={3}/{4}", + LogHeader, + (int)BSParam.TerrainImplementation, + BSParam.TerrainImplementation, + m_physicsScene.RegionName, terrainRegionBase); + break; + } + return newTerrainPhys; + } + + // Return 'true' of this position is somewhere in known physical terrain space + public bool IsWithinKnownTerrain(Vector3 pos) + { + Vector3 terrainBaseXYZ; + BSTerrainPhys physTerrain; + return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ); + } + + // Return a new position that is over known terrain if the position is outside our terrain. + public Vector3 ClampPositionIntoKnownTerrain(Vector3 pPos) + { + float edgeEpsilon = 0.1f; + + Vector3 ret = pPos; + + // First, base addresses are never negative so correct for that possible problem. + if (ret.X < 0f || ret.Y < 0f) + { + ret.X = Util.Clamp(ret.X, 0f, 1000000f); + ret.Y = Util.Clamp(ret.Y, 0f, 1000000f); + DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,zeroingNegXorY,oldPos={1},newPos={2}", + BSScene.DetailLogZero, pPos, ret); + } + + // Can't do this function if we don't know about any terrain. + if (m_terrains.Count == 0) + return ret; + + int loopPrevention = 10; + Vector3 terrainBaseXYZ; + BSTerrainPhys physTerrain; + while (!GetTerrainPhysicalAtXYZ(ret, out physTerrain, out terrainBaseXYZ)) + { + // The passed position is not within a known terrain area. + // NOTE that GetTerrainPhysicalAtXYZ will set 'terrainBaseXYZ' to the base of the unfound region. + + // Must be off the top of a region. Find an adjacent region to move into. + // The returned terrain is always 'lower'. That is, closer to <0,0>. + Vector3 adjacentTerrainBase = FindAdjacentTerrainBase(terrainBaseXYZ); + + if (adjacentTerrainBase.X < terrainBaseXYZ.X) + { + // moving down into a new region in the X dimension. New position will be the max in the new base. + ret.X = adjacentTerrainBase.X + DefaultRegionSize.X - edgeEpsilon; + } + if (adjacentTerrainBase.Y < terrainBaseXYZ.Y) + { + // moving down into a new region in the X dimension. New position will be the max in the new base. + ret.Y = adjacentTerrainBase.Y + DefaultRegionSize.Y - edgeEpsilon; + } + DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,findingAdjacentRegion,adjacentRegBase={1},oldPos={2},newPos={3}", + BSScene.DetailLogZero, adjacentTerrainBase, pPos, ret); + + if (loopPrevention-- < 0f) + { + // The 'while' is a little dangerous so this prevents looping forever if the + // mapping of the terrains ever gets messed up (like nothing at <0,0>) or + // the list of terrains is in transition. + DetailLog("{0},BSTerrainManager.ClampPositionToKnownTerrain,suppressingFindAdjacentRegionLoop", BSScene.DetailLogZero); + break; + } + } + + return ret; + } + + // Given an X and Y, find the height of the terrain. + // Since we could be handling multiple terrains for a mega-region, + // the base of the region is calcuated assuming all regions are + // the same size and that is the default. + // Once the heightMapInfo is found, we have all the information to + // compute the offset into the array. + private float lastHeightTX = 999999f; + private float lastHeightTY = 999999f; + private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT; + public float GetTerrainHeightAtXYZ(Vector3 pos) + { + float tX = pos.X; + float tY = pos.Y; + // You'd be surprized at the number of times this routine is called + // with the same parameters as last time. + if (!m_terrainModified && (lastHeightTX == tX) && (lastHeightTY == tY)) + return lastHeight; + m_terrainModified = false; + + lastHeightTX = tX; + lastHeightTY = tY; + float ret = HEIGHT_GETHEIGHT_RET; + + Vector3 terrainBaseXYZ; + BSTerrainPhys physTerrain; + if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ)) + { + ret = physTerrain.GetTerrainHeightAtXYZ(pos - terrainBaseXYZ); + } + else + { + m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", + LogHeader, m_physicsScene.RegionName, tX, tY); + DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}", + BSScene.DetailLogZero, pos, terrainBaseXYZ); + } + + lastHeight = ret; + return ret; + } + + public float GetWaterLevelAtXYZ(Vector3 pos) + { + float ret = WATER_HEIGHT_GETHEIGHT_RET; + + Vector3 terrainBaseXYZ; + BSTerrainPhys physTerrain; + if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ)) + { + ret = physTerrain.GetWaterLevelAtXYZ(pos); + } + else + { + m_physicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}", + LogHeader, m_physicsScene.RegionName, pos, terrainBaseXYZ, ret); + } + return ret; + } + + // Given an address, return 'true' of there is a description of that terrain and output + // the descriptor class and the 'base' fo the addresses therein. + private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase) + { + bool ret = false; + + Vector3 terrainBaseXYZ = Vector3.Zero; + if (pos.X < 0f || pos.Y < 0f) + { + // We don't handle negative addresses so just make up a base that will not be found. + terrainBaseXYZ = new Vector3(-DefaultRegionSize.X, -DefaultRegionSize.Y, 0f); + } + else + { + int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; + int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y; + terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f); + } + + BSTerrainPhys physTerrain = null; + lock (m_terrains) + { + ret = m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain); + } + outTerrainBase = terrainBaseXYZ; + outPhysTerrain = physTerrain; + return ret; + } + + // Given a terrain base, return a terrain base for a terrain that is closer to <0,0> than + // this one. Usually used to return an out of bounds object to a known place. + private Vector3 FindAdjacentTerrainBase(Vector3 pTerrainBase) + { + Vector3 ret = pTerrainBase; + + // Can't do this function if we don't know about any terrain. + if (m_terrains.Count == 0) + return ret; + + // Just some sanity + ret.X = Util.Clamp(ret.X, 0f, 1000000f); + ret.Y = Util.Clamp(ret.Y, 0f, 1000000f); + ret.Z = 0f; + + lock (m_terrains) + { + // Once down to the <0,0> region, we have to be done. + while (ret.X > 0f || ret.Y > 0f) + { + if (ret.X > 0f) + { + ret.X = Math.Max(0f, ret.X - DefaultRegionSize.X); + DetailLog("{0},BSTerrainManager.FindAdjacentTerrainBase,reducingX,terrainBase={1}", BSScene.DetailLogZero, ret); + if (m_terrains.ContainsKey(ret)) + break; + } + if (ret.Y > 0f) + { + ret.Y = Math.Max(0f, ret.Y - DefaultRegionSize.Y); + DetailLog("{0},BSTerrainManager.FindAdjacentTerrainBase,reducingY,terrainBase={1}", BSScene.DetailLogZero, ret); + if (m_terrains.ContainsKey(ret)) + break; + } + } + } + + return ret; + } + + // Although no one seems to check this, I do support combining. + public bool SupportsCombining() + { + return true; + } + + // This routine is called two ways: + // One with 'offset' and 'pScene' zero and null but 'extents' giving the maximum + // extent of the combined regions. This is to inform the parent of the size + // of the combined regions. + // and one with 'offset' as the offset of the child region to the base region, + // 'pScene' pointing to the parent and 'extents' of zero. This informs the + // child of its relative base and new parent. + public void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents) + { + m_worldOffset = offset; + m_worldMax = extents; + MegaRegionParentPhysicsScene = pScene; + if (pScene != null) + { + // We are a child. + // We want m_worldMax to be the highest coordinate of our piece of terrain. + m_worldMax = offset + DefaultRegionSize; + } + DetailLog("{0},BSTerrainManager.Combine,offset={1},extents={2},wOffset={3},wMax={4}", + BSScene.DetailLogZero, offset, extents, m_worldOffset, m_worldMax); + } + + // Unhook all the combining that I know about. + public void UnCombine(PhysicsScene pScene) + { + // Just like ODE, we don't do anything yet. + DetailLog("{0},BSTerrainManager.UnCombine", BSScene.DetailLogZero); + } + + + private void DetailLog(string msg, params Object[] args) + { + m_physicsScene.PhysicsLogging.Write(msg, args); + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs new file mode 100755 index 0000000..e4ca098 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs @@ -0,0 +1,441 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; + +using OpenSim.Framework; +using OpenSim.Region.Framework; +using OpenSim.Region.CoreModules; +using OpenSim.Region.Physics.Manager; + +using Nini.Config; +using log4net; + +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public sealed class BSTerrainMesh : BSTerrainPhys +{ + static string LogHeader = "[BULLETSIM TERRAIN MESH]"; + + private float[] m_savedHeightMap; + int m_sizeX; + int m_sizeY; + + BulletShape m_terrainShape; + BulletBody m_terrainBody; + + public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, Vector3 regionSize) + : base(physicsScene, regionBase, id) + { + } + + public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id /* parameters for making mesh */) + : base(physicsScene, regionBase, id) + { + } + + // Create terrain mesh from a heightmap. + public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap, + Vector3 minCoords, Vector3 maxCoords) + : base(physicsScene, regionBase, id) + { + int indicesCount; + int[] indices; + int verticesCount; + float[] vertices; + + m_savedHeightMap = initialMap; + + m_sizeX = (int)(maxCoords.X - minCoords.X); + m_sizeY = (int)(maxCoords.Y - minCoords.Y); + + bool meshCreationSuccess = false; + if (BSParam.TerrainMeshMagnification == 1) + { + // If a magnification of one, use the old routine that is tried and true. + meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh(m_physicsScene, + initialMap, m_sizeX, m_sizeY, // input size + Vector3.Zero, // base for mesh + out indicesCount, out indices, out verticesCount, out vertices); + } + else + { + // Other magnifications use the newer routine + meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh2(m_physicsScene, + initialMap, m_sizeX, m_sizeY, // input size + BSParam.TerrainMeshMagnification, + physicsScene.TerrainManager.DefaultRegionSize, + Vector3.Zero, // base for mesh + out indicesCount, out indices, out verticesCount, out vertices); + } + if (!meshCreationSuccess) + { + // DISASTER!! + m_physicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap,id={1}", BSScene.DetailLogZero, ID); + m_physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader, TerrainBase); + // Something is very messed up and a crash is in our future. + return; + } + + m_physicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,id={1},indices={2},indSz={3},vertices={4},vertSz={5}", + BSScene.DetailLogZero, ID, indicesCount, indices.Length, verticesCount, vertices.Length); + + m_terrainShape = m_physicsScene.PE.CreateMeshShape(m_physicsScene.World, indicesCount, indices, verticesCount, vertices); + if (!m_terrainShape.HasPhysicalShape) + { + // DISASTER!! + m_physicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape,id={1}", BSScene.DetailLogZero, ID); + m_physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase); + // Something is very messed up and a crash is in our future. + return; + } + + Vector3 pos = regionBase; + Quaternion rot = Quaternion.Identity; + + m_terrainBody = m_physicsScene.PE.CreateBodyWithDefaultMotionState(m_terrainShape, ID, pos, rot); + if (!m_terrainBody.HasPhysicalBody) + { + // DISASTER!! + m_physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase); + // Something is very messed up and a crash is in our future. + return; + } + physicsScene.PE.SetShapeCollisionMargin(m_terrainShape, BSParam.TerrainCollisionMargin); + + // Set current terrain attributes + m_physicsScene.PE.SetFriction(m_terrainBody, BSParam.TerrainFriction); + m_physicsScene.PE.SetHitFraction(m_terrainBody, BSParam.TerrainHitFraction); + m_physicsScene.PE.SetRestitution(m_terrainBody, BSParam.TerrainRestitution); + m_physicsScene.PE.SetContactProcessingThreshold(m_terrainBody, BSParam.TerrainContactProcessingThreshold); + m_physicsScene.PE.SetCollisionFlags(m_terrainBody, CollisionFlags.CF_STATIC_OBJECT); + + // Static objects are not very massive. + m_physicsScene.PE.SetMassProps(m_terrainBody, 0f, Vector3.Zero); + + // Put the new terrain to the world of physical objects + m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, m_terrainBody); + + // Redo its bounding box now that it is in the world + m_physicsScene.PE.UpdateSingleAabb(m_physicsScene.World, m_terrainBody); + + m_terrainBody.collisionType = CollisionType.Terrain; + m_terrainBody.ApplyCollisionMask(m_physicsScene); + + if (BSParam.UseSingleSidedMeshes) + { + m_physicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id); + m_physicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK); + } + + // Make it so the terrain will not move or be considered for movement. + m_physicsScene.PE.ForceActivationState(m_terrainBody, ActivationState.DISABLE_SIMULATION); + } + + public override void Dispose() + { + if (m_terrainBody.HasPhysicalBody) + { + m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, m_terrainBody); + // Frees both the body and the shape. + m_physicsScene.PE.DestroyObject(m_physicsScene.World, m_terrainBody); + m_terrainBody.Clear(); + m_terrainShape.Clear(); + } + } + + public override float GetTerrainHeightAtXYZ(Vector3 pos) + { + // For the moment use the saved heightmap to get the terrain height. + // TODO: raycast downward to find the true terrain below the position. + float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; + + int mapIndex = (int)pos.Y * m_sizeY + (int)pos.X; + try + { + ret = m_savedHeightMap[mapIndex]; + } + catch + { + // Sometimes they give us wonky values of X and Y. Give a warning and return something. + m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, pos={2}", + LogHeader, TerrainBase, pos); + ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; + } + return ret; + } + + // The passed position is relative to the base of the region. + public override float GetWaterLevelAtXYZ(Vector3 pos) + { + return m_physicsScene.SimpleWaterLevel; + } + + // Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). + // Return 'true' if successfully created. + public static bool ConvertHeightmapToMesh( BSScene physicsScene, + float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap + Vector3 extentBase, // base to be added to all vertices + out int indicesCountO, out int[] indicesO, + out int verticesCountO, out float[] verticesO) + { + bool ret = false; + + int indicesCount = 0; + int verticesCount = 0; + int[] indices = new int[0]; + float[] vertices = new float[0]; + + // Simple mesh creation which assumes magnification == 1. + // TODO: do a more general solution that scales, adds new vertices and smoothes the result. + + // Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop + // from zero to <= sizeX). The triangle indices are then generated as two triangles + // per heightmap point. There are sizeX by sizeY of these squares. The extra row and + // column of vertices are used to complete the triangles of the last row and column + // of the heightmap. + try + { + // One vertice per heightmap value plus the vertices off the side and bottom edge. + int totalVertices = (sizeX + 1) * (sizeY + 1); + vertices = new float[totalVertices * 3]; + int totalIndices = sizeX * sizeY * 6; + indices = new int[totalIndices]; + + if (physicsScene != null) + physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3}", + BSScene.DetailLogZero, totalVertices, totalIndices, extentBase); + float minHeight = float.MaxValue; + // Note that sizeX+1 vertices are created since there is land between this and the next region. + for (int yy = 0; yy <= sizeY; yy++) + { + for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times + { + int offset = yy * sizeX + xx; + // Extend the height with the height from the last row or column + if (yy == sizeY) offset -= sizeX; + if (xx == sizeX) offset -= 1; + float height = heightMap[offset]; + minHeight = Math.Min(minHeight, height); + vertices[verticesCount + 0] = (float)xx + extentBase.X; + vertices[verticesCount + 1] = (float)yy + extentBase.Y; + vertices[verticesCount + 2] = height + extentBase.Z; + verticesCount += 3; + } + } + verticesCount = verticesCount / 3; + + for (int yy = 0; yy < sizeY; yy++) + { + for (int xx = 0; xx < sizeX; xx++) + { + int offset = yy * (sizeX + 1) + xx; + // Each vertices is presumed to be the upper left corner of a box of two triangles + indices[indicesCount + 0] = offset; + indices[indicesCount + 1] = offset + 1; + indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column + indices[indicesCount + 3] = offset + 1; + indices[indicesCount + 4] = offset + sizeX + 2; + indices[indicesCount + 5] = offset + sizeX + 1; + indicesCount += 6; + } + } + + ret = true; + } + catch (Exception e) + { + if (physicsScene != null) + physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}", + LogHeader, physicsScene.RegionName, extentBase, e); + } + + indicesCountO = indicesCount; + indicesO = indices; + verticesCountO = verticesCount; + verticesO = vertices; + + return ret; + } + + private class HeightMapGetter + { + private float[] m_heightMap; + private int m_sizeX; + private int m_sizeY; + public HeightMapGetter(float[] pHeightMap, int pSizeX, int pSizeY) + { + m_heightMap = pHeightMap; + m_sizeX = pSizeX; + m_sizeY = pSizeY; + } + // The heightmap is extended as an infinite plane at the last height + public float GetHeight(int xx, int yy) + { + int offset = 0; + // Extend the height with the height from the last row or column + if (yy >= m_sizeY) + if (xx >= m_sizeX) + offset = (m_sizeY - 1) * m_sizeX + (m_sizeX - 1); + else + offset = (m_sizeY - 1) * m_sizeX + xx; + else + if (xx >= m_sizeX) + offset = yy * m_sizeX + (m_sizeX - 1); + else + offset = yy * m_sizeX + xx; + + return m_heightMap[offset]; + } + } + + // Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). + // Version that handles magnification. + // Return 'true' if successfully created. + public static bool ConvertHeightmapToMesh2( BSScene physicsScene, + float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap + int magnification, // number of vertices per heighmap step + Vector3 extent, // dimensions of the output mesh + Vector3 extentBase, // base to be added to all vertices + out int indicesCountO, out int[] indicesO, + out int verticesCountO, out float[] verticesO) + { + bool ret = false; + + int indicesCount = 0; + int verticesCount = 0; + int[] indices = new int[0]; + float[] vertices = new float[0]; + + HeightMapGetter hmap = new HeightMapGetter(heightMap, sizeX, sizeY); + + // The vertices dimension of the output mesh + int meshX = sizeX * magnification; + int meshY = sizeY * magnification; + // The output size of one mesh step + float meshXStep = extent.X / meshX; + float meshYStep = extent.Y / meshY; + + // Create an array of vertices that is meshX+1 by meshY+1 (note the loop + // from zero to <= meshX). The triangle indices are then generated as two triangles + // per heightmap point. There are meshX by meshY of these squares. The extra row and + // column of vertices are used to complete the triangles of the last row and column + // of the heightmap. + try + { + // Vertices for the output heightmap plus one on the side and bottom to complete triangles + int totalVertices = (meshX + 1) * (meshY + 1); + vertices = new float[totalVertices * 3]; + int totalIndices = meshX * meshY * 6; + indices = new int[totalIndices]; + + if (physicsScene != null) + physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,inSize={1},outSize={2},totVert={3},totInd={4},extentBase={5}", + BSScene.DetailLogZero, new Vector2(sizeX, sizeY), new Vector2(meshX, meshY), + totalVertices, totalIndices, extentBase); + + float minHeight = float.MaxValue; + // Note that sizeX+1 vertices are created since there is land between this and the next region. + // Loop through the output vertices and compute the mediun height in between the input vertices + for (int yy = 0; yy <= meshY; yy++) + { + for (int xx = 0; xx <= meshX; xx++) // Hint: the "<=" means we go around sizeX + 1 times + { + float offsetY = (float)yy * (float)sizeY / (float)meshY; // The Y that is closest to the mesh point + int stepY = (int)offsetY; + float fractionalY = offsetY - (float)stepY; + float offsetX = (float)xx * (float)sizeX / (float)meshX; // The X that is closest to the mesh point + int stepX = (int)offsetX; + float fractionalX = offsetX - (float)stepX; + + // physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,xx={1},yy={2},offX={3},stepX={4},fractX={5},offY={6},stepY={7},fractY={8}", + // BSScene.DetailLogZero, xx, yy, offsetX, stepX, fractionalX, offsetY, stepY, fractionalY); + + // get the four corners of the heightmap square the mesh point is in + float heightUL = hmap.GetHeight(stepX , stepY ); + float heightUR = hmap.GetHeight(stepX + 1, stepY ); + float heightLL = hmap.GetHeight(stepX , stepY + 1); + float heightLR = hmap.GetHeight(stepX + 1, stepY + 1); + + // bilinear interplolation + float height = heightUL * (1 - fractionalX) * (1 - fractionalY) + + heightUR * fractionalX * (1 - fractionalY) + + heightLL * (1 - fractionalX) * fractionalY + + heightLR * fractionalX * fractionalY; + + // physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,heightUL={1},heightUR={2},heightLL={3},heightLR={4},heightMap={5}", + // BSScene.DetailLogZero, heightUL, heightUR, heightLL, heightLR, height); + + minHeight = Math.Min(minHeight, height); + + vertices[verticesCount + 0] = (float)xx * meshXStep + extentBase.X; + vertices[verticesCount + 1] = (float)yy * meshYStep + extentBase.Y; + vertices[verticesCount + 2] = height + extentBase.Z; + verticesCount += 3; + } + } + // The number of vertices generated + verticesCount /= 3; + + // Loop through all the heightmap squares and create indices for the two triangles for that square + for (int yy = 0; yy < meshY; yy++) + { + for (int xx = 0; xx < meshX; xx++) + { + int offset = yy * (meshX + 1) + xx; + // Each vertices is presumed to be the upper left corner of a box of two triangles + indices[indicesCount + 0] = offset; + indices[indicesCount + 1] = offset + 1; + indices[indicesCount + 2] = offset + meshX + 1; // accounting for the extra column + indices[indicesCount + 3] = offset + 1; + indices[indicesCount + 4] = offset + meshX + 2; + indices[indicesCount + 5] = offset + meshX + 1; + indicesCount += 6; + } + } + + ret = true; + } + catch (Exception e) + { + if (physicsScene != null) + physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}", + LogHeader, physicsScene.RegionName, extentBase, e); + } + + indicesCountO = indicesCount; + indicesO = indices; + verticesCountO = verticesCount; + verticesO = vertices; + + return ret; + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BulletSimData.cs b/OpenSim/Region/PhysicsModules/BulletS/BulletSimData.cs new file mode 100755 index 0000000..5932461 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BulletSimData.cs @@ -0,0 +1,277 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +// Classes to allow some type checking for the API +// These hold pointers to allocated objects in the unmanaged space. +// These classes are subclassed by the various physical implementations of +// objects. In particular, there is a version for physical instances in +// unmanaged memory ("unman") and one for in managed memory ("XNA"). + +// Currently, the instances of these classes are a reference to a +// physical representation and this has no releationship to other +// instances. Someday, refarb the usage of these classes so each instance +// refers to a particular physical instance and this class controls reference +// counts and such. This should be done along with adding BSShapes. + +public class BulletWorld +{ + public BulletWorld(uint worldId, BSScene bss) + { + worldID = worldId; + physicsScene = bss; + } + public uint worldID; + // The scene is only in here so very low level routines have a handle to print debug/error messages + public BSScene physicsScene; +} + +// An allocated Bullet btRigidBody +public class BulletBody +{ + public BulletBody(uint id) + { + ID = id; + collisionType = CollisionType.Static; + } + public uint ID; + public CollisionType collisionType; + + public virtual void Clear() { } + public virtual bool HasPhysicalBody { get { return false; } } + + // Apply the specificed collision mask into the physical world + public virtual bool ApplyCollisionMask(BSScene physicsScene) + { + // Should assert the body has been added to the physical world. + // (The collision masks are stored in the collision proxy cache which only exists for + // a collision body that is in the world.) + return physicsScene.PE.SetCollisionGroupMask(this, + BulletSimData.CollisionTypeMasks[collisionType].group, + BulletSimData.CollisionTypeMasks[collisionType].mask); + } + + // Used for log messages for a unique display of the memory/object allocated to this instance + public virtual string AddrString + { + get { return "unknown"; } + } + + public override string ToString() + { + StringBuilder buff = new StringBuilder(); + buff.Append(""); + return buff.ToString(); + } +} + +public class BulletShape +{ + public BulletShape() + { + shapeType = BSPhysicsShapeType.SHAPE_UNKNOWN; + shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE; + isNativeShape = false; + } + public BSPhysicsShapeType shapeType; + public System.UInt64 shapeKey; + public bool isNativeShape; + + public virtual void Clear() { } + public virtual bool HasPhysicalShape { get { return false; } } + + // Make another reference to this physical object. + public virtual BulletShape Clone() { return new BulletShape(); } + + // Return 'true' if this and other refer to the same physical object + public virtual bool ReferenceSame(BulletShape xx) { return false; } + + // Used for log messages for a unique display of the memory/object allocated to this instance + public virtual string AddrString + { + get { return "unknown"; } + } + + public override string ToString() + { + StringBuilder buff = new StringBuilder(); + buff.Append(""); + return buff.ToString(); + } +} + +// An allocated Bullet btConstraint +public class BulletConstraint +{ + public BulletConstraint() + { + } + public virtual void Clear() { } + public virtual bool HasPhysicalConstraint { get { return false; } } + + // Used for log messages for a unique display of the memory/object allocated to this instance + public virtual string AddrString + { + get { return "unknown"; } + } +} + +// An allocated HeightMapThing which holds various heightmap info. +// Made a class rather than a struct so there would be only one +// instance of this and C# will pass around pointers rather +// than making copies. +public class BulletHMapInfo +{ + public BulletHMapInfo(uint id, float[] hm, float pSizeX, float pSizeY) { + ID = id; + heightMap = hm; + terrainRegionBase = OMV.Vector3.Zero; + minCoords = new OMV.Vector3(100f, 100f, 25f); + maxCoords = new OMV.Vector3(101f, 101f, 26f); + minZ = maxZ = 0f; + sizeX = pSizeX; + sizeY = pSizeY; + } + public uint ID; + public float[] heightMap; + public OMV.Vector3 terrainRegionBase; + public OMV.Vector3 minCoords; + public OMV.Vector3 maxCoords; + public float sizeX, sizeY; + public float minZ, maxZ; + public BulletShape terrainShape; + public BulletBody terrainBody; +} + +// The general class of collsion object. +public enum CollisionType +{ + Avatar, + PhantomToOthersAvatar, // An avatar that it phantom to other avatars but not to anything else + Groundplane, + Terrain, + Static, + Dynamic, + VolumeDetect, + // Linkset, // A linkset should be either Static or Dynamic + LinksetChild, + Unknown +}; + +// Hold specification of group and mask collision flags for a CollisionType +public struct CollisionTypeFilterGroup +{ + public CollisionTypeFilterGroup(CollisionType t, uint g, uint m) + { + type = t; + group = g; + mask = m; + } + public CollisionType type; + public uint group; + public uint mask; +}; + +public static class BulletSimData +{ + +// Map of collisionTypes to flags for collision groups and masks. +// An object's 'group' is the collison groups this object belongs to +// An object's 'filter' is the groups another object has to belong to in order to collide with me +// A collision happens if ((obj1.group & obj2.filter) != 0) || ((obj2.group & obj1.filter) != 0) +// +// As mentioned above, don't use the CollisionFilterGroups definitions directly in the code +// but, instead, use references to this dictionary. Finding and debugging +// collision flag problems will be made easier. +public static Dictionary CollisionTypeMasks + = new Dictionary() +{ + { CollisionType.Avatar, + new CollisionTypeFilterGroup(CollisionType.Avatar, + (uint)CollisionFilterGroups.BCharacterGroup, + (uint)(CollisionFilterGroups.BAllGroup)) + }, + { CollisionType.PhantomToOthersAvatar, + new CollisionTypeFilterGroup(CollisionType.PhantomToOthersAvatar, + (uint)CollisionFilterGroups.BCharacterGroup, + (uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BCharacterGroup)) + }, + { CollisionType.Groundplane, + new CollisionTypeFilterGroup(CollisionType.Groundplane, + (uint)CollisionFilterGroups.BGroundPlaneGroup, + // (uint)CollisionFilterGroups.BAllGroup) + (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup)) + }, + { CollisionType.Terrain, + new CollisionTypeFilterGroup(CollisionType.Terrain, + (uint)CollisionFilterGroups.BTerrainGroup, + (uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup)) + }, + { CollisionType.Static, + new CollisionTypeFilterGroup(CollisionType.Static, + (uint)CollisionFilterGroups.BStaticGroup, + (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup)) + }, + { CollisionType.Dynamic, + new CollisionTypeFilterGroup(CollisionType.Dynamic, + (uint)CollisionFilterGroups.BSolidGroup, + (uint)(CollisionFilterGroups.BAllGroup)) + }, + { CollisionType.VolumeDetect, + new CollisionTypeFilterGroup(CollisionType.VolumeDetect, + (uint)CollisionFilterGroups.BSensorTrigger, + (uint)(~CollisionFilterGroups.BSensorTrigger)) + }, + { CollisionType.LinksetChild, + new CollisionTypeFilterGroup(CollisionType.LinksetChild, + (uint)CollisionFilterGroups.BLinksetChildGroup, + (uint)(CollisionFilterGroups.BNoneGroup)) + // (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup)) + }, +}; + +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BulletSimTODO.txt b/OpenSim/Region/PhysicsModules/BulletS/BulletSimTODO.txt new file mode 100755 index 0000000..0453376 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BulletSimTODO.txt @@ -0,0 +1,379 @@ +CURRENT PROBLEMS TO FIX AND/OR LOOK AT +================================================= +Vehicle buoyancy. Computed correctly? Possibly creating very large effective mass. + Interaction of llSetBuoyancy and vehicle buoyancy. Should be additive? + Negative buoyancy computed correctly +Center-of-gravity +Computation of mesh mass. How done? How should it be done? +Enable vehicle border crossings (at least as poorly as ODE) + Terrain skirts + Avatar created in previous region and not new region when crossing border + Vehicle recreated in new sim at small Z value (offset from root value?) (DONE) +User settable terrain mesh + Allow specifying as convex or concave and use different getHeight functions depending +Boats, when turning nose down into the water + Acts like rotation around Z is also effecting rotation around X and Y +Deleting a linkset while standing on the root will leave the physical shape of the root behind. + Not sure if it is because standing on it. Done with large prim linksets. +Linkset child rotations. + Nebadon spiral tube has middle sections which are rotated wrong. + Select linked spiral tube. Delink and note where the middle section ends up. +Teravus llMoveToTarget script debug + Mixing of hover, buoyancy/gravity, moveToTarget, into one force + Setting hover height to zero disables hover even if hover flags are on (from SL wiki) +limitMotorUp calibration (more down?) +llRotLookAt +llLookAt +Convert to avatar mesh capsule. Include rotation of capsule. +Vehicle script tuning/debugging + Avanti speed script + Weapon shooter script +Move material definitions (friction, ...) into simulator. +osGetPhysicsEngineVerion() and create a version code for the C++ DLL +One sided meshes? Should terrain be built into a closed shape? + When meshes get partially wedged into the terrain, they cannot push themselves out. + It is possible that Bullet processes collisions whether entering or leaving a mesh. + Ref: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4869 +Small physical objects do not interact correctly + Create chain of .5x.5x.1 torui and make all but top physical so to hang. + The chain will fall apart and pairs will dance around on ground + Chains of 1x1x.2 will stay connected but will dance. + Chains above 2x2x.4 are more stable and get stablier as torui get larger. + +VEHICLES TODO LIST: +================================================= +LINEAR_MOTOR_DIRECTION values should be clamped to reasonable numbers. + What are the limits in SL? + Same for other velocity settings. +UBit improvements to remove rubber-banding of avatars sitting on vehicle child prims: + https://github.com/UbitUmarov/Ubit-opensim +Some vehicles should not be able to turn if no speed or off ground. +Cannot edit/move a vehicle being ridden: it jumps back to the origional position. +Neb car jiggling left and right + Happens on terrain and any other mesh object. Flat cubes are much smoother. + This has been reduced but not eliminated. +Implement referenceFrame for all the motion routines. +Verify llGetVel() is returning a smooth and good value for vehicle movement. +llGetVel() should return the root's velocity if requested in a child prim. +Implement function efficiency for lineaar and angular motion. +Linkset explosion after three "rides" on Nebadon lite vehicle (LinksetConstraint) +Remove vehicle angular velocity zeroing in BSPrim.UpdateProperties(). + A kludge that isn't fixing the real problem of Bullet adding extra motion. +Incorporate inter-relationship of angular corrections. For instance, angularDeflection + and angularMotorUp will compute same X or Y correction. When added together + creates over-correction and over-shoot and wabbling. +Vehicle attributes are not restored when a vehicle is rezzed on region creation + Create vehicle, setup vehicle properties, restart region, vehicle is not reinitialized. +What to do if vehicle and prim buoyancy differ? + +GENERAL TODO LIST: +================================================= +Resitution of a prim works on another prim but not on terrain. + The dropped prim doesn't bounce properly on the terrain. +Add a sanity check for PIDTarget location. +Level-of-detail for mesh creation. Prims with circular interiors require lod of 32. + Is much saved with lower LODs? At the moment, all set to 32. +Collisions are inconsistant: arrows are supposed to hit and report collision. Often don't. + If arrow show at prim, collision reported about 1/3 of time. If collision reported, + both arrow and prim report it. The arrow bounces off the prim 9 out of 10 times. + Shooting 5m sphere "arrows" at 60m/s. +llMoveToTarget objects are not effected by gravity until target is removed. +Compute CCD parameters based on body size +Can solver iterations be changed per body/shape? Can be for constraints but what + about regular vehicles? +Implement llSetPhysicalMaterial. + extend it with Center-of-mass, rolling friction, density +Implement llSetForceAndTorque. +Change BSPrim.moveToTarget to used forces rather than changing position + Changing position allows one to move through walls +Implement an avatar mesh shape. The Bullet capsule is way too limited. + Consider just hand creating a vertex/index array in a new BSShapeAvatar. +Verify/fix phantom, volume-detect objects do not fall to infinity. Should stop at terrain. +Revisit CollisionMargin. Builders notice the 0.04 spacing between prims. +Duplicating a physical prim causes old prim to jump away + Dup a phys prim and the original become unselected and thus interacts w/ selected prim. +Scenes with hundred of thousands of static objects take a lot of physics CPU time. +Gun sending shooter flying. +Collision margin (gap between physical objects lying on each other) +Boundry checking (crashes related to crossing boundry) + Add check for border edge position for avatars and objects. + Verify the events are created for border crossings. +Implement ShapeCollection.Dispose() +Implement water as a plain or mesh so raycasting and collisions can happen with same. +Add collision penetration return + Add field passed back by BulletSim.dll and fill with info in ManifoldConstact.GetDistance() +Linkset.Position and Linkset.Orientation requre rewrite to properly return + child position. LinksetConstraint acts like it's at taint time!! +Implement LockAngularMotion -- implements llSetStatus(ROTATE_AXIS_*, T/F) +Should the different PID factors have non-equal contributions for different + values of Efficiency? +Selecting and deselecting physical objects causes CPU processing time to jump + http://www.youtube.com/watch?v=Hjg57fWg8yI&hd=1 + put thousand physical objects, select and deselect same. CPU time will be large. +Re-implement buoyancy as a separate force on the object rather than diddling gravity. + Register a pre-step event to add the force. +More efficient memory usage when passing hull information from BSPrim to BulletSim +Physical and phantom will drop through the terrain + + +LINKSETS +====================================================== +Child prims do not report collisions +Allow children of a linkset to be phantom: + http://opensim-dev.2196679.n2.nabble.com/Setting-a-single-child-prim-to-Phantom-tp7578513.html + Add OS_STATUS_PHANTOM_PRIM to llSetLinkPrimitaveParamsFast. +Editing a child of a linkset causes the child to go phantom + Move a child prim once when it is physical and can never move it again without it going phantom +Offset the center of the linkset to be the geometric center of all the prims + Not quite the same as the center-of-gravity +Linksets should allow collisions to individual children + Add LocalID to children shapes in LinksetCompound and create events for individuals +LinksetCompound: when one of the children changes orientation (like tires + turning on a vehicle, the whole compound object is rebuilt. Optimize this + so orientation/position of individual children can change without a rebuild. +Verify/think through scripts in children of linksets. What do they reference + and return when getting position, velocity, ... +Confirm constraint linksets still work after making all the changes for compound linksets. +Use PostTaint callback to do rebuilds for constraint linksets to reduce rebuilding +Add 'changed' flag or similar to reduce the number of times a linkset is rebuilt. + For compound linksets, add ability to remove or reposition individual child shapes. +Speed up creation of large physical linksets + For instance, sitting in Neb's car (130 prims) takes several seconds to become physical. + REALLY bad for very large physical linksets (freezes the sim for many seconds). +Eliminate collisions between objects in a linkset. (LinksetConstraint) + Have UserPointer point to struct with localID and linksetID? + Objects in original linkset still collide with each other? + +MORE +====================================================== +Compute avatar size and scale correctly. Now it is a bit off from the capsule size. +Create tests for different interface components + Have test objects/scripts measure themselves and turn color if correct/bad + Test functions in SL and calibrate correctness there + Create auto rezzer and tracker to run through the tests +Do we need to do convex hulls all the time? Can complex meshes be left meshes? + There is some problem with meshes and collisions + Hulls are not as detailed as meshes. Hulled vehicles insides are different shape. +Debounce avatar contact so legs don't keep folding up when standing. +Add border extensions to terrain to help region crossings and objects leaving region. +Use a different capsule shape for avatar when sitting + LL uses a pyrimidal shape scaled by the avatar's bounding box + http://wiki.secondlife.com/wiki/File:Avmeshforms.png +Performance test with lots of avatars. Can BulletSim support a thousand? +Optimize collisions in C++: only send up to the object subscribed to collisions. + Use collision subscription and remove the collsion(A,B) and collision(B,A) +Check whether SimMotionState needs large if statement (see TODO). +Implement 'top colliders' info. +Avatar jump +Performance measurement and changes to make quicker. +Implement detailed physics stats (GetStats()). +Measure performance improvement from hulls +Test not using ghost objects for volume detect implementation. +Performance of closures and delegates for taint processing + Are there faster ways? + Is any slowdown introduced by the existing implementation significant? +Is there are more efficient method of implementing pre and post step actions? + See http://www.codeproject.com/Articles/29922/Weak-Events-in-C +Physics Arena central pyramid: why is one side permiable? +In SL, perfect spheres don't seem to have rolling friction. Add special case. +Enforce physical parameter min/max: + Gravity: [-1, 28] + Friction: [0, 255] + Density: [1, 22587] + Restitution [0, 1] + http://wiki.secondlife.com/wiki/Physics_Material_Settings_test +Avatar attachments have no mass? http://forums-archive.secondlife.com/54/f0/31796/1.html +Keep avatar scaling correct. http://pennycow.blogspot.fr/2011/07/matter-of-scale.html + +INTERNAL IMPROVEMENT/CLEANUP +================================================= +Create the physical wrapper classes (BulletBody, BulletShape) by methods on + BSAPITemplate and make their actual implementation Bullet engine specific. + For the short term, just call the existing functions in ShapeCollection. +Consider moving prim/character body and shape destruction in destroy() + to postTimeTime rather than protecting all the potential sets that + might have been queued up. +Remove unused fields from ShapeData (not used in API2) +Remove unused fields from pinned memory shared parameter block + Create parameter variables in BSScene to replace same. +Breakout code for mesh/hull/compound/native into separate BSShape* classes + Standardize access to building and reference code. + The skeleton classes are in the sources but are not complete or linked in. +Make BSBody and BSShape real classes to centralize creation/changin/destruction + Convert state and parameter calls from BulletSimAPI direct calls to + calls on BSBody and BSShape +Generalize Dynamics and PID with standardized motors. +Generalize Linkset and vehicles into PropertyManagers + Methods for Refresh, RemoveBodyDependencies, RestoreBodyDependencies + Potentially add events for shape destruction, etc. +Better mechanism for resetting linkset set and vehicle parameters when body rebuilt. + BSPrim.CreateGeomAndObject is kludgy with the callbacks, etc. +Implement linkset by setting position of children when root updated. (LinksetManual) + Linkset implementation using manual prim movement. +LinkablePrim class? Would that simplify/centralize the linkset logic? +BSScene.UpdateParameterSet() is broken. How to set params on objects? +Add floating motor for BS_FLOATS_ON_WATER so prim and avatar will + bob at the water level. BSPrim.PositionSanityCheck() +Should taints check for existance or activeness of target? + When destroying linksets/etc, taints can be generated for objects that are + actually gone when the taint happens. Crashes don't happen because the taint closure + keeps the object from being freed, but that is just an accident. + Possibly have an 'active' flag that is checked by the taint processor? +Parameters for physics logging should be moved from BSScene to BSParam (at least boolean ones) +Can some of the physical wrapper classes (BulletBody, BulletWorld, BulletShape) be 'sealed'? +There are TOO MANY interfaces from BulletSim core to Bullet itself + Think of something to eliminate one or more of the layers + +THREADING +================================================= +Do taint action immediately if not actually executing Bullet. + Add lock around Bullet execution and just do taint actions if simulation is not happening. + +DONE DONE DONE DONE +================================================= +Cleanup code in BSDynamics by using motors. (Resolution: started) +Consider implementing terrain with a mesh rather than heightmap. (Resolution: done) + Would have better and adjustable resolution. +Build terrain mesh so heighmap is height of the center of the square meter. + Resolution: NOT DONE: SL and ODE define meter square as being at one corner with one diagional. +Terrain as mesh. (Resolution: done) +How are static linksets seen by the physics engine? + Resolution: they are not linked in physics. When moved, all the children are repositioned. +Convert BSCharacter to use all API2 (Resolution: done) +Avatar pushing difficult (too heavy?) +Use asset service passed to BulletSim to get sculptie bodies, etc. (Resolution: done) +Remove old code in DLL (all non-API2 stuff). (Resolution: done) +Measurements of mega-physical prim performance (with graph) (Resolution: done, email) +Debug Bullet internal stats output (why is timing all wrong?) + Resolution: Bullet stats logging only works with a single instance of Bullet (one region). +Implement meshes or just verify that they work. (Resolution: they do!) +Do prim hash codes work for sculpties and meshes? (Resolution: yes) +Linkset implementation using compound shapes. (Resolution: implemented LinksetCompound) + Compound shapes will need the LocalID in the shapes and collision + processing to get it from there. +Light cycle not banking (Resolution: It doesn't. Banking is roll adding yaw.) +Package Bullet source mods for Bullet internal stats output + (Resolution: move code into WorldData.h rather than relying on patches) +Single prim vehicles don't seem to properly vehiclize. + (Resolution: mass was not getting set properly for single prim linksets) +Add material type linkage and input all the material property definitions. + Skeleton classes and table are in the sources but are not filled or used. + (Resolution: +Neb vehicle taking > 25ms of physics time!! + (Resolution: compound linksets were being rebuild WAY too often) +Avatar height off after unsitting (floats off ground) + Editting appearance then moving restores. + Must not be initializing height when recreating capsule after unsit. + (Resolution: confusion of scale vs size for native objects removed) +Light cycle falling over when driving (Resolution: implemented angularMotorUp) +Should vehicle angular/linear movement friction happen after all the components + or does it only apply to the basic movement? + (Resolution: friction added before returning newly computed motor value. + What is expected by some vehicles (turning up friction to moderate speed)) +Tune terrain/object friction to be closer to SL. + (Resolution: added material type with friction and resolution) +Smooth avatar movement with motor (DONE) + Should motor update be all at taint-time? (Yes, DONE) + Fix avatar slowly sliding when standing (zero motion when stopped) (DONE) + (Resolution: added BSVMotor for avatar starting and stopping) +llApplyImpulse() + Compare mass/movement in OS and SL. Calibrate actions. (DONE) + (Resolution: tested on SL and OS. AddForce scales the force for timestep) +llSetBuoyancy() (DONE) + (Resolution: Bullet resets object gravity when added to world. Moved set gravity) +Avatar density is WAY off. Compare and calibrate with what's in SL. (DONE) + (Resolution: set default density to 3.5 (from 60) which is closer to SL) +Redo BulletSimAPI to allow native C# implementation of Bullet option (DONE) + (Resolution: added BSAPITemplate and then interfaces for C++ Bullet and C# BulletXNA +Meshes rendering as bounding boxes (DONE) + (Resolution: Added test for mesh/sculpties in native shapes so it didn't think it was a box) +llMoveToTarget (Resolution: added simple motor to update the position.) +Angular motor direction is global coordinates rather than local coordinates (DONE) +Add vehicle collisions so IsColliding is properly reported. (DONE) + Needed for banking, limitMotorUp, movementLimiting, ... + (Resolution: added CollisionFlags.BS_VEHICLE_COLLISION and code to use it) +VehicleAddForce is not scaled by the simulation step but it is only + applied for one step. Should it be scaled? (DONE) + (Resolution: use force for timed things, Impulse for immediate, non-timed things) +Complete implemention of preStepActions (DONE) + Replace vehicle step call with prestep event. + Is there a need for postStepActions? postStepTaints? +Disable activity of passive linkset children. (DONE) + Since the linkset is a compound object, the old prims are left lying + around and need to be phantomized so they don't collide, ... +Remove HeightmapInfo from terrain specification (DONE) + Since C++ code does not need terrain height, this structure et al are not needed. +Surfboard go wonky when turning (DONE) + Angular motor direction is global coordinates rather than local coordinates? + (Resolution: made angular motor direction correct coordinate system) +Mantis 6040 script http://opensimulator.org/mantis/view.php?id=6040 (DONE) + Msg Kayaker on OSGrid when working + (Resolution: LINEAR_DIRECTION is in vehicle coords. Test script does the + same in SL as in OS/BulletSim) +Boats float low in the water (DONE) +Boats floating at proper level (DONE) +When is force introduced by SetForce removed? The prestep action could go forever. (DONE) + (Resolution: setForce registers a prestep action which keeps applying the force) +Child movement in linkset (don't rebuild linkset) (DONE 20130122)) +Avatar standing on a moving object should start to move with the object. (DONE 20130125) +Angular motion around Z moves the vehicle in world Z and not vehicle Z in ODE. + Verify that angular motion specified around Z moves in the vehicle coordinates. + DONE 20130120: BulletSim properly applies force in vehicle relative coordinates. +Nebadon vehicles turning funny in arena (DONE) +Lock axis (DONE 20130401) +Terrain detail: double terrain mesh detail (DONE) +Use the HACD convex hull routine in Bullet rather than the C# version. + Speed up hullifying large meshes. (DONE) +Vehicle ride, get up, ride again. Second time vehicle does not act correctly. + Have to rez new vehicle and delete the old to fix situation. + (DONE 20130520: normalize rotations) +Hitting RESET on Nebadon's vehicle while riding causes vehicle to get into odd + position state where it will not settle onto ground properly, etc + (DONE 20130520: normalize rotations) +Two of Nebadon vehicles in a sim max the CPU. This is new. + (DONE 20130520: two problems: if asset failed to mesh, constantly refetched + asset; vehicle was sending too many messages to all linkset members) +Add material densities to the material types. (WILL NOT BE DONE: not how it is done) +Avatars walking up stairs (DONE) +Avatar movement + flying into a wall doesn't stop avatar who keeps appearing to move through the obstacle (DONE) + walking up stairs is not calibrated correctly (stairs out of Kepler cabin) (DONE) + avatar capsule rotation completed (NOT DONE - Bullet's capsule shape is not the solution) +After getting off a vehicle, the root prim is phantom (can be walked through) + Need to force a position update for the root prim after compound shape destruction + (DONE) +Explore btGImpactMeshShape as alternative to convex hulls for simplified physical objects. + Regular triangle meshes don't do physical collisions. + (DONE: discovered GImpact is VERY CPU intensive) +Script changing rotation of child prim while vehicle moving (eg turning wheel) causes + the wheel to appear to jump back. Looks like sending position from previous update. + (DONE: redo of compound linksets fixed problem) +Refarb compound linkset creation to create a pseudo-root for center-of-mass + Let children change their shape to physical indendently and just add shapes to compound + (DONE: redo of compound linkset fixed problem) +Vehicle angular vertical attraction (DONE: vegaslon code) +vehicle angular banking (DONE: vegaslon code) +Vehicle angular deflection (DONE: vegaslon code) + Preferred orientation angular correction fix +Vehicles (Move smoothly) +For limitMotorUp, use raycast down to find if vehicle is in the air. + (WILL NOT BE DONE: gravity does the job well enough) +BSPrim.Force should set a continious force on the prim. The force should be + applied each tick. Some limits? + (DONE: added physical actors. Implemented SetForce, SetTorque, ...) +Implement LSL physics controls. Like STATUS_ROTATE_X. (DONE) +Add osGetPhysicsEngineName() so scripters can tell whether BulletSim or ODE +Avatar rotation (check out changes to ScenePresence for physical rotation) (DONE) +Avatar running (what does phys engine need to do?) (DONE: multiplies run factor by walking force) +setForce should set a constant force. Different than AddImpulse. (DONE) +Add PID motor for avatar movement (slow to stop, ...) (WNBD: current works ok) +Avatar movement motor check for zero or small movement. Somehow suppress small movements + when avatar has stopped and is just standing. Simple test for near zero has + the problem of preventing starting up (increase from zero) especially when falling. + (DONE: avatar movement actor knows if standing on stationary object and zeros motion) +Can the 'inTaintTime' flag be cleaned up and used? For instance, a call to + BSScene.TaintedObject() could immediately execute the callback if already in taint time. + (DONE) + + + diff --git a/OpenSim/Region/PhysicsModules/BulletS/Properties/AssemblyInfo.cs b/OpenSim/Region/PhysicsModules/BulletS/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4f90eee --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenSim.Region.Physics.BulletSPlugin")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("http://opensimulator.org")] +[assembly: AssemblyProduct("OpenSim")] +[assembly: AssemblyCopyright("OpenSimulator developers")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("520ea11b-20cb-449d-ba05-c01015fed841")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs new file mode 100755 index 0000000..48e74eb --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs @@ -0,0 +1,156 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using NUnit.Framework; +using log4net; + +using OpenSim.Framework; +using OpenSim.Region.Physics.BulletSPlugin; +using OpenSim.Region.Physics.Manager; +using OpenSim.Tests.Common; + +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin.Tests +{ +[TestFixture] +public class BasicVehicles : OpenSimTestCase +{ + // Documentation on attributes: http://www.nunit.org/index.php?p=attributes&r=2.6.1 + // Documentation on assertions: http://www.nunit.org/index.php?p=assertions&r=2.6.1 + + BSScene PhysicsScene { get; set; } + BSPrim TestVehicle { get; set; } + Vector3 TestVehicleInitPosition { get; set; } + float simulationTimeStep = 0.089f; + + [TestFixtureSetUp] + public void Init() + { + Dictionary engineParams = new Dictionary(); + engineParams.Add("VehicleEnableAngularVerticalAttraction", "true"); + engineParams.Add("VehicleAngularVerticalAttractionAlgorithm", "1"); + PhysicsScene = BulletSimTestsUtil.CreateBasicPhysicsEngine(engineParams); + + PrimitiveBaseShape pbs = PrimitiveBaseShape.CreateSphere(); + Vector3 pos = new Vector3(100.0f, 100.0f, 0f); + pos.Z = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 2f; + TestVehicleInitPosition = pos; + Vector3 size = new Vector3(1f, 1f, 1f); + pbs.Scale = size; + Quaternion rot = Quaternion.Identity; + bool isPhys = false; + uint localID = 123; + + PhysicsScene.AddPrimShape("testPrim", pbs, pos, size, rot, isPhys, localID); + TestVehicle = (BSPrim)PhysicsScene.PhysObjects[localID]; + // The actual prim shape creation happens at taint time + PhysicsScene.ProcessTaints(); + + } + + [TestFixtureTearDown] + public void TearDown() + { + if (PhysicsScene != null) + { + // The Dispose() will also free any physical objects in the scene + PhysicsScene.Dispose(); + PhysicsScene = null; + } + } + + [TestCase(2f, 0.2f, 0.25f, 0.25f, 0.25f)] + [TestCase(2f, 0.2f, -0.25f, 0.25f, 0.25f)] + [TestCase(2f, 0.2f, 0.25f, -0.25f, 0.25f)] + [TestCase(2f, 0.2f, -0.25f, -0.25f, 0.25f)] + // [TestCase(2f, 0.2f, 0.785f, 0.0f, 0.25f) /*, "Leaning 45 degrees to the side" */] + // [TestCase(2f, 0.2f, 1.650f, 0.0f, 0.25f) /*, "Leaning more than 90 degrees to the side" */] + // [TestCase(2f, 0.2f, 2.750f, 0.0f, 0.25f) /*, "Almost upside down, tipped right" */] + // [TestCase(2f, 0.2f,-2.750f, 0.0f, 0.25f) /*, "Almost upside down, tipped left" */] + // [TestCase(2f, 0.2f, 0.0f, 0.785f, 0.25f) /*, "Tipped back 45 degrees" */] + // [TestCase(2f, 0.2f, 0.0f, 1.650f, 0.25f) /*, "Tipped back more than 90 degrees" */] + // [TestCase(2f, 0.2f, 0.0f, 2.750f, 0.25f) /*, "Almost upside down, tipped back" */] + // [TestCase(2f, 0.2f, 0.0f,-2.750f, 0.25f) /*, "Almost upside down, tipped forward" */] + public void AngularVerticalAttraction(float timeScale, float efficiency, float initRoll, float initPitch, float initYaw) + { + // Enough simulation steps to cover the timescale the operation should take + int simSteps = (int)(timeScale / simulationTimeStep) + 1; + + // Tip the vehicle + Quaternion initOrientation = Quaternion.CreateFromEulers(initRoll, initPitch, initYaw); + TestVehicle.Orientation = initOrientation; + + TestVehicle.Position = TestVehicleInitPosition; + + // The vehicle controller is not enabled directly (by setting a vehicle type). + // Instead the appropriate values are set and calls are made just the parts of the + // controller we want to exercise. Stepping the physics engine then applies + // the actions of that one feature. + BSDynamics vehicleActor = TestVehicle.GetVehicleActor(true /* createIfNone */); + if (vehicleActor != null) + { + vehicleActor.ProcessFloatVehicleParam(Vehicle.VERTICAL_ATTRACTION_EFFICIENCY, efficiency); + vehicleActor.ProcessFloatVehicleParam(Vehicle.VERTICAL_ATTRACTION_TIMESCALE, timeScale); + // vehicleActor.enableAngularVerticalAttraction = true; + + TestVehicle.IsPhysical = true; + PhysicsScene.ProcessTaints(); + + // Step the simulator a bunch of times and vertical attraction should orient the vehicle up + for (int ii = 0; ii < simSteps; ii++) + { + vehicleActor.ForgetKnownVehicleProperties(); + vehicleActor.ComputeAngularVerticalAttraction(); + vehicleActor.PushKnownChanged(); + + PhysicsScene.Simulate(simulationTimeStep); + } + } + + TestVehicle.IsPhysical = false; + PhysicsScene.ProcessTaints(); + + // After these steps, the vehicle should be upright + /* + float finalRoll, finalPitch, finalYaw; + TestVehicle.Orientation.GetEulerAngles(out finalRoll, out finalPitch, out finalYaw); + Assert.That(finalRoll, Is.InRange(-0.01f, 0.01f)); + Assert.That(finalPitch, Is.InRange(-0.01f, 0.01f)); + Assert.That(finalYaw, Is.InRange(initYaw - 0.1f, initYaw + 0.1f)); + */ + + Vector3 upPointer = Vector3.UnitZ * TestVehicle.Orientation; + Assert.That(upPointer.Z, Is.GreaterThan(0.99f)); + } +} +} \ No newline at end of file diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTests.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTests.cs new file mode 100755 index 0000000..35cbc1d --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTests.cs @@ -0,0 +1,56 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using NUnit.Framework; +using log4net; + +using OpenSim.Tests.Common; + +namespace OpenSim.Region.Physics.BulletSPlugin.Tests +{ +[TestFixture] +public class BulletSimTests : OpenSimTestCase +{ + // Documentation on attributes: http://www.nunit.org/index.php?p=attributes&r=2.6.1 + // Documentation on assertions: http://www.nunit.org/index.php?p=assertions&r=2.6.1 + + [TestFixtureSetUp] + public void Init() + { + } + + [TestFixtureTearDown] + public void TearDown() + { + } +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs new file mode 100755 index 0000000..775bca2 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs @@ -0,0 +1,100 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Text; + +using Nini.Config; + +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Physics.Meshing; + +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin.Tests +{ +// Utility functions for building up and tearing down the sample physics environments +public static class BulletSimTestsUtil +{ + // 'engineName' is the Bullet engine to use. Either null (for unmanaged), "BulletUnmanaged" or "BulletXNA" + // 'params' is a set of keyValue pairs to set in the engine's configuration file (override defaults) + // May be 'null' if there are no overrides. + public static BSScene CreateBasicPhysicsEngine(Dictionary paramOverrides) + { + IConfigSource openSimINI = new IniConfigSource(); + IConfig startupConfig = openSimINI.AddConfig("Startup"); + startupConfig.Set("physics", "BulletSim"); + startupConfig.Set("meshing", "Meshmerizer"); + startupConfig.Set("cacheSculptMaps", "false"); // meshmerizer shouldn't save maps + + IConfig bulletSimConfig = openSimINI.AddConfig("BulletSim"); + // If the caller cares, specify the bullet engine otherwise it will default to "BulletUnmanaged". + // bulletSimConfig.Set("BulletEngine", "BulletUnmanaged"); + // bulletSimConfig.Set("BulletEngine", "BulletXNA"); + bulletSimConfig.Set("MeshSculptedPrim", "false"); + bulletSimConfig.Set("ForceSimplePrimMeshing", "true"); + if (paramOverrides != null) + { + foreach (KeyValuePair kvp in paramOverrides) + { + bulletSimConfig.Set(kvp.Key, kvp.Value); + } + } + + // If a special directory exists, put detailed logging therein. + // This allows local testing/debugging without having to worry that the build engine will output logs. + if (Directory.Exists("physlogs")) + { + bulletSimConfig.Set("PhysicsLoggingDir","./physlogs"); + bulletSimConfig.Set("PhysicsLoggingEnabled","True"); + bulletSimConfig.Set("PhysicsLoggingDoFlush","True"); + bulletSimConfig.Set("VehicleLoggingEnabled","True"); + } + + PhysicsPluginManager physicsPluginManager; + physicsPluginManager = new PhysicsPluginManager(); + physicsPluginManager.LoadPluginsFromAssemblies("Physics"); + + Vector3 regionExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); + + PhysicsScene pScene = physicsPluginManager.GetPhysicsScene( + "BulletSim", "Meshmerizer", openSimINI, "BSTestRegion", regionExtent); + + BSScene bsScene = pScene as BSScene; + + // Since the asset requestor is not initialized, any mesh or sculptie will be a cube. + // In the future, add a fake asset fetcher to get meshes and sculpts. + // bsScene.RequestAssetMethod = ???; + + return bsScene; + } + +} +} diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs new file mode 100644 index 0000000..5a5de11 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs @@ -0,0 +1,205 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using NUnit.Framework; +using log4net; + +using OpenSim.Framework; +using OpenSim.Region.Physics.BulletSPlugin; +using OpenSim.Region.Physics.Manager; +using OpenSim.Tests.Common; + +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin.Tests +{ +[TestFixture] +public class HullCreation : OpenSimTestCase +{ + // Documentation on attributes: http://www.nunit.org/index.php?p=attributes&r=2.6.1 + // Documentation on assertions: http://www.nunit.org/index.php?p=assertions&r=2.6.1 + + BSScene PhysicsScene { get; set; } + Vector3 ObjectInitPosition; + + [TestFixtureSetUp] + public void Init() + { + + } + + [TestFixtureTearDown] + public void TearDown() + { + if (PhysicsScene != null) + { + // The Dispose() will also free any physical objects in the scene + PhysicsScene.Dispose(); + PhysicsScene = null; + } + } + + [TestCase(7, 2, 5f, 5f, 32, 0f)] /* default hull parameters */ + public void GeomHullConvexDecomp( int maxDepthSplit, + int maxDepthSplitForSimpleShapes, + float concavityThresholdPercent, + float volumeConservationThresholdPercent, + int maxVertices, + float maxSkinWidth) + { + // Setup the physics engine to use the C# version of convex decomp + Dictionary engineParams = new Dictionary(); + engineParams.Add("MeshSculptedPrim", "true"); // ShouldMeshSculptedPrim + engineParams.Add("ForceSimplePrimMeshing", "false"); // ShouldForceSimplePrimMeshing + engineParams.Add("UseHullsForPhysicalObjects", "true"); // ShouldUseHullsForPhysicalObjects + engineParams.Add("ShouldRemoveZeroWidthTriangles", "true"); + engineParams.Add("ShouldUseBulletHACD", "false"); + engineParams.Add("ShouldUseSingleConvexHullForPrims", "true"); + engineParams.Add("ShouldUseGImpactShapeForPrims", "false"); + engineParams.Add("ShouldUseAssetHulls", "true"); + + engineParams.Add("CSHullMaxDepthSplit", maxDepthSplit.ToString()); + engineParams.Add("CSHullMaxDepthSplitForSimpleShapes", maxDepthSplitForSimpleShapes.ToString()); + engineParams.Add("CSHullConcavityThresholdPercent", concavityThresholdPercent.ToString()); + engineParams.Add("CSHullVolumeConservationThresholdPercent", volumeConservationThresholdPercent.ToString()); + engineParams.Add("CSHullMaxVertices", maxVertices.ToString()); + engineParams.Add("CSHullMaxSkinWidth", maxSkinWidth.ToString()); + + PhysicsScene = BulletSimTestsUtil.CreateBasicPhysicsEngine(engineParams); + + PrimitiveBaseShape pbs; + Vector3 pos; + Vector3 size; + Quaternion rot; + bool isPhys; + + // Cylinder + pbs = PrimitiveBaseShape.CreateCylinder(); + pos = new Vector3(100.0f, 100.0f, 0f); + pos.Z = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 10f; + ObjectInitPosition = pos; + size = new Vector3(2f, 2f, 2f); + pbs.Scale = size; + rot = Quaternion.Identity; + isPhys = true; + uint cylinderLocalID = 123; + PhysicsScene.AddPrimShape("testCylinder", pbs, pos, size, rot, isPhys, cylinderLocalID); + BSPrim primTypeCylinder = (BSPrim)PhysicsScene.PhysObjects[cylinderLocalID]; + + // Hollow Cylinder + pbs = PrimitiveBaseShape.CreateCylinder(); + pbs.ProfileHollow = (ushort)(0.70f * 50000); + pos = new Vector3(110.0f, 110.0f, 0f); + pos.Z = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 10f; + ObjectInitPosition = pos; + size = new Vector3(2f, 2f, 2f); + pbs.Scale = size; + rot = Quaternion.Identity; + isPhys = true; + uint hollowCylinderLocalID = 124; + PhysicsScene.AddPrimShape("testHollowCylinder", pbs, pos, size, rot, isPhys, hollowCylinderLocalID); + BSPrim primTypeHollowCylinder = (BSPrim)PhysicsScene.PhysObjects[hollowCylinderLocalID]; + + // Torus + // ProfileCurve = Circle, PathCurve = Curve1 + pbs = PrimitiveBaseShape.CreateSphere(); + pbs.ProfileShape = (byte)ProfileShape.Circle; + pbs.PathCurve = (byte)Extrusion.Curve1; + pbs.PathScaleX = 100; // default hollow info as set in the viewer + pbs.PathScaleY = (int)(.25f / 0.01f) + 200; + pos = new Vector3(120.0f, 120.0f, 0f); + pos.Z = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 10f; + ObjectInitPosition = pos; + size = new Vector3(2f, 4f, 4f); + pbs.Scale = size; + rot = Quaternion.Identity; + isPhys = true; + uint torusLocalID = 125; + PhysicsScene.AddPrimShape("testTorus", pbs, pos, size, rot, isPhys, torusLocalID); + BSPrim primTypeTorus = (BSPrim)PhysicsScene.PhysObjects[torusLocalID]; + + // The actual prim shape creation happens at taint time + PhysicsScene.ProcessTaints(); + + // Check out the created hull shapes and report their characteristics + ReportShapeGeom(primTypeCylinder); + ReportShapeGeom(primTypeHollowCylinder); + ReportShapeGeom(primTypeTorus); + } + + [TestCase] + public void GeomHullBulletHACD() + { + // Cylinder + // Hollow Cylinder + // Torus + } + + private void ReportShapeGeom(BSPrim prim) + { + if (prim != null) + { + if (prim.PhysShape.HasPhysicalShape) + { + BSShape physShape = prim.PhysShape; + string shapeType = physShape.GetType().ToString(); + switch (shapeType) + { + case "OpenSim.Region.Physics.BulletSPlugin.BSShapeNative": + BSShapeNative nShape = physShape as BSShapeNative; + prim.PhysScene.DetailLog("{0}, type={1}", prim.Name, shapeType); + break; + case "OpenSim.Region.Physics.BulletSPlugin.BSShapeMesh": + BSShapeMesh mShape = physShape as BSShapeMesh; + prim.PhysScene.DetailLog("{0}, mesh, shapeInfo={1}", prim.Name, mShape.shapeInfo); + break; + case "OpenSim.Region.Physics.BulletSPlugin.BSShapeHull": + // BSShapeHull hShape = physShape as BSShapeHull; + // prim.PhysScene.DetailLog("{0}, hull, shapeInfo={1}", prim.Name, hShape.shapeInfo); + break; + case "OpenSim.Region.Physics.BulletSPlugin.BSShapeConvexHull": + BSShapeConvexHull chShape = physShape as BSShapeConvexHull; + prim.PhysScene.DetailLog("{0}, convexHull, shapeInfo={1}", prim.Name, chShape.shapeInfo); + break; + case "OpenSim.Region.Physics.BulletSPlugin.BSShapeCompound": + BSShapeCompound cShape = physShape as BSShapeCompound; + prim.PhysScene.DetailLog("{0}, type={1}", prim.Name, shapeType); + break; + default: + prim.PhysScene.DetailLog("{0}, type={1}", prim.Name, shapeType); + break; + } + } + } + } +} +} \ No newline at end of file -- cgit v1.1 From 2c0cad6dd3014e9ad363090f5f7872c43daffcbd Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 30 Aug 2015 20:29:31 -0700 Subject: Renamed the namespaces too --- OpenSim/Region/PhysicsModules/BulletS/BSAPIUnman.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSAPIXNA.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSActorLockAxis.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSActors.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSConstraint.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSConstraint6Dof.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSConstraintCollection.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSConstraintConeTwist.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSConstraintHinge.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSConstraintSlider.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSConstraintSpring.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSLinkset.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSLinksetCompound.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSMaterials.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSMotors.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSParam.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs | 6 +++--- OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSScene.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs | 6 +++--- OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs | 8 ++++---- OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs | 4 ++-- OpenSim/Region/PhysicsModules/BulletS/BulletSimData.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs | 6 +++--- OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTests.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs | 6 +++--- OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs | 6 +++--- 41 files changed, 70 insertions(+), 70 deletions(-) (limited to 'OpenSim/Region/PhysicsModules/BulletS') diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSAPIUnman.cs b/OpenSim/Region/PhysicsModules/BulletS/BSAPIUnman.cs index 3bd81d4..c4a923c 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSAPIUnman.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSAPIUnman.cs @@ -35,7 +35,7 @@ using OpenSim.Framework; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSAPIUnman : BSAPITemplate { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSAPIXNA.cs b/OpenSim/Region/PhysicsModules/BulletS/BSAPIXNA.cs index 741f8db..887311d 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSAPIXNA.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSAPIXNA.cs @@ -40,7 +40,7 @@ using BulletXNA.BulletCollision; using BulletXNA.BulletDynamics; using BulletXNA.BulletCollision.CollisionDispatch; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSAPIXNA : BSAPITemplate { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs index bde4557..f1a06cc 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs @@ -31,11 +31,11 @@ using System.Linq; using System.Text; using OpenSim.Framework; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public class BSActorAvatarMove : BSActor { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs index e54c27b..7fbe920 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs @@ -30,11 +30,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public class BSActorHover : BSActor { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorLockAxis.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorLockAxis.cs index 3b3c161..78c1b6a 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorLockAxis.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorLockAxis.cs @@ -32,7 +32,7 @@ using System.Text; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public class BSActorLockAxis : BSActor { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs index 1145006..6a886b0 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs @@ -30,11 +30,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public class BSActorMoveToTarget : BSActor { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs index 4e81363..e81452a 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs @@ -30,11 +30,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public class BSActorSetForce : BSActor { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs index 79e1d38..7eeb533 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs @@ -30,11 +30,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public class BSActorSetTorque : BSActor { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActors.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActors.cs index 7f45e2c..851347b 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActors.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActors.cs @@ -28,7 +28,7 @@ using System; using System.Collections.Generic; using System.Text; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public class BSActorCollection { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs b/OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs index 8491c0f..7756b10 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSApiTemplate.cs @@ -31,7 +31,7 @@ using System.Security; using System.Text; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin { +namespace OpenSim.Region.PhysicsModule.BulletS { // Constraint type values as defined by Bullet public enum ConstraintType : int diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs index 9c3f160..d6b8551 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs @@ -30,9 +30,9 @@ using System.Reflection; using log4net; using OMV = OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSCharacter : BSPhysObject { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraint.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraint.cs index b47e9a8..e42e868 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSConstraint.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraint.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Text; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public abstract class BSConstraint : IDisposable diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraint6Dof.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraint6Dof.cs index 7fcb75c..4bcde2b 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSConstraint6Dof.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraint6Dof.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Text; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public class BSConstraint6Dof : BSConstraint diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintCollection.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintCollection.cs index 5c8d94e..5746ac1 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintCollection.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintCollection.cs @@ -30,7 +30,7 @@ using System.Text; using log4net; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSConstraintCollection : IDisposable diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintConeTwist.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintConeTwist.cs index 7a76a9a..e7566a8 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintConeTwist.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintConeTwist.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Text; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSConstraintConeTwist : BSConstraint diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintHinge.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintHinge.cs index ed89f63..d20538d 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintHinge.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintHinge.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Text; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSConstraintHinge : BSConstraint diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSlider.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSlider.cs index 37cfa07..83d42af 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSlider.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSlider.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Text; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSConstraintSlider : BSConstraint diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSpring.cs b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSpring.cs index 8e7ddff..563a1b1 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSpring.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSConstraintSpring.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Text; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSConstraintSpring : BSConstraint6Dof diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs b/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs index c6d6331..fcf0dd5 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs @@ -36,9 +36,9 @@ using System.Reflection; using System.Runtime.InteropServices; using OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSDynamics : BSActor { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSLinkset.cs b/OpenSim/Region/PhysicsModules/BulletS/BSLinkset.cs index 87eba33..8312239 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSLinkset.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSLinkset.cs @@ -30,7 +30,7 @@ using System.Text; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public abstract class BSLinkset diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSLinksetCompound.cs b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetCompound.cs index cae9efa..953ddee 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSLinksetCompound.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetCompound.cs @@ -32,7 +32,7 @@ using OpenSim.Framework; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSLinksetCompound : BSLinkset diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs index 4384cdc..d7be470 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs @@ -32,7 +32,7 @@ using OpenSim.Region.OptionalModules.Scripting; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSLinksetConstraints : BSLinkset { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSMaterials.cs b/OpenSim/Region/PhysicsModules/BulletS/BSMaterials.cs index ee77d6e..0e44d03 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSMaterials.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSMaterials.cs @@ -30,7 +30,7 @@ using System.Text; using System.Reflection; using Nini.Config; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public struct MaterialAttributes diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSMotors.cs b/OpenSim/Region/PhysicsModules/BulletS/BSMotors.cs index 7693195..2faf2d4 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSMotors.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSMotors.cs @@ -31,7 +31,7 @@ using System.Text; using OpenMetaverse; using OpenSim.Framework; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public abstract class BSMotor { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs b/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs index 6d46fe6..10bbfb3 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs @@ -29,12 +29,12 @@ using System.Collections.Generic; using System.Reflection; using System.Text; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using OpenMetaverse; using Nini.Config; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public static class BSParam { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs index 90da7a6..4d69f61 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs @@ -30,9 +30,9 @@ using System.Text; using OMV = OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { /* * Class to wrap all objects. diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs index 9442854..e28cd92 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs @@ -27,10 +27,10 @@ using System; using System.Collections.Generic; using OpenSim.Framework; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { /// /// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim. diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs index a00991f..ab6aa5d 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs @@ -32,11 +32,11 @@ using System.Xml; using log4net; using OMV = OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.Physics.Manager; -using OpenSim.Region.Physics.ConvexDecompositionDotNet; +using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModule.ConvexDecompositionDotNet; using OpenSim.Region.OptionalModules.Scripting; // for ExtendedPhysics -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { [Serializable] diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs index 2eb1440..6b234af 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs @@ -31,11 +31,11 @@ using System.Reflection; using System.Runtime.InteropServices; using OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public class BSPrimDisplaced : BSPrim { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs index 430d645..91550cb 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs @@ -34,7 +34,7 @@ using OpenSim.Region.OptionalModules.Scripting; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public class BSPrimLinkable : BSPrimDisplaced { @@ -72,7 +72,7 @@ public class BSPrimLinkable : BSPrimDisplaced base.Destroy(); } - public override void link(Manager.PhysicsActor obj) + public override void link(SharedBase.PhysicsActor obj) { BSPrimLinkable parent = obj as BSPrimLinkable; if (parent != null) diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs index 8a19944..18a4a58 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs @@ -36,12 +36,12 @@ using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework; using OpenSim.Region.CoreModules; using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using Nini.Config; using log4net; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSScene : PhysicsScene, IPhysicsParameters { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs b/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs index d1de844..8dba3e4 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs @@ -29,10 +29,10 @@ using System.Collections.Generic; using System.Text; using OMV = OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.Physics.Manager; -using OpenSim.Region.Physics.ConvexDecompositionDotNet; +using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModule.ConvexDecompositionDotNet; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSShapeCollection : IDisposable { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs index 86d86cb..d8189e0 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs @@ -30,13 +30,13 @@ using System.Collections.Generic; using System.Text; using OpenSim.Framework; -using OpenSim.Region.Physics.Manager; -using OpenSim.Region.Physics.Meshing; -using OpenSim.Region.Physics.ConvexDecompositionDotNet; +using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModule.Meshing; +using OpenSim.Region.PhysicsModule.ConvexDecompositionDotNet; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { // Information class that holds stats for the shape. Which values mean // something depends on the type of shape. diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs index d70b2fb..7704d1f 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs @@ -31,14 +31,14 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.Framework; using OpenSim.Region.CoreModules; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using Nini.Config; using log4net; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSTerrainHeightmap : BSTerrainPhys { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs index 50f917a..6e686ce 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs @@ -31,14 +31,14 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.Framework; using OpenSim.Region.CoreModules; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using Nini.Config; using log4net; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { // The physical implementation of the terrain is wrapped in this class. diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs index e4ca098..4c3e33a 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs @@ -31,14 +31,14 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.Framework; using OpenSim.Region.CoreModules; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.SharedBase; using Nini.Config; using log4net; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { public sealed class BSTerrainMesh : BSTerrainPhys { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BulletSimData.cs b/OpenSim/Region/PhysicsModules/BulletS/BulletSimData.cs index 5932461..3329395 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BulletSimData.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BulletSimData.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Text; using OMV = OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin +namespace OpenSim.Region.PhysicsModule.BulletS { // Classes to allow some type checking for the API // These hold pointers to allocated objects in the unmanaged space. diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs index 48e74eb..160ebb5 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs @@ -34,13 +34,13 @@ using NUnit.Framework; using log4net; using OpenSim.Framework; -using OpenSim.Region.Physics.BulletSPlugin; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.BulletS; +using OpenSim.Region.PhysicsModule.SharedBase; using OpenSim.Tests.Common; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin.Tests +namespace OpenSim.Region.PhysicsModule.BulletS.Tests { [TestFixture] public class BasicVehicles : OpenSimTestCase diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTests.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTests.cs index 35cbc1d..0be1f4c 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTests.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTests.cs @@ -35,7 +35,7 @@ using log4net; using OpenSim.Tests.Common; -namespace OpenSim.Region.Physics.BulletSPlugin.Tests +namespace OpenSim.Region.PhysicsModule.BulletS.Tests { [TestFixture] public class BulletSimTests : OpenSimTestCase diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs index 775bca2..173e20d 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs @@ -33,12 +33,12 @@ using System.Text; using Nini.Config; using OpenSim.Framework; -using OpenSim.Region.Physics.Manager; -using OpenSim.Region.Physics.Meshing; +using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModule.Meshing; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin.Tests +namespace OpenSim.Region.PhysicsModule.BulletS.Tests { // Utility functions for building up and tearing down the sample physics environments public static class BulletSimTestsUtil diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs index 5a5de11..69078f2 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs @@ -34,13 +34,13 @@ using NUnit.Framework; using log4net; using OpenSim.Framework; -using OpenSim.Region.Physics.BulletSPlugin; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModule.BulletS; +using OpenSim.Region.PhysicsModule.SharedBase; using OpenSim.Tests.Common; using OpenMetaverse; -namespace OpenSim.Region.Physics.BulletSPlugin.Tests +namespace OpenSim.Region.PhysicsModule.BulletS.Tests { [TestFixture] public class HullCreation : OpenSimTestCase -- cgit v1.1 From ce2c67876e0ebf4f84420696f660dc77d96dea6b Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sun, 30 Aug 2015 21:05:36 -0700 Subject: More namespace and dll name changes. Still no functional changes. --- OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSParam.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSScene.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) (limited to 'OpenSim/Region/PhysicsModules/BulletS') diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs index f1a06cc..0191893 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs @@ -31,7 +31,7 @@ using System.Linq; using System.Text; using OpenSim.Framework; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OMV = OpenMetaverse; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs index 7fbe920..7ff171e 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorHover.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OMV = OpenMetaverse; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs index 6a886b0..3db8f2c 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorMoveToTarget.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OMV = OpenMetaverse; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs index e81452a..ecb4b7f 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetForce.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OMV = OpenMetaverse; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs index 7eeb533..a1cf4db 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorSetTorque.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OMV = OpenMetaverse; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs index d6b8551..83fc3a6 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs @@ -30,7 +30,7 @@ using System.Reflection; using log4net; using OMV = OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; namespace OpenSim.Region.PhysicsModule.BulletS { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs b/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs index fcf0dd5..0fc5577 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs @@ -36,7 +36,7 @@ using System.Reflection; using System.Runtime.InteropServices; using OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; namespace OpenSim.Region.PhysicsModule.BulletS { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs b/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs index 10bbfb3..c296008 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Reflection; using System.Text; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OpenMetaverse; using Nini.Config; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs index 4d69f61..da3fc18 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPhysObject.cs @@ -30,7 +30,7 @@ using System.Text; using OMV = OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; namespace OpenSim.Region.PhysicsModule.BulletS { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs index e28cd92..f7954d8 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs @@ -27,7 +27,7 @@ using System; using System.Collections.Generic; using OpenSim.Framework; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OpenMetaverse; namespace OpenSim.Region.PhysicsModule.BulletS diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs index ab6aa5d..3e74428 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs @@ -32,7 +32,7 @@ using System.Xml; using log4net; using OMV = OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OpenSim.Region.PhysicsModule.ConvexDecompositionDotNet; using OpenSim.Region.OptionalModules.Scripting; // for ExtendedPhysics diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs index 6b234af..d8ed56b 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrimDisplaced.cs @@ -31,7 +31,7 @@ using System.Reflection; using System.Runtime.InteropServices; using OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OMV = OpenMetaverse; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs index 91550cb..8f494cc 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs @@ -72,7 +72,7 @@ public class BSPrimLinkable : BSPrimDisplaced base.Destroy(); } - public override void link(SharedBase.PhysicsActor obj) + public override void link(OpenSim.Region.PhysicsModules.SharedBase.PhysicsActor obj) { BSPrimLinkable parent = obj as BSPrimLinkable; if (parent != null) diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs index 18a4a58..80f98ef 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs @@ -36,7 +36,7 @@ using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework; using OpenSim.Region.CoreModules; using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using Nini.Config; using log4net; using OpenMetaverse; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs b/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs index 8dba3e4..b100273 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapeCollection.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using System.Text; using OMV = OpenMetaverse; using OpenSim.Framework; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OpenSim.Region.PhysicsModule.ConvexDecompositionDotNet; namespace OpenSim.Region.PhysicsModule.BulletS diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs index d8189e0..7357962 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Text; using OpenSim.Framework; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OpenSim.Region.PhysicsModule.Meshing; using OpenSim.Region.PhysicsModule.ConvexDecompositionDotNet; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs index 7704d1f..e560796 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs @@ -31,7 +31,7 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.Framework; using OpenSim.Region.CoreModules; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using Nini.Config; using log4net; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs index 6e686ce..bd84fce 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs @@ -31,7 +31,7 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.Framework; using OpenSim.Region.CoreModules; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using Nini.Config; using log4net; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs index 4c3e33a..ae96491 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs @@ -31,7 +31,7 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.Framework; using OpenSim.Region.CoreModules; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using Nini.Config; using log4net; diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs index 160ebb5..35eba29 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/BasicVehicles.cs @@ -35,7 +35,7 @@ using log4net; using OpenSim.Framework; using OpenSim.Region.PhysicsModule.BulletS; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OpenSim.Tests.Common; using OpenMetaverse; diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs index 173e20d..8915e6f 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs @@ -33,7 +33,7 @@ using System.Text; using Nini.Config; using OpenSim.Framework; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OpenSim.Region.PhysicsModule.Meshing; using OpenMetaverse; diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs index 69078f2..c0cf19a 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/HullCreation.cs @@ -35,7 +35,7 @@ using log4net; using OpenSim.Framework; using OpenSim.Region.PhysicsModule.BulletS; -using OpenSim.Region.PhysicsModule.SharedBase; +using OpenSim.Region.PhysicsModules.SharedBase; using OpenSim.Tests.Common; using OpenMetaverse; -- cgit v1.1 From 49ab478d286b873e9ff1b4f6748b920abcc0f1b1 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 31 Aug 2015 09:41:00 -0700 Subject: Fixed a namespace dependency. Also started preparing prebuild.xml for making physics region modules. --- OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs | 2 +- OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'OpenSim/Region/PhysicsModules/BulletS') diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs index 7357962..1c55b51 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs @@ -31,7 +31,7 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.PhysicsModules.SharedBase; -using OpenSim.Region.PhysicsModule.Meshing; +using OpenSim.Region.PhysicsModules.Meshing; using OpenSim.Region.PhysicsModule.ConvexDecompositionDotNet; using OMV = OpenMetaverse; diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs index 8915e6f..34c0571 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs @@ -34,7 +34,7 @@ using Nini.Config; using OpenSim.Framework; using OpenSim.Region.PhysicsModules.SharedBase; -using OpenSim.Region.PhysicsModule.Meshing; +using OpenSim.Region.PhysicsModules.Meshing; using OpenMetaverse; -- cgit v1.1 From 134d4300f0b6e9b0df0326cfe0b5df9f41f42865 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 31 Aug 2015 13:02:51 -0700 Subject: All physics plugins are now region modules. Compiles but doesn't run. --- OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs | 54 +- OpenSim/Region/PhysicsModules/BulletS/BSScene.cs | 1995 ++++++++++---------- OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs | 4 +- .../PhysicsModules/BulletS/BSTerrainHeightmap.cs | 1 - .../PhysicsModules/BulletS/BSTerrainManager.cs | 1 - .../Region/PhysicsModules/BulletS/BSTerrainMesh.cs | 1 - .../BulletS/Properties/AssemblyInfo.cs | 3 + 7 files changed, 1052 insertions(+), 1007 deletions(-) (limited to 'OpenSim/Region/PhysicsModules/BulletS') diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs index f7954d8..7fc51f7 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs @@ -40,37 +40,37 @@ namespace OpenSim.Region.PhysicsModule.BulletS /// The unmanaged library is compiled and linked statically with Bullet /// to create BulletSim.dll and libBulletSim.so (for both 32 and 64 bit). /// -public class BSPlugin : IPhysicsPlugin -{ - //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); +//public class BSPlugin : IPhysicsPlugin +//{ +// //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - private BSScene _mScene; +// private BSScene _mScene; - public BSPlugin() - { - } +// public BSPlugin() +// { +// } - public bool Init() - { - return true; - } +// public bool Init() +// { +// return true; +// } - public PhysicsScene GetScene(String sceneIdentifier) - { - if (_mScene == null) - { - _mScene = new BSScene(GetName(), sceneIdentifier); - } - return (_mScene); - } +// public PhysicsScene GetScene(String sceneIdentifier) +// { +// if (_mScene == null) +// { +// _mScene = new BSScene(GetName(), sceneIdentifier); +// } +// return (_mScene); +// } - public string GetName() - { - return ("BulletSim"); - } +// public string GetName() +// { +// return ("BulletSim"); +// } - public void Dispose() - { - } -} +// public void Dispose() +// { +// } +//} } diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs index 80f98ef..26af343 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs @@ -33,1249 +33,1294 @@ using System.Text; using System.Threading; using OpenSim.Framework; using OpenSim.Framework.Monitoring; -using OpenSim.Region.Framework; -using OpenSim.Region.CoreModules; -using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.PhysicsModules.SharedBase; using Nini.Config; using log4net; using OpenMetaverse; +using Mono.Addins; namespace OpenSim.Region.PhysicsModule.BulletS { -public sealed class BSScene : PhysicsScene, IPhysicsParameters -{ - internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - internal static readonly string LogHeader = "[BULLETS SCENE]"; + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "BulletSPhysicsScene")] + public sealed class BSScene : PhysicsScene, IPhysicsParameters, INonSharedRegionModule + { + internal static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + internal static readonly string LogHeader = "[BULLETS SCENE]"; - // The name of the region we're working for. - public string RegionName { get; private set; } + private bool m_Enabled = false; + private IConfigSource m_Config; - public string BulletSimVersion = "?"; + // The name of the region we're working for. + public string RegionName { get; private set; } - // The handle to the underlying managed or unmanaged version of Bullet being used. - public string BulletEngineName { get; private set; } - public BSAPITemplate PE; + public string BulletSimVersion = "?"; - // If the physics engine is running on a separate thread - public Thread m_physicsThread; + // The handle to the underlying managed or unmanaged version of Bullet being used. + public string BulletEngineName { get; private set; } + public BSAPITemplate PE; - public Dictionary PhysObjects; - public BSShapeCollection Shapes; + // If the physics engine is running on a separate thread + public Thread m_physicsThread; - // Keeping track of the objects with collisions so we can report begin and end of a collision - public HashSet ObjectsWithCollisions = new HashSet(); - public HashSet ObjectsWithNoMoreCollisions = new HashSet(); + public Dictionary PhysObjects; + public BSShapeCollection Shapes; - // All the collision processing is protected with this lock object - public Object CollisionLock = new Object(); + // Keeping track of the objects with collisions so we can report begin and end of a collision + public HashSet ObjectsWithCollisions = new HashSet(); + public HashSet ObjectsWithNoMoreCollisions = new HashSet(); - // Properties are updated here - public Object UpdateLock = new Object(); - public HashSet ObjectsWithUpdates = new HashSet(); + // All the collision processing is protected with this lock object + public Object CollisionLock = new Object(); - // Keep track of all the avatars so we can send them a collision event - // every tick so OpenSim will update its animation. - private HashSet AvatarsInScene = new HashSet(); - private Object AvatarsInSceneLock = new Object(); + // Properties are updated here + public Object UpdateLock = new Object(); + public HashSet ObjectsWithUpdates = new HashSet(); - // let my minuions use my logger - public ILog Logger { get { return m_log; } } + // Keep track of all the avatars so we can send them a collision event + // every tick so OpenSim will update its animation. + private HashSet AvatarsInScene = new HashSet(); + private Object AvatarsInSceneLock = new Object(); - public IMesher mesher; - public uint WorldID { get; private set; } - public BulletWorld World { get; private set; } + // let my minuions use my logger + public ILog Logger { get { return m_log; } } - // All the constraints that have been allocated in this instance. - public BSConstraintCollection Constraints { get; private set; } + public IMesher mesher; + public uint WorldID { get; private set; } + public BulletWorld World { get; private set; } - // Simulation parameters - //internal float m_physicsStepTime; // if running independently, the interval simulated by default + // All the constraints that have been allocated in this instance. + public BSConstraintCollection Constraints { get; private set; } - internal int m_maxSubSteps; - internal float m_fixedTimeStep; + // Simulation parameters + //internal float m_physicsStepTime; // if running independently, the interval simulated by default - internal float m_simulatedTime; // the time simulated previously. Used for physics framerate calc. + internal int m_maxSubSteps; + internal float m_fixedTimeStep; - internal long m_simulationStep = 0; // The current simulation step. - public long SimulationStep { get { return m_simulationStep; } } - // A number to use for SimulationStep that is probably not any step value - // Used by the collision code (which remembers the step when a collision happens) to remember not any simulation step. - public static long NotASimulationStep = -1234; + internal float m_simulatedTime; // the time simulated previously. Used for physics framerate calc. - internal float LastTimeStep { get; private set; } // The simulation time from the last invocation of Simulate() + internal long m_simulationStep = 0; // The current simulation step. + public long SimulationStep { get { return m_simulationStep; } } + // A number to use for SimulationStep that is probably not any step value + // Used by the collision code (which remembers the step when a collision happens) to remember not any simulation step. + public static long NotASimulationStep = -1234; - internal float NominalFrameRate { get; set; } // Parameterized ideal frame rate that simulation is scaled to + internal float LastTimeStep { get; private set; } // The simulation time from the last invocation of Simulate() - // Physical objects can register for prestep or poststep events - public delegate void PreStepAction(float timeStep); - public delegate void PostStepAction(float timeStep); - public event PreStepAction BeforeStep; - public event PostStepAction AfterStep; + internal float NominalFrameRate { get; set; } // Parameterized ideal frame rate that simulation is scaled to - // A value of the time 'now' so all the collision and update routines do not have to get their own - // Set to 'now' just before all the prims and actors are called for collisions and updates - public int SimulationNowTime { get; private set; } + // Physical objects can register for prestep or poststep events + public delegate void PreStepAction(float timeStep); + public delegate void PostStepAction(float timeStep); + public event PreStepAction BeforeStep; + public event PostStepAction AfterStep; - // True if initialized and ready to do simulation steps - private bool m_initialized = false; + // A value of the time 'now' so all the collision and update routines do not have to get their own + // Set to 'now' just before all the prims and actors are called for collisions and updates + public int SimulationNowTime { get; private set; } - // Flag which is true when processing taints. - // Not guaranteed to be correct all the time (don't depend on this) but good for debugging. - public bool InTaintTime { get; private set; } + // True if initialized and ready to do simulation steps + private bool m_initialized = false; - // Pinned memory used to pass step information between managed and unmanaged - internal int m_maxCollisionsPerFrame; - internal CollisionDesc[] m_collisionArray; + // Flag which is true when processing taints. + // Not guaranteed to be correct all the time (don't depend on this) but good for debugging. + public bool InTaintTime { get; private set; } - internal int m_maxUpdatesPerFrame; - internal EntityProperties[] m_updateArray; + // Pinned memory used to pass step information between managed and unmanaged + internal int m_maxCollisionsPerFrame; + internal CollisionDesc[] m_collisionArray; - /// - /// Used to control physics simulation timing if Bullet is running on its own thread. - /// - private ManualResetEvent m_updateWaitEvent; + internal int m_maxUpdatesPerFrame; + internal EntityProperties[] m_updateArray; - public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero - public const uint GROUNDPLANE_ID = 1; - public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here + /// + /// Used to control physics simulation timing if Bullet is running on its own thread. + /// + private ManualResetEvent m_updateWaitEvent; - public float SimpleWaterLevel { get; set; } - public BSTerrainManager TerrainManager { get; private set; } + public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero + public const uint GROUNDPLANE_ID = 1; + public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here - public ConfigurationParameters Params - { - get { return UnmanagedParams[0]; } - } - public Vector3 DefaultGravity - { - get { return new Vector3(0f, 0f, Params.gravity); } - } - // Just the Z value of the gravity - public float DefaultGravityZ - { - get { return Params.gravity; } - } + public float SimpleWaterLevel { get; set; } + public BSTerrainManager TerrainManager { get; private set; } - // When functions in the unmanaged code must be called, it is only - // done at a known time just before the simulation step. The taint - // system saves all these function calls and executes them in - // order before the simulation. - public delegate void TaintCallback(); - private struct TaintCallbackEntry - { - public String originator; - public String ident; - public TaintCallback callback; - public TaintCallbackEntry(string pIdent, TaintCallback pCallBack) + public ConfigurationParameters Params { - originator = BSScene.DetailLogZero; - ident = pIdent; - callback = pCallBack; + get { return UnmanagedParams[0]; } } - public TaintCallbackEntry(string pOrigin, string pIdent, TaintCallback pCallBack) + public Vector3 DefaultGravity { - originator = pOrigin; - ident = pIdent; - callback = pCallBack; + get { return new Vector3(0f, 0f, Params.gravity); } + } + // Just the Z value of the gravity + public float DefaultGravityZ + { + get { return Params.gravity; } } - } - private Object _taintLock = new Object(); // lock for using the next object - private List _taintOperations; - private Dictionary _postTaintOperations; - private List _postStepOperations; - - // A pointer to an instance if this structure is passed to the C++ code - // Used to pass basic configuration values to the unmanaged code. - internal ConfigurationParameters[] UnmanagedParams; - - // Sometimes you just have to log everything. - public Logging.LogWriter PhysicsLogging; - private bool m_physicsLoggingEnabled; - private string m_physicsLoggingDir; - private string m_physicsLoggingPrefix; - private int m_physicsLoggingFileMinutes; - private bool m_physicsLoggingDoFlush; - private bool m_physicsPhysicalDumpEnabled; - public int PhysicsMetricDumpFrames { get; set; } - // 'true' of the vehicle code is to log lots of details - public bool VehicleLoggingEnabled { get; private set; } - public bool VehiclePhysicalLoggingEnabled { get; private set; } - - #region Construction and Initialization - public BSScene(string engineType, string identifier) - { - m_initialized = false; - - // The name of the region we're working for is passed to us. Keep for identification. - RegionName = identifier; - - // Set identifying variables in the PhysicsScene interface. - EngineType = engineType; - Name = EngineType + "/" + RegionName; - } - - // Old version of initialization that assumes legacy sized regions (256x256) - public override void Initialise(IMesher meshmerizer, IConfigSource config) - { - m_log.ErrorFormat("{0} WARNING WARNING WARNING! BulletSim initialized without region extent specification. Terrain will be messed up."); - Vector3 regionExtent = new Vector3( Constants.RegionSize, Constants.RegionSize, Constants.RegionSize); - Initialise(meshmerizer, config, regionExtent); - - } - public override void Initialise(IMesher meshmerizer, IConfigSource config, Vector3 regionExtent) - { - mesher = meshmerizer; - _taintOperations = new List(); - _postTaintOperations = new Dictionary(); - _postStepOperations = new List(); - PhysObjects = new Dictionary(); - Shapes = new BSShapeCollection(this); + // When functions in the unmanaged code must be called, it is only + // done at a known time just before the simulation step. The taint + // system saves all these function calls and executes them in + // order before the simulation. + public delegate void TaintCallback(); + private struct TaintCallbackEntry + { + public String originator; + public String ident; + public TaintCallback callback; + public TaintCallbackEntry(string pIdent, TaintCallback pCallBack) + { + originator = BSScene.DetailLogZero; + ident = pIdent; + callback = pCallBack; + } + public TaintCallbackEntry(string pOrigin, string pIdent, TaintCallback pCallBack) + { + originator = pOrigin; + ident = pIdent; + callback = pCallBack; + } + } + private Object _taintLock = new Object(); // lock for using the next object + private List _taintOperations; + private Dictionary _postTaintOperations; + private List _postStepOperations; + + // A pointer to an instance if this structure is passed to the C++ code + // Used to pass basic configuration values to the unmanaged code. + internal ConfigurationParameters[] UnmanagedParams; + + // Sometimes you just have to log everything. + public LogWriter PhysicsLogging; + private bool m_physicsLoggingEnabled; + private string m_physicsLoggingDir; + private string m_physicsLoggingPrefix; + private int m_physicsLoggingFileMinutes; + private bool m_physicsLoggingDoFlush; + private bool m_physicsPhysicalDumpEnabled; + public int PhysicsMetricDumpFrames { get; set; } + // 'true' of the vehicle code is to log lots of details + public bool VehicleLoggingEnabled { get; private set; } + public bool VehiclePhysicalLoggingEnabled { get; private set; } + + #region INonSharedRegionModule + public string Name + { + get { return "BulletSim"; } + } - m_simulatedTime = 0f; - LastTimeStep = 0.1f; + public Type ReplaceableInterface + { + get { return null; } + } - // Allocate pinned memory to pass parameters. - UnmanagedParams = new ConfigurationParameters[1]; + public void Initialise(IConfigSource source) + { + // TODO: Move this out of Startup + IConfig config = source.Configs["Startup"]; + if (config != null) + { + string physics = config.GetString("physics", string.Empty); + if (physics == Name) + { + m_Enabled = true; + m_Config = source; + } + } - // Set default values for physics parameters plus any overrides from the ini file - GetInitialParameterValues(config); + } - // Force some parameters to values depending on other configurations - // Only use heightmap terrain implementation if terrain larger than legacy size - if ((uint)regionExtent.X > Constants.RegionSize || (uint)regionExtent.Y > Constants.RegionSize) + public void Close() { - m_log.WarnFormat("{0} Forcing terrain implementation to heightmap for large region", LogHeader); - BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap; } - // Get the connection to the physics engine (could be native or one of many DLLs) - PE = SelectUnderlyingBulletEngine(BulletEngineName); - - // Enable very detailed logging. - // By creating an empty logger when not logging, the log message invocation code - // can be left in and every call doesn't have to check for null. - if (m_physicsLoggingEnabled) + public void AddRegion(Scene scene) { - PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes, m_physicsLoggingDoFlush); - PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output its own error messages. + if (!m_Enabled) + return; + + EngineType = Name; + RegionName = scene.RegionInfo.RegionName; + PhysicsSceneName = EngineType + "/" + RegionName; + + scene.RegisterModuleInterface(this); + Vector3 extent = new Vector3(scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY, scene.RegionInfo.RegionSizeZ); + Initialise(m_Config, extent); } - else + + public void RemoveRegion(Scene scene) { - PhysicsLogging = new Logging.LogWriter(); + if (!m_Enabled) + return; } - // Allocate memory for returning of the updates and collisions from the physics engine - m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame]; - m_updateArray = new EntityProperties[m_maxUpdatesPerFrame]; - - // The bounding box for the simulated world. The origin is 0,0,0 unless we're - // a child in a mega-region. - // Bullet actually doesn't care about the extents of the simulated - // area. It tracks active objects no matter where they are. - Vector3 worldExtent = regionExtent; + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; - World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray); + mesher = scene.RequestModuleInterface(); + if (mesher == null) + m_log.WarnFormat("{0} No mesher. Things will not work well.", LogHeader); + } + #endregion - Constraints = new BSConstraintCollection(World); + #region Initialization - TerrainManager = new BSTerrainManager(this, worldExtent); - TerrainManager.CreateInitialGroundPlaneAndTerrain(); + private void Initialise(IConfigSource config, Vector3 regionExtent) + { + _taintOperations = new List(); + _postTaintOperations = new Dictionary(); + _postStepOperations = new List(); + PhysObjects = new Dictionary(); + Shapes = new BSShapeCollection(this); - // Put some informational messages into the log file. - m_log.InfoFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); + m_simulatedTime = 0f; + LastTimeStep = 0.1f; - InTaintTime = false; - m_initialized = true; + // Allocate pinned memory to pass parameters. + UnmanagedParams = new ConfigurationParameters[1]; - // If the physics engine runs on its own thread, start same. - if (BSParam.UseSeparatePhysicsThread) - { - // The physics simulation should happen independently of the heartbeat loop - m_physicsThread - = WorkManager.StartThread( - BulletSPluginPhysicsThread, - string.Format("{0} ({1})", BulletEngineName, RegionName), - ThreadPriority.Normal, - true, - true); - } - } + // Set default values for physics parameters plus any overrides from the ini file + GetInitialParameterValues(config); - // All default parameter values are set here. There should be no values set in the - // variable definitions. - private void GetInitialParameterValues(IConfigSource config) - { - ConfigurationParameters parms = new ConfigurationParameters(); - UnmanagedParams[0] = parms; + // Force some parameters to values depending on other configurations + // Only use heightmap terrain implementation if terrain larger than legacy size + if ((uint)regionExtent.X > Constants.RegionSize || (uint)regionExtent.Y > Constants.RegionSize) + { + m_log.WarnFormat("{0} Forcing terrain implementation to heightmap for large region", LogHeader); + BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap; + } - BSParam.SetParameterDefaultValues(this); + // Get the connection to the physics engine (could be native or one of many DLLs) + PE = SelectUnderlyingBulletEngine(BulletEngineName); - if (config != null) - { - // If there are specifications in the ini file, use those values - IConfig pConfig = config.Configs["BulletSim"]; - if (pConfig != null) + // Enable very detailed logging. + // By creating an empty logger when not logging, the log message invocation code + // can be left in and every call doesn't have to check for null. + if (m_physicsLoggingEnabled) { - BSParam.SetParameterConfigurationValues(this, pConfig); - - // There are two Bullet implementations to choose from - BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged"); - - // Very detailed logging for physics debugging - // TODO: the boolean values can be moved to the normal parameter processing. - m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); - m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); - m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); - m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); - m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false); - m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false); - // Very detailed logging for vehicle debugging - VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); - VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false); - - // Do any replacements in the parameters - m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); + PhysicsLogging = new LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes, m_physicsLoggingDoFlush); + PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output its own error messages. } else { - // Nothing in the configuration INI file so assume unmanaged and other defaults. - BulletEngineName = "BulletUnmanaged"; - m_physicsLoggingEnabled = false; - VehicleLoggingEnabled = false; + PhysicsLogging = new LogWriter(); } - // The material characteristics. - BSMaterials.InitializeFromDefaults(Params); - if (pConfig != null) + // Allocate memory for returning of the updates and collisions from the physics engine + m_collisionArray = new CollisionDesc[m_maxCollisionsPerFrame]; + m_updateArray = new EntityProperties[m_maxUpdatesPerFrame]; + + // The bounding box for the simulated world. The origin is 0,0,0 unless we're + // a child in a mega-region. + // Bullet actually doesn't care about the extents of the simulated + // area. It tracks active objects no matter where they are. + Vector3 worldExtent = regionExtent; + + World = PE.Initialize(worldExtent, Params, m_maxCollisionsPerFrame, ref m_collisionArray, m_maxUpdatesPerFrame, ref m_updateArray); + + Constraints = new BSConstraintCollection(World); + + TerrainManager = new BSTerrainManager(this, worldExtent); + TerrainManager.CreateInitialGroundPlaneAndTerrain(); + + // Put some informational messages into the log file. + m_log.InfoFormat("{0} Linksets implemented with {1}", LogHeader, (BSLinkset.LinksetImplementation)BSParam.LinksetImplementation); + + InTaintTime = false; + m_initialized = true; + + // If the physics engine runs on its own thread, start same. + if (BSParam.UseSeparatePhysicsThread) { - // Let the user add new and interesting material property values. - BSMaterials.InitializefromParameters(pConfig); + // The physics simulation should happen independently of the heartbeat loop + m_physicsThread + = WorkManager.StartThread( + BulletSPluginPhysicsThread, + string.Format("{0} ({1})", BulletEngineName, RegionName), + ThreadPriority.Normal, + true, + true); } } - } - // A helper function that handles a true/false parameter and returns the proper float number encoding - float ParamBoolean(IConfig config, string parmName, float deflt) - { - float ret = deflt; - if (config.Contains(parmName)) + // All default parameter values are set here. There should be no values set in the + // variable definitions. + private void GetInitialParameterValues(IConfigSource config) { - ret = ConfigurationParameters.numericFalse; - if (config.GetBoolean(parmName, false)) - { - ret = ConfigurationParameters.numericTrue; - } - } - return ret; - } + ConfigurationParameters parms = new ConfigurationParameters(); + UnmanagedParams[0] = parms; - // Select the connection to the actual Bullet implementation. - // The main engine selection is the engineName up to the first hypen. - // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name - // is passed to the engine to do its special selection, etc. - private BSAPITemplate SelectUnderlyingBulletEngine(string engineName) - { - // For the moment, do a simple switch statement. - // Someday do fancyness with looking up the interfaces in the assembly. - BSAPITemplate ret = null; + BSParam.SetParameterDefaultValues(this); - string selectionName = engineName.ToLower(); - int hyphenIndex = engineName.IndexOf("-"); - if (hyphenIndex > 0) - selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1); + if (config != null) + { + // If there are specifications in the ini file, use those values + IConfig pConfig = config.Configs["BulletSim"]; + if (pConfig != null) + { + BSParam.SetParameterConfigurationValues(this, pConfig); + + // There are two Bullet implementations to choose from + BulletEngineName = pConfig.GetString("BulletEngine", "BulletUnmanaged"); + + // Very detailed logging for physics debugging + // TODO: the boolean values can be moved to the normal parameter processing. + m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); + m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); + m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); + m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); + m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false); + m_physicsPhysicalDumpEnabled = pConfig.GetBoolean("PhysicsPhysicalDumpEnabled", false); + // Very detailed logging for vehicle debugging + VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); + VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false); + + // Do any replacements in the parameters + m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); + } + else + { + // Nothing in the configuration INI file so assume unmanaged and other defaults. + BulletEngineName = "BulletUnmanaged"; + m_physicsLoggingEnabled = false; + VehicleLoggingEnabled = false; + } - switch (selectionName) - { - case "bullet": - case "bulletunmanaged": - ret = new BSAPIUnman(engineName, this); - break; - case "bulletxna": - ret = new BSAPIXNA(engineName, this); - // Disable some features that are not implemented in BulletXNA - m_log.InfoFormat("{0} Disabling some physics features not implemented by BulletXNA", LogHeader); - m_log.InfoFormat("{0} Disabling ShouldUseBulletHACD", LogHeader); - BSParam.ShouldUseBulletHACD = false; - m_log.InfoFormat("{0} Disabling ShouldUseSingleConvexHullForPrims", LogHeader); - BSParam.ShouldUseSingleConvexHullForPrims = false; - m_log.InfoFormat("{0} Disabling ShouldUseGImpactShapeForPrims", LogHeader); - BSParam.ShouldUseGImpactShapeForPrims = false; - m_log.InfoFormat("{0} Setting terrain implimentation to Heightmap", LogHeader); - BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap; - break; + // The material characteristics. + BSMaterials.InitializeFromDefaults(Params); + if (pConfig != null) + { + // Let the user add new and interesting material property values. + BSMaterials.InitializefromParameters(pConfig); + } + } } - if (ret == null) + // A helper function that handles a true/false parameter and returns the proper float number encoding + float ParamBoolean(IConfig config, string parmName, float deflt) { - m_log.ErrorFormat("{0} COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*' or 'BulletXNA-*'", LogHeader); - } - else - { - m_log.InfoFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion); + float ret = deflt; + if (config.Contains(parmName)) + { + ret = ConfigurationParameters.numericFalse; + if (config.GetBoolean(parmName, false)) + { + ret = ConfigurationParameters.numericTrue; + } + } + return ret; } - return ret; - } + // Select the connection to the actual Bullet implementation. + // The main engine selection is the engineName up to the first hypen. + // So "Bullet-2.80-OpenCL-Intel" specifies the 'bullet' class here and the whole name + // is passed to the engine to do its special selection, etc. + private BSAPITemplate SelectUnderlyingBulletEngine(string engineName) + { + // For the moment, do a simple switch statement. + // Someday do fancyness with looking up the interfaces in the assembly. + BSAPITemplate ret = null; - public override void Dispose() - { - // m_log.DebugFormat("{0}: Dispose()", LogHeader); + string selectionName = engineName.ToLower(); + int hyphenIndex = engineName.IndexOf("-"); + if (hyphenIndex > 0) + selectionName = engineName.ToLower().Substring(0, hyphenIndex - 1); - // make sure no stepping happens while we're deleting stuff - m_initialized = false; + switch (selectionName) + { + case "bullet": + case "bulletunmanaged": + ret = new BSAPIUnman(engineName, this); + break; + case "bulletxna": + ret = new BSAPIXNA(engineName, this); + // Disable some features that are not implemented in BulletXNA + m_log.InfoFormat("{0} Disabling some physics features not implemented by BulletXNA", LogHeader); + m_log.InfoFormat("{0} Disabling ShouldUseBulletHACD", LogHeader); + BSParam.ShouldUseBulletHACD = false; + m_log.InfoFormat("{0} Disabling ShouldUseSingleConvexHullForPrims", LogHeader); + BSParam.ShouldUseSingleConvexHullForPrims = false; + m_log.InfoFormat("{0} Disabling ShouldUseGImpactShapeForPrims", LogHeader); + BSParam.ShouldUseGImpactShapeForPrims = false; + m_log.InfoFormat("{0} Setting terrain implimentation to Heightmap", LogHeader); + BSParam.TerrainImplementation = (float)BSTerrainPhys.TerrainImplementation.Heightmap; + break; + } - lock (PhysObjects) - { - foreach (KeyValuePair kvp in PhysObjects) + if (ret == null) { - kvp.Value.Destroy(); + m_log.ErrorFormat("{0} COULD NOT SELECT BULLET ENGINE: '[BulletSim]PhysicsEngine' must be either 'BulletUnmanaged-*' or 'BulletXNA-*'", LogHeader); + } + else + { + m_log.InfoFormat("{0} Selected bullet engine {1} -> {2}/{3}", LogHeader, engineName, ret.BulletEngineName, ret.BulletEngineVersion); } - PhysObjects.Clear(); - } - // Now that the prims are all cleaned up, there should be no constraints left - if (Constraints != null) - { - Constraints.Dispose(); - Constraints = null; + return ret; } - if (Shapes != null) + public override void Dispose() { - Shapes.Dispose(); - Shapes = null; - } + // m_log.DebugFormat("{0}: Dispose()", LogHeader); - if (TerrainManager != null) - { - TerrainManager.ReleaseGroundPlaneAndTerrain(); - TerrainManager.Dispose(); - TerrainManager = null; - } + // make sure no stepping happens while we're deleting stuff + m_initialized = false; - // Anything left in the unmanaged code should be cleaned out - PE.Shutdown(World); + lock (PhysObjects) + { + foreach (KeyValuePair kvp in PhysObjects) + { + kvp.Value.Destroy(); + } + PhysObjects.Clear(); + } - // Not logging any more - PhysicsLogging.Close(); - } - #endregion // Construction and Initialization + // Now that the prims are all cleaned up, there should be no constraints left + if (Constraints != null) + { + Constraints.Dispose(); + Constraints = null; + } - #region Prim and Avatar addition and removal + if (Shapes != null) + { + Shapes.Dispose(); + Shapes = null; + } - public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying) - { - m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); - return null; - } + if (TerrainManager != null) + { + TerrainManager.ReleaseGroundPlaneAndTerrain(); + TerrainManager.Dispose(); + TerrainManager = null; + } - public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying) - { - // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName); + // Anything left in the unmanaged code should be cleaned out + PE.Shutdown(World); - if (!m_initialized) return null; + // Not logging any more + PhysicsLogging.Close(); + } + #endregion // Construction and Initialization - BSCharacter actor = new BSCharacter(localID, avName, this, position, velocity, size, isFlying); - lock (PhysObjects) - PhysObjects.Add(localID, actor); + #region Prim and Avatar addition and removal - // TODO: Remove kludge someday. - // We must generate a collision for avatars whether they collide or not. - // This is required by OpenSim to update avatar animations, etc. - lock (AvatarsInSceneLock) - AvatarsInScene.Add(actor); + public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying) + { + m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); + return null; + } - return actor; - } + public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 velocity, Vector3 size, bool isFlying) + { + // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName); - public override void RemoveAvatar(PhysicsActor actor) - { - // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader); + if (!m_initialized) return null; + + BSCharacter actor = new BSCharacter(localID, avName, this, position, velocity, size, isFlying); + lock (PhysObjects) + PhysObjects.Add(localID, actor); + + // TODO: Remove kludge someday. + // We must generate a collision for avatars whether they collide or not. + // This is required by OpenSim to update avatar animations, etc. + lock (AvatarsInSceneLock) + AvatarsInScene.Add(actor); - if (!m_initialized) return; + return actor; + } - BSCharacter bsactor = actor as BSCharacter; - if (bsactor != null) + public override void RemoveAvatar(PhysicsActor actor) { - try + // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader); + + if (!m_initialized) return; + + BSCharacter bsactor = actor as BSCharacter; + if (bsactor != null) { - lock (PhysObjects) - PhysObjects.Remove(bsactor.LocalID); - // Remove kludge someday - lock (AvatarsInSceneLock) - AvatarsInScene.Remove(bsactor); + try + { + lock (PhysObjects) + PhysObjects.Remove(bsactor.LocalID); + // Remove kludge someday + lock (AvatarsInSceneLock) + AvatarsInScene.Remove(bsactor); + } + catch (Exception e) + { + m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e); + } + bsactor.Destroy(); + // bsactor.dispose(); } - catch (Exception e) + else { - m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e); + m_log.ErrorFormat("{0}: Requested to remove avatar that is not a BSCharacter. ID={1}, type={2}", + LogHeader, actor.LocalID, actor.GetType().Name); } - bsactor.Destroy(); - // bsactor.dispose(); - } - else - { - m_log.ErrorFormat("{0}: Requested to remove avatar that is not a BSCharacter. ID={1}, type={2}", - LogHeader, actor.LocalID, actor.GetType().Name); } - } - - public override void RemovePrim(PhysicsActor prim) - { - if (!m_initialized) return; - BSPhysObject bsprim = prim as BSPhysObject; - if (bsprim != null) + public override void RemovePrim(PhysicsActor prim) { - DetailLog("{0},RemovePrim,call", bsprim.LocalID); - // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID); - try + if (!m_initialized) return; + + BSPhysObject bsprim = prim as BSPhysObject; + if (bsprim != null) { - lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID); + DetailLog("{0},RemovePrim,call", bsprim.LocalID); + // m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID); + try + { + lock (PhysObjects) PhysObjects.Remove(bsprim.LocalID); + } + catch (Exception e) + { + m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e); + } + bsprim.Destroy(); + // bsprim.dispose(); } - catch (Exception e) + else { - m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e); + m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader); } - bsprim.Destroy(); - // bsprim.dispose(); - } - else - { - m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader); } - } - public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, - Vector3 size, Quaternion rotation, bool isPhysical, uint localID) - { - // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName); + public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, + Vector3 size, Quaternion rotation, bool isPhysical, uint localID) + { + // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName); - if (!m_initialized) return null; + if (!m_initialized) return null; - // DetailLog("{0},BSScene.AddPrimShape,call", localID); + // DetailLog("{0},BSScene.AddPrimShape,call", localID); - BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical); - lock (PhysObjects) PhysObjects.Add(localID, prim); - return prim; - } + BSPhysObject prim = new BSPrimLinkable(localID, primName, this, position, size, rotation, pbs, isPhysical); + lock (PhysObjects) PhysObjects.Add(localID, prim); + return prim; + } - // This is a call from the simulator saying that some physical property has been updated. - // The BulletSim driver senses the changing of relevant properties so this taint - // information call is not needed. - public override void AddPhysicsActorTaint(PhysicsActor prim) { } + // This is a call from the simulator saying that some physical property has been updated. + // The BulletSim driver senses the changing of relevant properties so this taint + // information call is not needed. + public override void AddPhysicsActorTaint(PhysicsActor prim) { } - #endregion // Prim and Avatar addition and removal + #endregion // Prim and Avatar addition and removal - #region Simulation + #region Simulation - // Call from the simulator to send physics information to the simulator objects. - // This pushes all the collision and property update events into the objects in - // the simulator and, since it is on the heartbeat thread, there is an implicit - // locking of those data structures from other heartbeat events. - // If the physics engine is running on a separate thread, the update information - // will be in the ObjectsWithCollions and ObjectsWithUpdates structures. - public override float Simulate(float timeStep) - { - if (!BSParam.UseSeparatePhysicsThread) + // Call from the simulator to send physics information to the simulator objects. + // This pushes all the collision and property update events into the objects in + // the simulator and, since it is on the heartbeat thread, there is an implicit + // locking of those data structures from other heartbeat events. + // If the physics engine is running on a separate thread, the update information + // will be in the ObjectsWithCollions and ObjectsWithUpdates structures. + public override float Simulate(float timeStep) { - DoPhysicsStep(timeStep); + if (!BSParam.UseSeparatePhysicsThread) + { + DoPhysicsStep(timeStep); + } + return SendUpdatesToSimulator(timeStep); } - return SendUpdatesToSimulator(timeStep); - } - // Call the physics engine to do one 'timeStep' and collect collisions and updates - // into ObjectsWithCollisions and ObjectsWithUpdates data structures. - private void DoPhysicsStep(float timeStep) - { - // prevent simulation until we've been initialized - if (!m_initialized) return; + // Call the physics engine to do one 'timeStep' and collect collisions and updates + // into ObjectsWithCollisions and ObjectsWithUpdates data structures. + private void DoPhysicsStep(float timeStep) + { + // prevent simulation until we've been initialized + if (!m_initialized) return; - LastTimeStep = timeStep; + LastTimeStep = timeStep; - int updatedEntityCount = 0; - int collidersCount = 0; + int updatedEntityCount = 0; + int collidersCount = 0; - int beforeTime = Util.EnvironmentTickCount(); - int simTime = 0; + int beforeTime = Util.EnvironmentTickCount(); + int simTime = 0; - int numTaints = _taintOperations.Count; - InTaintTime = true; // Only used for debugging so locking is not necessary. + int numTaints = _taintOperations.Count; + InTaintTime = true; // Only used for debugging so locking is not necessary. - // update the prim states while we know the physics engine is not busy - ProcessTaints(); + // update the prim states while we know the physics engine is not busy + ProcessTaints(); - // Some of the physical objects requre individual, pre-step calls - // (vehicles and avatar movement, in particular) - TriggerPreStepEvent(timeStep); + // Some of the physical objects requre individual, pre-step calls + // (vehicles and avatar movement, in particular) + TriggerPreStepEvent(timeStep); - // the prestep actions might have added taints - numTaints += _taintOperations.Count; - ProcessTaints(); + // the prestep actions might have added taints + numTaints += _taintOperations.Count; + ProcessTaints(); - InTaintTime = false; // Only used for debugging so locking is not necessary. + InTaintTime = false; // Only used for debugging so locking is not necessary. - // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. - // Only enable this in a limited test world with few objects. - if (m_physicsPhysicalDumpEnabled) - PE.DumpAllInfo(World); + // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. + // Only enable this in a limited test world with few objects. + if (m_physicsPhysicalDumpEnabled) + PE.DumpAllInfo(World); - // step the physical world one interval - m_simulationStep++; - int numSubSteps = 0; - try - { - numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); + // step the physical world one interval + m_simulationStep++; + int numSubSteps = 0; + try + { + numSubSteps = PE.PhysicsStep(World, timeStep, m_maxSubSteps, m_fixedTimeStep, out updatedEntityCount, out collidersCount); - } - catch (Exception e) - { - m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}", - LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e); - DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}", - DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); - updatedEntityCount = 0; - collidersCount = 0; - } + } + catch (Exception e) + { + m_log.WarnFormat("{0},PhysicsStep Exception: nTaints={1}, substeps={2}, updates={3}, colliders={4}, e={5}", + LogHeader, numTaints, numSubSteps, updatedEntityCount, collidersCount, e); + DetailLog("{0},PhysicsStepException,call, nTaints={1}, substeps={2}, updates={3}, colliders={4}", + DetailLogZero, numTaints, numSubSteps, updatedEntityCount, collidersCount); + updatedEntityCount = 0; + collidersCount = 0; + } - // Make the physics engine dump useful statistics periodically - if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) - PE.DumpPhysicsStatistics(World); + // Make the physics engine dump useful statistics periodically + if (PhysicsMetricDumpFrames != 0 && ((m_simulationStep % PhysicsMetricDumpFrames) == 0)) + PE.DumpPhysicsStatistics(World); - // Get a value for 'now' so all the collision and update routines don't have to get their own. - SimulationNowTime = Util.EnvironmentTickCount(); + // Get a value for 'now' so all the collision and update routines don't have to get their own. + SimulationNowTime = Util.EnvironmentTickCount(); - // Send collision information to the colliding objects. The objects decide if the collision - // is 'real' (like linksets don't collide with themselves) and the individual objects - // know if the simulator has subscribed to collisions. - lock (CollisionLock) - { - if (collidersCount > 0) + // Send collision information to the colliding objects. The objects decide if the collision + // is 'real' (like linksets don't collide with themselves) and the individual objects + // know if the simulator has subscribed to collisions. + lock (CollisionLock) { - lock (PhysObjects) + if (collidersCount > 0) { - for (int ii = 0; ii < collidersCount; ii++) + lock (PhysObjects) { - uint cA = m_collisionArray[ii].aID; - uint cB = m_collisionArray[ii].bID; - Vector3 point = m_collisionArray[ii].point; - Vector3 normal = m_collisionArray[ii].normal; - float penetration = m_collisionArray[ii].penetration; - SendCollision(cA, cB, point, normal, penetration); - SendCollision(cB, cA, point, -normal, penetration); + for (int ii = 0; ii < collidersCount; ii++) + { + uint cA = m_collisionArray[ii].aID; + uint cB = m_collisionArray[ii].bID; + Vector3 point = m_collisionArray[ii].point; + Vector3 normal = m_collisionArray[ii].normal; + float penetration = m_collisionArray[ii].penetration; + SendCollision(cA, cB, point, normal, penetration); + SendCollision(cB, cA, point, -normal, penetration); + } } } } - } - // If any of the objects had updated properties, tell the managed objects about the update - // and remember that there was a change so it will be passed to the simulator. - lock (UpdateLock) - { - if (updatedEntityCount > 0) + // If any of the objects had updated properties, tell the managed objects about the update + // and remember that there was a change so it will be passed to the simulator. + lock (UpdateLock) { - lock (PhysObjects) + if (updatedEntityCount > 0) { - for (int ii = 0; ii < updatedEntityCount; ii++) + lock (PhysObjects) { - EntityProperties entprop = m_updateArray[ii]; - BSPhysObject pobj; - if (PhysObjects.TryGetValue(entprop.ID, out pobj)) + for (int ii = 0; ii < updatedEntityCount; ii++) { - if (pobj.IsInitialized) - pobj.UpdateProperties(entprop); + EntityProperties entprop = m_updateArray[ii]; + BSPhysObject pobj; + if (PhysObjects.TryGetValue(entprop.ID, out pobj)) + { + if (pobj.IsInitialized) + pobj.UpdateProperties(entprop); + } } } } } - } - - // Some actors want to know when the simulation step is complete. - TriggerPostStepEvent(timeStep); - simTime = Util.EnvironmentTickCountSubtract(beforeTime); - if (PhysicsLogging.Enabled) - { - DetailLog("{0},DoPhysicsStep,complete,frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}", - DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, - updatedEntityCount, collidersCount, ObjectsWithCollisions.Count); - } + // Some actors want to know when the simulation step is complete. + TriggerPostStepEvent(timeStep); - // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. - // Only enable this in a limited test world with few objects. - if (m_physicsPhysicalDumpEnabled) - PE.DumpAllInfo(World); + simTime = Util.EnvironmentTickCountSubtract(beforeTime); + if (PhysicsLogging.Enabled) + { + DetailLog("{0},DoPhysicsStep,complete,frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}", + DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, + updatedEntityCount, collidersCount, ObjectsWithCollisions.Count); + } - // The physics engine returns the number of milliseconds it simulated this call. - // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. - // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55). - m_simulatedTime += (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate; - } + // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world. + // Only enable this in a limited test world with few objects. + if (m_physicsPhysicalDumpEnabled) + PE.DumpAllInfo(World); - // Called by a BSPhysObject to note that it has changed properties and this information - // should be passed up to the simulator at the proper time. - // Note: this is called by the BSPhysObject from invocation via DoPhysicsStep() above so - // this is is under UpdateLock. - public void PostUpdate(BSPhysObject updatee) - { - lock (UpdateLock) - { - ObjectsWithUpdates.Add(updatee); + // The physics engine returns the number of milliseconds it simulated this call. + // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. + // Multiply by a fixed nominal frame rate to give a rate similar to the simulator (usually 55). + m_simulatedTime += (float)numSubSteps * m_fixedTimeStep * 1000f * NominalFrameRate; } - } - - // The simulator thinks it is physics time so return all the collisions and position - // updates that were collected in actual physics simulation. - private float SendUpdatesToSimulator(float timeStep) - { - if (!m_initialized) return 5.0f; - DetailLog("{0},SendUpdatesToSimulator,collisions={1},updates={2},simedTime={3}", - BSScene.DetailLogZero, ObjectsWithCollisions.Count, ObjectsWithUpdates.Count, m_simulatedTime); - // Push the collisions into the simulator. - lock (CollisionLock) + // Called by a BSPhysObject to note that it has changed properties and this information + // should be passed up to the simulator at the proper time. + // Note: this is called by the BSPhysObject from invocation via DoPhysicsStep() above so + // this is is under UpdateLock. + public void PostUpdate(BSPhysObject updatee) { - if (ObjectsWithCollisions.Count > 0) + lock (UpdateLock) { - foreach (BSPhysObject bsp in ObjectsWithCollisions) - if (!bsp.SendCollisions()) - { - // If the object is done colliding, see that it's removed from the colliding list - ObjectsWithNoMoreCollisions.Add(bsp); - } + ObjectsWithUpdates.Add(updatee); } + } - // This is a kludge to get avatar movement updates. - // The simulator expects collisions for avatars even if there are have been no collisions. - // The event updates avatar animations and stuff. - // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. - // Note that we get a copy of the list to search because SendCollision() can take a while. - HashSet tempAvatarsInScene; - lock (AvatarsInSceneLock) + // The simulator thinks it is physics time so return all the collisions and position + // updates that were collected in actual physics simulation. + private float SendUpdatesToSimulator(float timeStep) + { + if (!m_initialized) return 5.0f; + + DetailLog("{0},SendUpdatesToSimulator,collisions={1},updates={2},simedTime={3}", + BSScene.DetailLogZero, ObjectsWithCollisions.Count, ObjectsWithUpdates.Count, m_simulatedTime); + // Push the collisions into the simulator. + lock (CollisionLock) { - tempAvatarsInScene = new HashSet(AvatarsInScene); + if (ObjectsWithCollisions.Count > 0) + { + foreach (BSPhysObject bsp in ObjectsWithCollisions) + if (!bsp.SendCollisions()) + { + // If the object is done colliding, see that it's removed from the colliding list + ObjectsWithNoMoreCollisions.Add(bsp); + } + } + + // This is a kludge to get avatar movement updates. + // The simulator expects collisions for avatars even if there are have been no collisions. + // The event updates avatar animations and stuff. + // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. + // Note that we get a copy of the list to search because SendCollision() can take a while. + HashSet tempAvatarsInScene; + lock (AvatarsInSceneLock) + { + tempAvatarsInScene = new HashSet(AvatarsInScene); + } + foreach (BSPhysObject actor in tempAvatarsInScene) + { + if (!ObjectsWithCollisions.Contains(actor)) // don't call avatars twice + actor.SendCollisions(); + } + tempAvatarsInScene = null; + + // Objects that are done colliding are removed from the ObjectsWithCollisions list. + // Not done above because it is inside an iteration of ObjectWithCollisions. + // This complex collision processing is required to create an empty collision + // event call after all real collisions have happened on an object. This allows + // the simulator to generate the 'collision end' event. + if (ObjectsWithNoMoreCollisions.Count > 0) + { + foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) + ObjectsWithCollisions.Remove(po); + ObjectsWithNoMoreCollisions.Clear(); + } } - foreach (BSPhysObject actor in tempAvatarsInScene) + + // Call the simulator for each object that has physics property updates. + HashSet updatedObjects = null; + lock (UpdateLock) { - if (!ObjectsWithCollisions.Contains(actor)) // don't call avatars twice - actor.SendCollisions(); + if (ObjectsWithUpdates.Count > 0) + { + updatedObjects = ObjectsWithUpdates; + ObjectsWithUpdates = new HashSet(); + } } - tempAvatarsInScene = null; - - // Objects that are done colliding are removed from the ObjectsWithCollisions list. - // Not done above because it is inside an iteration of ObjectWithCollisions. - // This complex collision processing is required to create an empty collision - // event call after all real collisions have happened on an object. This allows - // the simulator to generate the 'collision end' event. - if (ObjectsWithNoMoreCollisions.Count > 0) + if (updatedObjects != null) { - foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) - ObjectsWithCollisions.Remove(po); - ObjectsWithNoMoreCollisions.Clear(); + foreach (BSPhysObject obj in updatedObjects) + { + obj.RequestPhysicsterseUpdate(); + } + updatedObjects.Clear(); } + + // Return the framerate simulated to give the above returned results. + // (Race condition here but this is just bookkeeping so rare mistakes do not merit a lock). + float simTime = m_simulatedTime; + m_simulatedTime = 0f; + return simTime; } - // Call the simulator for each object that has physics property updates. - HashSet updatedObjects = null; - lock (UpdateLock) + // Something has collided + private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration) { - if (ObjectsWithUpdates.Count > 0) + if (localID <= TerrainManager.HighestTerrainID) { - updatedObjects = ObjectsWithUpdates; - ObjectsWithUpdates = new HashSet(); + return; // don't send collisions to the terrain } - } - if (updatedObjects != null) - { - foreach (BSPhysObject obj in updatedObjects) + + BSPhysObject collider; + // NOTE that PhysObjects was locked before the call to SendCollision(). + if (!PhysObjects.TryGetValue(localID, out collider)) { - obj.RequestPhysicsterseUpdate(); + // If the object that is colliding cannot be found, just ignore the collision. + DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith); + return; } - updatedObjects.Clear(); - } - - // Return the framerate simulated to give the above returned results. - // (Race condition here but this is just bookkeeping so rare mistakes do not merit a lock). - float simTime = m_simulatedTime; - m_simulatedTime = 0f; - return simTime; - } - // Something has collided - private void SendCollision(uint localID, uint collidingWith, Vector3 collidePoint, Vector3 collideNormal, float penetration) - { - if (localID <= TerrainManager.HighestTerrainID) - { - return; // don't send collisions to the terrain - } - - BSPhysObject collider; - // NOTE that PhysObjects was locked before the call to SendCollision(). - if (!PhysObjects.TryGetValue(localID, out collider)) - { - // If the object that is colliding cannot be found, just ignore the collision. - DetailLog("{0},BSScene.SendCollision,colliderNotInObjectList,id={1},with={2}", DetailLogZero, localID, collidingWith); - return; - } - - // Note: the terrain is not in the physical object list so 'collidee' can be null when Collide() is called. - BSPhysObject collidee = null; - PhysObjects.TryGetValue(collidingWith, out collidee); + // Note: the terrain is not in the physical object list so 'collidee' can be null when Collide() is called. + BSPhysObject collidee = null; + PhysObjects.TryGetValue(collidingWith, out collidee); - // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); + // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); - if (collider.IsInitialized) - { - if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) + if (collider.IsInitialized) { - // If a collision was 'good', remember to send it to the simulator - lock (CollisionLock) + if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) { - ObjectsWithCollisions.Add(collider); + // If a collision was 'good', remember to send it to the simulator + lock (CollisionLock) + { + ObjectsWithCollisions.Add(collider); + } } } + + return; } - return; - } + public void BulletSPluginPhysicsThread() + { + Thread.CurrentThread.Priority = ThreadPriority.Highest; + m_updateWaitEvent = new ManualResetEvent(false); - public void BulletSPluginPhysicsThread() - { - Thread.CurrentThread.Priority = ThreadPriority.Highest; - m_updateWaitEvent = new ManualResetEvent(false); + while (m_initialized) + { + int beginSimulationRealtimeMS = Util.EnvironmentTickCount(); - while (m_initialized) - { - int beginSimulationRealtimeMS = Util.EnvironmentTickCount(); + if (BSParam.Active) + DoPhysicsStep(BSParam.PhysicsTimeStep); - if (BSParam.Active) - DoPhysicsStep(BSParam.PhysicsTimeStep); + int simulationRealtimeMS = Util.EnvironmentTickCountSubtract(beginSimulationRealtimeMS); + int simulationTimeVsRealtimeDifferenceMS = ((int)(BSParam.PhysicsTimeStep*1000f)) - simulationRealtimeMS; - int simulationRealtimeMS = Util.EnvironmentTickCountSubtract(beginSimulationRealtimeMS); - int simulationTimeVsRealtimeDifferenceMS = ((int)(BSParam.PhysicsTimeStep*1000f)) - simulationRealtimeMS; + if (simulationTimeVsRealtimeDifferenceMS > 0) + { + // The simulation of the time interval took less than realtime. + // Do a wait for the rest of realtime. + m_updateWaitEvent.WaitOne(simulationTimeVsRealtimeDifferenceMS); + //Thread.Sleep(simulationTimeVsRealtimeDifferenceMS); + } + else + { + // The simulation took longer than realtime. + // Do some scaling of simulation time. + // TODO. + DetailLog("{0},BulletSPluginPhysicsThread,longerThanRealtime={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS); + } - if (simulationTimeVsRealtimeDifferenceMS > 0) - { - // The simulation of the time interval took less than realtime. - // Do a wait for the rest of realtime. - m_updateWaitEvent.WaitOne(simulationTimeVsRealtimeDifferenceMS); - //Thread.Sleep(simulationTimeVsRealtimeDifferenceMS); - } - else - { - // The simulation took longer than realtime. - // Do some scaling of simulation time. - // TODO. - DetailLog("{0},BulletSPluginPhysicsThread,longerThanRealtime={1}", BSScene.DetailLogZero, simulationTimeVsRealtimeDifferenceMS); + Watchdog.UpdateThread(); } - Watchdog.UpdateThread(); + Watchdog.RemoveThread(); } - Watchdog.RemoveThread(); - } - - #endregion // Simulation - - public override void GetResults() { } + #endregion // Simulation - #region Terrain + public override void GetResults() { } - public override void SetTerrain(float[] heightMap) { - TerrainManager.SetTerrain(heightMap); - } + #region Terrain - public override void SetWaterLevel(float baseheight) - { - SimpleWaterLevel = baseheight; - } + public override void SetTerrain(float[] heightMap) { + TerrainManager.SetTerrain(heightMap); + } - public override void DeleteTerrain() - { - // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); - } + public override void SetWaterLevel(float baseheight) + { + SimpleWaterLevel = baseheight; + } - // Although no one seems to check this, I do support combining. - public override bool SupportsCombining() - { - return TerrainManager.SupportsCombining(); - } - // This call says I am a child to region zero in a mega-region. 'pScene' is that - // of region zero, 'offset' is my offset from regions zero's origin, and - // 'extents' is the largest XY that is handled in my region. - public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents) - { - TerrainManager.Combine(pScene, offset, extents); - } + public override void DeleteTerrain() + { + // m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); + } - // Unhook all the combining that I know about. - public override void UnCombine(PhysicsScene pScene) - { - TerrainManager.UnCombine(pScene); - } + // Although no one seems to check this, I do support combining. + public override bool SupportsCombining() + { + return TerrainManager.SupportsCombining(); + } + // This call says I am a child to region zero in a mega-region. 'pScene' is that + // of region zero, 'offset' is my offset from regions zero's origin, and + // 'extents' is the largest XY that is handled in my region. + public override void Combine(PhysicsScene pScene, Vector3 offset, Vector3 extents) + { + TerrainManager.Combine(pScene, offset, extents); + } - #endregion // Terrain + // Unhook all the combining that I know about. + public override void UnCombine(PhysicsScene pScene) + { + TerrainManager.UnCombine(pScene); + } - public override Dictionary GetTopColliders() - { - Dictionary topColliders; + #endregion // Terrain - lock (PhysObjects) + public override Dictionary GetTopColliders() { - foreach (KeyValuePair kvp in PhysObjects) + Dictionary topColliders; + + lock (PhysObjects) { - kvp.Value.ComputeCollisionScore(); + foreach (KeyValuePair kvp in PhysObjects) + { + kvp.Value.ComputeCollisionScore(); + } + + List orderedPrims = new List(PhysObjects.Values); + orderedPrims.OrderByDescending(p => p.CollisionScore); + topColliders = orderedPrims.Take(25).ToDictionary(p => p.LocalID, p => p.CollisionScore); } - List orderedPrims = new List(PhysObjects.Values); - orderedPrims.OrderByDescending(p => p.CollisionScore); - topColliders = orderedPrims.Take(25).ToDictionary(p => p.LocalID, p => p.CollisionScore); + return topColliders; } - return topColliders; - } - - public override bool IsThreaded { get { return false; } } - - #region Extensions - public override object Extension(string pFunct, params object[] pParams) - { - DetailLog("{0} BSScene.Extension,op={1}", DetailLogZero, pFunct); - return base.Extension(pFunct, pParams); - } - #endregion // Extensions + public override bool IsThreaded { get { return false; } } - public static string PrimitiveBaseShapeToString(PrimitiveBaseShape pbs) - { - float pathShearX = pbs.PathShearX < 128 ? (float)pbs.PathShearX * 0.01f : (float)(pbs.PathShearX - 256) * 0.01f; - float pathShearY = pbs.PathShearY < 128 ? (float)pbs.PathShearY * 0.01f : (float)(pbs.PathShearY - 256) * 0.01f; - float pathBegin = (float)pbs.PathBegin * 2.0e-5f; - float pathEnd = 1.0f - (float)pbs.PathEnd * 2.0e-5f; - float pathScaleX = (float)(200 - pbs.PathScaleX) * 0.01f; - float pathScaleY = (float)(200 - pbs.PathScaleY) * 0.01f; - float pathTaperX = pbs.PathTaperX * 0.01f; - float pathTaperY = pbs.PathTaperY * 0.01f; - - float profileBegin = (float)pbs.ProfileBegin * 2.0e-5f; - float profileEnd = 1.0f - (float)pbs.ProfileEnd * 2.0e-5f; - float profileHollow = (float)pbs.ProfileHollow * 2.0e-5f; - if (profileHollow > 0.95f) - profileHollow = 0.95f; - - StringBuilder buff = new StringBuilder(); - buff.Append("shape="); - buff.Append(((ProfileShape)pbs.ProfileShape).ToString()); - buff.Append(","); - buff.Append("hollow="); - buff.Append(((HollowShape)pbs.HollowShape).ToString()); - buff.Append(","); - buff.Append("pathCurve="); - buff.Append(((Extrusion)pbs.PathCurve).ToString()); - buff.Append(","); - buff.Append("profCurve="); - buff.Append(((Extrusion)pbs.ProfileCurve).ToString()); - buff.Append(","); - buff.Append("profHollow="); - buff.Append(profileHollow.ToString()); - buff.Append(","); - buff.Append("pathBegEnd="); - buff.Append(pathBegin.ToString()); - buff.Append("/"); - buff.Append(pathEnd.ToString()); - buff.Append(","); - buff.Append("profileBegEnd="); - buff.Append(profileBegin.ToString()); - buff.Append("/"); - buff.Append(profileEnd.ToString()); - buff.Append(","); - buff.Append("scaleXY="); - buff.Append(pathScaleX.ToString()); - buff.Append("/"); - buff.Append(pathScaleY.ToString()); - buff.Append(","); - buff.Append("shearXY="); - buff.Append(pathShearX.ToString()); - buff.Append("/"); - buff.Append(pathShearY.ToString()); - buff.Append(","); - buff.Append("taperXY="); - buff.Append(pbs.PathTaperX.ToString()); - buff.Append("/"); - buff.Append(pbs.PathTaperY.ToString()); - buff.Append(","); - buff.Append("skew="); - buff.Append(pbs.PathSkew.ToString()); - buff.Append(","); - buff.Append("twist/Beg="); - buff.Append(pbs.PathTwist.ToString()); - buff.Append("/"); - buff.Append(pbs.PathTwistBegin.ToString()); - - return buff.ToString(); - } + #region Extensions + public override object Extension(string pFunct, params object[] pParams) + { + DetailLog("{0} BSScene.Extension,op={1}", DetailLogZero, pFunct); + return base.Extension(pFunct, pParams); + } + #endregion // Extensions - #region Taints - // The simulation execution order is: - // Simulate() - // DoOneTimeTaints - // TriggerPreStepEvent - // DoOneTimeTaints - // Step() - // ProcessAndSendToSimulatorCollisions - // ProcessAndSendToSimulatorPropertyUpdates - // TriggerPostStepEvent - - // Calls to the PhysicsActors can't directly call into the physics engine - // because it might be busy. We delay changes to a known time. - // We rely on C#'s closure to save and restore the context for the delegate. - public void TaintedObject(string pOriginator, string pIdent, TaintCallback pCallback) - { - TaintedObject(false /*inTaintTime*/, pOriginator, pIdent, pCallback); - } - public void TaintedObject(uint pOriginator, String pIdent, TaintCallback pCallback) - { - TaintedObject(false /*inTaintTime*/, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); - } - public void TaintedObject(bool inTaintTime, String pIdent, TaintCallback pCallback) - { - TaintedObject(inTaintTime, BSScene.DetailLogZero, pIdent, pCallback); - } - public void TaintedObject(bool inTaintTime, uint pOriginator, String pIdent, TaintCallback pCallback) - { - TaintedObject(inTaintTime, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); - } - // Sometimes a potentially tainted operation can be used in and out of taint time. - // This routine executes the command immediately if in taint-time otherwise it is queued. - public void TaintedObject(bool inTaintTime, string pOriginator, string pIdent, TaintCallback pCallback) - { - if (!m_initialized) return; + public static string PrimitiveBaseShapeToString(PrimitiveBaseShape pbs) + { + float pathShearX = pbs.PathShearX < 128 ? (float)pbs.PathShearX * 0.01f : (float)(pbs.PathShearX - 256) * 0.01f; + float pathShearY = pbs.PathShearY < 128 ? (float)pbs.PathShearY * 0.01f : (float)(pbs.PathShearY - 256) * 0.01f; + float pathBegin = (float)pbs.PathBegin * 2.0e-5f; + float pathEnd = 1.0f - (float)pbs.PathEnd * 2.0e-5f; + float pathScaleX = (float)(200 - pbs.PathScaleX) * 0.01f; + float pathScaleY = (float)(200 - pbs.PathScaleY) * 0.01f; + float pathTaperX = pbs.PathTaperX * 0.01f; + float pathTaperY = pbs.PathTaperY * 0.01f; + + float profileBegin = (float)pbs.ProfileBegin * 2.0e-5f; + float profileEnd = 1.0f - (float)pbs.ProfileEnd * 2.0e-5f; + float profileHollow = (float)pbs.ProfileHollow * 2.0e-5f; + if (profileHollow > 0.95f) + profileHollow = 0.95f; + + StringBuilder buff = new StringBuilder(); + buff.Append("shape="); + buff.Append(((ProfileShape)pbs.ProfileShape).ToString()); + buff.Append(","); + buff.Append("hollow="); + buff.Append(((HollowShape)pbs.HollowShape).ToString()); + buff.Append(","); + buff.Append("pathCurve="); + buff.Append(((Extrusion)pbs.PathCurve).ToString()); + buff.Append(","); + buff.Append("profCurve="); + buff.Append(((Extrusion)pbs.ProfileCurve).ToString()); + buff.Append(","); + buff.Append("profHollow="); + buff.Append(profileHollow.ToString()); + buff.Append(","); + buff.Append("pathBegEnd="); + buff.Append(pathBegin.ToString()); + buff.Append("/"); + buff.Append(pathEnd.ToString()); + buff.Append(","); + buff.Append("profileBegEnd="); + buff.Append(profileBegin.ToString()); + buff.Append("/"); + buff.Append(profileEnd.ToString()); + buff.Append(","); + buff.Append("scaleXY="); + buff.Append(pathScaleX.ToString()); + buff.Append("/"); + buff.Append(pathScaleY.ToString()); + buff.Append(","); + buff.Append("shearXY="); + buff.Append(pathShearX.ToString()); + buff.Append("/"); + buff.Append(pathShearY.ToString()); + buff.Append(","); + buff.Append("taperXY="); + buff.Append(pbs.PathTaperX.ToString()); + buff.Append("/"); + buff.Append(pbs.PathTaperY.ToString()); + buff.Append(","); + buff.Append("skew="); + buff.Append(pbs.PathSkew.ToString()); + buff.Append(","); + buff.Append("twist/Beg="); + buff.Append(pbs.PathTwist.ToString()); + buff.Append("/"); + buff.Append(pbs.PathTwistBegin.ToString()); + + return buff.ToString(); + } - if (inTaintTime) - pCallback(); - else + #region Taints + // The simulation execution order is: + // Simulate() + // DoOneTimeTaints + // TriggerPreStepEvent + // DoOneTimeTaints + // Step() + // ProcessAndSendToSimulatorCollisions + // ProcessAndSendToSimulatorPropertyUpdates + // TriggerPostStepEvent + + // Calls to the PhysicsActors can't directly call into the physics engine + // because it might be busy. We delay changes to a known time. + // We rely on C#'s closure to save and restore the context for the delegate. + public void TaintedObject(string pOriginator, string pIdent, TaintCallback pCallback) { - lock (_taintLock) + TaintedObject(false /*inTaintTime*/, pOriginator, pIdent, pCallback); + } + public void TaintedObject(uint pOriginator, String pIdent, TaintCallback pCallback) + { + TaintedObject(false /*inTaintTime*/, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); + } + public void TaintedObject(bool inTaintTime, String pIdent, TaintCallback pCallback) + { + TaintedObject(inTaintTime, BSScene.DetailLogZero, pIdent, pCallback); + } + public void TaintedObject(bool inTaintTime, uint pOriginator, String pIdent, TaintCallback pCallback) + { + TaintedObject(inTaintTime, m_physicsLoggingEnabled ? pOriginator.ToString() : BSScene.DetailLogZero, pIdent, pCallback); + } + // Sometimes a potentially tainted operation can be used in and out of taint time. + // This routine executes the command immediately if in taint-time otherwise it is queued. + public void TaintedObject(bool inTaintTime, string pOriginator, string pIdent, TaintCallback pCallback) + { + if (!m_initialized) return; + + if (inTaintTime) + pCallback(); + else { - _taintOperations.Add(new TaintCallbackEntry(pOriginator, pIdent, pCallback)); + lock (_taintLock) + { + _taintOperations.Add(new TaintCallbackEntry(pOriginator, pIdent, pCallback)); + } } } - } - private void TriggerPreStepEvent(float timeStep) - { - PreStepAction actions = BeforeStep; - if (actions != null) - actions(timeStep); - - } + private void TriggerPreStepEvent(float timeStep) + { + PreStepAction actions = BeforeStep; + if (actions != null) + actions(timeStep); - private void TriggerPostStepEvent(float timeStep) - { - PostStepAction actions = AfterStep; - if (actions != null) - actions(timeStep); + } - } + private void TriggerPostStepEvent(float timeStep) + { + PostStepAction actions = AfterStep; + if (actions != null) + actions(timeStep); - // When someone tries to change a property on a BSPrim or BSCharacter, the object queues - // a callback into itself to do the actual property change. That callback is called - // here just before the physics engine is called to step the simulation. - public void ProcessTaints() - { - ProcessRegularTaints(); - ProcessPostTaintTaints(); - } + } - private void ProcessRegularTaints() - { - if (m_initialized && _taintOperations.Count > 0) // save allocating new list if there is nothing to process + // When someone tries to change a property on a BSPrim or BSCharacter, the object queues + // a callback into itself to do the actual property change. That callback is called + // here just before the physics engine is called to step the simulation. + public void ProcessTaints() { - // swizzle a new list into the list location so we can process what's there - List oldList; - lock (_taintLock) - { - oldList = _taintOperations; - _taintOperations = new List(); - } + ProcessRegularTaints(); + ProcessPostTaintTaints(); + } - foreach (TaintCallbackEntry tcbe in oldList) + private void ProcessRegularTaints() + { + if (m_initialized && _taintOperations.Count > 0) // save allocating new list if there is nothing to process { - try + // swizzle a new list into the list location so we can process what's there + List oldList; + lock (_taintLock) { - DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", tcbe.originator, tcbe.ident); // DEBUG DEBUG DEBUG - tcbe.callback(); + oldList = _taintOperations; + _taintOperations = new List(); } - catch (Exception e) + + foreach (TaintCallbackEntry tcbe in oldList) { - m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e); + try + { + DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", tcbe.originator, tcbe.ident); // DEBUG DEBUG DEBUG + tcbe.callback(); + } + catch (Exception e) + { + m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, tcbe.ident, e); + } } + oldList.Clear(); } - oldList.Clear(); } - } - - // Schedule an update to happen after all the regular taints are processed. - // Note that new requests for the same operation ("ident") for the same object ("ID") - // will replace any previous operation by the same object. - public void PostTaintObject(String ident, uint ID, TaintCallback callback) - { - string IDAsString = ID.ToString(); - string uniqueIdent = ident + "-" + IDAsString; - lock (_taintLock) - { - _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(IDAsString, uniqueIdent, callback); - } - - return; - } - // Taints that happen after the normal taint processing but before the simulation step. - private void ProcessPostTaintTaints() - { - if (m_initialized && _postTaintOperations.Count > 0) + // Schedule an update to happen after all the regular taints are processed. + // Note that new requests for the same operation ("ident") for the same object ("ID") + // will replace any previous operation by the same object. + public void PostTaintObject(String ident, uint ID, TaintCallback callback) { - Dictionary oldList; + string IDAsString = ID.ToString(); + string uniqueIdent = ident + "-" + IDAsString; lock (_taintLock) { - oldList = _postTaintOperations; - _postTaintOperations = new Dictionary(); + _postTaintOperations[uniqueIdent] = new TaintCallbackEntry(IDAsString, uniqueIdent, callback); } - foreach (KeyValuePair kvp in oldList) + return; + } + + // Taints that happen after the normal taint processing but before the simulation step. + private void ProcessPostTaintTaints() + { + if (m_initialized && _postTaintOperations.Count > 0) { - try + Dictionary oldList; + lock (_taintLock) { - DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG - kvp.Value.callback(); + oldList = _postTaintOperations; + _postTaintOperations = new Dictionary(); } - catch (Exception e) + + foreach (KeyValuePair kvp in oldList) { - m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e); + try + { + DetailLog("{0},BSScene.ProcessPostTaintTaints,doTaint,id={1}", DetailLogZero, kvp.Key); // DEBUG DEBUG DEBUG + kvp.Value.callback(); + } + catch (Exception e) + { + m_log.ErrorFormat("{0}: ProcessPostTaintTaints: {1}: Exception: {2}", LogHeader, kvp.Key, e); + } } + oldList.Clear(); } - oldList.Clear(); } - } - // Only used for debugging. Does not change state of anything so locking is not necessary. - public bool AssertInTaintTime(string whereFrom) - { - if (!InTaintTime) + // Only used for debugging. Does not change state of anything so locking is not necessary. + public bool AssertInTaintTime(string whereFrom) { - DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); - m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); - // Util.PrintCallStack(DetailLog); + if (!InTaintTime) + { + DetailLog("{0},BSScene.AssertInTaintTime,NOT IN TAINT TIME,Region={1},Where={2}", DetailLogZero, RegionName, whereFrom); + m_log.ErrorFormat("{0} NOT IN TAINT TIME!! Region={1}, Where={2}", LogHeader, RegionName, whereFrom); + // Util.PrintCallStack(DetailLog); + } + return InTaintTime; } - return InTaintTime; - } - #endregion // Taints + #endregion // Taints - #region IPhysicsParameters - // Get the list of parameters this physics engine supports - public PhysParameterEntry[] GetParameterList() - { - BSParam.BuildParameterTable(); - return BSParam.SettableParameters; - } - - // Set parameter on a specific or all instances. - // Return 'false' if not able to set the parameter. - // Setting the value in the m_params block will change the value the physics engine - // will use the next time since it's pinned and shared memory. - // Some of the values require calling into the physics engine to get the new - // value activated ('terrainFriction' for instance). - public bool SetPhysicsParameter(string parm, string val, uint localID) - { - bool ret = false; + #region IPhysicsParameters + // Get the list of parameters this physics engine supports + public PhysParameterEntry[] GetParameterList() + { + BSParam.BuildParameterTable(); + return BSParam.SettableParameters; + } - BSParam.ParameterDefnBase theParam; - if (BSParam.TryGetParameter(parm, out theParam)) + // Set parameter on a specific or all instances. + // Return 'false' if not able to set the parameter. + // Setting the value in the m_params block will change the value the physics engine + // will use the next time since it's pinned and shared memory. + // Some of the values require calling into the physics engine to get the new + // value activated ('terrainFriction' for instance). + public bool SetPhysicsParameter(string parm, string val, uint localID) { - // Set the value in the C# code - theParam.SetValue(this, val); + bool ret = false; - // Optionally set the parameter in the unmanaged code - if (theParam.HasSetOnObject) + BSParam.ParameterDefnBase theParam; + if (BSParam.TryGetParameter(parm, out theParam)) { - // update all the localIDs specified - // If the local ID is APPLY_TO_NONE, just change the default value - // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs - // If the localID is a specific object, apply the parameter change to only that object - List objectIDs = new List(); - switch (localID) + // Set the value in the C# code + theParam.SetValue(this, val); + + // Optionally set the parameter in the unmanaged code + if (theParam.HasSetOnObject) { - case PhysParameterEntry.APPLY_TO_NONE: - // This will cause a call into the physical world if some operation is specified (SetOnObject). - objectIDs.Add(TERRAIN_ID); - TaintedUpdateParameter(parm, objectIDs, val); - break; - case PhysParameterEntry.APPLY_TO_ALL: - lock (PhysObjects) objectIDs = new List(PhysObjects.Keys); - TaintedUpdateParameter(parm, objectIDs, val); - break; - default: - // setting only one localID - objectIDs.Add(localID); - TaintedUpdateParameter(parm, objectIDs, val); - break; + // update all the localIDs specified + // If the local ID is APPLY_TO_NONE, just change the default value + // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs + // If the localID is a specific object, apply the parameter change to only that object + List objectIDs = new List(); + switch (localID) + { + case PhysParameterEntry.APPLY_TO_NONE: + // This will cause a call into the physical world if some operation is specified (SetOnObject). + objectIDs.Add(TERRAIN_ID); + TaintedUpdateParameter(parm, objectIDs, val); + break; + case PhysParameterEntry.APPLY_TO_ALL: + lock (PhysObjects) objectIDs = new List(PhysObjects.Keys); + TaintedUpdateParameter(parm, objectIDs, val); + break; + default: + // setting only one localID + objectIDs.Add(localID); + TaintedUpdateParameter(parm, objectIDs, val); + break; + } } - } - ret = true; + ret = true; + } + return ret; } - return ret; - } - // schedule the actual updating of the paramter to when the phys engine is not busy - private void TaintedUpdateParameter(string parm, List lIDs, string val) - { - string xval = val; - List xlIDs = lIDs; - string xparm = parm; - TaintedObject(DetailLogZero, "BSScene.UpdateParameterSet", delegate() { - BSParam.ParameterDefnBase thisParam; - if (BSParam.TryGetParameter(xparm, out thisParam)) - { - if (thisParam.HasSetOnObject) + // schedule the actual updating of the paramter to when the phys engine is not busy + private void TaintedUpdateParameter(string parm, List lIDs, string val) + { + string xval = val; + List xlIDs = lIDs; + string xparm = parm; + TaintedObject(DetailLogZero, "BSScene.UpdateParameterSet", delegate() { + BSParam.ParameterDefnBase thisParam; + if (BSParam.TryGetParameter(xparm, out thisParam)) { - foreach (uint lID in xlIDs) + if (thisParam.HasSetOnObject) { - BSPhysObject theObject = null; - if (PhysObjects.TryGetValue(lID, out theObject)) - thisParam.SetOnObject(this, theObject); + foreach (uint lID in xlIDs) + { + BSPhysObject theObject = null; + if (PhysObjects.TryGetValue(lID, out theObject)) + thisParam.SetOnObject(this, theObject); + } } } - } - }); - } + }); + } - // Get parameter. - // Return 'false' if not able to get the parameter. - public bool GetPhysicsParameter(string parm, out string value) - { - string val = String.Empty; - bool ret = false; - BSParam.ParameterDefnBase theParam; - if (BSParam.TryGetParameter(parm, out theParam)) + // Get parameter. + // Return 'false' if not able to get the parameter. + public bool GetPhysicsParameter(string parm, out string value) { - val = theParam.GetValue(this); - ret = true; + string val = String.Empty; + bool ret = false; + BSParam.ParameterDefnBase theParam; + if (BSParam.TryGetParameter(parm, out theParam)) + { + val = theParam.GetValue(this); + ret = true; + } + value = val; + return ret; } - value = val; - return ret; - } - #endregion IPhysicsParameters + #endregion IPhysicsParameters - // Invoke the detailed logger and output something if it's enabled. - public void DetailLog(string msg, params Object[] args) - { - PhysicsLogging.Write(msg, args); - } - // Used to fill in the LocalID when there isn't one. It's the correct number of characters. - public const string DetailLogZero = "0000000000"; + // Invoke the detailed logger and output something if it's enabled. + public void DetailLog(string msg, params Object[] args) + { + PhysicsLogging.Write(msg, args); + } + // Used to fill in the LocalID when there isn't one. It's the correct number of characters. + public const string DetailLogZero = "0000000000"; -} + } } diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs index 1c55b51..086a412 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSShapes.cs @@ -299,7 +299,7 @@ public abstract class BSShape { xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch; physicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", - LogHeader, physicsScene.Name); + LogHeader, physicsScene.PhysicsSceneName); } } else @@ -336,7 +336,7 @@ public abstract class BSShape if (pScene != null) { buff.Append("/rgn="); - buff.Append(pScene.Name); + buff.Append(pScene.PhysicsSceneName); } return buff.ToString(); } diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs index e560796..42fc11b 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainHeightmap.cs @@ -30,7 +30,6 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.Framework; -using OpenSim.Region.CoreModules; using OpenSim.Region.PhysicsModules.SharedBase; using Nini.Config; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs index bd84fce..d11baa6 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainManager.cs @@ -30,7 +30,6 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.Framework; -using OpenSim.Region.CoreModules; using OpenSim.Region.PhysicsModules.SharedBase; using Nini.Config; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs index ae96491..cd59b65 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSTerrainMesh.cs @@ -30,7 +30,6 @@ using System.Text; using OpenSim.Framework; using OpenSim.Region.Framework; -using OpenSim.Region.CoreModules; using OpenSim.Region.PhysicsModules.SharedBase; using Nini.Config; diff --git a/OpenSim/Region/PhysicsModules/BulletS/Properties/AssemblyInfo.cs b/OpenSim/Region/PhysicsModules/BulletS/Properties/AssemblyInfo.cs index 4f90eee..5a33bdf 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Properties/AssemblyInfo.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Mono.Addins; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information @@ -31,3 +32,5 @@ using System.Runtime.InteropServices; // [assembly: AssemblyVersion("0.8.2.*")] +[assembly: Addin("OpenSim.Region.PhysicsModule.BulletS", OpenSim.VersionInfo.VersionNumber)] +[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)] -- cgit v1.1 From 11194209df8a29f5103e6e34104eae7834f3280a Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 31 Aug 2015 14:09:15 -0700 Subject: First commit where physics work as region module. Moved all physics dlls out of Physics and into bin directly, so they can be found by the module loader. Removed call to PhysicsPluginManager. --- OpenSim/Region/PhysicsModules/BulletS/BSScene.cs | 5 +++- .../BulletS/Tests/BulletSimTestsUtil.cs | 27 +++++++++++++++------- 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'OpenSim/Region/PhysicsModules/BulletS') diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs index 26af343..b412561 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs @@ -248,7 +248,10 @@ namespace OpenSim.Region.PhysicsModule.BulletS scene.RegisterModuleInterface(this); Vector3 extent = new Vector3(scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY, scene.RegionInfo.RegionSizeZ); - Initialise(m_Config, extent); + Initialise(m_Config, extent); + + base.Initialise(scene.PhysicsRequestAsset, scene.Heightmap.GetFloatsSerialised(), (float)scene.RegionInfo.RegionSettings.WaterHeight); + } public void RemoveRegion(Scene scene) diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs index 34c0571..d86c841 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs @@ -35,6 +35,7 @@ using Nini.Config; using OpenSim.Framework; using OpenSim.Region.PhysicsModules.SharedBase; using OpenSim.Region.PhysicsModules.Meshing; +using OpenSim.Region.Framework.Interfaces; using OpenMetaverse; @@ -78,22 +79,32 @@ public static class BulletSimTestsUtil bulletSimConfig.Set("VehicleLoggingEnabled","True"); } - PhysicsPluginManager physicsPluginManager; - physicsPluginManager = new PhysicsPluginManager(); - physicsPluginManager.LoadPluginsFromAssemblies("Physics"); - Vector3 regionExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); - PhysicsScene pScene = physicsPluginManager.GetPhysicsScene( - "BulletSim", "Meshmerizer", openSimINI, "BSTestRegion", regionExtent); + //PhysicsScene pScene = physicsPluginManager.GetPhysicsScene( + // "BulletSim", "Meshmerizer", openSimINI, "BSTestRegion", regionExtent); + RegionInfo info = new RegionInfo(); + info.RegionName = "BSTestRegion"; + info.RegionSizeX = info.RegionSizeY = info.RegionSizeZ = Constants.RegionSize; + OpenSim.Region.Framework.Scenes.Scene scene = new OpenSim.Region.Framework.Scenes.Scene(info); + + IMesher mesher = new OpenSim.Region.PhysicsModules.Meshing.Meshmerizer(); + INonSharedRegionModule mod = mesher as INonSharedRegionModule; + mod.Initialise(openSimINI); + mod.AddRegion(scene); + mod.RegionLoaded(scene); - BSScene bsScene = pScene as BSScene; + BSScene pScene = new BSScene(); + mod = (pScene as INonSharedRegionModule); + mod.Initialise(openSimINI); + mod.AddRegion(scene); + mod.RegionLoaded(scene); // Since the asset requestor is not initialized, any mesh or sculptie will be a cube. // In the future, add a fake asset fetcher to get meshes and sculpts. // bsScene.RequestAssetMethod = ???; - return bsScene; + return pScene; } } -- cgit v1.1 From 50e7e38f450599b5755c9ab887b8d9bd9c3d8981 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 31 Aug 2015 15:04:10 -0700 Subject: Physics refactoring: all unit tests pass. --- OpenSim/Region/PhysicsModules/BulletS/BSScene.cs | 4 +++- OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'OpenSim/Region/PhysicsModules/BulletS') diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs index b412561..d116c3b 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs @@ -250,7 +250,9 @@ namespace OpenSim.Region.PhysicsModule.BulletS Vector3 extent = new Vector3(scene.RegionInfo.RegionSizeX, scene.RegionInfo.RegionSizeY, scene.RegionInfo.RegionSizeZ); Initialise(m_Config, extent); - base.Initialise(scene.PhysicsRequestAsset, scene.Heightmap.GetFloatsSerialised(), (float)scene.RegionInfo.RegionSettings.WaterHeight); + base.Initialise(scene.PhysicsRequestAsset, + (scene.Heightmap != null ? scene.Heightmap.GetFloatsSerialised() : new float[Constants.RegionSize * Constants.RegionSize]), + (float)scene.RegionInfo.RegionSettings.WaterHeight); } diff --git a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs index d86c841..4eeea4d 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/Tests/BulletSimTestsUtil.cs @@ -81,8 +81,6 @@ public static class BulletSimTestsUtil Vector3 regionExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); - //PhysicsScene pScene = physicsPluginManager.GetPhysicsScene( - // "BulletSim", "Meshmerizer", openSimINI, "BSTestRegion", regionExtent); RegionInfo info = new RegionInfo(); info.RegionName = "BSTestRegion"; info.RegionSizeX = info.RegionSizeY = info.RegionSizeZ = Constants.RegionSize; -- cgit v1.1 From 9435405ca1c173963dd6e97116a27b798a211801 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 31 Aug 2015 16:23:43 -0700 Subject: Deleted physics plugin classes. More unit tests fixed. --- OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs | 76 ----------------------- OpenSim/Region/PhysicsModules/BulletS/BSScene.cs | 2 +- 2 files changed, 1 insertion(+), 77 deletions(-) delete mode 100644 OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs (limited to 'OpenSim/Region/PhysicsModules/BulletS') diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs deleted file mode 100644 index 7fc51f7..0000000 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPlugin.cs +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyrightD - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -using System; -using System.Collections.Generic; -using OpenSim.Framework; -using OpenSim.Region.PhysicsModules.SharedBase; -using OpenMetaverse; - -namespace OpenSim.Region.PhysicsModule.BulletS -{ - /// - /// Entry for a port of Bullet (http://bulletphysics.org/) to OpenSim. - /// This module interfaces to an unmanaged C++ library which makes the - /// actual calls into the Bullet physics engine. - /// The unmanaged library is found in opensim-libs::trunk/unmanaged/BulletSim/. - /// The unmanaged library is compiled and linked statically with Bullet - /// to create BulletSim.dll and libBulletSim.so (for both 32 and 64 bit). - /// -//public class BSPlugin : IPhysicsPlugin -//{ -// //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - -// private BSScene _mScene; - -// public BSPlugin() -// { -// } - -// public bool Init() -// { -// return true; -// } - -// public PhysicsScene GetScene(String sceneIdentifier) -// { -// if (_mScene == null) -// { -// _mScene = new BSScene(GetName(), sceneIdentifier); -// } -// return (_mScene); -// } - -// public string GetName() -// { -// return ("BulletSim"); -// } - -// public void Dispose() -// { -// } -//} -} diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs index d116c3b..db4d3cf 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs @@ -251,7 +251,7 @@ namespace OpenSim.Region.PhysicsModule.BulletS Initialise(m_Config, extent); base.Initialise(scene.PhysicsRequestAsset, - (scene.Heightmap != null ? scene.Heightmap.GetFloatsSerialised() : new float[Constants.RegionSize * Constants.RegionSize]), + (scene.Heightmap != null ? scene.Heightmap.GetFloatsSerialised() : new float[scene.RegionInfo.RegionSizeX * scene.RegionInfo.RegionSizeY]), (float)scene.RegionInfo.RegionSettings.WaterHeight); } -- cgit v1.1 From 0235e58fff47d3794777392a987feb4de8a5f99a Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 1 Sep 2015 09:20:20 -0700 Subject: Moved ExtendedPhysics from OptionalModules to Bullet project, because it's very much an optional Bullet feature. This way, Bullet doesn't need to depend on the OptionalModules dll. No changes in configs or behavior. --- .../PhysicsModules/BulletS/BSLinksetConstraints.cs | 2 - OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs | 1 - .../PhysicsModules/BulletS/BSPrimLinkable.cs | 1 - .../PhysicsModules/BulletS/ExtendedPhysics.cs | 622 +++++++++++++++++++++ 4 files changed, 622 insertions(+), 4 deletions(-) create mode 100755 OpenSim/Region/PhysicsModules/BulletS/ExtendedPhysics.cs (limited to 'OpenSim/Region/PhysicsModules/BulletS') diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs index d7be470..c4b4c86 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs @@ -28,8 +28,6 @@ using System; using System.Collections.Generic; using System.Text; -using OpenSim.Region.OptionalModules.Scripting; - using OMV = OpenMetaverse; namespace OpenSim.Region.PhysicsModule.BulletS diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs index 3e74428..6f27ac7 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrim.cs @@ -34,7 +34,6 @@ using OMV = OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.PhysicsModules.SharedBase; using OpenSim.Region.PhysicsModule.ConvexDecompositionDotNet; -using OpenSim.Region.OptionalModules.Scripting; // for ExtendedPhysics namespace OpenSim.Region.PhysicsModule.BulletS { diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs b/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs index 8f494cc..55b5da0 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSPrimLinkable.cs @@ -30,7 +30,6 @@ using System.Linq; using System.Text; using OpenSim.Framework; -using OpenSim.Region.OptionalModules.Scripting; using OMV = OpenMetaverse; diff --git a/OpenSim/Region/PhysicsModules/BulletS/ExtendedPhysics.cs b/OpenSim/Region/PhysicsModules/BulletS/ExtendedPhysics.cs new file mode 100755 index 0000000..2ba3c5a --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/ExtendedPhysics.cs @@ -0,0 +1,622 @@ +/* + * 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.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.PhysicsModules.SharedBase; + +using Mono.Addins; +using Nini.Config; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Region.PhysicsModule.BulletS +{ + [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 PhysFunctGetLinkType = "BulletSim.GetLinkType"; + public const string PhysFunctChangeLinkParams = "BulletSim.ChangeLinkParams"; + public const string PhysFunctAxisLockLimits = "BulletSim.AxisLockLimits"; + + // ============================================================= + + 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(); + 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 const 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; + } + + // Code for specifying params. + // The choice if 14700 is arbitrary and only serves to catch parameter code misuse. + [ScriptConstant] + public const int PHYS_AXIS_LOCK_LINEAR = 14700; + [ScriptConstant] + public const int PHYS_AXIS_LOCK_LINEAR_X = 14701; + [ScriptConstant] + public const int PHYS_AXIS_LIMIT_LINEAR_X = 14702; + [ScriptConstant] + public const int PHYS_AXIS_LOCK_LINEAR_Y = 14703; + [ScriptConstant] + public const int PHYS_AXIS_LIMIT_LINEAR_Y = 14704; + [ScriptConstant] + public const int PHYS_AXIS_LOCK_LINEAR_Z = 14705; + [ScriptConstant] + public const int PHYS_AXIS_LIMIT_LINEAR_Z = 14706; + [ScriptConstant] + public const int PHYS_AXIS_LOCK_ANGULAR = 14707; + [ScriptConstant] + public const int PHYS_AXIS_LOCK_ANGULAR_X = 14708; + [ScriptConstant] + public const int PHYS_AXIS_LIMIT_ANGULAR_X = 14709; + [ScriptConstant] + public const int PHYS_AXIS_LOCK_ANGULAR_Y = 14710; + [ScriptConstant] + public const int PHYS_AXIS_LIMIT_ANGULAR_Y = 14711; + [ScriptConstant] + public const int PHYS_AXIS_LOCK_ANGULAR_Z = 14712; + [ScriptConstant] + public const int PHYS_AXIS_LIMIT_ANGULAR_Z = 14713; + [ScriptConstant] + public const int PHYS_AXIS_UNLOCK_LINEAR = 14714; + [ScriptConstant] + public const int PHYS_AXIS_UNLOCK_LINEAR_X = 14715; + [ScriptConstant] + public const int PHYS_AXIS_UNLOCK_LINEAR_Y = 14716; + [ScriptConstant] + public const int PHYS_AXIS_UNLOCK_LINEAR_Z = 14717; + [ScriptConstant] + public const int PHYS_AXIS_UNLOCK_ANGULAR = 14718; + [ScriptConstant] + public const int PHYS_AXIS_UNLOCK_ANGULAR_X = 14719; + [ScriptConstant] + public const int PHYS_AXIS_UNLOCK_ANGULAR_Y = 14720; + [ScriptConstant] + public const int PHYS_AXIS_UNLOCK_ANGULAR_Z = 14721; + [ScriptConstant] + public const int PHYS_AXIS_UNLOCK = 14722; + // physAxisLockLimits() + [ScriptInvocation] + public int physAxisLock(UUID hostID, UUID scriptID, object[] parms) + { + int ret = -1; + if (!Enabled) return ret; + + PhysicsActor rootPhysActor; + if (GetRootPhysActor(hostID, out rootPhysActor)) + { + object[] parms2 = AddToBeginningOfArray(rootPhysActor, null, parms); + ret = MakeIntError(rootPhysActor.Extension(PhysFunctAxisLockLimits, parms2)); + } + + return ret; + } + + [ScriptConstant] + public const int PHYS_LINKSET_TYPE_CONSTRAINT = 0; + [ScriptConstant] + public const int PHYS_LINKSET_TYPE_COMPOUND = 1; + [ScriptConstant] + public const 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); + + object[] parms2 = { rootPhysActor, null, linksetType }; + ret = MakeIntError(rootPhysActor.Extension(PhysFunctSetLinksetType, parms2)); + 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. + object[] parms2 = { rootPhysActor, null, linksetType }; + ret = MakeIntError(rootPhysActor.Extension(PhysFunctSetLinksetType, parms2)); + } + } + 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; + + PhysicsActor rootPhysActor; + if (GetRootPhysActor(hostID, out rootPhysActor)) + { + object[] parms2 = { rootPhysActor, null }; + ret = MakeIntError(rootPhysActor.Extension(PhysFunctGetLinksetType, parms2)); + } + else + { + m_log.WarnFormat("{0} physGetLinsetType: cannot find script object in scene. hostID={1}", LogHeader, hostID); + } + return ret; + } + + [ScriptConstant] + public const int PHYS_LINK_TYPE_FIXED = 1234; + [ScriptConstant] + public const int PHYS_LINK_TYPE_HINGE = 4; + [ScriptConstant] + public const int PHYS_LINK_TYPE_SPRING = 9; + [ScriptConstant] + public const int PHYS_LINK_TYPE_6DOF = 6; + [ScriptConstant] + public const 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)) + { + object[] parms2 = { rootPhysActor, childPhysActor, typeCode }; + ret = MakeIntError(rootPhysActor.Extension(PhysFunctChangeLinkType, parms2)); + } + + return ret; + } + + // physGetLinkType(integer linkNum) + [ScriptInvocation] + public int physGetLinkType(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)) + { + object[] parms2 = { rootPhysActor, childPhysActor }; + ret = MakeIntError(rootPhysActor.Extension(PhysFunctGetLinkType, parms2)); + } + + 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)) + { + object[] parms2 = { rootPhysActor, childPhysActor , PHYS_LINK_TYPE_FIXED }; + ret = MakeIntError(rootPhysActor.Extension(PhysFunctChangeLinkType, parms2)); + } + + return ret; + } + + // Code for specifying params. + // The choice if 14400 is arbitrary and only serves to catch parameter code misuse. + public const int PHYS_PARAM_MIN = 14401; + + [ScriptConstant] + public const int PHYS_PARAM_FRAMEINA_LOC = 14401; + [ScriptConstant] + public const int PHYS_PARAM_FRAMEINA_ROT = 14402; + [ScriptConstant] + public const int PHYS_PARAM_FRAMEINB_LOC = 14403; + [ScriptConstant] + public const int PHYS_PARAM_FRAMEINB_ROT = 14404; + [ScriptConstant] + public const int PHYS_PARAM_LINEAR_LIMIT_LOW = 14405; + [ScriptConstant] + public const int PHYS_PARAM_LINEAR_LIMIT_HIGH = 14406; + [ScriptConstant] + public const int PHYS_PARAM_ANGULAR_LIMIT_LOW = 14407; + [ScriptConstant] + public const int PHYS_PARAM_ANGULAR_LIMIT_HIGH = 14408; + [ScriptConstant] + public const int PHYS_PARAM_USE_FRAME_OFFSET = 14409; + [ScriptConstant] + public const int PHYS_PARAM_ENABLE_TRANSMOTOR = 14410; + [ScriptConstant] + public const int PHYS_PARAM_TRANSMOTOR_MAXVEL = 14411; + [ScriptConstant] + public const int PHYS_PARAM_TRANSMOTOR_MAXFORCE = 14412; + [ScriptConstant] + public const int PHYS_PARAM_CFM = 14413; + [ScriptConstant] + public const int PHYS_PARAM_ERP = 14414; + [ScriptConstant] + public const int PHYS_PARAM_SOLVER_ITERATIONS = 14415; + [ScriptConstant] + public const int PHYS_PARAM_SPRING_AXIS_ENABLE = 14416; + [ScriptConstant] + public const int PHYS_PARAM_SPRING_DAMPING = 14417; + [ScriptConstant] + public const int PHYS_PARAM_SPRING_STIFFNESS = 14418; + [ScriptConstant] + public const int PHYS_PARAM_LINK_TYPE = 14419; + [ScriptConstant] + public const int PHYS_PARAM_USE_LINEAR_FRAMEA = 14420; + [ScriptConstant] + public const int PHYS_PARAM_SPRING_EQUILIBRIUM_POINT = 14421; + + public const int PHYS_PARAM_MAX = 14421; + + // Used when specifying a parameter that has settings for the three linear and three angular axis + [ScriptConstant] + public const int PHYS_AXIS_ALL = -1; + [ScriptConstant] + public const int PHYS_AXIS_LINEAR_ALL = -2; + [ScriptConstant] + public const int PHYS_AXIS_ANGULAR_ALL = -3; + [ScriptConstant] + public const int PHYS_AXIS_LINEAR_X = 0; + [ScriptConstant] + public const int PHYS_AXIS_LINEAR_Y = 1; + [ScriptConstant] + public const int PHYS_AXIS_LINEAR_Z = 2; + [ScriptConstant] + public const int PHYS_AXIS_ANGULAR_X = 3; + [ScriptConstant] + public const int PHYS_AXIS_ANGULAR_Y = 4; + [ScriptConstant] + public const int PHYS_AXIS_ANGULAR_Z = 5; + + // 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)) + { + object[] parms2 = AddToBeginningOfArray(rootPhysActor, childPhysActor, parms); + ret = MakeIntError(rootPhysActor.Extension(PhysFunctChangeLinkParams, parms2)); + } + + 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; + } + + // Return an array of objects with the passed object as the first object of a new array + private object[] AddToBeginningOfArray(object firstOne, object secondOne, object[] prevArray) + { + object[] newArray = new object[2 + prevArray.Length]; + newArray[0] = firstOne; + newArray[1] = secondOne; + prevArray.CopyTo(newArray, 2); + return newArray; + } + + // 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; + } + } +} -- cgit v1.1 From 2210c5807f9d75eab87098c7d2c49d67543043e3 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Fri, 4 Sep 2015 16:33:07 -0700 Subject: Fixes a problem with Bullet physics when it is configured to run on a separate thread. --- OpenSim/Region/PhysicsModules/BulletS/BSScene.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'OpenSim/Region/PhysicsModules/BulletS') diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs index db4d3cf..452ce55 100644 --- a/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSScene.cs @@ -270,6 +270,8 @@ namespace OpenSim.Region.PhysicsModule.BulletS mesher = scene.RequestModuleInterface(); if (mesher == null) m_log.WarnFormat("{0} No mesher. Things will not work well.", LogHeader); + + scene.PhysicsEnabled = true; } #endregion -- cgit v1.1