diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs')
-rwxr-xr-x | OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs | 439 |
1 files changed, 339 insertions, 100 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs index b9c2cf9..e05562a 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs | |||
@@ -28,22 +28,80 @@ using System; | |||
28 | using System.Collections.Generic; | 28 | using System.Collections.Generic; |
29 | using System.Text; | 29 | using System.Text; |
30 | 30 | ||
31 | using OpenSim.Framework; | ||
32 | |||
31 | using OMV = OpenMetaverse; | 33 | using OMV = OpenMetaverse; |
32 | 34 | ||
33 | namespace OpenSim.Region.Physics.BulletSPlugin | 35 | namespace OpenSim.Region.Physics.BulletSPlugin |
34 | { | 36 | { |
37 | |||
38 | // When a child is linked, the relationship position of the child to the parent | ||
39 | // is remembered so the child's world position can be recomputed when it is | ||
40 | // removed from the linkset. | ||
41 | sealed class BSLinksetCompoundInfo : BSLinksetInfo | ||
42 | { | ||
43 | public int Index; | ||
44 | public OMV.Vector3 OffsetFromRoot; | ||
45 | public OMV.Vector3 OffsetFromCenterOfMass; | ||
46 | public OMV.Quaternion OffsetRot; | ||
47 | public BSLinksetCompoundInfo(int indx, OMV.Vector3 p, OMV.Quaternion r) | ||
48 | { | ||
49 | Index = indx; | ||
50 | OffsetFromRoot = p; | ||
51 | OffsetFromCenterOfMass = p; | ||
52 | OffsetRot = r; | ||
53 | } | ||
54 | // 'centerDisplacement' is the distance from the root the the center-of-mass (Bullet 'zero' of the shape) | ||
55 | public BSLinksetCompoundInfo(int indx, BSPrimLinkable root, BSPrimLinkable child, OMV.Vector3 centerDisplacement) | ||
56 | { | ||
57 | // Each child position and rotation is given relative to the center-of-mass. | ||
58 | OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(root.RawOrientation); | ||
59 | OMV.Vector3 displacementFromRoot = (child.RawPosition - root.RawPosition) * invRootOrientation; | ||
60 | OMV.Vector3 displacementFromCOM = displacementFromRoot - centerDisplacement; | ||
61 | OMV.Quaternion displacementRot = child.RawOrientation * invRootOrientation; | ||
62 | |||
63 | // Save relative position for recomputing child's world position after moving linkset. | ||
64 | Index = indx; | ||
65 | OffsetFromRoot = displacementFromRoot; | ||
66 | OffsetFromCenterOfMass = displacementFromCOM; | ||
67 | OffsetRot = displacementRot; | ||
68 | } | ||
69 | public override void Clear() | ||
70 | { | ||
71 | Index = 0; | ||
72 | OffsetFromRoot = OMV.Vector3.Zero; | ||
73 | OffsetFromCenterOfMass = OMV.Vector3.Zero; | ||
74 | OffsetRot = OMV.Quaternion.Identity; | ||
75 | } | ||
76 | public override string ToString() | ||
77 | { | ||
78 | StringBuilder buff = new StringBuilder(); | ||
79 | buff.Append("<i="); | ||
80 | buff.Append(Index.ToString()); | ||
81 | buff.Append(",p="); | ||
82 | buff.Append(OffsetFromRoot.ToString()); | ||
83 | buff.Append(",m="); | ||
84 | buff.Append(OffsetFromCenterOfMass.ToString()); | ||
85 | buff.Append(",r="); | ||
86 | buff.Append(OffsetRot.ToString()); | ||
87 | buff.Append(">"); | ||
88 | return buff.ToString(); | ||
89 | } | ||
90 | }; | ||
91 | |||
35 | public sealed class BSLinksetCompound : BSLinkset | 92 | public sealed class BSLinksetCompound : BSLinkset |
36 | { | 93 | { |
37 | private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]"; | 94 | private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]"; |
38 | 95 | ||
39 | public BSLinksetCompound(BSScene scene, BSPhysObject parent) | 96 | public BSLinksetCompound(BSScene scene, BSPrimLinkable parent) |
97 | : base(scene, parent) | ||
40 | { | 98 | { |
41 | base.Initialize(scene, parent); | ||
42 | } | 99 | } |
43 | 100 | ||
44 | // For compound implimented linksets, if there are children, use compound shape for the root. | 101 | // For compound implimented linksets, if there are children, use compound shape for the root. |
45 | public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) | 102 | public override BSPhysicsShapeType PreferredPhysicalShape(BSPrimLinkable requestor) |
46 | { | 103 | { |
104 | // Returning 'unknown' means we don't have a preference. | ||
47 | BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; | 105 | BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; |
48 | if (IsRoot(requestor) && HasAnyChildren) | 106 | if (IsRoot(requestor) && HasAnyChildren) |
49 | { | 107 | { |
@@ -55,41 +113,57 @@ public sealed class BSLinksetCompound : BSLinkset | |||
55 | 113 | ||
56 | // When physical properties are changed the linkset needs to recalculate | 114 | // When physical properties are changed the linkset needs to recalculate |
57 | // its internal properties. | 115 | // its internal properties. |
58 | // This is queued in the 'post taint' queue so the | 116 | public override void Refresh(BSPrimLinkable requestor) |
59 | // refresh will happen once after all the other taints are applied. | ||
60 | public override void Refresh(BSPhysObject requestor) | ||
61 | { | 117 | { |
62 | // External request for Refresh (from BSPrim) is not necessary | 118 | base.Refresh(requestor); |
63 | // InternalRefresh(requestor); | 119 | |
120 | // Something changed so do the rebuilding thing | ||
121 | // ScheduleRebuild(); | ||
64 | } | 122 | } |
65 | 123 | ||
66 | private void InternalRefresh(BSPhysObject requestor) | 124 | // Schedule a refresh to happen after all the other taint processing. |
125 | private void ScheduleRebuild(BSPrimLinkable requestor) | ||
67 | { | 126 | { |
68 | DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,requestor={1}", LinksetRoot.LocalID, requestor.LocalID); | 127 | DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}", |
69 | // Queue to happen after all the other taint processing | 128 | requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren)); |
70 | PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", requestor.LocalID, delegate() | 129 | // When rebuilding, it is possible to set properties that would normally require a rebuild. |
130 | // If already rebuilding, don't request another rebuild. | ||
131 | // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. | ||
132 | if (!Rebuilding && HasAnyChildren) | ||
71 | { | 133 | { |
72 | if (IsRoot(requestor) && HasAnyChildren) | 134 | PhysicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate() |
73 | RecomputeLinksetCompound(); | 135 | { |
74 | }); | 136 | if (HasAnyChildren) |
137 | RecomputeLinksetCompound(); | ||
138 | }); | ||
139 | } | ||
75 | } | 140 | } |
76 | 141 | ||
77 | // The object is going dynamic (physical). Do any setup necessary | 142 | // The object is going dynamic (physical). Do any setup necessary for a dynamic linkset. |
78 | // for a dynamic linkset. | ||
79 | // Only the state of the passed object can be modified. The rest of the linkset | 143 | // Only the state of the passed object can be modified. The rest of the linkset |
80 | // has not yet been fully constructed. | 144 | // has not yet been fully constructed. |
81 | // Return 'true' if any properties updated on the passed object. | 145 | // Return 'true' if any properties updated on the passed object. |
82 | // Called at taint-time! | 146 | // Called at taint-time! |
83 | public override bool MakeDynamic(BSPhysObject child) | 147 | public override bool MakeDynamic(BSPrimLinkable child) |
84 | { | 148 | { |
85 | bool ret = false; | 149 | bool ret = false; |
86 | DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); | 150 | DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); |
87 | if (!IsRoot(child)) | 151 | if (IsRoot(child)) |
152 | { | ||
153 | // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly. | ||
154 | ScheduleRebuild(LinksetRoot); | ||
155 | } | ||
156 | else | ||
88 | { | 157 | { |
89 | // Physical children are removed from the world as the shape ofthe root compound | 158 | // The origional prims are removed from the world as the shape of the root compound |
90 | // shape takes over. | 159 | // shape takes over. |
91 | BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); | 160 | PhysicsScene.PE.AddToCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); |
92 | BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION); | 161 | PhysicsScene.PE.ForceActivationState(child.PhysBody, ActivationState.DISABLE_SIMULATION); |
162 | // We don't want collisions from the old linkset children. | ||
163 | PhysicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); | ||
164 | |||
165 | child.PhysBody.collisionType = CollisionType.LinksetChild; | ||
166 | |||
93 | ret = true; | 167 | ret = true; |
94 | } | 168 | } |
95 | return ret; | 169 | return ret; |
@@ -100,73 +174,181 @@ public sealed class BSLinksetCompound : BSLinkset | |||
100 | // This doesn't normally happen -- OpenSim removes the objects from the physical | 174 | // This doesn't normally happen -- OpenSim removes the objects from the physical |
101 | // world if it is a static linkset. | 175 | // world if it is a static linkset. |
102 | // Called at taint-time! | 176 | // Called at taint-time! |
103 | public override bool MakeStatic(BSPhysObject child) | 177 | public override bool MakeStatic(BSPrimLinkable child) |
104 | { | 178 | { |
105 | bool ret = false; | 179 | bool ret = false; |
106 | DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); | 180 | DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); |
107 | if (!IsRoot(child)) | 181 | if (IsRoot(child)) |
182 | { | ||
183 | ScheduleRebuild(LinksetRoot); | ||
184 | } | ||
185 | else | ||
108 | { | 186 | { |
109 | // The non-physical children can come back to life. | 187 | // The non-physical children can come back to life. |
110 | BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); | 188 | PhysicsScene.PE.RemoveFromCollisionFlags(child.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); |
111 | // Don't force activation so setting of DISABLE_SIMULATION can stay. | 189 | |
112 | BulletSimAPI.Activate2(child.PhysBody.ptr, false); | 190 | child.PhysBody.collisionType = CollisionType.LinksetChild; |
191 | |||
192 | // Don't force activation so setting of DISABLE_SIMULATION can stay if used. | ||
193 | PhysicsScene.PE.Activate(child.PhysBody, false); | ||
113 | ret = true; | 194 | ret = true; |
114 | } | 195 | } |
115 | return ret; | 196 | return ret; |
116 | } | 197 | } |
117 | 198 | ||
118 | // Called at taint-time!! | 199 | // 'physicalUpdate' is true if these changes came directly from the physics engine. Don't need to rebuild then. |
119 | public override void UpdateProperties(BSPhysObject updated) | 200 | // Called at taint-time. |
120 | { | 201 | public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable updated) |
121 | // Nothing to do for constraints on property updates | ||
122 | } | ||
123 | |||
124 | // The children move around in relationship to the root. | ||
125 | // Just grab the current values of wherever it is right now. | ||
126 | public override OMV.Vector3 Position(BSPhysObject member) | ||
127 | { | 202 | { |
128 | return BulletSimAPI.GetPosition2(member.PhysBody.ptr); | 203 | // The user moving a child around requires the rebuilding of the linkset compound shape |
129 | } | 204 | // One problem is this happens when a border is crossed -- the simulator implementation |
205 | // stores the position into the group which causes the move of the object | ||
206 | // but it also means all the child positions get updated. | ||
207 | // What would cause an unnecessary rebuild so we make sure the linkset is in a | ||
208 | // region before bothering to do a rebuild. | ||
209 | if (!IsRoot(updated) && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition)) | ||
210 | { | ||
211 | // If a child of the linkset is updating only the position or rotation, that can be done | ||
212 | // without rebuilding the linkset. | ||
213 | // If a handle for the child can be fetch, we update the child here. If a rebuild was | ||
214 | // scheduled by someone else, the rebuild will just replace this setting. | ||
215 | |||
216 | bool updatedChild = false; | ||
217 | // Anything other than updating position or orientation usually means a physical update | ||
218 | // and that is caused by us updating the object. | ||
219 | if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0) | ||
220 | { | ||
221 | // Find the physical instance of the child | ||
222 | if (LinksetRoot.PhysShape.HasPhysicalShape && PhysicsScene.PE.IsCompound(LinksetRoot.PhysShape)) | ||
223 | { | ||
224 | // It is possible that the linkset is still under construction and the child is not yet | ||
225 | // inserted into the compound shape. A rebuild of the linkset in a pre-step action will | ||
226 | // build the whole thing with the new position or rotation. | ||
227 | // The index must be checked because Bullet references the child array but does no validity | ||
228 | // checking of the child index passed. | ||
229 | int numLinksetChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape); | ||
230 | if (updated.LinksetChildIndex < numLinksetChildren) | ||
231 | { | ||
232 | BulletShape linksetChildShape = PhysicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape, updated.LinksetChildIndex); | ||
233 | if (linksetChildShape.HasPhysicalShape) | ||
234 | { | ||
235 | // Found the child shape within the compound shape | ||
236 | PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, updated.LinksetChildIndex, | ||
237 | updated.RawPosition - LinksetRoot.RawPosition, | ||
238 | updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation), | ||
239 | true /* shouldRecalculateLocalAabb */); | ||
240 | updatedChild = true; | ||
241 | DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1},pos={2},rot={3}", | ||
242 | updated.LocalID, whichUpdated, updated.RawPosition, updated.RawOrientation); | ||
243 | } | ||
244 | else // DEBUG DEBUG | ||
245 | { // DEBUG DEBUG | ||
246 | DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}", | ||
247 | updated.LocalID, linksetChildShape); | ||
248 | } // DEBUG DEBUG | ||
249 | } | ||
250 | else // DEBUG DEBUG | ||
251 | { // DEBUG DEBUG | ||
252 | // the child is not yet in the compound shape. This is non-fatal. | ||
253 | DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,childNotInCompoundShape,numChildren={1},index={2}", | ||
254 | updated.LocalID, numLinksetChildren, updated.LinksetChildIndex); | ||
255 | } // DEBUG DEBUG | ||
256 | } | ||
257 | else // DEBUG DEBUG | ||
258 | { // DEBUG DEBUG | ||
259 | DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noBodyOrNotCompound", updated.LocalID); | ||
260 | } // DEBUG DEBUG | ||
130 | 261 | ||
131 | public override OMV.Quaternion Orientation(BSPhysObject member) | 262 | if (!updatedChild) |
132 | { | 263 | { |
133 | return BulletSimAPI.GetOrientation2(member.PhysBody.ptr); | 264 | // If couldn't do the individual child, the linkset needs a rebuild to incorporate the new child info. |
265 | // Note: there are several ways through this code that will not update the child if | ||
266 | // the linkset is being rebuilt. In this case, scheduling a rebuild is a NOOP since | ||
267 | // there will already be a rebuild scheduled. | ||
268 | DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild.schedulingRebuild,whichUpdated={1}", | ||
269 | updated.LocalID, whichUpdated); | ||
270 | updated.LinksetInfo = null; // setting to 'null' causes relative position to be recomputed. | ||
271 | ScheduleRebuild(updated); | ||
272 | } | ||
273 | } | ||
274 | } | ||
134 | } | 275 | } |
135 | 276 | ||
136 | // Routine called when rebuilding the body of some member of the linkset. | 277 | // Routine called when rebuilding the body of some member of the linkset. |
137 | // Since we don't keep in world relationships, do nothing unless it's a child changing. | 278 | // Since we don't keep in world relationships, do nothing unless it's a child changing. |
138 | // Returns 'true' of something was actually removed and would need restoring | 279 | // Returns 'true' of something was actually removed and would need restoring |
139 | // Called at taint-time!! | 280 | // Called at taint-time!! |
140 | public override bool RemoveBodyDependencies(BSPrim child) | 281 | public override bool RemoveBodyDependencies(BSPrimLinkable child) |
141 | { | 282 | { |
142 | bool ret = false; | 283 | bool ret = false; |
143 | 284 | ||
144 | DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}", | 285 | DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}", |
145 | child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString("X"), IsRoot(child)); | 286 | child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody, IsRoot(child)); |
146 | 287 | ||
147 | if (!IsRoot(child)) | 288 | if (!IsRoot(child)) |
148 | { | 289 | { |
149 | // Cause the current shape to be freed and the new one to be built. | 290 | // Because it is a convenient time, recompute child world position and rotation based on |
150 | InternalRefresh(LinksetRoot); | 291 | // its position in the linkset. |
151 | ret = true; | 292 | RecomputeChildWorldPosition(child, true /* inTaintTime */); |
293 | child.LinksetInfo = null; | ||
152 | } | 294 | } |
153 | 295 | ||
296 | // Cannot schedule a refresh/rebuild here because this routine is called when | ||
297 | // the linkset is being rebuilt. | ||
298 | // InternalRefresh(LinksetRoot); | ||
299 | |||
154 | return ret; | 300 | return ret; |
155 | } | 301 | } |
156 | 302 | ||
157 | // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true', | 303 | // When the linkset is built, the child shape is added to the compound shape relative to the |
158 | // this routine will restore the removed constraints. | 304 | // root shape. The linkset then moves around but this does not move the actual child |
159 | // Called at taint-time!! | 305 | // prim. The child prim's location must be recomputed based on the location of the root shape. |
160 | public override void RestoreBodyDependencies(BSPrim child) | 306 | private void RecomputeChildWorldPosition(BSPrimLinkable child, bool inTaintTime) |
161 | { | 307 | { |
162 | // The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints. | 308 | // For the moment (20130201), disable this computation (converting the child physical addr back to |
309 | // a region address) until we have a good handle on center-of-mass offsets and what the physics | ||
310 | // engine moving a child actually means. | ||
311 | // The simulator keeps track of where children should be as the linkset moves. Setting | ||
312 | // the pos/rot here does not effect that knowledge as there is no good way for the | ||
313 | // physics engine to send the simulator an update for a child. | ||
314 | |||
315 | /* | ||
316 | BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo; | ||
317 | if (lci != null) | ||
318 | { | ||
319 | if (inTaintTime) | ||
320 | { | ||
321 | OMV.Vector3 oldPos = child.RawPosition; | ||
322 | child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetFromRoot; | ||
323 | child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot; | ||
324 | DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}", | ||
325 | child.LocalID, oldPos, lci, child.RawPosition); | ||
326 | } | ||
327 | else | ||
328 | { | ||
329 | // TaintedObject is not used here so the raw position is set now and not at taint-time. | ||
330 | child.Position = LinksetRoot.RawPosition + lci.OffsetFromRoot; | ||
331 | child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot; | ||
332 | } | ||
333 | } | ||
334 | else | ||
335 | { | ||
336 | // This happens when children have been added to the linkset but the linkset | ||
337 | // has not been constructed yet. So like, at taint time, adding children to a linkset | ||
338 | // and then changing properties of the children (makePhysical, for instance) | ||
339 | // but the post-print action of actually rebuilding the linkset has not yet happened. | ||
340 | // PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}", | ||
341 | // LogHeader, child.LocalID); | ||
342 | DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID); | ||
343 | } | ||
344 | */ | ||
163 | } | 345 | } |
164 | 346 | ||
165 | // ================================================================ | 347 | // ================================================================ |
166 | 348 | ||
167 | // Add a new child to the linkset. | 349 | // Add a new child to the linkset. |
168 | // Called while LinkActivity is locked. | 350 | // Called while LinkActivity is locked. |
169 | protected override void AddChildToLinkset(BSPhysObject child) | 351 | protected override void AddChildToLinkset(BSPrimLinkable child) |
170 | { | 352 | { |
171 | if (!HasChild(child)) | 353 | if (!HasChild(child)) |
172 | { | 354 | { |
@@ -174,24 +356,28 @@ public sealed class BSLinksetCompound : BSLinkset | |||
174 | 356 | ||
175 | DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); | 357 | DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); |
176 | 358 | ||
177 | // Cause constraints and assorted properties to be recomputed before the next simulation step. | 359 | // Rebuild the compound shape with the new child shape included |
178 | InternalRefresh(LinksetRoot); | 360 | ScheduleRebuild(child); |
179 | } | 361 | } |
180 | return; | 362 | return; |
181 | } | 363 | } |
182 | 364 | ||
183 | // Remove the specified child from the linkset. | 365 | // Remove the specified child from the linkset. |
184 | // Safe to call even if the child is not really in my linkset. | 366 | // Safe to call even if the child is not really in the linkset. |
185 | protected override void RemoveChildFromLinkset(BSPhysObject child) | 367 | protected override void RemoveChildFromLinkset(BSPrimLinkable child) |
186 | { | 368 | { |
369 | child.ClearDisplacement(); | ||
370 | |||
187 | if (m_children.Remove(child)) | 371 | if (m_children.Remove(child)) |
188 | { | 372 | { |
189 | DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", | 373 | DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", |
190 | child.LocalID, | 374 | child.LocalID, |
191 | LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString("X"), | 375 | LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString, |
192 | child.LocalID, child.PhysBody.ptr.ToString("X")); | 376 | child.LocalID, child.PhysBody.AddrString); |
193 | 377 | ||
194 | // Cause the child's body to be rebuilt and thus restored to normal operation | 378 | // Cause the child's body to be rebuilt and thus restored to normal operation |
379 | RecomputeChildWorldPosition(child, false); | ||
380 | child.LinksetInfo = null; | ||
195 | child.ForceBodyShapeRebuild(false); | 381 | child.ForceBodyShapeRebuild(false); |
196 | 382 | ||
197 | if (!HasAnyChildren) | 383 | if (!HasAnyChildren) |
@@ -201,8 +387,8 @@ public sealed class BSLinksetCompound : BSLinkset | |||
201 | } | 387 | } |
202 | else | 388 | else |
203 | { | 389 | { |
204 | // Schedule a rebuild of the linkset before the next simulation tick. | 390 | // Rebuild the compound shape with the child removed |
205 | InternalRefresh(LinksetRoot); | 391 | ScheduleRebuild(LinksetRoot); |
206 | } | 392 | } |
207 | } | 393 | } |
208 | return; | 394 | return; |
@@ -213,63 +399,116 @@ public sealed class BSLinksetCompound : BSLinkset | |||
213 | // Constraint linksets are rebuilt every time. | 399 | // Constraint linksets are rebuilt every time. |
214 | // Note that this works for rebuilding just the root after a linkset is taken apart. | 400 | // Note that this works for rebuilding just the root after a linkset is taken apart. |
215 | // Called at taint time!! | 401 | // Called at taint time!! |
402 | private bool disableCOM = true; // DEBUG DEBUG: disable until we get this debugged | ||
216 | private void RecomputeLinksetCompound() | 403 | private void RecomputeLinksetCompound() |
217 | { | 404 | { |
218 | // Cause the root shape to be rebuilt as a compound object with just the root in it | 405 | try |
219 | LinksetRoot.ForceBodyShapeRebuild(true); | 406 | { |
407 | // Suppress rebuilding while rebuilding. (We know rebuilding is on only one thread.) | ||
408 | Rebuilding = true; | ||
220 | 409 | ||
221 | DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}", | 410 | // Cause the root shape to be rebuilt as a compound object with just the root in it |
222 | LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren); | 411 | LinksetRoot.ForceBodyShapeRebuild(true /* inTaintTime */); |
223 | 412 | ||
224 | // Add a shape for each of the other children in the linkset | 413 | // The center of mass for the linkset is the geometric center of the group. |
225 | ForEachMember(delegate(BSPhysObject cPrim) | 414 | // Compute a displacement for each component so it is relative to the center-of-mass. |
226 | { | 415 | // Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass |
227 | if (!IsRoot(cPrim)) | 416 | OMV.Vector3 centerOfMassW = LinksetRoot.RawPosition; |
417 | if (!disableCOM) // DEBUG DEBUG | ||
228 | { | 418 | { |
229 | // Each child position and rotation is given relative to the root. | 419 | // Compute a center-of-mass in world coordinates. |
230 | OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation); | 420 | centerOfMassW = ComputeLinksetCenterOfMass(); |
231 | OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation; | 421 | } |
232 | OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation; | 422 | |
423 | OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation); | ||
424 | |||
425 | // 'centerDisplacement' is the value to subtract from children to give physical offset position | ||
426 | OMV.Vector3 centerDisplacement = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation; | ||
427 | LinksetRoot.SetEffectiveCenterOfMassW(centerDisplacement); | ||
233 | 428 | ||
234 | DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}", | 429 | // This causes the physical position of the root prim to be offset to accomodate for the displacements |
235 | LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, displacementPos, displacementRot); | 430 | LinksetRoot.ForcePosition = LinksetRoot.RawPosition; |
236 | 431 | ||
237 | if (cPrim.PhysShape.isNativeShape) | 432 | // Update the local transform for the root child shape so it is offset from the <0,0,0> which is COM |
433 | PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, 0 /* childIndex */, | ||
434 | -centerDisplacement, | ||
435 | OMV.Quaternion.Identity, // LinksetRoot.RawOrientation, | ||
436 | false /* shouldRecalculateLocalAabb (is done later after linkset built) */); | ||
437 | |||
438 | DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,com={1},rootPos={2},centerDisp={3}", | ||
439 | LinksetRoot.LocalID, centerOfMassW, LinksetRoot.RawPosition, centerDisplacement); | ||
440 | |||
441 | DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}", | ||
442 | LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren); | ||
443 | |||
444 | // Add a shape for each of the other children in the linkset | ||
445 | int memberIndex = 1; | ||
446 | ForEachMember(delegate(BSPrimLinkable cPrim) | ||
447 | { | ||
448 | if (IsRoot(cPrim)) | ||
238 | { | 449 | { |
239 | // Native shapes are not shared so we need to create a new one. | 450 | cPrim.LinksetChildIndex = 0; |
240 | // A mesh or hull is created because scale is not available on a native shape. | ||
241 | // (TODO: Bullet does have a btScaledCollisionShape. Can that be used?) | ||
242 | BulletShape saveShape = cPrim.PhysShape; | ||
243 | cPrim.PhysShape.ptr = IntPtr.Zero; // Don't let the create free the child's shape | ||
244 | PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null); | ||
245 | BulletShape newShape = cPrim.PhysShape; | ||
246 | cPrim.PhysShape = saveShape; | ||
247 | BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, displacementPos, displacementRot); | ||
248 | } | 451 | } |
249 | else | 452 | else |
250 | { | 453 | { |
251 | // For the shared shapes (meshes and hulls), just use the shape in the child. | 454 | cPrim.LinksetChildIndex = memberIndex; |
252 | if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape)) | 455 | |
456 | if (cPrim.PhysShape.isNativeShape) | ||
253 | { | 457 | { |
254 | PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}", | 458 | // A native shape is turned into a hull collision shape because native |
255 | LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape); | 459 | // shapes are not shared so we have to hullify it so it will be tracked |
460 | // and freed at the correct time. This also solves the scaling problem | ||
461 | // (native shapes scale but hull/meshes are assumed to not be). | ||
462 | // TODO: decide of the native shape can just be used in the compound shape. | ||
463 | // Use call to CreateGeomNonSpecial(). | ||
464 | BulletShape saveShape = cPrim.PhysShape; | ||
465 | cPrim.PhysShape.Clear(); // Don't let the create free the child's shape | ||
466 | PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null); | ||
467 | BulletShape newShape = cPrim.PhysShape; | ||
468 | cPrim.PhysShape = saveShape; | ||
469 | |||
470 | OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; | ||
471 | OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; | ||
472 | PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, newShape, offsetPos, offsetRot); | ||
473 | DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addNative,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", | ||
474 | LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, newShape, offsetPos, offsetRot); | ||
256 | } | 475 | } |
257 | BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, displacementPos, displacementRot); | 476 | else |
258 | } | 477 | { |
259 | } | 478 | // For the shared shapes (meshes and hulls), just use the shape in the child. |
260 | return false; // 'false' says to move onto the next child in the list | 479 | // The reference count added here will be decremented when the compound shape |
261 | }); | 480 | // is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced). |
481 | if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape)) | ||
482 | { | ||
483 | PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}", | ||
484 | LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape); | ||
485 | } | ||
486 | OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement; | ||
487 | OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation; | ||
488 | PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot); | ||
489 | DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addNonNative,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}", | ||
490 | LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot); | ||
262 | 491 | ||
263 | // With all of the linkset packed into the root prim, it has the mass of everyone. | 492 | } |
264 | float linksetMass = LinksetMass; | 493 | memberIndex++; |
265 | LinksetRoot.UpdatePhysicalMassProperties(linksetMass); | 494 | } |
495 | return false; // 'false' says to move onto the next child in the list | ||
496 | }); | ||
266 | 497 | ||
267 | BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr); | 498 | // With all of the linkset packed into the root prim, it has the mass of everyone. |
499 | LinksetMass = ComputeLinksetMass(); | ||
500 | LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true); | ||
268 | 501 | ||
269 | // DEBUG: see of inter-linkset collisions are causing problems for constraint linksets. | 502 | // Enable the physical position updator to return the position and rotation of the root shape |
270 | // BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr, | 503 | PhysicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE); |
271 | // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask); | 504 | } |
505 | finally | ||
506 | { | ||
507 | Rebuilding = false; | ||
508 | } | ||
272 | 509 | ||
510 | // See that the Aabb surrounds the new shape | ||
511 | PhysicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape); | ||
273 | } | 512 | } |
274 | } | 513 | } |
275 | } \ No newline at end of file | 514 | } \ No newline at end of file |