aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/OdePlugin/OdeScene.cs')
-rw-r--r--OpenSim/Region/Physics/OdePlugin/OdeScene.cs799
1 files changed, 406 insertions, 393 deletions
diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
index a307469..6e603e8 100644
--- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
+++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
@@ -100,7 +100,7 @@ namespace OpenSim.Region.Physics.OdePlugin
100 Rubber = 6 100 Rubber = 6
101 } 101 }
102 102
103 public sealed class OdeScene : PhysicsScene 103 public class OdeScene : PhysicsScene
104 { 104 {
105 private readonly ILog m_log; 105 private readonly ILog m_log;
106 // private Dictionary<string, sCollisionData> m_storedCollisions = new Dictionary<string, sCollisionData>(); 106 // private Dictionary<string, sCollisionData> m_storedCollisions = new Dictionary<string, sCollisionData>();
@@ -198,7 +198,12 @@ namespace OpenSim.Region.Physics.OdePlugin
198 private readonly List<OdePrim> _taintedPrimL = new List<OdePrim>(); 198 private readonly List<OdePrim> _taintedPrimL = new List<OdePrim>();
199 private readonly HashSet<OdeCharacter> _taintedActors = new HashSet<OdeCharacter>(); 199 private readonly HashSet<OdeCharacter> _taintedActors = new HashSet<OdeCharacter>();
200 private readonly List<d.ContactGeom> _perloopContact = new List<d.ContactGeom>(); 200 private readonly List<d.ContactGeom> _perloopContact = new List<d.ContactGeom>();
201
202 /// <summary>
203 /// A list of actors that should receive collision events.
204 /// </summary>
201 private readonly List<PhysicsActor> _collisionEventPrim = new List<PhysicsActor>(); 205 private readonly List<PhysicsActor> _collisionEventPrim = new List<PhysicsActor>();
206
202 private readonly HashSet<OdeCharacter> _badCharacter = new HashSet<OdeCharacter>(); 207 private readonly HashSet<OdeCharacter> _badCharacter = new HashSet<OdeCharacter>();
203 public Dictionary<IntPtr, String> geom_name_map = new Dictionary<IntPtr, String>(); 208 public Dictionary<IntPtr, String> geom_name_map = new Dictionary<IntPtr, String>();
204 public Dictionary<IntPtr, PhysicsActor> actor_name_map = new Dictionary<IntPtr, PhysicsActor>(); 209 public Dictionary<IntPtr, PhysicsActor> actor_name_map = new Dictionary<IntPtr, PhysicsActor>();
@@ -299,7 +304,6 @@ namespace OpenSim.Region.Physics.OdePlugin
299 // Create the world and the first space 304 // Create the world and the first space
300 world = d.WorldCreate(); 305 world = d.WorldCreate();
301 space = d.HashSpaceCreate(IntPtr.Zero); 306 space = d.HashSpaceCreate(IntPtr.Zero);
302
303 307
304 contactgroup = d.JointGroupCreate(0); 308 contactgroup = d.JointGroupCreate(0);
305 //contactgroup 309 //contactgroup
@@ -952,7 +956,6 @@ namespace OpenSim.Region.Physics.OdePlugin
952 character.SetPidStatus(true); 956 character.SetPidStatus(true);
953 } 957 }
954 } 958 }
955
956 959
957 if (p1.PhysicsActorType == (int) ActorTypes.Agent) 960 if (p1.PhysicsActorType == (int) ActorTypes.Agent)
958 { 961 {
@@ -1053,9 +1056,7 @@ namespace OpenSim.Region.Physics.OdePlugin
1053 { 1056 {
1054 joint = d.JointCreateContact(world, contactgroup, ref m_materialContacts[material, movintYN]); 1057 joint = d.JointCreateContact(world, contactgroup, ref m_materialContacts[material, movintYN]);
1055 m_global_contactcount++; 1058 m_global_contactcount++;
1056
1057 } 1059 }
1058
1059 } 1060 }
1060 else 1061 else
1061 { 1062 {
@@ -1078,7 +1079,6 @@ namespace OpenSim.Region.Physics.OdePlugin
1078 { 1079 {
1079 joint = d.JointCreateContact(world, contactgroup, ref m_materialContacts[material, movintYN]); 1080 joint = d.JointCreateContact(world, contactgroup, ref m_materialContacts[material, movintYN]);
1080 m_global_contactcount++; 1081 m_global_contactcount++;
1081
1082 } 1082 }
1083 } 1083 }
1084 } 1084 }
@@ -1290,6 +1290,7 @@ namespace OpenSim.Region.Physics.OdePlugin
1290 1290
1291 //returncollisions = true; 1291 //returncollisions = true;
1292 break; 1292 break;
1293
1293 case ActorTypes.Prim: 1294 case ActorTypes.Prim:
1294 if (p1 is OdePrim) 1295 if (p1 is OdePrim)
1295 { 1296 {
@@ -1317,6 +1318,7 @@ namespace OpenSim.Region.Physics.OdePlugin
1317 1318
1318 cc2.AddCollisionEvent(obj2LocalID, contact); 1319 cc2.AddCollisionEvent(obj2LocalID, contact);
1319 break; 1320 break;
1321
1320 case ActorTypes.Prim: 1322 case ActorTypes.Prim:
1321 1323
1322 if (p2 is OdePrim) 1324 if (p2 is OdePrim)
@@ -1421,18 +1423,18 @@ namespace OpenSim.Region.Physics.OdePlugin
1421 1423
1422 public int TriCallback(IntPtr trimesh, IntPtr refObject, int triangleIndex) 1424 public int TriCallback(IntPtr trimesh, IntPtr refObject, int triangleIndex)
1423 { 1425 {
1424 String name1 = null; 1426// String name1 = null;
1425 String name2 = null; 1427// String name2 = null;
1426 1428//
1427 if (!geom_name_map.TryGetValue(trimesh, out name1)) 1429// if (!geom_name_map.TryGetValue(trimesh, out name1))
1428 { 1430// {
1429 name1 = "null"; 1431// name1 = "null";
1430 } 1432// }
1431 1433//
1432 if (!geom_name_map.TryGetValue(refObject, out name2)) 1434// if (!geom_name_map.TryGetValue(refObject, out name2))
1433 { 1435// {
1434 name2 = "null"; 1436// name2 = "null";
1435 } 1437// }
1436 1438
1437 // m_log.InfoFormat("TriCallback: A collision was detected between {1} and {2}. Index was {3}", 0, name1, name2, triangleIndex); 1439 // m_log.InfoFormat("TriCallback: A collision was detected between {1} and {2}. Index was {3}", 0, name1, name2, triangleIndex);
1438 1440
@@ -1604,7 +1606,11 @@ namespace OpenSim.Region.Physics.OdePlugin
1604 } 1606 }
1605// End recovered. Kitto Flora 1607// End recovered. Kitto Flora
1606 1608
1607 public void addCollisionEventReporting(PhysicsActor obj) 1609 /// <summary>
1610 /// Add actor to the list that should receive collision events in the simulate loop.
1611 /// </summary>
1612 /// <param name="obj"></param>
1613 public void AddCollisionEventReporting(PhysicsActor obj)
1608 { 1614 {
1609 lock (_collisionEventPrim) 1615 lock (_collisionEventPrim)
1610 { 1616 {
@@ -1613,7 +1619,11 @@ namespace OpenSim.Region.Physics.OdePlugin
1613 } 1619 }
1614 } 1620 }
1615 1621
1616 public void remCollisionEventReporting(PhysicsActor obj) 1622 /// <summary>
1623 /// Remove actor from the list that should receive collision events in the simulate loop.
1624 /// </summary>
1625 /// <param name="obj"></param>
1626 public void RemoveCollisionEventReporting(PhysicsActor obj)
1617 { 1627 {
1618 lock (_collisionEventPrim) 1628 lock (_collisionEventPrim)
1619 { 1629 {
@@ -1677,7 +1687,7 @@ namespace OpenSim.Region.Physics.OdePlugin
1677 } 1687 }
1678 1688
1679 private PhysicsActor AddPrim(String name, Vector3 position, Vector3 size, Quaternion rotation, 1689 private PhysicsActor AddPrim(String name, Vector3 position, Vector3 size, Quaternion rotation,
1680 IMesh mesh, PrimitiveBaseShape pbs, bool isphysical, uint localID) 1690 PrimitiveBaseShape pbs, bool isphysical, uint localID)
1681 { 1691 {
1682 Vector3 pos = position; 1692 Vector3 pos = position;
1683 Vector3 siz = size; 1693 Vector3 siz = size;
@@ -1686,7 +1696,7 @@ namespace OpenSim.Region.Physics.OdePlugin
1686 OdePrim newPrim; 1696 OdePrim newPrim;
1687 lock (OdeLock) 1697 lock (OdeLock)
1688 { 1698 {
1689 newPrim = new OdePrim(name, this, pos, siz, rot, mesh, pbs, isphysical, ode); 1699 newPrim = new OdePrim(name, this, pos, siz, rot, pbs, isphysical, ode);
1690 1700
1691 lock (_prims) 1701 lock (_prims)
1692 _prims.Add(newPrim); 1702 _prims.Add(newPrim);
@@ -1714,28 +1724,7 @@ namespace OpenSim.Region.Physics.OdePlugin
1714 m_log.DebugFormat("[PHYSICS]: Adding physics actor to {0}", primName); 1724 m_log.DebugFormat("[PHYSICS]: Adding physics actor to {0}", primName);
1715#endif 1725#endif
1716 1726
1717 PhysicsActor result; 1727 return AddPrim(primName, position, size, rotation, pbs, isPhysical, localid);
1718 IMesh mesh = null;
1719
1720 // Don't create the mesh here - wait until the mesh data is loaded from the asset store.
1721// if (needsMeshing(pbs))
1722// {
1723// try
1724// {
1725// mesh = mesher.CreateMesh(primName, pbs, size, 32f, isPhysical);
1726// }
1727// catch(Exception e)
1728// {
1729// m_log.ErrorFormat("[PHYSICS]: Exception while meshing prim {0}.", primName);
1730// m_log.Debug(e.ToString());
1731// mesh = null;
1732// return null;
1733// }
1734// }
1735
1736 result = AddPrim(primName, position, size, rotation, mesh, pbs, isPhysical, localid);
1737
1738 return result;
1739 } 1728 }
1740 1729
1741 public override float TimeDilation 1730 public override float TimeDilation
@@ -2105,6 +2094,8 @@ namespace OpenSim.Region.Physics.OdePlugin
2105 2094
2106 public override void RemovePrim(PhysicsActor prim) 2095 public override void RemovePrim(PhysicsActor prim)
2107 { 2096 {
2097 // As with all ODE physics operations, we don't remove the prim immediately but signal that it should be
2098 // removed in the next physics simulate pass.
2108 if (prim is OdePrim) 2099 if (prim is OdePrim)
2109 { 2100 {
2110 lock (OdeLock) 2101 lock (OdeLock)
@@ -2121,6 +2112,9 @@ namespace OpenSim.Region.Physics.OdePlugin
2121 /// <summary> 2112 /// <summary>
2122 /// This is called from within simulate but outside the locked portion 2113 /// This is called from within simulate but outside the locked portion
2123 /// We need to do our own locking here 2114 /// We need to do our own locking here
2115 /// (Note: As of 20110801 this no longer appears to be true - this is being called within lock (odeLock) in
2116 /// Simulate() -- justincc).
2117 ///
2124 /// Essentially, we need to remove the prim from our space segment, whatever segment it's in. 2118 /// Essentially, we need to remove the prim from our space segment, whatever segment it's in.
2125 /// 2119 ///
2126 /// If there are no more prim in the segment, we need to empty (spacedestroy)the segment and reclaim memory 2120 /// If there are no more prim in the segment, we need to empty (spacedestroy)the segment and reclaim memory
@@ -2132,7 +2126,7 @@ namespace OpenSim.Region.Physics.OdePlugin
2132//Console.WriteLine("RemovePrimThreadLocked " + prim.m_primName); 2126//Console.WriteLine("RemovePrimThreadLocked " + prim.m_primName);
2133 lock (prim) 2127 lock (prim)
2134 { 2128 {
2135 remCollisionEventReporting(prim); 2129 RemoveCollisionEventReporting(prim);
2136 lock (ode) 2130 lock (ode)
2137 { 2131 {
2138 if (prim.prim_geom != IntPtr.Zero) 2132 if (prim.prim_geom != IntPtr.Zero)
@@ -2177,24 +2171,12 @@ namespace OpenSim.Region.Physics.OdePlugin
2177 //} 2171 //}
2178 //} 2172 //}
2179 //m_log.Warn(prim.prim_geom); 2173 //m_log.Warn(prim.prim_geom);
2180 try 2174
2181 { 2175 if (!prim.RemoveGeom())
2182 if (prim.prim_geom != IntPtr.Zero) 2176 m_log.Warn("[PHYSICS]: Unable to remove prim from physics scene");
2183 { 2177
2184 d.GeomDestroy(prim.prim_geom);
2185 prim.prim_geom = IntPtr.Zero;
2186 }
2187 else
2188 {
2189 m_log.Warn("[PHYSICS]: Unable to remove prim from physics scene");
2190 }
2191 }
2192 catch (AccessViolationException)
2193 {
2194 m_log.Info("[PHYSICS]: Couldn't remove prim from physics scene, it was already be removed.");
2195 }
2196 lock (_prims) 2178 lock (_prims)
2197 _prims.Remove(prim); 2179 _prims.Remove(prim);
2198 2180
2199 //If there are no more geometries in the sub-space, we don't need it in the main space anymore 2181 //If there are no more geometries in the sub-space, we don't need it in the main space anymore
2200 //if (d.SpaceGetNumGeoms(prim.m_targetSpace) == 0) 2182 //if (d.SpaceGetNumGeoms(prim.m_targetSpace) == 0)
@@ -2687,320 +2669,148 @@ Console.WriteLine("AddPhysicsActorTaint to " + taintedprim.Name);
2687 //if (!ode.lockquery()) 2669 //if (!ode.lockquery())
2688 //{ 2670 //{
2689 // ode.dlock(world); 2671 // ode.dlock(world);
2690 try
2691 {
2692 // Insert, remove Characters
2693 bool processedtaints = false;
2694 2672
2695 lock (_taintedActors) 2673 try
2696 { 2674 {
2697 if (_taintedActors.Count > 0) 2675 // Insert, remove Characters
2698 { 2676 bool processedtaints = false;
2699 foreach (OdeCharacter character in _taintedActors)
2700 {
2701 character.ProcessTaints(timeStep);
2702 2677
2703 processedtaints = true; 2678 lock (_taintedActors)
2704 //character.m_collisionscore = 0; 2679 {
2705 } 2680 if (_taintedActors.Count > 0)
2681 {
2682 foreach (OdeCharacter character in _taintedActors)
2683 {
2684 character.ProcessTaints(timeStep);
2706 2685
2707 if (processedtaints) 2686 processedtaints = true;
2708 _taintedActors.Clear(); 2687 //character.m_collisionscore = 0;
2709 }
2710 } 2688 }
2711 2689
2712 // Modify other objects in the scene. 2690 if (processedtaints)
2713 processedtaints = false; 2691 _taintedActors.Clear();
2692 }
2693 }
2694
2695 // Modify other objects in the scene.
2696 processedtaints = false;
2714 2697
2715 lock (_taintedPrimLock) 2698 lock (_taintedPrimLock)
2699 {
2700 foreach (OdePrim prim in _taintedPrimL)
2701 {
2702 if (prim.m_taintremove)
2716 { 2703 {
2717 foreach (OdePrim prim in _taintedPrimL) 2704// Console.WriteLine("Simulate calls RemovePrimThreadLocked for {0}", prim.Name);
2718 { 2705 RemovePrimThreadLocked(prim);
2719 if (prim.m_taintremove) 2706 }
2720 { 2707 else
2721 //Console.WriteLine("Simulate calls RemovePrimThreadLocked"); 2708 {
2722 RemovePrimThreadLocked(prim); 2709// Console.WriteLine("Simulate calls ProcessTaints for {0}", prim.Name);
2723 } 2710 prim.ProcessTaints(timeStep);
2724 else 2711 }
2725 {
2726 //Console.WriteLine("Simulate calls ProcessTaints");
2727 prim.ProcessTaints(timeStep);
2728 }
2729 processedtaints = true;
2730 prim.m_collisionscore = 0;
2731
2732 // This loop can block up the Heartbeat for a very long time on large regions.
2733 // We need to let the Watchdog know that the Heartbeat is not dead
2734 // NOTE: This is currently commented out, but if things like OAR loading are
2735 // timing the heartbeat out we will need to uncomment it
2736 //Watchdog.UpdateThread();
2737 }
2738 2712
2739 if (SupportsNINJAJoints) 2713 processedtaints = true;
2740 { 2714 prim.m_collisionscore = 0;
2741 // Create pending joints, if possible
2742 2715
2743 // joints can only be processed after ALL bodies are processed (and exist in ODE), since creating 2716 // This loop can block up the Heartbeat for a very long time on large regions.
2744 // a joint requires specifying the body id of both involved bodies 2717 // We need to let the Watchdog know that the Heartbeat is not dead
2745 if (pendingJoints.Count > 0) 2718 // NOTE: This is currently commented out, but if things like OAR loading are
2746 { 2719 // timing the heartbeat out we will need to uncomment it
2747 List<PhysicsJoint> successfullyProcessedPendingJoints = new List<PhysicsJoint>(); 2720 //Watchdog.UpdateThread();
2748 //DoJointErrorMessage(joints_connecting_actor, "taint: " + pendingJoints.Count + " pending joints"); 2721 }
2749 foreach (PhysicsJoint joint in pendingJoints)
2750 {
2751 //DoJointErrorMessage(joint, "taint: time to create joint with parms: " + joint.RawParams);
2752 string[] jointParams = joint.RawParams.Split(" ".ToCharArray(), System.StringSplitOptions.RemoveEmptyEntries);
2753 List<IntPtr> jointBodies = new List<IntPtr>();
2754 bool allJointBodiesAreReady = true;
2755 foreach (string jointParam in jointParams)
2756 {
2757 if (jointParam == "NULL")
2758 {
2759 //DoJointErrorMessage(joint, "attaching NULL joint to world");
2760 jointBodies.Add(IntPtr.Zero);
2761 }
2762 else
2763 {
2764 //DoJointErrorMessage(joint, "looking for prim name: " + jointParam);
2765 bool foundPrim = false;
2766 lock (_prims)
2767 {
2768 foreach (OdePrim prim in _prims) // FIXME: inefficient
2769 {
2770 if (prim.SOPName == jointParam)
2771 {
2772 //DoJointErrorMessage(joint, "found for prim name: " + jointParam);
2773 if (prim.IsPhysical && prim.Body != IntPtr.Zero)
2774 {
2775 jointBodies.Add(prim.Body);
2776 foundPrim = true;
2777 break;
2778 }
2779 else
2780 {
2781 DoJointErrorMessage(joint, "prim name " + jointParam +
2782 " exists but is not (yet) physical; deferring joint creation. " +
2783 "IsPhysical property is " + prim.IsPhysical +
2784 " and body is " + prim.Body);
2785 foundPrim = false;
2786 break;
2787 }
2788 }
2789 }
2790 }
2791 if (foundPrim)
2792 {
2793 // all is fine
2794 }
2795 else
2796 {
2797 allJointBodiesAreReady = false;
2798 break;
2799 }
2800 }
2801 }
2802 if (allJointBodiesAreReady)
2803 {
2804 //DoJointErrorMessage(joint, "allJointBodiesAreReady for " + joint.ObjectNameInScene + " with parms " + joint.RawParams);
2805 if (jointBodies[0] == jointBodies[1])
2806 {
2807 DoJointErrorMessage(joint, "ERROR: joint cannot be created; the joint bodies are the same, body1==body2. Raw body is " + jointBodies[0] + ". raw parms: " + joint.RawParams);
2808 }
2809 else
2810 {
2811 switch (joint.Type)
2812 {
2813 case PhysicsJointType.Ball:
2814 {
2815 IntPtr odeJoint;
2816 //DoJointErrorMessage(joint, "ODE creating ball joint ");
2817 odeJoint = d.JointCreateBall(world, IntPtr.Zero);
2818 //DoJointErrorMessage(joint, "ODE attaching ball joint: " + odeJoint + " with b1:" + jointBodies[0] + " b2:" + jointBodies[1]);
2819 d.JointAttach(odeJoint, jointBodies[0], jointBodies[1]);
2820 //DoJointErrorMessage(joint, "ODE setting ball anchor: " + odeJoint + " to vec:" + joint.Position);
2821 d.JointSetBallAnchor(odeJoint,
2822 joint.Position.X,
2823 joint.Position.Y,
2824 joint.Position.Z);
2825 //DoJointErrorMessage(joint, "ODE joint setting OK");
2826 //DoJointErrorMessage(joint, "The ball joint's bodies are here: b0: ");
2827 //DoJointErrorMessage(joint, "" + (jointBodies[0] != IntPtr.Zero ? "" + d.BodyGetPosition(jointBodies[0]) : "fixed environment"));
2828 //DoJointErrorMessage(joint, "The ball joint's bodies are here: b1: ");
2829 //DoJointErrorMessage(joint, "" + (jointBodies[1] != IntPtr.Zero ? "" + d.BodyGetPosition(jointBodies[1]) : "fixed environment"));
2830
2831 if (joint is OdePhysicsJoint)
2832 {
2833 ((OdePhysicsJoint)joint).jointID = odeJoint;
2834 }
2835 else
2836 {
2837 DoJointErrorMessage(joint, "WARNING: non-ode joint in ODE!");
2838 }
2839 }
2840 break;
2841 case PhysicsJointType.Hinge:
2842 {
2843 IntPtr odeJoint;
2844 //DoJointErrorMessage(joint, "ODE creating hinge joint ");
2845 odeJoint = d.JointCreateHinge(world, IntPtr.Zero);
2846 //DoJointErrorMessage(joint, "ODE attaching hinge joint: " + odeJoint + " with b1:" + jointBodies[0] + " b2:" + jointBodies[1]);
2847 d.JointAttach(odeJoint, jointBodies[0], jointBodies[1]);
2848 //DoJointErrorMessage(joint, "ODE setting hinge anchor: " + odeJoint + " to vec:" + joint.Position);
2849 d.JointSetHingeAnchor(odeJoint,
2850 joint.Position.X,
2851 joint.Position.Y,
2852 joint.Position.Z);
2853 // We use the orientation of the x-axis of the joint's coordinate frame
2854 // as the axis for the hinge.
2855
2856 // Therefore, we must get the joint's coordinate frame based on the
2857 // joint.Rotation field, which originates from the orientation of the
2858 // joint's proxy object in the scene.
2859
2860 // The joint's coordinate frame is defined as the transformation matrix
2861 // that converts a vector from joint-local coordinates into world coordinates.
2862 // World coordinates are defined as the XYZ coordinate system of the sim,
2863 // as shown in the top status-bar of the viewer.
2864
2865 // Once we have the joint's coordinate frame, we extract its X axis (AtAxis)
2866 // and use that as the hinge axis.
2867
2868 //joint.Rotation.Normalize();
2869 Matrix4 proxyFrame = Matrix4.CreateFromQuaternion(joint.Rotation);
2870
2871 // Now extract the X axis of the joint's coordinate frame.
2872
2873 // Do not try to use proxyFrame.AtAxis or you will become mired in the
2874 // tar pit of transposed, inverted, and generally messed-up orientations.
2875 // (In other words, Matrix4.AtAxis() is borked.)
2876 // Vector3 jointAxis = proxyFrame.AtAxis; <--- this path leadeth to madness
2877
2878 // Instead, compute the X axis of the coordinate frame by transforming
2879 // the (1,0,0) vector. At least that works.
2880
2881 //m_log.Debug("PHY: making axis: complete matrix is " + proxyFrame);
2882 Vector3 jointAxis = Vector3.Transform(Vector3.UnitX, proxyFrame);
2883 //m_log.Debug("PHY: making axis: hinge joint axis is " + jointAxis);
2884 //DoJointErrorMessage(joint, "ODE setting hinge axis: " + odeJoint + " to vec:" + jointAxis);
2885 d.JointSetHingeAxis(odeJoint,
2886 jointAxis.X,
2887 jointAxis.Y,
2888 jointAxis.Z);
2889 //d.JointSetHingeParam(odeJoint, (int)dParam.CFM, 0.1f);
2890 if (joint is OdePhysicsJoint)
2891 {
2892 ((OdePhysicsJoint)joint).jointID = odeJoint;
2893 }
2894 else
2895 {
2896 DoJointErrorMessage(joint, "WARNING: non-ode joint in ODE!");
2897 }
2898 }
2899 break;
2900 }
2901 successfullyProcessedPendingJoints.Add(joint);
2902 }
2903 }
2904 else
2905 {
2906 DoJointErrorMessage(joint, "joint could not yet be created; still pending");
2907 }
2908 }
2909 foreach (PhysicsJoint successfullyProcessedJoint in successfullyProcessedPendingJoints)
2910 {
2911 //DoJointErrorMessage(successfullyProcessedJoint, "finalizing succesfully procsssed joint " + successfullyProcessedJoint.ObjectNameInScene + " parms " + successfullyProcessedJoint.RawParams);
2912 //DoJointErrorMessage(successfullyProcessedJoint, "removing from pending");
2913 InternalRemovePendingJoint(successfullyProcessedJoint);
2914 //DoJointErrorMessage(successfullyProcessedJoint, "adding to active");
2915 InternalAddActiveJoint(successfullyProcessedJoint);
2916 //DoJointErrorMessage(successfullyProcessedJoint, "done");
2917 }
2918 }
2919 }
2920 2722
2921 if (processedtaints) 2723 if (SupportsNINJAJoints)
2724 SimulatePendingNINJAJoints();
2725
2726 if (processedtaints)
2727 {
2922//Console.WriteLine("Simulate calls Clear of _taintedPrim list"); 2728//Console.WriteLine("Simulate calls Clear of _taintedPrim list");
2923 _taintedPrimH.Clear(); 2729 _taintedPrimH.Clear();
2924 _taintedPrimL.Clear(); 2730 _taintedPrimL.Clear();
2925 } 2731 }
2732 }
2926 2733
2927 // Move characters 2734 // Move characters
2928 lock (_characters) 2735 lock (_characters)
2736 {
2737 List<OdeCharacter> defects = new List<OdeCharacter>();
2738 foreach (OdeCharacter actor in _characters)
2739 {
2740 if (actor != null)
2741 actor.Move(timeStep, defects);
2742 }
2743 if (0 != defects.Count)
2744 {
2745 foreach (OdeCharacter defect in defects)
2929 { 2746 {
2930 List<OdeCharacter> defects = new List<OdeCharacter>(); 2747 RemoveCharacter(defect);
2931 foreach (OdeCharacter actor in _characters)
2932 {
2933 if (actor != null)
2934 actor.Move(timeStep, defects);
2935 }
2936 if (0 != defects.Count)
2937 {
2938 foreach (OdeCharacter defect in defects)
2939 {
2940 RemoveCharacter(defect);
2941 }
2942 }
2943 } 2748 }
2749 }
2750 }
2944 2751
2945 // Move other active objects 2752 // Move other active objects
2946 lock (_activeprims) 2753 lock (_activeprims)
2947 { 2754 {
2948 foreach (OdePrim prim in _activeprims) 2755 foreach (OdePrim prim in _activeprims)
2949 { 2756 {
2950 prim.m_collisionscore = 0; 2757 prim.m_collisionscore = 0;
2951 prim.Move(timeStep); 2758 prim.Move(timeStep);
2952 } 2759 }
2953 } 2760 }
2761
2762 //if ((framecount % m_randomizeWater) == 0)
2763 // randomizeWater(waterlevel);
2954 2764
2955 //if ((framecount % m_randomizeWater) == 0) 2765 //int RayCastTimeMS = m_rayCastManager.ProcessQueuedRequests();
2956 // randomizeWater(waterlevel); 2766 m_rayCastManager.ProcessQueuedRequests();
2957 2767
2958 //int RayCastTimeMS = m_rayCastManager.ProcessQueuedRequests(); 2768 collision_optimized(timeStep);
2959 m_rayCastManager.ProcessQueuedRequests();
2960 2769
2961 collision_optimized(timeStep); 2770 lock (_collisionEventPrim)
2771 {
2772 foreach (PhysicsActor obj in _collisionEventPrim)
2773 {
2774 if (obj == null)
2775 continue;
2962 2776
2963 lock (_collisionEventPrim) 2777// m_log.DebugFormat("[PHYSICS]: Assessing {0} for collision events", obj.SOPName);
2778
2779 switch ((ActorTypes)obj.PhysicsActorType)
2964 { 2780 {
2965 foreach (PhysicsActor obj in _collisionEventPrim) 2781 case ActorTypes.Agent:
2966 { 2782 OdeCharacter cobj = (OdeCharacter)obj;
2967 if (obj == null) 2783 cobj.AddCollisionFrameTime(100);
2968 continue; 2784 cobj.SendCollisions();
2785 break;
2969 2786
2970 switch ((ActorTypes)obj.PhysicsActorType) 2787 case ActorTypes.Prim:
2971 { 2788 OdePrim pobj = (OdePrim)obj;
2972 case ActorTypes.Agent: 2789 pobj.SendCollisions();
2973 OdeCharacter cobj = (OdeCharacter)obj; 2790 break;
2974 cobj.AddCollisionFrameTime(100);
2975 cobj.SendCollisions();
2976 break;
2977 case ActorTypes.Prim:
2978 OdePrim pobj = (OdePrim)obj;
2979 pobj.SendCollisions();
2980 break;
2981 }
2982 }
2983 } 2791 }
2792 }
2793 }
2984 2794
2985 //if (m_global_contactcount > 5) 2795 //if (m_global_contactcount > 5)
2986 //{ 2796 //{
2987 // m_log.DebugFormat("[PHYSICS]: Contacts:{0}", m_global_contactcount); 2797 // m_log.DebugFormat("[PHYSICS]: Contacts:{0}", m_global_contactcount);
2988 //} 2798 //}
2989 2799
2990 m_global_contactcount = 0; 2800 m_global_contactcount = 0;
2991 2801
2992 d.WorldQuickStep(world, ODE_STEPSIZE); 2802 d.WorldQuickStep(world, ODE_STEPSIZE);
2993 d.JointGroupEmpty(contactgroup); 2803 d.JointGroupEmpty(contactgroup);
2994 //ode.dunlock(world); 2804 //ode.dunlock(world);
2995 } 2805 }
2996 catch (Exception e) 2806 catch (Exception e)
2997 { 2807 {
2998 m_log.ErrorFormat("[PHYSICS]: {0}, {1}, {2}", e.Message, e.TargetSite, e); 2808 m_log.ErrorFormat("[PHYSICS]: {0}, {1}, {2}", e.Message, e.TargetSite, e);
2999 ode.dunlock(world); 2809 ode.dunlock(world);
3000 } 2810 }
3001 2811
3002 step_time -= ODE_STEPSIZE; 2812 step_time -= ODE_STEPSIZE;
3003 i++; 2813 i++;
3004 //} 2814 //}
3005 //else 2815 //else
3006 //{ 2816 //{
@@ -3017,6 +2827,7 @@ Console.WriteLine("AddPhysicsActorTaint to " + taintedprim.Name);
3017 { 2827 {
3018 if (actor.bad) 2828 if (actor.bad)
3019 m_log.WarnFormat("[PHYSICS]: BAD Actor {0} in _characters list was not removed?", actor.m_uuid); 2829 m_log.WarnFormat("[PHYSICS]: BAD Actor {0} in _characters list was not removed?", actor.m_uuid);
2830
3020 actor.UpdatePositionAndVelocity(); 2831 actor.UpdatePositionAndVelocity();
3021 } 2832 }
3022 } 2833 }
@@ -3030,6 +2841,7 @@ Console.WriteLine("AddPhysicsActorTaint to " + taintedprim.Name);
3030 { 2841 {
3031 RemoveCharacter(chr); 2842 RemoveCharacter(chr);
3032 } 2843 }
2844
3033 _badCharacter.Clear(); 2845 _badCharacter.Clear();
3034 } 2846 }
3035 } 2847 }
@@ -3045,30 +2857,7 @@ Console.WriteLine("AddPhysicsActorTaint to " + taintedprim.Name);
3045 actor.UpdatePositionAndVelocity(); 2857 actor.UpdatePositionAndVelocity();
3046 2858
3047 if (SupportsNINJAJoints) 2859 if (SupportsNINJAJoints)
3048 { 2860 SimulateActorPendingJoints(actor);
3049 // If an actor moved, move its joint proxy objects as well.
3050 // There seems to be an event PhysicsActor.OnPositionUpdate that could be used
3051 // for this purpose but it is never called! So we just do the joint
3052 // movement code here.
3053
3054 if (actor.SOPName != null &&
3055 joints_connecting_actor.ContainsKey(actor.SOPName) &&
3056 joints_connecting_actor[actor.SOPName] != null &&
3057 joints_connecting_actor[actor.SOPName].Count > 0)
3058 {
3059 foreach (PhysicsJoint affectedJoint in joints_connecting_actor[actor.SOPName])
3060 {
3061 if (affectedJoint.IsInPhysicsEngine)
3062 {
3063 DoJointMoved(affectedJoint);
3064 }
3065 else
3066 {
3067 DoJointErrorMessage(affectedJoint, "a body connected to a joint was moved, but the joint doesn't exist yet! this will lead to joint error. joint was: " + affectedJoint.ObjectNameInScene + " parms:" + affectedJoint.RawParams);
3068 }
3069 }
3070 }
3071 }
3072 } 2861 }
3073 } 2862 }
3074 } 2863 }
@@ -3079,7 +2868,7 @@ Console.WriteLine("AddPhysicsActorTaint to " + taintedprim.Name);
3079 // Finished with all sim stepping. If requested, dump world state to file for debugging. 2868 // Finished with all sim stepping. If requested, dump world state to file for debugging.
3080 // TODO: This call to the export function is already inside lock (OdeLock) - but is an extra lock needed? 2869 // TODO: This call to the export function is already inside lock (OdeLock) - but is an extra lock needed?
3081 // TODO: This overwrites all dump files in-place. Should this be a growing logfile, or separate snapshots? 2870 // TODO: This overwrites all dump files in-place. Should this be a growing logfile, or separate snapshots?
3082 if (physics_logging && (physics_logging_interval>0) && (framecount % physics_logging_interval == 0)) 2871 if (physics_logging && (physics_logging_interval > 0) && (framecount % physics_logging_interval == 0))
3083 { 2872 {
3084 string fname = "state-" + world.ToString() + ".DIF"; // give each physics world a separate filename 2873 string fname = "state-" + world.ToString() + ".DIF"; // give each physics world a separate filename
3085 string prefix = "world" + world.ToString(); // prefix for variable names in exported .DIF file 2874 string prefix = "world" + world.ToString(); // prefix for variable names in exported .DIF file
@@ -3091,8 +2880,10 @@ Console.WriteLine("AddPhysicsActorTaint to " + taintedprim.Name);
3091 fwriter.WriteLine(header); 2880 fwriter.WriteLine(header);
3092 fwriter.Close(); 2881 fwriter.Close();
3093 } 2882 }
2883
3094 d.WorldExportDIF(world, fname, physics_logging_append_existing_logfile, prefix); 2884 d.WorldExportDIF(world, fname, physics_logging_append_existing_logfile, prefix);
3095 } 2885 }
2886
3096 latertickcount = Util.EnvironmentTickCount() - tickCountFrameRun; 2887 latertickcount = Util.EnvironmentTickCount() - tickCountFrameRun;
3097 2888
3098 // OpenSimulator above does 10 fps. 10 fps = means that the main thread loop and physics 2889 // OpenSimulator above does 10 fps. 10 fps = means that the main thread loop and physics
@@ -3101,7 +2892,9 @@ Console.WriteLine("AddPhysicsActorTaint to " + taintedprim.Name);
3101 // If Physics stalls, it takes longer which makes the tick count ms larger. 2892 // If Physics stalls, it takes longer which makes the tick count ms larger.
3102 2893
3103 if (latertickcount < 100) 2894 if (latertickcount < 100)
2895 {
3104 m_timeDilation = 1.0f; 2896 m_timeDilation = 1.0f;
2897 }
3105 else 2898 else
3106 { 2899 {
3107 m_timeDilation = 100f / latertickcount; 2900 m_timeDilation = 100f / latertickcount;
@@ -3114,6 +2907,229 @@ Console.WriteLine("AddPhysicsActorTaint to " + taintedprim.Name);
3114 return fps; 2907 return fps;
3115 } 2908 }
3116 2909
2910 /// <summary>
2911 /// Simulate pending NINJA joints.
2912 /// </summary>
2913 /// <remarks>
2914 /// Called by the main Simulate() loop if NINJA joints are active. Should not be called from anywhere else.
2915 /// </remarks>
2916 protected void SimulatePendingNINJAJoints()
2917 {
2918 // Create pending joints, if possible
2919
2920 // joints can only be processed after ALL bodies are processed (and exist in ODE), since creating
2921 // a joint requires specifying the body id of both involved bodies
2922 if (pendingJoints.Count > 0)
2923 {
2924 List<PhysicsJoint> successfullyProcessedPendingJoints = new List<PhysicsJoint>();
2925 //DoJointErrorMessage(joints_connecting_actor, "taint: " + pendingJoints.Count + " pending joints");
2926 foreach (PhysicsJoint joint in pendingJoints)
2927 {
2928 //DoJointErrorMessage(joint, "taint: time to create joint with parms: " + joint.RawParams);
2929 string[] jointParams = joint.RawParams.Split(" ".ToCharArray(), System.StringSplitOptions.RemoveEmptyEntries);
2930 List<IntPtr> jointBodies = new List<IntPtr>();
2931 bool allJointBodiesAreReady = true;
2932 foreach (string jointParam in jointParams)
2933 {
2934 if (jointParam == "NULL")
2935 {
2936 //DoJointErrorMessage(joint, "attaching NULL joint to world");
2937 jointBodies.Add(IntPtr.Zero);
2938 }
2939 else
2940 {
2941 //DoJointErrorMessage(joint, "looking for prim name: " + jointParam);
2942 bool foundPrim = false;
2943 lock (_prims)
2944 {
2945 foreach (OdePrim prim in _prims) // FIXME: inefficient
2946 {
2947 if (prim.SOPName == jointParam)
2948 {
2949 //DoJointErrorMessage(joint, "found for prim name: " + jointParam);
2950 if (prim.IsPhysical && prim.Body != IntPtr.Zero)
2951 {
2952 jointBodies.Add(prim.Body);
2953 foundPrim = true;
2954 break;
2955 }
2956 else
2957 {
2958 DoJointErrorMessage(joint, "prim name " + jointParam +
2959 " exists but is not (yet) physical; deferring joint creation. " +
2960 "IsPhysical property is " + prim.IsPhysical +
2961 " and body is " + prim.Body);
2962 foundPrim = false;
2963 break;
2964 }
2965 }
2966 }
2967 }
2968 if (foundPrim)
2969 {
2970 // all is fine
2971 }
2972 else
2973 {
2974 allJointBodiesAreReady = false;
2975 break;
2976 }
2977 }
2978 }
2979
2980 if (allJointBodiesAreReady)
2981 {
2982 //DoJointErrorMessage(joint, "allJointBodiesAreReady for " + joint.ObjectNameInScene + " with parms " + joint.RawParams);
2983 if (jointBodies[0] == jointBodies[1])
2984 {
2985 DoJointErrorMessage(joint, "ERROR: joint cannot be created; the joint bodies are the same, body1==body2. Raw body is " + jointBodies[0] + ". raw parms: " + joint.RawParams);
2986 }
2987 else
2988 {
2989 switch (joint.Type)
2990 {
2991 case PhysicsJointType.Ball:
2992 {
2993 IntPtr odeJoint;
2994 //DoJointErrorMessage(joint, "ODE creating ball joint ");
2995 odeJoint = d.JointCreateBall(world, IntPtr.Zero);
2996 //DoJointErrorMessage(joint, "ODE attaching ball joint: " + odeJoint + " with b1:" + jointBodies[0] + " b2:" + jointBodies[1]);
2997 d.JointAttach(odeJoint, jointBodies[0], jointBodies[1]);
2998 //DoJointErrorMessage(joint, "ODE setting ball anchor: " + odeJoint + " to vec:" + joint.Position);
2999 d.JointSetBallAnchor(odeJoint,
3000 joint.Position.X,
3001 joint.Position.Y,
3002 joint.Position.Z);
3003 //DoJointErrorMessage(joint, "ODE joint setting OK");
3004 //DoJointErrorMessage(joint, "The ball joint's bodies are here: b0: ");
3005 //DoJointErrorMessage(joint, "" + (jointBodies[0] != IntPtr.Zero ? "" + d.BodyGetPosition(jointBodies[0]) : "fixed environment"));
3006 //DoJointErrorMessage(joint, "The ball joint's bodies are here: b1: ");
3007 //DoJointErrorMessage(joint, "" + (jointBodies[1] != IntPtr.Zero ? "" + d.BodyGetPosition(jointBodies[1]) : "fixed environment"));
3008
3009 if (joint is OdePhysicsJoint)
3010 {
3011 ((OdePhysicsJoint)joint).jointID = odeJoint;
3012 }
3013 else
3014 {
3015 DoJointErrorMessage(joint, "WARNING: non-ode joint in ODE!");
3016 }
3017 }
3018 break;
3019 case PhysicsJointType.Hinge:
3020 {
3021 IntPtr odeJoint;
3022 //DoJointErrorMessage(joint, "ODE creating hinge joint ");
3023 odeJoint = d.JointCreateHinge(world, IntPtr.Zero);
3024 //DoJointErrorMessage(joint, "ODE attaching hinge joint: " + odeJoint + " with b1:" + jointBodies[0] + " b2:" + jointBodies[1]);
3025 d.JointAttach(odeJoint, jointBodies[0], jointBodies[1]);
3026 //DoJointErrorMessage(joint, "ODE setting hinge anchor: " + odeJoint + " to vec:" + joint.Position);
3027 d.JointSetHingeAnchor(odeJoint,
3028 joint.Position.X,
3029 joint.Position.Y,
3030 joint.Position.Z);
3031 // We use the orientation of the x-axis of the joint's coordinate frame
3032 // as the axis for the hinge.
3033
3034 // Therefore, we must get the joint's coordinate frame based on the
3035 // joint.Rotation field, which originates from the orientation of the
3036 // joint's proxy object in the scene.
3037
3038 // The joint's coordinate frame is defined as the transformation matrix
3039 // that converts a vector from joint-local coordinates into world coordinates.
3040 // World coordinates are defined as the XYZ coordinate system of the sim,
3041 // as shown in the top status-bar of the viewer.
3042
3043 // Once we have the joint's coordinate frame, we extract its X axis (AtAxis)
3044 // and use that as the hinge axis.
3045
3046 //joint.Rotation.Normalize();
3047 Matrix4 proxyFrame = Matrix4.CreateFromQuaternion(joint.Rotation);
3048
3049 // Now extract the X axis of the joint's coordinate frame.
3050
3051 // Do not try to use proxyFrame.AtAxis or you will become mired in the
3052 // tar pit of transposed, inverted, and generally messed-up orientations.
3053 // (In other words, Matrix4.AtAxis() is borked.)
3054 // Vector3 jointAxis = proxyFrame.AtAxis; <--- this path leadeth to madness
3055
3056 // Instead, compute the X axis of the coordinate frame by transforming
3057 // the (1,0,0) vector. At least that works.
3058
3059 //m_log.Debug("PHY: making axis: complete matrix is " + proxyFrame);
3060 Vector3 jointAxis = Vector3.Transform(Vector3.UnitX, proxyFrame);
3061 //m_log.Debug("PHY: making axis: hinge joint axis is " + jointAxis);
3062 //DoJointErrorMessage(joint, "ODE setting hinge axis: " + odeJoint + " to vec:" + jointAxis);
3063 d.JointSetHingeAxis(odeJoint,
3064 jointAxis.X,
3065 jointAxis.Y,
3066 jointAxis.Z);
3067 //d.JointSetHingeParam(odeJoint, (int)dParam.CFM, 0.1f);
3068 if (joint is OdePhysicsJoint)
3069 {
3070 ((OdePhysicsJoint)joint).jointID = odeJoint;
3071 }
3072 else
3073 {
3074 DoJointErrorMessage(joint, "WARNING: non-ode joint in ODE!");
3075 }
3076 }
3077 break;
3078 }
3079 successfullyProcessedPendingJoints.Add(joint);
3080 }
3081 }
3082 else
3083 {
3084 DoJointErrorMessage(joint, "joint could not yet be created; still pending");
3085 }
3086 }
3087
3088 foreach (PhysicsJoint successfullyProcessedJoint in successfullyProcessedPendingJoints)
3089 {
3090 //DoJointErrorMessage(successfullyProcessedJoint, "finalizing succesfully procsssed joint " + successfullyProcessedJoint.ObjectNameInScene + " parms " + successfullyProcessedJoint.RawParams);
3091 //DoJointErrorMessage(successfullyProcessedJoint, "removing from pending");
3092 InternalRemovePendingJoint(successfullyProcessedJoint);
3093 //DoJointErrorMessage(successfullyProcessedJoint, "adding to active");
3094 InternalAddActiveJoint(successfullyProcessedJoint);
3095 //DoJointErrorMessage(successfullyProcessedJoint, "done");
3096 }
3097 }
3098 }
3099
3100 /// <summary>
3101 /// Simulate the joint proxies of a NINJA actor.
3102 /// </summary>
3103 /// <remarks>
3104 /// Called as part of the Simulate() loop if NINJA physics is active. Must only be called from there.
3105 /// </remarks>
3106 /// <param name="actor"></param>
3107 protected void SimulateActorPendingJoints(OdePrim actor)
3108 {
3109 // If an actor moved, move its joint proxy objects as well.
3110 // There seems to be an event PhysicsActor.OnPositionUpdate that could be used
3111 // for this purpose but it is never called! So we just do the joint
3112 // movement code here.
3113
3114 if (actor.SOPName != null &&
3115 joints_connecting_actor.ContainsKey(actor.SOPName) &&
3116 joints_connecting_actor[actor.SOPName] != null &&
3117 joints_connecting_actor[actor.SOPName].Count > 0)
3118 {
3119 foreach (PhysicsJoint affectedJoint in joints_connecting_actor[actor.SOPName])
3120 {
3121 if (affectedJoint.IsInPhysicsEngine)
3122 {
3123 DoJointMoved(affectedJoint);
3124 }
3125 else
3126 {
3127 DoJointErrorMessage(affectedJoint, "a body connected to a joint was moved, but the joint doesn't exist yet! this will lead to joint error. joint was: " + affectedJoint.ObjectNameInScene + " parms:" + affectedJoint.RawParams);
3128 }
3129 }
3130 }
3131 }
3132
3117 public override void GetResults() 3133 public override void GetResults()
3118 { 3134 {
3119 } 3135 }
@@ -3459,24 +3475,21 @@ Console.WriteLine("AddPhysicsActorTaint to " + taintedprim.Name);
3459 float hfmin = 2000; 3475 float hfmin = 2000;
3460 float hfmax = -2000; 3476 float hfmax = -2000;
3461 3477
3462 for (int x = 0; x < heightmapWidthSamples; x++) 3478 for (int x = 0; x < heightmapWidthSamples; x++)
3479 {
3480 for (int y = 0; y < heightmapHeightSamples; y++)
3463 { 3481 {
3464 for (int y = 0; y < heightmapHeightSamples; y++) 3482 int xx = Util.Clip(x - 1, 0, regionsize - 1);
3465 { 3483 int yy = Util.Clip(y - 1, 0, regionsize - 1);
3466 int xx = Util.Clip(x - 1, 0, regionsize - 1); 3484
3467 int yy = Util.Clip(y - 1, 0, regionsize - 1); 3485
3468 3486 float val= heightMap[yy * (int)Constants.RegionSize + xx];
3469 3487 _heightmap[x * ((int)Constants.RegionSize + 2) + y] = val;
3470 float val= heightMap[yy * (int)Constants.RegionSize + xx]; 3488
3471 _heightmap[x * ((int)Constants.RegionSize + 2) + y] = val; 3489 hfmin = (val < hfmin) ? val : hfmin;
3472 3490 hfmax = (val > hfmax) ? val : hfmax;
3473 hfmin = (val < hfmin) ? val : hfmin;
3474 hfmax = (val > hfmax) ? val : hfmax;
3475 }
3476 } 3491 }
3477 3492 }
3478
3479
3480 3493
3481 lock (OdeLock) 3494 lock (OdeLock)
3482 { 3495 {
@@ -3531,7 +3544,6 @@ Console.WriteLine("AddPhysicsActorTaint to " + taintedprim.Name);
3531 } 3544 }
3532 RegionTerrain.Add(pOffset, GroundGeom, GroundGeom); 3545 RegionTerrain.Add(pOffset, GroundGeom, GroundGeom);
3533 TerrainHeightFieldHeights.Add(GroundGeom,_heightmap); 3546 TerrainHeightFieldHeights.Add(GroundGeom,_heightmap);
3534
3535 } 3547 }
3536 } 3548 }
3537 3549
@@ -3694,6 +3706,7 @@ Console.WriteLine("AddPhysicsActorTaint to " + taintedprim.Name);
3694 //d.CloseODE(); 3706 //d.CloseODE();
3695 } 3707 }
3696 } 3708 }
3709
3697 public override Dictionary<uint, float> GetTopColliders() 3710 public override Dictionary<uint, float> GetTopColliders()
3698 { 3711 {
3699 Dictionary<uint, float> returncolliders = new Dictionary<uint, float>(); 3712 Dictionary<uint, float> returncolliders = new Dictionary<uint, float>();