diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs')
-rw-r--r-- | OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs new file mode 100644 index 0000000..9a977e6 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSNPlugin/BSLinksetCompound.cs | |||
@@ -0,0 +1,397 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyrightD | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | using System; | ||
28 | using System.Collections.Generic; | ||
29 | using System.Text; | ||
30 | |||
31 | using OpenSim.Framework; | ||
32 | |||
33 | using OMV = OpenMetaverse; | ||
34 | |||
35 | namespace OpenSim.Region.Physics.BulletSNPlugin | ||
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 OMV.Vector3 OffsetPos; | ||
44 | public OMV.Quaternion OffsetRot; | ||
45 | public BSLinksetCompoundInfo(OMV.Vector3 p, OMV.Quaternion r) | ||
46 | { | ||
47 | OffsetPos = p; | ||
48 | OffsetRot = r; | ||
49 | } | ||
50 | public override void Clear() | ||
51 | { | ||
52 | OffsetPos = OMV.Vector3.Zero; | ||
53 | OffsetRot = OMV.Quaternion.Identity; | ||
54 | } | ||
55 | public override string ToString() | ||
56 | { | ||
57 | StringBuilder buff = new StringBuilder(); | ||
58 | buff.Append("<p="); | ||
59 | buff.Append(OffsetPos.ToString()); | ||
60 | buff.Append(",r="); | ||
61 | buff.Append(OffsetRot.ToString()); | ||
62 | buff.Append(">"); | ||
63 | return buff.ToString(); | ||
64 | } | ||
65 | }; | ||
66 | |||
67 | public sealed class BSLinksetCompound : BSLinkset | ||
68 | { | ||
69 | private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]"; | ||
70 | |||
71 | public BSLinksetCompound(BSScene scene, BSPhysObject parent) : base(scene, parent) | ||
72 | { | ||
73 | } | ||
74 | |||
75 | // For compound implimented linksets, if there are children, use compound shape for the root. | ||
76 | public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) | ||
77 | { | ||
78 | // Returning 'unknown' means we don't have a preference. | ||
79 | BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; | ||
80 | if (IsRoot(requestor) && HasAnyChildren) | ||
81 | { | ||
82 | ret = BSPhysicsShapeType.SHAPE_COMPOUND; | ||
83 | } | ||
84 | // DetailLog("{0},BSLinksetCompound.PreferredPhysicalShape,call,shape={1}", LinksetRoot.LocalID, ret); | ||
85 | return ret; | ||
86 | } | ||
87 | |||
88 | // When physical properties are changed the linkset needs to recalculate | ||
89 | // its internal properties. | ||
90 | public override void Refresh(BSPhysObject requestor) | ||
91 | { | ||
92 | base.Refresh(requestor); | ||
93 | |||
94 | // Something changed so do the rebuilding thing | ||
95 | // ScheduleRebuild(); | ||
96 | } | ||
97 | |||
98 | // Schedule a refresh to happen after all the other taint processing. | ||
99 | private void ScheduleRebuild(BSPhysObject requestor) | ||
100 | { | ||
101 | DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1}", | ||
102 | requestor.LocalID, Rebuilding); | ||
103 | // When rebuilding, it is possible to set properties that would normally require a rebuild. | ||
104 | // If already rebuilding, don't request another rebuild. | ||
105 | if (!Rebuilding) | ||
106 | { | ||
107 | PhysicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate() | ||
108 | { | ||
109 | if (HasAnyChildren) | ||
110 | RecomputeLinksetCompound(); | ||
111 | }); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | // The object is going dynamic (physical). Do any setup necessary | ||
116 | // for a dynamic linkset. | ||
117 | // Only the state of the passed object can be modified. The rest of the linkset | ||
118 | // has not yet been fully constructed. | ||
119 | // Return 'true' if any properties updated on the passed object. | ||
120 | // Called at taint-time! | ||
121 | public override bool MakeDynamic(BSPhysObject child) | ||
122 | { | ||
123 | bool ret = false; | ||
124 | DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); | ||
125 | if (IsRoot(child)) | ||
126 | { | ||
127 | // The root is going dynamic. Make sure mass is properly set. | ||
128 | ScheduleRebuild(LinksetRoot); | ||
129 | } | ||
130 | else | ||
131 | { | ||
132 | // The origional prims are removed from the world as the shape of the root compound | ||
133 | // shape takes over. | ||
134 | BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); | ||
135 | BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION); | ||
136 | // We don't want collisions from the old linkset children. | ||
137 | BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); | ||
138 | |||
139 | child.PhysBody.collisionType = CollisionType.LinksetChild; | ||
140 | |||
141 | ret = true; | ||
142 | } | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | // The object is going static (non-physical). Do any setup necessary for a static linkset. | ||
147 | // Return 'true' if any properties updated on the passed object. | ||
148 | // This doesn't normally happen -- OpenSim removes the objects from the physical | ||
149 | // world if it is a static linkset. | ||
150 | // Called at taint-time! | ||
151 | public override bool MakeStatic(BSPhysObject child) | ||
152 | { | ||
153 | bool ret = false; | ||
154 | DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); | ||
155 | if (IsRoot(child)) | ||
156 | { | ||
157 | ScheduleRebuild(LinksetRoot); | ||
158 | } | ||
159 | else | ||
160 | { | ||
161 | // The non-physical children can come back to life. | ||
162 | BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); | ||
163 | |||
164 | child.PhysBody.collisionType = CollisionType.LinksetChild; | ||
165 | |||
166 | // Don't force activation so setting of DISABLE_SIMULATION can stay if used. | ||
167 | BulletSimAPI.Activate2(child.PhysBody.ptr, false); | ||
168 | ret = true; | ||
169 | } | ||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | public override void UpdateProperties(BSPhysObject updated, bool physicalUpdate) | ||
174 | { | ||
175 | // The user moving a child around requires the rebuilding of the linkset compound shape | ||
176 | // One problem is this happens when a border is crossed -- the simulator implementation | ||
177 | // is to store the position into the group which causes the move of the object | ||
178 | // but it also means all the child positions get updated. | ||
179 | // What would cause an unnecessary rebuild so we make sure the linkset is in a | ||
180 | // region before bothering to do a rebuild. | ||
181 | if (!IsRoot(updated) | ||
182 | && !physicalUpdate | ||
183 | && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition)) | ||
184 | { | ||
185 | updated.LinksetInfo = null; | ||
186 | ScheduleRebuild(updated); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | // Routine called when rebuilding the body of some member of the linkset. | ||
191 | // Since we don't keep in world relationships, do nothing unless it's a child changing. | ||
192 | // Returns 'true' of something was actually removed and would need restoring | ||
193 | // Called at taint-time!! | ||
194 | public override bool RemoveBodyDependencies(BSPrim child) | ||
195 | { | ||
196 | bool ret = false; | ||
197 | |||
198 | DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}", | ||
199 | child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), IsRoot(child)); | ||
200 | |||
201 | if (!IsRoot(child)) | ||
202 | { | ||
203 | // Because it is a convenient time, recompute child world position and rotation based on | ||
204 | // its position in the linkset. | ||
205 | RecomputeChildWorldPosition(child, true); | ||
206 | } | ||
207 | |||
208 | // Cannot schedule a refresh/rebuild here because this routine is called when | ||
209 | // the linkset is being rebuilt. | ||
210 | // InternalRefresh(LinksetRoot); | ||
211 | |||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true', | ||
216 | // this routine will restore the removed constraints. | ||
217 | // Called at taint-time!! | ||
218 | public override void RestoreBodyDependencies(BSPrim child) | ||
219 | { | ||
220 | } | ||
221 | |||
222 | // When the linkset is built, the child shape is added to the compound shape relative to the | ||
223 | // root shape. The linkset then moves around but this does not move the actual child | ||
224 | // prim. The child prim's location must be recomputed based on the location of the root shape. | ||
225 | private void RecomputeChildWorldPosition(BSPhysObject child, bool inTaintTime) | ||
226 | { | ||
227 | BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo; | ||
228 | if (lci != null) | ||
229 | { | ||
230 | if (inTaintTime) | ||
231 | { | ||
232 | OMV.Vector3 oldPos = child.RawPosition; | ||
233 | child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetPos; | ||
234 | child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot; | ||
235 | DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}", | ||
236 | child.LocalID, oldPos, lci, child.RawPosition); | ||
237 | } | ||
238 | else | ||
239 | { | ||
240 | // TaintedObject is not used here so the raw position is set now and not at taint-time. | ||
241 | child.Position = LinksetRoot.RawPosition + lci.OffsetPos; | ||
242 | child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot; | ||
243 | } | ||
244 | } | ||
245 | else | ||
246 | { | ||
247 | // This happens when children have been added to the linkset but the linkset | ||
248 | // has not been constructed yet. So like, at taint time, adding children to a linkset | ||
249 | // and then changing properties of the children (makePhysical, for instance) | ||
250 | // but the post-print action of actually rebuilding the linkset has not yet happened. | ||
251 | // PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}", | ||
252 | // LogHeader, child.LocalID); | ||
253 | DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID); | ||
254 | } | ||
255 | } | ||
256 | |||
257 | // ================================================================ | ||
258 | |||
259 | // Add a new child to the linkset. | ||
260 | // Called while LinkActivity is locked. | ||
261 | protected override void AddChildToLinkset(BSPhysObject child) | ||
262 | { | ||
263 | if (!HasChild(child)) | ||
264 | { | ||
265 | m_children.Add(child); | ||
266 | |||
267 | DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); | ||
268 | |||
269 | // Rebuild the compound shape with the new child shape included | ||
270 | ScheduleRebuild(child); | ||
271 | } | ||
272 | return; | ||
273 | } | ||
274 | |||
275 | // Remove the specified child from the linkset. | ||
276 | // Safe to call even if the child is not really in the linkset. | ||
277 | protected override void RemoveChildFromLinkset(BSPhysObject child) | ||
278 | { | ||
279 | if (m_children.Remove(child)) | ||
280 | { | ||
281 | DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", | ||
282 | child.LocalID, | ||
283 | LinksetRoot.LocalID, LinksetRoot.PhysBody.ptr.ToString(), | ||
284 | child.LocalID, child.PhysBody.ptr.ToString()); | ||
285 | |||
286 | // Cause the child's body to be rebuilt and thus restored to normal operation | ||
287 | RecomputeChildWorldPosition(child, false); | ||
288 | child.ForceBodyShapeRebuild(false); | ||
289 | |||
290 | if (!HasAnyChildren) | ||
291 | { | ||
292 | // The linkset is now empty. The root needs rebuilding. | ||
293 | LinksetRoot.ForceBodyShapeRebuild(false); | ||
294 | } | ||
295 | else | ||
296 | { | ||
297 | // Rebuild the compound shape with the child removed | ||
298 | ScheduleRebuild(child); | ||
299 | } | ||
300 | } | ||
301 | return; | ||
302 | } | ||
303 | |||
304 | // Called before the simulation step to make sure the compound based linkset | ||
305 | // is all initialized. | ||
306 | // Constraint linksets are rebuilt every time. | ||
307 | // Note that this works for rebuilding just the root after a linkset is taken apart. | ||
308 | // Called at taint time!! | ||
309 | private void RecomputeLinksetCompound() | ||
310 | { | ||
311 | try | ||
312 | { | ||
313 | // Suppress rebuilding while rebuilding | ||
314 | Rebuilding = true; | ||
315 | |||
316 | // Cause the root shape to be rebuilt as a compound object with just the root in it | ||
317 | LinksetRoot.ForceBodyShapeRebuild(true); | ||
318 | |||
319 | DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}", | ||
320 | LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren); | ||
321 | |||
322 | // Add a shape for each of the other children in the linkset | ||
323 | ForEachMember(delegate(BSPhysObject cPrim) | ||
324 | { | ||
325 | if (!IsRoot(cPrim)) | ||
326 | { | ||
327 | // Compute the displacement of the child from the root of the linkset. | ||
328 | // This info is saved in the child prim so the relationship does not | ||
329 | // change over time and the new child position can be computed | ||
330 | // when the linkset is being disassembled (the linkset may have moved). | ||
331 | BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo; | ||
332 | if (lci == null) | ||
333 | { | ||
334 | // Each child position and rotation is given relative to the root. | ||
335 | OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation); | ||
336 | OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation; | ||
337 | OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation; | ||
338 | |||
339 | // Save relative position for recomputing child's world position after moving linkset. | ||
340 | lci = new BSLinksetCompoundInfo(displacementPos, displacementRot); | ||
341 | cPrim.LinksetInfo = lci; | ||
342 | DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci); | ||
343 | } | ||
344 | |||
345 | DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}", | ||
346 | LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot); | ||
347 | |||
348 | if (cPrim.PhysShape.isNativeShape) | ||
349 | { | ||
350 | // A native shape is turning into a hull collision shape because native | ||
351 | // shapes are not shared so we have to hullify it so it will be tracked | ||
352 | // and freed at the correct time. This also solves the scaling problem | ||
353 | // (native shapes scaled but hull/meshes are assumed to not be). | ||
354 | // TODO: decide of the native shape can just be used in the compound shape. | ||
355 | // Use call to CreateGeomNonSpecial(). | ||
356 | BulletShape saveShape = cPrim.PhysShape; | ||
357 | cPrim.PhysShape.Clear(); // Don't let the create free the child's shape | ||
358 | // PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null); | ||
359 | PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null); | ||
360 | BulletShape newShape = cPrim.PhysShape; | ||
361 | cPrim.PhysShape = saveShape; | ||
362 | BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, lci.OffsetPos, lci.OffsetRot); | ||
363 | } | ||
364 | else | ||
365 | { | ||
366 | // For the shared shapes (meshes and hulls), just use the shape in the child. | ||
367 | // The reference count added here will be decremented when the compound shape | ||
368 | // is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced). | ||
369 | if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape)) | ||
370 | { | ||
371 | PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}", | ||
372 | LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape); | ||
373 | } | ||
374 | BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, lci.OffsetPos, lci.OffsetRot); | ||
375 | } | ||
376 | } | ||
377 | return false; // 'false' says to move onto the next child in the list | ||
378 | }); | ||
379 | |||
380 | // With all of the linkset packed into the root prim, it has the mass of everyone. | ||
381 | LinksetMass = LinksetMass; | ||
382 | LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true); | ||
383 | } | ||
384 | finally | ||
385 | { | ||
386 | Rebuilding = false; | ||
387 | } | ||
388 | |||
389 | BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr); | ||
390 | |||
391 | // DEBUG: see of inter-linkset collisions are causing problems for constraint linksets. | ||
392 | // BulletSimAPI.SetCollisionFilterMask2(LinksetRoot.BSBody.ptr, | ||
393 | // (uint)CollisionFilterGroups.LinksetFilter, (uint)CollisionFilterGroups.LinksetMask); | ||
394 | |||
395 | } | ||
396 | } | ||
397 | } \ No newline at end of file | ||