diff options
Diffstat (limited to 'OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs')
-rwxr-xr-x | OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs | 852 |
1 files changed, 852 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs new file mode 100755 index 0000000..c4b4c86 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSLinksetConstraints.cs | |||
@@ -0,0 +1,852 @@ | |||
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 OMV = OpenMetaverse; | ||
32 | |||
33 | namespace OpenSim.Region.PhysicsModule.BulletS | ||
34 | { | ||
35 | public sealed class BSLinksetConstraints : BSLinkset | ||
36 | { | ||
37 | // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]"; | ||
38 | |||
39 | public class BSLinkInfoConstraint : BSLinkInfo | ||
40 | { | ||
41 | public ConstraintType constraintType; | ||
42 | public BSConstraint constraint; | ||
43 | public OMV.Vector3 linearLimitLow; | ||
44 | public OMV.Vector3 linearLimitHigh; | ||
45 | public OMV.Vector3 angularLimitLow; | ||
46 | public OMV.Vector3 angularLimitHigh; | ||
47 | public bool useFrameOffset; | ||
48 | public bool enableTransMotor; | ||
49 | public float transMotorMaxVel; | ||
50 | public float transMotorMaxForce; | ||
51 | public float cfm; | ||
52 | public float erp; | ||
53 | public float solverIterations; | ||
54 | // | ||
55 | public OMV.Vector3 frameInAloc; | ||
56 | public OMV.Quaternion frameInArot; | ||
57 | public OMV.Vector3 frameInBloc; | ||
58 | public OMV.Quaternion frameInBrot; | ||
59 | public bool useLinearReferenceFrameA; | ||
60 | // Spring | ||
61 | public bool[] springAxisEnable; | ||
62 | public float[] springDamping; | ||
63 | public float[] springStiffness; | ||
64 | public OMV.Vector3 springLinearEquilibriumPoint; | ||
65 | public OMV.Vector3 springAngularEquilibriumPoint; | ||
66 | |||
67 | public BSLinkInfoConstraint(BSPrimLinkable pMember) | ||
68 | : base(pMember) | ||
69 | { | ||
70 | constraint = null; | ||
71 | ResetLink(); | ||
72 | member.PhysScene.DetailLog("{0},BSLinkInfoConstraint.creation", member.LocalID); | ||
73 | } | ||
74 | |||
75 | // Set all the parameters for this constraint to a fixed, non-movable constraint. | ||
76 | public override void ResetLink() | ||
77 | { | ||
78 | // constraintType = ConstraintType.D6_CONSTRAINT_TYPE; | ||
79 | constraintType = ConstraintType.BS_FIXED_CONSTRAINT_TYPE; | ||
80 | linearLimitLow = OMV.Vector3.Zero; | ||
81 | linearLimitHigh = OMV.Vector3.Zero; | ||
82 | angularLimitLow = OMV.Vector3.Zero; | ||
83 | angularLimitHigh = OMV.Vector3.Zero; | ||
84 | useFrameOffset = BSParam.LinkConstraintUseFrameOffset; | ||
85 | enableTransMotor = BSParam.LinkConstraintEnableTransMotor; | ||
86 | transMotorMaxVel = BSParam.LinkConstraintTransMotorMaxVel; | ||
87 | transMotorMaxForce = BSParam.LinkConstraintTransMotorMaxForce; | ||
88 | cfm = BSParam.LinkConstraintCFM; | ||
89 | erp = BSParam.LinkConstraintERP; | ||
90 | solverIterations = BSParam.LinkConstraintSolverIterations; | ||
91 | frameInAloc = OMV.Vector3.Zero; | ||
92 | frameInArot = OMV.Quaternion.Identity; | ||
93 | frameInBloc = OMV.Vector3.Zero; | ||
94 | frameInBrot = OMV.Quaternion.Identity; | ||
95 | useLinearReferenceFrameA = true; | ||
96 | springAxisEnable = new bool[6]; | ||
97 | springDamping = new float[6]; | ||
98 | springStiffness = new float[6]; | ||
99 | for (int ii = 0; ii < springAxisEnable.Length; ii++) | ||
100 | { | ||
101 | springAxisEnable[ii] = false; | ||
102 | springDamping[ii] = BSAPITemplate.SPRING_NOT_SPECIFIED; | ||
103 | springStiffness[ii] = BSAPITemplate.SPRING_NOT_SPECIFIED; | ||
104 | } | ||
105 | springLinearEquilibriumPoint = OMV.Vector3.Zero; | ||
106 | springAngularEquilibriumPoint = OMV.Vector3.Zero; | ||
107 | member.PhysScene.DetailLog("{0},BSLinkInfoConstraint.ResetLink", member.LocalID); | ||
108 | } | ||
109 | |||
110 | // Given a constraint, apply the current constraint parameters to same. | ||
111 | public override void SetLinkParameters(BSConstraint constrain) | ||
112 | { | ||
113 | member.PhysScene.DetailLog("{0},BSLinkInfoConstraint.SetLinkParameters,type={1}", member.LocalID, constraintType); | ||
114 | switch (constraintType) | ||
115 | { | ||
116 | case ConstraintType.BS_FIXED_CONSTRAINT_TYPE: | ||
117 | case ConstraintType.D6_CONSTRAINT_TYPE: | ||
118 | BSConstraint6Dof constrain6dof = constrain as BSConstraint6Dof; | ||
119 | if (constrain6dof != null) | ||
120 | { | ||
121 | // NOTE: D6_SPRING_CONSTRAINT_TYPE should be updated if you change any of this code. | ||
122 | // zero linear and angular limits makes the objects unable to move in relation to each other | ||
123 | constrain6dof.SetLinearLimits(linearLimitLow, linearLimitHigh); | ||
124 | constrain6dof.SetAngularLimits(angularLimitLow, angularLimitHigh); | ||
125 | |||
126 | // tweek the constraint to increase stability | ||
127 | constrain6dof.UseFrameOffset(useFrameOffset); | ||
128 | constrain6dof.TranslationalLimitMotor(enableTransMotor, transMotorMaxVel, transMotorMaxForce); | ||
129 | constrain6dof.SetCFMAndERP(cfm, erp); | ||
130 | if (solverIterations != 0f) | ||
131 | { | ||
132 | constrain6dof.SetSolverIterations(solverIterations); | ||
133 | } | ||
134 | } | ||
135 | break; | ||
136 | case ConstraintType.D6_SPRING_CONSTRAINT_TYPE: | ||
137 | BSConstraintSpring constrainSpring = constrain as BSConstraintSpring; | ||
138 | if (constrainSpring != null) | ||
139 | { | ||
140 | // zero linear and angular limits makes the objects unable to move in relation to each other | ||
141 | constrainSpring.SetLinearLimits(linearLimitLow, linearLimitHigh); | ||
142 | constrainSpring.SetAngularLimits(angularLimitLow, angularLimitHigh); | ||
143 | |||
144 | // tweek the constraint to increase stability | ||
145 | constrainSpring.UseFrameOffset(useFrameOffset); | ||
146 | constrainSpring.TranslationalLimitMotor(enableTransMotor, transMotorMaxVel, transMotorMaxForce); | ||
147 | constrainSpring.SetCFMAndERP(cfm, erp); | ||
148 | if (solverIterations != 0f) | ||
149 | { | ||
150 | constrainSpring.SetSolverIterations(solverIterations); | ||
151 | } | ||
152 | for (int ii = 0; ii < springAxisEnable.Length; ii++) | ||
153 | { | ||
154 | constrainSpring.SetAxisEnable(ii, springAxisEnable[ii]); | ||
155 | if (springDamping[ii] != BSAPITemplate.SPRING_NOT_SPECIFIED) | ||
156 | constrainSpring.SetDamping(ii, springDamping[ii]); | ||
157 | if (springStiffness[ii] != BSAPITemplate.SPRING_NOT_SPECIFIED) | ||
158 | constrainSpring.SetStiffness(ii, springStiffness[ii]); | ||
159 | } | ||
160 | constrainSpring.CalculateTransforms(); | ||
161 | |||
162 | if (springLinearEquilibriumPoint != OMV.Vector3.Zero) | ||
163 | constrainSpring.SetEquilibriumPoint(springLinearEquilibriumPoint, springAngularEquilibriumPoint); | ||
164 | else | ||
165 | constrainSpring.SetEquilibriumPoint(BSAPITemplate.SPRING_NOT_SPECIFIED, BSAPITemplate.SPRING_NOT_SPECIFIED); | ||
166 | } | ||
167 | break; | ||
168 | default: | ||
169 | break; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | // Return 'true' if the property updates from the physics engine should be reported | ||
174 | // to the simulator. | ||
175 | // If the constraint is fixed, we don't need to report as the simulator and viewer will | ||
176 | // report the right things. | ||
177 | public override bool ShouldUpdateChildProperties() | ||
178 | { | ||
179 | bool ret = true; | ||
180 | if (constraintType == ConstraintType.BS_FIXED_CONSTRAINT_TYPE) | ||
181 | ret = false; | ||
182 | |||
183 | return ret; | ||
184 | } | ||
185 | } | ||
186 | |||
187 | public BSLinksetConstraints(BSScene scene, BSPrimLinkable parent) : base(scene, parent) | ||
188 | { | ||
189 | LinksetImpl = LinksetImplementation.Constraint; | ||
190 | } | ||
191 | |||
192 | private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINT]"; | ||
193 | |||
194 | // When physical properties are changed the linkset needs to recalculate | ||
195 | // its internal properties. | ||
196 | // This is queued in the 'post taint' queue so the | ||
197 | // refresh will happen once after all the other taints are applied. | ||
198 | public override void Refresh(BSPrimLinkable requestor) | ||
199 | { | ||
200 | ScheduleRebuild(requestor); | ||
201 | base.Refresh(requestor); | ||
202 | |||
203 | } | ||
204 | |||
205 | private void ScheduleRebuild(BSPrimLinkable requestor) | ||
206 | { | ||
207 | DetailLog("{0},BSLinksetConstraint.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}", | ||
208 | requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren)); | ||
209 | |||
210 | // When rebuilding, it is possible to set properties that would normally require a rebuild. | ||
211 | // If already rebuilding, don't request another rebuild. | ||
212 | // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding. | ||
213 | lock (this) | ||
214 | { | ||
215 | if (!RebuildScheduled) | ||
216 | { | ||
217 | if (!Rebuilding && HasAnyChildren) | ||
218 | { | ||
219 | RebuildScheduled = true; | ||
220 | // Queue to happen after all the other taint processing | ||
221 | m_physicsScene.PostTaintObject("BSLinksetContraints.Refresh", requestor.LocalID, delegate() | ||
222 | { | ||
223 | if (HasAnyChildren) | ||
224 | { | ||
225 | // Constraints that have not been changed are not rebuild but make sure | ||
226 | // the constraint of the requestor is rebuilt. | ||
227 | PhysicallyUnlinkAChildFromRoot(LinksetRoot, requestor); | ||
228 | // Rebuild the linkset and all its constraints. | ||
229 | RecomputeLinksetConstraints(); | ||
230 | } | ||
231 | RebuildScheduled = false; | ||
232 | }); | ||
233 | } | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | |||
238 | // The object is going dynamic (physical). Do any setup necessary | ||
239 | // for a dynamic linkset. | ||
240 | // Only the state of the passed object can be modified. The rest of the linkset | ||
241 | // has not yet been fully constructed. | ||
242 | // Return 'true' if any properties updated on the passed object. | ||
243 | // Called at taint-time! | ||
244 | public override bool MakeDynamic(BSPrimLinkable child) | ||
245 | { | ||
246 | bool ret = false; | ||
247 | DetailLog("{0},BSLinksetConstraints.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); | ||
248 | if (IsRoot(child)) | ||
249 | { | ||
250 | // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly. | ||
251 | Refresh(LinksetRoot); | ||
252 | } | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | // The object is going static (non-physical). Do any setup necessary for a static linkset. | ||
257 | // Return 'true' if any properties updated on the passed object. | ||
258 | // This doesn't normally happen -- OpenSim removes the objects from the physical | ||
259 | // world if it is a static linkset. | ||
260 | // Called at taint-time! | ||
261 | public override bool MakeStatic(BSPrimLinkable child) | ||
262 | { | ||
263 | bool ret = false; | ||
264 | |||
265 | DetailLog("{0},BSLinksetConstraint.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); | ||
266 | child.ClearDisplacement(); | ||
267 | if (IsRoot(child)) | ||
268 | { | ||
269 | // Schedule a rebuild to verify that the root shape is set to the real shape. | ||
270 | Refresh(LinksetRoot); | ||
271 | } | ||
272 | return ret; | ||
273 | } | ||
274 | |||
275 | // Called at taint-time!! | ||
276 | public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable pObj) | ||
277 | { | ||
278 | // Nothing to do for constraints on property updates | ||
279 | } | ||
280 | |||
281 | // Routine called when rebuilding the body of some member of the linkset. | ||
282 | // Destroy all the constraints have have been made to root and set | ||
283 | // up to rebuild the constraints before the next simulation step. | ||
284 | // Returns 'true' of something was actually removed and would need restoring | ||
285 | // Called at taint-time!! | ||
286 | public override bool RemoveDependencies(BSPrimLinkable child) | ||
287 | { | ||
288 | bool ret = false; | ||
289 | |||
290 | DetailLog("{0},BSLinksetConstraint.RemoveDependencies,removeChildrenForRoot,rID={1},rBody={2}", | ||
291 | child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString); | ||
292 | |||
293 | lock (m_linksetActivityLock) | ||
294 | { | ||
295 | // Just undo all the constraints for this linkset. Rebuild at the end of the step. | ||
296 | ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot); | ||
297 | // Cause the constraints, et al to be rebuilt before the next simulation step. | ||
298 | Refresh(LinksetRoot); | ||
299 | } | ||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | // ================================================================ | ||
304 | |||
305 | // Add a new child to the linkset. | ||
306 | // Called while LinkActivity is locked. | ||
307 | protected override void AddChildToLinkset(BSPrimLinkable child) | ||
308 | { | ||
309 | if (!HasChild(child)) | ||
310 | { | ||
311 | m_children.Add(child, new BSLinkInfoConstraint(child)); | ||
312 | |||
313 | DetailLog("{0},BSLinksetConstraints.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); | ||
314 | |||
315 | // Cause constraints and assorted properties to be recomputed before the next simulation step. | ||
316 | Refresh(LinksetRoot); | ||
317 | } | ||
318 | return; | ||
319 | } | ||
320 | |||
321 | // Remove the specified child from the linkset. | ||
322 | // Safe to call even if the child is not really in my linkset. | ||
323 | protected override void RemoveChildFromLinkset(BSPrimLinkable child, bool inTaintTime) | ||
324 | { | ||
325 | if (m_children.Remove(child)) | ||
326 | { | ||
327 | BSPrimLinkable rootx = LinksetRoot; // capture the root and body as of now | ||
328 | BSPrimLinkable childx = child; | ||
329 | |||
330 | DetailLog("{0},BSLinksetConstraints.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", | ||
331 | childx.LocalID, | ||
332 | rootx.LocalID, rootx.PhysBody.AddrString, | ||
333 | childx.LocalID, childx.PhysBody.AddrString); | ||
334 | |||
335 | m_physicsScene.TaintedObject(inTaintTime, childx.LocalID, "BSLinksetConstraints.RemoveChildFromLinkset", delegate() | ||
336 | { | ||
337 | PhysicallyUnlinkAChildFromRoot(rootx, childx); | ||
338 | }); | ||
339 | // See that the linkset parameters are recomputed at the end of the taint time. | ||
340 | Refresh(LinksetRoot); | ||
341 | } | ||
342 | else | ||
343 | { | ||
344 | // Non-fatal occurance. | ||
345 | // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader); | ||
346 | } | ||
347 | return; | ||
348 | } | ||
349 | |||
350 | // Create a constraint between me (root of linkset) and the passed prim (the child). | ||
351 | // Called at taint time! | ||
352 | private void PhysicallyLinkAChildToRoot(BSPrimLinkable rootPrim, BSPrimLinkable childPrim) | ||
353 | { | ||
354 | // Don't build the constraint when asked. Put it off until just before the simulation step. | ||
355 | Refresh(rootPrim); | ||
356 | } | ||
357 | |||
358 | // Create a static constraint between the two passed objects | ||
359 | private BSConstraint BuildConstraint(BSPrimLinkable rootPrim, BSLinkInfo li) | ||
360 | { | ||
361 | BSLinkInfoConstraint linkInfo = li as BSLinkInfoConstraint; | ||
362 | if (linkInfo == null) | ||
363 | return null; | ||
364 | |||
365 | // Zero motion for children so they don't interpolate | ||
366 | li.member.ZeroMotion(true); | ||
367 | |||
368 | BSConstraint constrain = null; | ||
369 | |||
370 | switch (linkInfo.constraintType) | ||
371 | { | ||
372 | case ConstraintType.BS_FIXED_CONSTRAINT_TYPE: | ||
373 | case ConstraintType.D6_CONSTRAINT_TYPE: | ||
374 | // Relative position normalized to the root prim | ||
375 | // Essentually a vector pointing from center of rootPrim to center of li.member | ||
376 | OMV.Vector3 childRelativePosition = linkInfo.member.Position - rootPrim.Position; | ||
377 | |||
378 | // real world coordinate of midpoint between the two objects | ||
379 | OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); | ||
380 | |||
381 | DetailLog("{0},BSLinksetConstraint.BuildConstraint,6Dof,rBody={1},cBody={2},rLoc={3},cLoc={4},midLoc={5}", | ||
382 | rootPrim.LocalID, rootPrim.PhysBody, linkInfo.member.PhysBody, | ||
383 | rootPrim.Position, linkInfo.member.Position, midPoint); | ||
384 | |||
385 | // create a constraint that allows no freedom of movement between the two objects | ||
386 | // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 | ||
387 | |||
388 | constrain = new BSConstraint6Dof( | ||
389 | m_physicsScene.World, rootPrim.PhysBody, linkInfo.member.PhysBody, midPoint, true, true ); | ||
390 | |||
391 | /* NOTE: below is an attempt to build constraint with full frame computation, etc. | ||
392 | * Using the midpoint is easier since it lets the Bullet code manipulate the transforms | ||
393 | * of the objects. | ||
394 | * Code left for future programmers. | ||
395 | // ================================================================================== | ||
396 | // relative position normalized to the root prim | ||
397 | OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation); | ||
398 | OMV.Vector3 childRelativePosition = (liConstraint.member.Position - rootPrim.Position) * invThisOrientation; | ||
399 | |||
400 | // relative rotation of the child to the parent | ||
401 | OMV.Quaternion childRelativeRotation = invThisOrientation * liConstraint.member.Orientation; | ||
402 | OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation); | ||
403 | |||
404 | DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, liConstraint.member.LocalID); | ||
405 | constrain = new BS6DofConstraint( | ||
406 | PhysicsScene.World, rootPrim.Body, liConstraint.member.Body, | ||
407 | OMV.Vector3.Zero, | ||
408 | OMV.Quaternion.Inverse(rootPrim.Orientation), | ||
409 | OMV.Vector3.Zero, | ||
410 | OMV.Quaternion.Inverse(liConstraint.member.Orientation), | ||
411 | true, | ||
412 | true | ||
413 | ); | ||
414 | // ================================================================================== | ||
415 | */ | ||
416 | |||
417 | break; | ||
418 | case ConstraintType.D6_SPRING_CONSTRAINT_TYPE: | ||
419 | constrain = new BSConstraintSpring(m_physicsScene.World, rootPrim.PhysBody, linkInfo.member.PhysBody, | ||
420 | linkInfo.frameInAloc, linkInfo.frameInArot, linkInfo.frameInBloc, linkInfo.frameInBrot, | ||
421 | linkInfo.useLinearReferenceFrameA, | ||
422 | true /*disableCollisionsBetweenLinkedBodies*/); | ||
423 | DetailLog("{0},BSLinksetConstraint.BuildConstraint,spring,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6}", | ||
424 | rootPrim.LocalID, | ||
425 | rootPrim.LocalID, rootPrim.PhysBody.AddrString, | ||
426 | linkInfo.member.LocalID, linkInfo.member.PhysBody.AddrString, | ||
427 | rootPrim.Position, linkInfo.member.Position); | ||
428 | |||
429 | break; | ||
430 | default: | ||
431 | break; | ||
432 | } | ||
433 | |||
434 | linkInfo.SetLinkParameters(constrain); | ||
435 | |||
436 | m_physicsScene.Constraints.AddConstraint(constrain); | ||
437 | |||
438 | return constrain; | ||
439 | } | ||
440 | |||
441 | // Remove linkage between the linkset root and a particular child | ||
442 | // The root and child bodies are passed in because we need to remove the constraint between | ||
443 | // the bodies that were present at unlink time. | ||
444 | // Called at taint time! | ||
445 | private bool PhysicallyUnlinkAChildFromRoot(BSPrimLinkable rootPrim, BSPrimLinkable childPrim) | ||
446 | { | ||
447 | bool ret = false; | ||
448 | DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", | ||
449 | rootPrim.LocalID, | ||
450 | rootPrim.LocalID, rootPrim.PhysBody.AddrString, | ||
451 | childPrim.LocalID, childPrim.PhysBody.AddrString); | ||
452 | |||
453 | // If asked to unlink root from root, just remove all the constraints | ||
454 | if (rootPrim == childPrim || childPrim == LinksetRoot) | ||
455 | { | ||
456 | PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot); | ||
457 | ret = true; | ||
458 | } | ||
459 | else | ||
460 | { | ||
461 | // Find the constraint for this link and get rid of it from the overall collection and from my list | ||
462 | if (m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody, childPrim.PhysBody)) | ||
463 | { | ||
464 | // Make the child refresh its location | ||
465 | m_physicsScene.PE.PushUpdate(childPrim.PhysBody); | ||
466 | ret = true; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | return ret; | ||
471 | } | ||
472 | |||
473 | // Remove linkage between myself and any possible children I might have. | ||
474 | // Returns 'true' of any constraints were destroyed. | ||
475 | // Called at taint time! | ||
476 | private bool PhysicallyUnlinkAllChildrenFromRoot(BSPrimLinkable rootPrim) | ||
477 | { | ||
478 | DetailLog("{0},BSLinksetConstraint.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); | ||
479 | |||
480 | return m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.PhysBody); | ||
481 | } | ||
482 | |||
483 | // Call each of the constraints that make up this linkset and recompute the | ||
484 | // various transforms and variables. Create constraints of not created yet. | ||
485 | // Called before the simulation step to make sure the constraint based linkset | ||
486 | // is all initialized. | ||
487 | // Called at taint time!! | ||
488 | private void RecomputeLinksetConstraints() | ||
489 | { | ||
490 | float linksetMass = LinksetMass; | ||
491 | LinksetRoot.UpdatePhysicalMassProperties(linksetMass, true); | ||
492 | |||
493 | DetailLog("{0},BSLinksetConstraint.RecomputeLinksetConstraints,set,rBody={1},linksetMass={2}", | ||
494 | LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString, linksetMass); | ||
495 | |||
496 | try | ||
497 | { | ||
498 | Rebuilding = true; | ||
499 | |||
500 | // There is no reason to build all this physical stuff for a non-physical linkset. | ||
501 | if (!LinksetRoot.IsPhysicallyActive || !HasAnyChildren) | ||
502 | { | ||
503 | DetailLog("{0},BSLinksetConstraint.RecomputeLinksetCompound,notPhysicalOrNoChildren", LinksetRoot.LocalID); | ||
504 | return; // Note the 'finally' clause at the botton which will get executed. | ||
505 | } | ||
506 | |||
507 | ForEachLinkInfo((li) => | ||
508 | { | ||
509 | // A child in the linkset physically shows the mass of the whole linkset. | ||
510 | // This allows Bullet to apply enough force on the child to move the whole linkset. | ||
511 | // (Also do the mass stuff before recomputing the constraint so mass is not zero.) | ||
512 | li.member.UpdatePhysicalMassProperties(linksetMass, true); | ||
513 | |||
514 | BSConstraint constrain; | ||
515 | if (!m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.PhysBody, li.member.PhysBody, out constrain)) | ||
516 | { | ||
517 | // If constraint doesn't exist yet, create it. | ||
518 | constrain = BuildConstraint(LinksetRoot, li); | ||
519 | } | ||
520 | li.SetLinkParameters(constrain); | ||
521 | constrain.RecomputeConstraintVariables(linksetMass); | ||
522 | |||
523 | // PhysicsScene.PE.DumpConstraint(PhysicsScene.World, constrain.Constraint); // DEBUG DEBUG | ||
524 | return false; // 'false' says to keep processing other members | ||
525 | }); | ||
526 | } | ||
527 | finally | ||
528 | { | ||
529 | Rebuilding = false; | ||
530 | } | ||
531 | } | ||
532 | |||
533 | #region Extension | ||
534 | public override object Extension(string pFunct, params object[] pParams) | ||
535 | { | ||
536 | object ret = null; | ||
537 | switch (pFunct) | ||
538 | { | ||
539 | // pParams = [ BSPhysObject root, BSPhysObject child, integer linkType ] | ||
540 | case ExtendedPhysics.PhysFunctChangeLinkType: | ||
541 | if (pParams.Length > 2) | ||
542 | { | ||
543 | int requestedType = (int)pParams[2]; | ||
544 | DetailLog("{0},BSLinksetConstraint.ChangeLinkType,requestedType={1}", LinksetRoot.LocalID, requestedType); | ||
545 | if (requestedType == (int)ConstraintType.BS_FIXED_CONSTRAINT_TYPE | ||
546 | || requestedType == (int)ConstraintType.D6_CONSTRAINT_TYPE | ||
547 | || requestedType == (int)ConstraintType.D6_SPRING_CONSTRAINT_TYPE | ||
548 | || requestedType == (int)ConstraintType.HINGE_CONSTRAINT_TYPE | ||
549 | || requestedType == (int)ConstraintType.CONETWIST_CONSTRAINT_TYPE | ||
550 | || requestedType == (int)ConstraintType.SLIDER_CONSTRAINT_TYPE) | ||
551 | { | ||
552 | BSPrimLinkable child = pParams[1] as BSPrimLinkable; | ||
553 | if (child != null) | ||
554 | { | ||
555 | DetailLog("{0},BSLinksetConstraint.ChangeLinkType,rootID={1},childID={2},type={3}", | ||
556 | LinksetRoot.LocalID, LinksetRoot.LocalID, child.LocalID, requestedType); | ||
557 | m_physicsScene.TaintedObject(child.LocalID, "BSLinksetConstraint.PhysFunctChangeLinkType", delegate() | ||
558 | { | ||
559 | // Pick up all the constraints currently created. | ||
560 | RemoveDependencies(child); | ||
561 | |||
562 | BSLinkInfo linkInfo = null; | ||
563 | if (TryGetLinkInfo(child, out linkInfo)) | ||
564 | { | ||
565 | BSLinkInfoConstraint linkInfoC = linkInfo as BSLinkInfoConstraint; | ||
566 | if (linkInfoC != null) | ||
567 | { | ||
568 | linkInfoC.constraintType = (ConstraintType)requestedType; | ||
569 | ret = (object)true; | ||
570 | DetailLog("{0},BSLinksetConstraint.ChangeLinkType,link={1},type={2}", | ||
571 | linkInfo.member.LocalID, linkInfo.member.LocalID, linkInfoC.constraintType); | ||
572 | } | ||
573 | else | ||
574 | { | ||
575 | DetailLog("{0},BSLinksetConstraint.ChangeLinkType,linkInfoNotConstraint,childID={1}", LinksetRoot.LocalID, child.LocalID); | ||
576 | } | ||
577 | } | ||
578 | else | ||
579 | { | ||
580 | DetailLog("{0},BSLinksetConstraint.ChangeLinkType,noLinkInfoForChild,childID={1}", LinksetRoot.LocalID, child.LocalID); | ||
581 | } | ||
582 | // Cause the whole linkset to be rebuilt in post-taint time. | ||
583 | Refresh(child); | ||
584 | }); | ||
585 | } | ||
586 | else | ||
587 | { | ||
588 | DetailLog("{0},BSLinksetConstraint.SetLinkType,childNotBSPrimLinkable", LinksetRoot.LocalID); | ||
589 | } | ||
590 | } | ||
591 | else | ||
592 | { | ||
593 | DetailLog("{0},BSLinksetConstraint.SetLinkType,illegalRequestedType,reqested={1},spring={2}", | ||
594 | LinksetRoot.LocalID, requestedType, ((int)ConstraintType.D6_SPRING_CONSTRAINT_TYPE)); | ||
595 | } | ||
596 | } | ||
597 | break; | ||
598 | // pParams = [ BSPhysObject root, BSPhysObject child ] | ||
599 | case ExtendedPhysics.PhysFunctGetLinkType: | ||
600 | if (pParams.Length > 0) | ||
601 | { | ||
602 | BSPrimLinkable child = pParams[1] as BSPrimLinkable; | ||
603 | if (child != null) | ||
604 | { | ||
605 | BSLinkInfo linkInfo = null; | ||
606 | if (TryGetLinkInfo(child, out linkInfo)) | ||
607 | { | ||
608 | BSLinkInfoConstraint linkInfoC = linkInfo as BSLinkInfoConstraint; | ||
609 | if (linkInfoC != null) | ||
610 | { | ||
611 | ret = (object)(int)linkInfoC.constraintType; | ||
612 | DetailLog("{0},BSLinksetConstraint.GetLinkType,link={1},type={2}", | ||
613 | linkInfo.member.LocalID, linkInfo.member.LocalID, linkInfoC.constraintType); | ||
614 | |||
615 | } | ||
616 | } | ||
617 | } | ||
618 | } | ||
619 | break; | ||
620 | // pParams = [ BSPhysObject root, BSPhysObject child, int op, object opParams, int op, object opParams, ... ] | ||
621 | case ExtendedPhysics.PhysFunctChangeLinkParams: | ||
622 | // There should be two parameters: the childActor and a list of parameters to set | ||
623 | if (pParams.Length > 2) | ||
624 | { | ||
625 | BSPrimLinkable child = pParams[1] as BSPrimLinkable; | ||
626 | BSLinkInfo baseLinkInfo = null; | ||
627 | if (TryGetLinkInfo(child, out baseLinkInfo)) | ||
628 | { | ||
629 | BSLinkInfoConstraint linkInfo = baseLinkInfo as BSLinkInfoConstraint; | ||
630 | if (linkInfo != null) | ||
631 | { | ||
632 | int valueInt; | ||
633 | float valueFloat; | ||
634 | bool valueBool; | ||
635 | OMV.Vector3 valueVector; | ||
636 | OMV.Vector3 valueVector2; | ||
637 | OMV.Quaternion valueQuaternion; | ||
638 | int axisLow, axisHigh; | ||
639 | |||
640 | int opIndex = 2; | ||
641 | while (opIndex < pParams.Length) | ||
642 | { | ||
643 | int thisOp = 0; | ||
644 | string errMsg = ""; | ||
645 | try | ||
646 | { | ||
647 | thisOp = (int)pParams[opIndex]; | ||
648 | DetailLog("{0},BSLinksetConstraint.ChangeLinkParams2,op={1},val={2}", | ||
649 | linkInfo.member.LocalID, thisOp, pParams[opIndex + 1]); | ||
650 | switch (thisOp) | ||
651 | { | ||
652 | case ExtendedPhysics.PHYS_PARAM_LINK_TYPE: | ||
653 | valueInt = (int)pParams[opIndex + 1]; | ||
654 | ConstraintType valueType = (ConstraintType)valueInt; | ||
655 | if (valueType == ConstraintType.BS_FIXED_CONSTRAINT_TYPE | ||
656 | || valueType == ConstraintType.D6_CONSTRAINT_TYPE | ||
657 | || valueType == ConstraintType.D6_SPRING_CONSTRAINT_TYPE | ||
658 | || valueType == ConstraintType.HINGE_CONSTRAINT_TYPE | ||
659 | || valueType == ConstraintType.CONETWIST_CONSTRAINT_TYPE | ||
660 | || valueType == ConstraintType.SLIDER_CONSTRAINT_TYPE) | ||
661 | { | ||
662 | linkInfo.constraintType = valueType; | ||
663 | } | ||
664 | opIndex += 2; | ||
665 | break; | ||
666 | case ExtendedPhysics.PHYS_PARAM_FRAMEINA_LOC: | ||
667 | errMsg = "PHYS_PARAM_FRAMEINA_LOC takes one parameter of type vector"; | ||
668 | valueVector = (OMV.Vector3)pParams[opIndex + 1]; | ||
669 | linkInfo.frameInAloc = valueVector; | ||
670 | opIndex += 2; | ||
671 | break; | ||
672 | case ExtendedPhysics.PHYS_PARAM_FRAMEINA_ROT: | ||
673 | errMsg = "PHYS_PARAM_FRAMEINA_ROT takes one parameter of type rotation"; | ||
674 | valueQuaternion = (OMV.Quaternion)pParams[opIndex + 1]; | ||
675 | linkInfo.frameInArot = valueQuaternion; | ||
676 | opIndex += 2; | ||
677 | break; | ||
678 | case ExtendedPhysics.PHYS_PARAM_FRAMEINB_LOC: | ||
679 | errMsg = "PHYS_PARAM_FRAMEINB_LOC takes one parameter of type vector"; | ||
680 | valueVector = (OMV.Vector3)pParams[opIndex + 1]; | ||
681 | linkInfo.frameInBloc = valueVector; | ||
682 | opIndex += 2; | ||
683 | break; | ||
684 | case ExtendedPhysics.PHYS_PARAM_FRAMEINB_ROT: | ||
685 | errMsg = "PHYS_PARAM_FRAMEINB_ROT takes one parameter of type rotation"; | ||
686 | valueQuaternion = (OMV.Quaternion)pParams[opIndex + 1]; | ||
687 | linkInfo.frameInBrot = valueQuaternion; | ||
688 | opIndex += 2; | ||
689 | break; | ||
690 | case ExtendedPhysics.PHYS_PARAM_LINEAR_LIMIT_LOW: | ||
691 | errMsg = "PHYS_PARAM_LINEAR_LIMIT_LOW takes one parameter of type vector"; | ||
692 | valueVector = (OMV.Vector3)pParams[opIndex + 1]; | ||
693 | linkInfo.linearLimitLow = valueVector; | ||
694 | opIndex += 2; | ||
695 | break; | ||
696 | case ExtendedPhysics.PHYS_PARAM_LINEAR_LIMIT_HIGH: | ||
697 | errMsg = "PHYS_PARAM_LINEAR_LIMIT_HIGH takes one parameter of type vector"; | ||
698 | valueVector = (OMV.Vector3)pParams[opIndex + 1]; | ||
699 | linkInfo.linearLimitHigh = valueVector; | ||
700 | opIndex += 2; | ||
701 | break; | ||
702 | case ExtendedPhysics.PHYS_PARAM_ANGULAR_LIMIT_LOW: | ||
703 | errMsg = "PHYS_PARAM_ANGULAR_LIMIT_LOW takes one parameter of type vector"; | ||
704 | valueVector = (OMV.Vector3)pParams[opIndex + 1]; | ||
705 | linkInfo.angularLimitLow = valueVector; | ||
706 | opIndex += 2; | ||
707 | break; | ||
708 | case ExtendedPhysics.PHYS_PARAM_ANGULAR_LIMIT_HIGH: | ||
709 | errMsg = "PHYS_PARAM_ANGULAR_LIMIT_HIGH takes one parameter of type vector"; | ||
710 | valueVector = (OMV.Vector3)pParams[opIndex + 1]; | ||
711 | linkInfo.angularLimitHigh = valueVector; | ||
712 | opIndex += 2; | ||
713 | break; | ||
714 | case ExtendedPhysics.PHYS_PARAM_USE_FRAME_OFFSET: | ||
715 | errMsg = "PHYS_PARAM_USE_FRAME_OFFSET takes one parameter of type integer (bool)"; | ||
716 | valueBool = ((int)pParams[opIndex + 1]) != 0; | ||
717 | linkInfo.useFrameOffset = valueBool; | ||
718 | opIndex += 2; | ||
719 | break; | ||
720 | case ExtendedPhysics.PHYS_PARAM_ENABLE_TRANSMOTOR: | ||
721 | errMsg = "PHYS_PARAM_ENABLE_TRANSMOTOR takes one parameter of type integer (bool)"; | ||
722 | valueBool = ((int)pParams[opIndex + 1]) != 0; | ||
723 | linkInfo.enableTransMotor = valueBool; | ||
724 | opIndex += 2; | ||
725 | break; | ||
726 | case ExtendedPhysics.PHYS_PARAM_TRANSMOTOR_MAXVEL: | ||
727 | errMsg = "PHYS_PARAM_TRANSMOTOR_MAXVEL takes one parameter of type float"; | ||
728 | valueFloat = (float)pParams[opIndex + 1]; | ||
729 | linkInfo.transMotorMaxVel = valueFloat; | ||
730 | opIndex += 2; | ||
731 | break; | ||
732 | case ExtendedPhysics.PHYS_PARAM_TRANSMOTOR_MAXFORCE: | ||
733 | errMsg = "PHYS_PARAM_TRANSMOTOR_MAXFORCE takes one parameter of type float"; | ||
734 | valueFloat = (float)pParams[opIndex + 1]; | ||
735 | linkInfo.transMotorMaxForce = valueFloat; | ||
736 | opIndex += 2; | ||
737 | break; | ||
738 | case ExtendedPhysics.PHYS_PARAM_CFM: | ||
739 | errMsg = "PHYS_PARAM_CFM takes one parameter of type float"; | ||
740 | valueFloat = (float)pParams[opIndex + 1]; | ||
741 | linkInfo.cfm = valueFloat; | ||
742 | opIndex += 2; | ||
743 | break; | ||
744 | case ExtendedPhysics.PHYS_PARAM_ERP: | ||
745 | errMsg = "PHYS_PARAM_ERP takes one parameter of type float"; | ||
746 | valueFloat = (float)pParams[opIndex + 1]; | ||
747 | linkInfo.erp = valueFloat; | ||
748 | opIndex += 2; | ||
749 | break; | ||
750 | case ExtendedPhysics.PHYS_PARAM_SOLVER_ITERATIONS: | ||
751 | errMsg = "PHYS_PARAM_SOLVER_ITERATIONS takes one parameter of type float"; | ||
752 | valueFloat = (float)pParams[opIndex + 1]; | ||
753 | linkInfo.solverIterations = valueFloat; | ||
754 | opIndex += 2; | ||
755 | break; | ||
756 | case ExtendedPhysics.PHYS_PARAM_SPRING_AXIS_ENABLE: | ||
757 | errMsg = "PHYS_PARAM_SPRING_AXIS_ENABLE takes two parameters of types integer and integer (bool)"; | ||
758 | valueInt = (int)pParams[opIndex + 1]; | ||
759 | valueBool = ((int)pParams[opIndex + 2]) != 0; | ||
760 | GetAxisRange(valueInt, out axisLow, out axisHigh); | ||
761 | for (int ii = axisLow; ii <= axisHigh; ii++) | ||
762 | linkInfo.springAxisEnable[ii] = valueBool; | ||
763 | opIndex += 3; | ||
764 | break; | ||
765 | case ExtendedPhysics.PHYS_PARAM_SPRING_DAMPING: | ||
766 | errMsg = "PHYS_PARAM_SPRING_DAMPING takes two parameters of types integer and float"; | ||
767 | valueInt = (int)pParams[opIndex + 1]; | ||
768 | valueFloat = (float)pParams[opIndex + 2]; | ||
769 | GetAxisRange(valueInt, out axisLow, out axisHigh); | ||
770 | for (int ii = axisLow; ii <= axisHigh; ii++) | ||
771 | linkInfo.springDamping[ii] = valueFloat; | ||
772 | opIndex += 3; | ||
773 | break; | ||
774 | case ExtendedPhysics.PHYS_PARAM_SPRING_STIFFNESS: | ||
775 | errMsg = "PHYS_PARAM_SPRING_STIFFNESS takes two parameters of types integer and float"; | ||
776 | valueInt = (int)pParams[opIndex + 1]; | ||
777 | valueFloat = (float)pParams[opIndex + 2]; | ||
778 | GetAxisRange(valueInt, out axisLow, out axisHigh); | ||
779 | for (int ii = axisLow; ii <= axisHigh; ii++) | ||
780 | linkInfo.springStiffness[ii] = valueFloat; | ||
781 | opIndex += 3; | ||
782 | break; | ||
783 | case ExtendedPhysics.PHYS_PARAM_SPRING_EQUILIBRIUM_POINT: | ||
784 | errMsg = "PHYS_PARAM_SPRING_EQUILIBRIUM_POINT takes two parameters of type vector"; | ||
785 | valueVector = (OMV.Vector3)pParams[opIndex + 1]; | ||
786 | valueVector2 = (OMV.Vector3)pParams[opIndex + 2]; | ||
787 | linkInfo.springLinearEquilibriumPoint = valueVector; | ||
788 | linkInfo.springAngularEquilibriumPoint = valueVector2; | ||
789 | opIndex += 3; | ||
790 | break; | ||
791 | case ExtendedPhysics.PHYS_PARAM_USE_LINEAR_FRAMEA: | ||
792 | errMsg = "PHYS_PARAM_USE_LINEAR_FRAMEA takes one parameter of type integer (bool)"; | ||
793 | valueBool = ((int)pParams[opIndex + 1]) != 0; | ||
794 | linkInfo.useLinearReferenceFrameA = valueBool; | ||
795 | opIndex += 2; | ||
796 | break; | ||
797 | default: | ||
798 | break; | ||
799 | } | ||
800 | } | ||
801 | catch (InvalidCastException e) | ||
802 | { | ||
803 | m_physicsScene.Logger.WarnFormat("{0} value of wrong type in physSetLinksetParams: {1}, err={2}", | ||
804 | LogHeader, errMsg, e); | ||
805 | } | ||
806 | catch (Exception e) | ||
807 | { | ||
808 | m_physicsScene.Logger.WarnFormat("{0} bad parameters in physSetLinksetParams: {1}", LogHeader, e); | ||
809 | } | ||
810 | } | ||
811 | } | ||
812 | // Something changed so a rebuild is in order | ||
813 | Refresh(child); | ||
814 | } | ||
815 | } | ||
816 | break; | ||
817 | default: | ||
818 | ret = base.Extension(pFunct, pParams); | ||
819 | break; | ||
820 | } | ||
821 | return ret; | ||
822 | } | ||
823 | |||
824 | // Bullet constraints keep some limit parameters for each linear and angular axis. | ||
825 | // Setting same is easier if there is an easy way to see all or types. | ||
826 | // This routine returns the array limits for the set of axis. | ||
827 | private void GetAxisRange(int rangeSpec, out int low, out int high) | ||
828 | { | ||
829 | switch (rangeSpec) | ||
830 | { | ||
831 | case ExtendedPhysics.PHYS_AXIS_LINEAR_ALL: | ||
832 | low = 0; | ||
833 | high = 2; | ||
834 | break; | ||
835 | case ExtendedPhysics.PHYS_AXIS_ANGULAR_ALL: | ||
836 | low = 3; | ||
837 | high = 5; | ||
838 | break; | ||
839 | case ExtendedPhysics.PHYS_AXIS_ALL: | ||
840 | low = 0; | ||
841 | high = 5; | ||
842 | break; | ||
843 | default: | ||
844 | low = high = rangeSpec; | ||
845 | break; | ||
846 | } | ||
847 | return; | ||
848 | } | ||
849 | #endregion // Extension | ||
850 | |||
851 | } | ||
852 | } | ||