clockwerk-opensim-stable – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 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 | |||
28 | using System; |
||
29 | using System.Collections.Generic; |
||
30 | using System.Linq; |
||
31 | using System.Text; |
||
32 | |||
33 | using OpenSim.Region.Physics.Manager; |
||
34 | |||
35 | using OMV = OpenMetaverse; |
||
36 | |||
37 | namespace OpenSim.Region.Physics.BulletSPlugin |
||
38 | { |
||
39 | public class BSActorAvatarMove : BSActor |
||
40 | { |
||
41 | BSVMotor m_velocityMotor; |
||
42 | |||
43 | // Set to true if we think we're going up stairs. |
||
44 | // This state is remembered because collisions will turn on and off as we go up stairs. |
||
45 | int m_walkingUpStairs; |
||
46 | // The amount the step up is applying. Used to smooth stair walking. |
||
47 | float m_lastStepUp; |
||
48 | |||
49 | // Jumping happens over several frames. If use applies up force while colliding, start the |
||
50 | // jump and allow the jump to continue for this number of frames. |
||
51 | int m_jumpFrames = 0; |
||
52 | float m_jumpVelocity = 0f; |
||
53 | |||
54 | public BSActorAvatarMove(BSScene physicsScene, BSPhysObject pObj, string actorName) |
||
55 | : base(physicsScene, pObj, actorName) |
||
56 | { |
||
57 | m_velocityMotor = null; |
||
58 | m_walkingUpStairs = 0; |
||
59 | m_physicsScene.DetailLog("{0},BSActorAvatarMove,constructor", m_controllingPrim.LocalID); |
||
60 | } |
||
61 | |||
62 | // BSActor.isActive |
||
63 | public override bool isActive |
||
64 | { |
||
65 | get { return Enabled && m_controllingPrim.IsPhysicallyActive; } |
||
66 | } |
||
67 | |||
68 | // Release any connections and resources used by the actor. |
||
69 | // BSActor.Dispose() |
||
70 | public override void Dispose() |
||
71 | { |
||
72 | base.SetEnabled(false); |
||
73 | // Now that turned off, remove any state we have in the scene. |
||
74 | Refresh(); |
||
75 | } |
||
76 | |||
77 | // Called when physical parameters (properties set in Bullet) need to be re-applied. |
||
78 | // Called at taint-time. |
||
79 | // BSActor.Refresh() |
||
80 | public override void Refresh() |
||
81 | { |
||
82 | m_physicsScene.DetailLog("{0},BSActorAvatarMove,refresh", m_controllingPrim.LocalID); |
||
83 | |||
84 | // If the object is physically active, add the hoverer prestep action |
||
85 | if (isActive) |
||
86 | { |
||
87 | ActivateAvatarMove(); |
||
88 | } |
||
89 | else |
||
90 | { |
||
91 | DeactivateAvatarMove(); |
||
92 | } |
||
93 | } |
||
94 | |||
95 | // The object's physical representation is being rebuilt so pick up any physical dependencies (constraints, ...). |
||
96 | // Register a prestep action to restore physical requirements before the next simulation step. |
||
97 | // Called at taint-time. |
||
98 | // BSActor.RemoveDependencies() |
||
99 | public override void RemoveDependencies() |
||
100 | { |
||
101 | // Nothing to do for the hoverer since it is all software at pre-step action time. |
||
102 | } |
||
103 | |||
104 | // Usually called when target velocity changes to set the current velocity and the target |
||
105 | // into the movement motor. |
||
106 | public void SetVelocityAndTarget(OMV.Vector3 vel, OMV.Vector3 targ, bool inTaintTime) |
||
107 | { |
||
108 | m_physicsScene.TaintedObject(inTaintTime, m_controllingPrim.LocalID, "BSActorAvatarMove.setVelocityAndTarget", delegate() |
||
109 | { |
||
110 | if (m_velocityMotor != null) |
||
111 | { |
||
112 | m_velocityMotor.Reset(); |
||
113 | m_velocityMotor.SetTarget(targ); |
||
114 | m_velocityMotor.SetCurrent(vel); |
||
115 | m_velocityMotor.Enabled = true; |
||
116 | } |
||
117 | }); |
||
118 | } |
||
119 | |||
120 | // If a hover motor has not been created, create one and start the hovering. |
||
121 | private void ActivateAvatarMove() |
||
122 | { |
||
123 | if (m_velocityMotor == null) |
||
124 | { |
||
125 | // Infinite decay and timescale values so motor only changes current to target values. |
||
126 | m_velocityMotor = new BSVMotor("BSCharacter.Velocity", |
||
127 | 0.2f, // time scale |
||
128 | BSMotor.Infinite, // decay time scale |
||
129 | 1f // efficiency |
||
130 | ); |
||
131 | m_velocityMotor.ErrorZeroThreshold = BSParam.AvatarStopZeroThreshold; |
||
132 | // _velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages. |
||
133 | SetVelocityAndTarget(m_controllingPrim.RawVelocity, m_controllingPrim.TargetVelocity, true /* inTaintTime */); |
||
134 | |||
135 | m_physicsScene.BeforeStep += Mover; |
||
136 | m_controllingPrim.OnPreUpdateProperty += Process_OnPreUpdateProperty; |
||
137 | |||
138 | m_walkingUpStairs = 0; |
||
139 | } |
||
140 | } |
||
141 | |||
142 | private void DeactivateAvatarMove() |
||
143 | { |
||
144 | if (m_velocityMotor != null) |
||
145 | { |
||
146 | m_controllingPrim.OnPreUpdateProperty -= Process_OnPreUpdateProperty; |
||
147 | m_physicsScene.BeforeStep -= Mover; |
||
148 | m_velocityMotor = null; |
||
149 | } |
||
150 | } |
||
151 | |||
152 | // Called just before the simulation step. Update the vertical position for hoverness. |
||
153 | private void Mover(float timeStep) |
||
154 | { |
||
155 | // Don't do movement while the object is selected. |
||
156 | if (!isActive) |
||
157 | return; |
||
158 | |||
159 | // TODO: Decide if the step parameters should be changed depending on the avatar's |
||
160 | // state (flying, colliding, ...). There is code in ODE to do this. |
||
161 | |||
162 | // COMMENTARY: when the user is making the avatar walk, except for falling, the velocity |
||
163 | // specified for the avatar is the one that should be used. For falling, if the avatar |
||
164 | // is not flying and is not colliding then it is presumed to be falling and the Z |
||
165 | // component is not fooled with (thus allowing gravity to do its thing). |
||
166 | // When the avatar is standing, though, the user has specified a velocity of zero and |
||
167 | // the avatar should be standing. But if the avatar is pushed by something in the world |
||
168 | // (raising elevator platform, moving vehicle, ...) the avatar should be allowed to |
||
169 | // move. Thus, the velocity cannot be forced to zero. The problem is that small velocity |
||
170 | // errors can creap in and the avatar will slowly float off in some direction. |
||
171 | // So, the problem is that, when an avatar is standing, we cannot tell creaping error |
||
172 | // from real pushing. |
||
173 | // The code below uses whether the collider is static or moving to decide whether to zero motion. |
||
174 | |||
175 | m_velocityMotor.Step(timeStep); |
||
176 | m_controllingPrim.IsStationary = false; |
||
177 | |||
178 | // If we're not supposed to be moving, make sure things are zero. |
||
179 | if (m_velocityMotor.ErrorIsZero() && m_velocityMotor.TargetValue == OMV.Vector3.Zero) |
||
180 | { |
||
181 | // The avatar shouldn't be moving |
||
182 | m_velocityMotor.Zero(); |
||
183 | |||
184 | if (m_controllingPrim.IsColliding) |
||
185 | { |
||
186 | // If we are colliding with a stationary object, presume we're standing and don't move around |
||
187 | if (!m_controllingPrim.ColliderIsMoving && !m_controllingPrim.ColliderIsVolumeDetect) |
||
188 | { |
||
189 | m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,collidingWithStationary,zeroingMotion", m_controllingPrim.LocalID); |
||
190 | m_controllingPrim.IsStationary = true; |
||
191 | m_controllingPrim.ZeroMotion(true /* inTaintTime */); |
||
192 | } |
||
193 | |||
194 | // Standing has more friction on the ground |
||
195 | if (m_controllingPrim.Friction != BSParam.AvatarStandingFriction) |
||
196 | { |
||
197 | m_controllingPrim.Friction = BSParam.AvatarStandingFriction; |
||
198 | m_physicsScene.PE.SetFriction(m_controllingPrim.PhysBody, m_controllingPrim.Friction); |
||
199 | } |
||
200 | } |
||
201 | else |
||
202 | { |
||
203 | if (m_controllingPrim.Flying) |
||
204 | { |
||
205 | // Flying and not colliding and velocity nearly zero. |
||
206 | m_controllingPrim.ZeroMotion(true /* inTaintTime */); |
||
207 | } |
||
208 | } |
||
209 | |||
210 | m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,taint,stopping,target={1},colliding={2}", |
||
211 | m_controllingPrim.LocalID, m_velocityMotor.TargetValue, m_controllingPrim.IsColliding); |
||
212 | } |
||
213 | else |
||
214 | { |
||
215 | // Supposed to be moving. |
||
216 | OMV.Vector3 stepVelocity = m_velocityMotor.CurrentValue; |
||
217 | |||
218 | if (m_controllingPrim.Friction != BSParam.AvatarFriction) |
||
219 | { |
||
220 | // Probably starting to walk. Set friction to moving friction. |
||
221 | m_controllingPrim.Friction = BSParam.AvatarFriction; |
||
222 | m_physicsScene.PE.SetFriction(m_controllingPrim.PhysBody, m_controllingPrim.Friction); |
||
223 | } |
||
224 | |||
225 | if (!m_controllingPrim.Flying && !m_controllingPrim.IsColliding) |
||
226 | { |
||
227 | stepVelocity.Z = m_controllingPrim.RawVelocity.Z; |
||
228 | } |
||
229 | |||
230 | |||
231 | // Colliding and not flying with an upward force. The avatar must be trying to jump. |
||
232 | if (!m_controllingPrim.Flying && m_controllingPrim.IsColliding && stepVelocity.Z > 0) |
||
233 | { |
||
234 | // We allow the upward force to happen for this many frames. |
||
235 | m_jumpFrames = BSParam.AvatarJumpFrames; |
||
236 | m_jumpVelocity = stepVelocity.Z; |
||
237 | } |
||
238 | |||
239 | // The case where the avatar is not colliding and is not flying is special. |
||
240 | // The avatar is either falling or jumping and the user can be applying force to the avatar |
||
241 | // (force in some direction or force up or down). |
||
242 | // If the avatar has negative Z velocity and is not colliding, presume we're falling and keep the velocity. |
||
243 | // If the user is trying to apply upward force but we're not colliding, assume the avatar |
||
244 | // is trying to jump and don't apply the upward force if not touching the ground any more. |
||
245 | if (!m_controllingPrim.Flying && !m_controllingPrim.IsColliding) |
||
246 | { |
||
247 | // If upward velocity is being applied, this must be a jump and only allow that to go on so long |
||
248 | if (m_jumpFrames > 0) |
||
249 | { |
||
250 | // Since not touching the ground, only apply upward force for so long. |
||
251 | m_jumpFrames--; |
||
252 | stepVelocity.Z = m_jumpVelocity; |
||
253 | } |
||
254 | else |
||
255 | { |
||
256 | // Since we're not affected by anything, whatever vertical motion the avatar has, continue that. |
||
257 | stepVelocity.Z = m_controllingPrim.RawVelocity.Z; |
||
258 | } |
||
259 | // DetailLog("{0},BSCharacter.MoveMotor,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity); |
||
260 | } |
||
261 | |||
262 | // 'stepVelocity' is now the speed we'd like the avatar to move in. Turn that into an instantanous force. |
||
263 | OMV.Vector3 moveForce = (stepVelocity - m_controllingPrim.RawVelocity) * m_controllingPrim.Mass; |
||
264 | |||
265 | // Add special movement force to allow avatars to walk up stepped surfaces. |
||
266 | moveForce += WalkUpStairs(); |
||
267 | |||
268 | m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", |
||
269 | m_controllingPrim.LocalID, stepVelocity, m_controllingPrim.RawVelocity, m_controllingPrim.Mass, moveForce); |
||
270 | m_physicsScene.PE.ApplyCentralImpulse(m_controllingPrim.PhysBody, moveForce); |
||
271 | } |
||
272 | } |
||
273 | |||
274 | // Called just as the property update is received from the physics engine. |
||
275 | // Do any mode necessary for avatar movement. |
||
276 | private void Process_OnPreUpdateProperty(ref EntityProperties entprop) |
||
277 | { |
||
278 | // Don't change position if standing on a stationary object. |
||
279 | if (m_controllingPrim.IsStationary) |
||
280 | { |
||
281 | entprop.Position = m_controllingPrim.RawPosition; |
||
282 | entprop.Velocity = OMV.Vector3.Zero; |
||
283 | m_physicsScene.PE.SetTranslation(m_controllingPrim.PhysBody, entprop.Position, entprop.Rotation); |
||
284 | } |
||
285 | |||
286 | } |
||
287 | |||
288 | // Decide if the character is colliding with a low object and compute a force to pop the |
||
289 | // avatar up so it can walk up and over the low objects. |
||
290 | private OMV.Vector3 WalkUpStairs() |
||
291 | { |
||
292 | OMV.Vector3 ret = OMV.Vector3.Zero; |
||
293 | |||
294 | m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs,IsColliding={1},flying={2},targSpeed={3},collisions={4},avHeight={5}", |
||
295 | m_controllingPrim.LocalID, m_controllingPrim.IsColliding, m_controllingPrim.Flying, |
||
296 | m_controllingPrim.TargetVelocitySpeed, m_controllingPrim.CollisionsLastTick.Count, m_controllingPrim.Size.Z); |
||
297 | |||
298 | // Check for stairs climbing if colliding, not flying and moving forward |
||
299 | if ( m_controllingPrim.IsColliding |
||
300 | && !m_controllingPrim.Flying |
||
301 | && m_controllingPrim.TargetVelocitySpeed > 0.1f ) |
||
302 | { |
||
303 | // The range near the character's feet where we will consider stairs |
||
304 | // float nearFeetHeightMin = m_controllingPrim.RawPosition.Z - (m_controllingPrim.Size.Z / 2f) + 0.05f; |
||
305 | // Note: there is a problem with the computation of the capsule height. Thus RawPosition is off |
||
306 | // from the height. Revisit size and this computation when height is scaled properly. |
||
307 | float nearFeetHeightMin = m_controllingPrim.RawPosition.Z - (m_controllingPrim.Size.Z / 2f) - 0.05f; |
||
308 | float nearFeetHeightMax = nearFeetHeightMin + BSParam.AvatarStepHeight; |
||
309 | |||
310 | // Look for a collision point that is near the character's feet and is oriented the same as the charactor is. |
||
311 | // Find the highest 'good' collision. |
||
312 | OMV.Vector3 highestTouchPosition = OMV.Vector3.Zero; |
||
313 | foreach (KeyValuePair<uint, ContactPoint> kvp in m_controllingPrim.CollisionsLastTick.m_objCollisionList) |
||
314 | { |
||
315 | // Don't care about collisions with the terrain |
||
316 | if (kvp.Key > m_physicsScene.TerrainManager.HighestTerrainID) |
||
317 | { |
||
318 | BSPhysObject collisionObject; |
||
319 | if (m_physicsScene.PhysObjects.TryGetValue(kvp.Key, out collisionObject)) |
||
320 | { |
||
321 | if (!collisionObject.IsVolumeDetect) |
||
322 | { |
||
323 | OMV.Vector3 touchPosition = kvp.Value.Position; |
||
324 | m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs,min={1},max={2},touch={3}", |
||
325 | m_controllingPrim.LocalID, nearFeetHeightMin, nearFeetHeightMax, touchPosition); |
||
326 | if (touchPosition.Z >= nearFeetHeightMin && touchPosition.Z <= nearFeetHeightMax) |
||
327 | { |
||
328 | // This contact is within the 'near the feet' range. |
||
329 | // The normal should be our contact point to the object so it is pointing away |
||
330 | // thus the difference between our facing orientation and the normal should be small. |
||
331 | OMV.Vector3 directionFacing = OMV.Vector3.UnitX * m_controllingPrim.RawOrientation; |
||
332 | OMV.Vector3 touchNormal = OMV.Vector3.Normalize(kvp.Value.SurfaceNormal); |
||
333 | float diff = Math.Abs(OMV.Vector3.Distance(directionFacing, touchNormal)); |
||
334 | if (diff < BSParam.AvatarStepApproachFactor) |
||
335 | { |
||
336 | if (highestTouchPosition.Z < touchPosition.Z) |
||
337 | highestTouchPosition = touchPosition; |
||
338 | } |
||
339 | } |
||
340 | } |
||
341 | } |
||
342 | } |
||
343 | } |
||
344 | m_walkingUpStairs = 0; |
||
345 | // If there is a good step sensing, move the avatar over the step. |
||
346 | if (highestTouchPosition != OMV.Vector3.Zero) |
||
347 | { |
||
348 | // Remember that we are going up stairs. This is needed because collisions |
||
349 | // will stop when we move up so this smoothes out that effect. |
||
350 | m_walkingUpStairs = BSParam.AvatarStepSmoothingSteps; |
||
351 | |||
352 | m_lastStepUp = highestTouchPosition.Z - nearFeetHeightMin; |
||
353 | ret = ComputeStairCorrection(m_lastStepUp); |
||
354 | m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs,touchPos={1},nearFeetMin={2},ret={3}", |
||
355 | m_controllingPrim.LocalID, highestTouchPosition, nearFeetHeightMin, ret); |
||
356 | } |
||
357 | } |
||
358 | else |
||
359 | { |
||
360 | // If we used to be going up stairs but are not now, smooth the case where collision goes away while |
||
361 | // we are bouncing up the stairs. |
||
362 | if (m_walkingUpStairs > 0) |
||
363 | { |
||
364 | m_walkingUpStairs--; |
||
365 | ret = ComputeStairCorrection(m_lastStepUp); |
||
366 | } |
||
367 | } |
||
368 | |||
369 | return ret; |
||
370 | } |
||
371 | |||
372 | private OMV.Vector3 ComputeStairCorrection(float stepUp) |
||
373 | { |
||
374 | OMV.Vector3 ret = OMV.Vector3.Zero; |
||
375 | OMV.Vector3 displacement = OMV.Vector3.Zero; |
||
376 | |||
377 | if (stepUp > 0f) |
||
378 | { |
||
379 | // Found the stairs contact point. Push up a little to raise the character. |
||
380 | if (BSParam.AvatarStepForceFactor > 0f) |
||
381 | { |
||
382 | float upForce = stepUp * m_controllingPrim.Mass * BSParam.AvatarStepForceFactor; |
||
383 | ret = new OMV.Vector3(0f, 0f, upForce); |
||
384 | } |
||
385 | |||
386 | // Also move the avatar up for the new height |
||
387 | if (BSParam.AvatarStepUpCorrectionFactor > 0f) |
||
388 | { |
||
389 | // Move the avatar up related to the height of the collision |
||
390 | displacement = new OMV.Vector3(0f, 0f, stepUp * BSParam.AvatarStepUpCorrectionFactor); |
||
391 | m_controllingPrim.ForcePosition = m_controllingPrim.RawPosition + displacement; |
||
392 | } |
||
393 | else |
||
394 | { |
||
395 | if (BSParam.AvatarStepUpCorrectionFactor < 0f) |
||
396 | { |
||
397 | // Move the avatar up about the specified step height |
||
398 | displacement = new OMV.Vector3(0f, 0f, BSParam.AvatarStepHeight); |
||
399 | m_controllingPrim.ForcePosition = m_controllingPrim.RawPosition + displacement; |
||
400 | } |
||
401 | } |
||
402 | m_physicsScene.DetailLog("{0},BSCharacter.WalkUpStairs.ComputeStairCorrection,disp={1},force={2}", |
||
403 | m_controllingPrim.LocalID, displacement, ret); |
||
404 | |||
405 | } |
||
406 | return ret; |
||
407 | } |
||
408 | } |
||
409 | } |
||
410 | |||
411 |