diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | 553 |
1 files changed, 553 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs new file mode 100644 index 0000000..beffd21 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs | |||
@@ -0,0 +1,553 @@ | |||
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.Runtime.InteropServices; | ||
30 | using System.Text; | ||
31 | using System.Threading; | ||
32 | using Nini.Config; | ||
33 | using log4net; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Region.Physics.Manager; | ||
36 | using OpenMetaverse; | ||
37 | using OpenSim.Region.Framework; | ||
38 | |||
39 | // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) | ||
40 | // Fix folding up feet | ||
41 | // Fix terrain. Only flat terrain works. Terrain with shape is oriented wrong? Origined wrong? | ||
42 | // Parameterize BulletSim. Pass a structure of parameters to the C++ code. Capsule size, friction, ... | ||
43 | // Shift drag duplication of objects does not work | ||
44 | // Adjust character capsule size when height is adjusted (ScenePresence.SetHeight) | ||
45 | // Test sculpties | ||
46 | // Compute physics FPS reasonably | ||
47 | // Based on material, set density and friction | ||
48 | // More efficient memory usage in passing hull information from BSPrim to BulletSim | ||
49 | // Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly? | ||
50 | // In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground) | ||
51 | // At the moment, physical and phantom causes object to drop through the terrain | ||
52 | // Should prim.link() and prim.delink() membership checking happen at taint time? | ||
53 | // Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once | ||
54 | // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect | ||
55 | // Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions) | ||
56 | // Implement LockAngularMotion | ||
57 | // Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) | ||
58 | // Built Galton board (lots of MoveTo's) and some slats were not positioned correctly (mistakes scattered) | ||
59 | // No mistakes with ODE. Shape creation race condition? | ||
60 | // Does NeedsMeshing() really need to exclude all the different shapes? | ||
61 | // | ||
62 | namespace OpenSim.Region.Physics.BulletSPlugin | ||
63 | { | ||
64 | public class BSScene : PhysicsScene | ||
65 | { | ||
66 | private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||
67 | private static readonly string LogHeader = "[BULLETS SCENE]"; | ||
68 | |||
69 | private Dictionary<uint, BSCharacter> m_avatars = new Dictionary<uint, BSCharacter>(); | ||
70 | private Dictionary<uint, BSPrim> m_prims = new Dictionary<uint, BSPrim>(); | ||
71 | private List<BSPrim> m_vehicles = new List<BSPrim>(); | ||
72 | private float[] m_heightMap; | ||
73 | private float m_waterLevel; | ||
74 | private uint m_worldID; | ||
75 | public uint WorldID { get { return m_worldID; } } | ||
76 | |||
77 | public IMesher mesher; | ||
78 | public int meshLOD = 32; | ||
79 | |||
80 | private int m_maxSubSteps = 10; | ||
81 | private float m_fixedTimeStep = 1f / 60f; | ||
82 | private long m_simulationStep = 0; | ||
83 | public long SimulationStep { get { return m_simulationStep; } } | ||
84 | |||
85 | private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed | ||
86 | private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes | ||
87 | public float maximumMassObject = 10000.01f; | ||
88 | |||
89 | public const uint TERRAIN_ID = 0; | ||
90 | public const uint GROUNDPLANE_ID = 1; | ||
91 | |||
92 | public float DefaultFriction = 0.70f; | ||
93 | public float DefaultDensity = 10.000006836f; // Aluminum g/cm3; TODO: compute based on object material | ||
94 | public Vector3 DefaultGravity = new Vector3(0, 0, -9.80665f); | ||
95 | |||
96 | public delegate void TaintCallback(); | ||
97 | private List<TaintCallback> _taintedObjects; | ||
98 | private Object _taintLock = new Object(); | ||
99 | |||
100 | private BulletSimAPI.DebugLogCallback debugLogCallbackHandle; | ||
101 | |||
102 | public BSScene(string identifier) | ||
103 | { | ||
104 | } | ||
105 | |||
106 | public override void Initialise(IMesher meshmerizer, IConfigSource config) | ||
107 | { | ||
108 | if (config != null) | ||
109 | { | ||
110 | IConfig pConfig = config.Configs["BulletSim"]; | ||
111 | if (pConfig != null) | ||
112 | { | ||
113 | DefaultFriction = pConfig.GetFloat("Friction", DefaultFriction); | ||
114 | DefaultDensity = pConfig.GetFloat("Density", DefaultDensity); | ||
115 | // TODO: a lot more parameters that are passed to BulletSim | ||
116 | } | ||
117 | } | ||
118 | // if Debug, enable logging from the unmanaged code | ||
119 | if (m_log.IsDebugEnabled) | ||
120 | { | ||
121 | m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); | ||
122 | debugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); | ||
123 | BulletSimAPI.SetDebugLogCallback(debugLogCallbackHandle); | ||
124 | } | ||
125 | |||
126 | _meshSculptedPrim = true; // mesh sculpted prims | ||
127 | _forceSimplePrimMeshing = false; // use complex meshing if called for | ||
128 | |||
129 | _taintedObjects = new List<TaintCallback>(); | ||
130 | |||
131 | mesher = meshmerizer; | ||
132 | // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); | ||
133 | m_worldID = BulletSimAPI.Initialize(new Vector3(Constants.RegionSize, Constants.RegionSize, 4096f)); | ||
134 | } | ||
135 | |||
136 | // Called directly from unmanaged code so don't do much | ||
137 | private void BulletLogger(string msg) | ||
138 | { | ||
139 | m_log.Debug("[BULLETS UNMANAGED]:" + msg); | ||
140 | } | ||
141 | |||
142 | public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying) | ||
143 | { | ||
144 | m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); | ||
145 | return null; | ||
146 | } | ||
147 | |||
148 | public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying) | ||
149 | { | ||
150 | // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName); | ||
151 | BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); | ||
152 | lock (m_avatars) m_avatars.Add(localID, actor); | ||
153 | return actor; | ||
154 | } | ||
155 | |||
156 | public override void RemoveAvatar(PhysicsActor actor) | ||
157 | { | ||
158 | // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader); | ||
159 | if (actor is BSCharacter) | ||
160 | { | ||
161 | ((BSCharacter)actor).Destroy(); | ||
162 | } | ||
163 | try | ||
164 | { | ||
165 | lock (m_avatars) m_avatars.Remove(actor.LocalID); | ||
166 | } | ||
167 | catch (Exception e) | ||
168 | { | ||
169 | m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | public override void RemovePrim(PhysicsActor prim) | ||
174 | { | ||
175 | // m_log.DebugFormat("{0}: RemovePrim", LogHeader); | ||
176 | if (prim is BSPrim) | ||
177 | { | ||
178 | ((BSPrim)prim).Destroy(); | ||
179 | } | ||
180 | try | ||
181 | { | ||
182 | lock (m_prims) m_prims.Remove(prim.LocalID); | ||
183 | } | ||
184 | catch (Exception e) | ||
185 | { | ||
186 | m_log.WarnFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e); | ||
187 | } | ||
188 | } | ||
189 | |||
190 | public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, | ||
191 | Vector3 size, Quaternion rotation) // deprecated | ||
192 | { | ||
193 | return null; | ||
194 | } | ||
195 | public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, | ||
196 | Vector3 size, Quaternion rotation, bool isPhysical) | ||
197 | { | ||
198 | m_log.ErrorFormat("{0}: CALL TO AddPrimShape in BSScene. NOT IMPLEMENTED", LogHeader); | ||
199 | return null; | ||
200 | } | ||
201 | |||
202 | public override PhysicsActor AddPrimShape(uint localID, string primName, PrimitiveBaseShape pbs, Vector3 position, | ||
203 | Vector3 size, Quaternion rotation, bool isPhysical) | ||
204 | { | ||
205 | // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName); | ||
206 | IMesh mesh = null; | ||
207 | if (NeedsMeshing(pbs)) | ||
208 | { | ||
209 | // if the prim is complex, create the mesh for it. | ||
210 | // If simple (box or sphere) leave 'mesh' null and physics will do a native shape. | ||
211 | mesh = mesher.CreateMesh(primName, pbs, size, this.meshLOD, isPhysical); | ||
212 | } | ||
213 | BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, mesh, pbs, isPhysical); | ||
214 | lock (m_prims) m_prims.Add(localID, prim); | ||
215 | return prim; | ||
216 | } | ||
217 | |||
218 | // This is a call from the simulator saying that some physical property has been updated. | ||
219 | // The BulletS driver senses the changing of relevant properties so this taint | ||
220 | // information call is not needed. | ||
221 | public override void AddPhysicsActorTaint(PhysicsActor prim) { } | ||
222 | |||
223 | // Simulate one timestep | ||
224 | public override float Simulate(float timeStep) | ||
225 | { | ||
226 | int updatedEntityCount; | ||
227 | IntPtr updatedEntitiesPtr; | ||
228 | IntPtr[] updatedEntities; | ||
229 | int collidersCount; | ||
230 | IntPtr collidersPtr; | ||
231 | int[] colliders; // should be uint but Marshal.Copy does not have that overload | ||
232 | |||
233 | // update the prim states while we know the physics engine is not busy | ||
234 | ProcessTaints(); | ||
235 | |||
236 | // Some of the prims operate with special vehicle properties | ||
237 | ProcessVehicles(timeStep); | ||
238 | ProcessTaints(); // the vehicles might have added taints | ||
239 | |||
240 | // step the physical world one interval | ||
241 | m_simulationStep++; | ||
242 | int numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep, | ||
243 | out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); | ||
244 | |||
245 | // if there were collisions, they show up here | ||
246 | if (collidersCount > 0) | ||
247 | { | ||
248 | colliders = new int[collidersCount]; | ||
249 | Marshal.Copy(collidersPtr, colliders, 0, collidersCount); | ||
250 | for (int ii = 0; ii < collidersCount; ii+=2) | ||
251 | { | ||
252 | uint cA = (uint)colliders[ii]; | ||
253 | uint cB = (uint)colliders[ii+1]; | ||
254 | SendCollision(cA, cB); | ||
255 | SendCollision(cB, cA); | ||
256 | } | ||
257 | } | ||
258 | |||
259 | // if any of the objects had updated properties, they are returned in the updatedEntity structure | ||
260 | // TODO: figure out how to pass all of the EntityProperties structures in one marshal call. | ||
261 | if (updatedEntityCount > 0) | ||
262 | { | ||
263 | updatedEntities = new IntPtr[updatedEntityCount]; | ||
264 | // fetch all the pointers to all the EntityProperties structures for these updates | ||
265 | Marshal.Copy(updatedEntitiesPtr, updatedEntities, 0, updatedEntityCount); | ||
266 | for (int ii = 0; ii < updatedEntityCount; ii++) | ||
267 | { | ||
268 | IntPtr updatePointer = updatedEntities[ii]; | ||
269 | EntityProperties entprop = (EntityProperties)Marshal.PtrToStructure(updatePointer, typeof(EntityProperties)); | ||
270 | // m_log.DebugFormat("{0}: entprop: id={1}, pos={2}", LogHeader, entprop.ID, entprop.Position); | ||
271 | BSCharacter actor; | ||
272 | if (m_avatars.TryGetValue(entprop.ID, out actor)) | ||
273 | { | ||
274 | actor.UpdateProperties(entprop); | ||
275 | continue; | ||
276 | } | ||
277 | BSPrim prim; | ||
278 | if (m_prims.TryGetValue(entprop.ID, out prim)) | ||
279 | { | ||
280 | prim.UpdateProperties(entprop); | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | |||
285 | // fps calculation wrong. This calculation returns about 1 in normal operation. | ||
286 | return timeStep / (numSubSteps * m_fixedTimeStep) * 1000f; | ||
287 | } | ||
288 | |||
289 | // Something has collided | ||
290 | private void SendCollision(uint localID, uint collidingWith) | ||
291 | { | ||
292 | if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID) | ||
293 | { | ||
294 | // we never send collisions to the terrain | ||
295 | return; | ||
296 | } | ||
297 | |||
298 | ActorTypes type = ActorTypes.Prim; | ||
299 | if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID) | ||
300 | type = ActorTypes.Ground; | ||
301 | else if (m_avatars.ContainsKey(collidingWith)) | ||
302 | type = ActorTypes.Agent; | ||
303 | |||
304 | BSPrim prim; | ||
305 | if (m_prims.TryGetValue(localID, out prim)) { | ||
306 | prim.Collide(collidingWith, type, Vector3.Zero, Vector3.UnitZ, 0.01f); | ||
307 | return; | ||
308 | } | ||
309 | BSCharacter actor; | ||
310 | if (m_avatars.TryGetValue(localID, out actor)) { | ||
311 | actor.Collide(collidingWith, type, Vector3.Zero, Vector3.UnitZ, 0.01f); | ||
312 | return; | ||
313 | } | ||
314 | return; | ||
315 | } | ||
316 | |||
317 | public override void GetResults() { } | ||
318 | |||
319 | public override void SetTerrain(float[] heightMap) { | ||
320 | m_log.DebugFormat("{0}: SetTerrain", LogHeader); | ||
321 | m_heightMap = heightMap; | ||
322 | this.TaintedObject(delegate() | ||
323 | { | ||
324 | BulletSimAPI.SetHeightmap(m_worldID, m_heightMap); | ||
325 | }); | ||
326 | } | ||
327 | |||
328 | public float GetTerrainHeightAtXY(float tX, float tY) | ||
329 | { | ||
330 | return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)]; | ||
331 | } | ||
332 | |||
333 | public override void SetWaterLevel(float baseheight) | ||
334 | { | ||
335 | m_waterLevel = baseheight; | ||
336 | } | ||
337 | public float GetWaterLevel() | ||
338 | { | ||
339 | return m_waterLevel; | ||
340 | } | ||
341 | |||
342 | public override void DeleteTerrain() | ||
343 | { | ||
344 | m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); | ||
345 | } | ||
346 | |||
347 | public override void Dispose() | ||
348 | { | ||
349 | m_log.DebugFormat("{0}: Dispose()", LogHeader); | ||
350 | } | ||
351 | |||
352 | public override Dictionary<uint, float> GetTopColliders() | ||
353 | { | ||
354 | return new Dictionary<uint, float>(); | ||
355 | } | ||
356 | |||
357 | public override bool IsThreaded { get { return false; } } | ||
358 | |||
359 | /// <summary> | ||
360 | /// Routine to figure out if we need to mesh this prim with our mesher | ||
361 | /// </summary> | ||
362 | /// <param name="pbs"></param> | ||
363 | /// <returns>true if the prim needs meshing</returns> | ||
364 | public bool NeedsMeshing(PrimitiveBaseShape pbs) | ||
365 | { | ||
366 | // most of this is redundant now as the mesher will return null if it cant mesh a prim | ||
367 | // but we still need to check for sculptie meshing being enabled so this is the most | ||
368 | // convenient place to do it for now... | ||
369 | |||
370 | // int iPropertiesNotSupportedDefault = 0; | ||
371 | |||
372 | if (pbs.SculptEntry && !_meshSculptedPrim) | ||
373 | { | ||
374 | // m_log.DebugFormat("{0}: NeedsMeshing: scultpy mesh", LogHeader); | ||
375 | return false; | ||
376 | } | ||
377 | |||
378 | // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet | ||
379 | // can use an internal representation for the prim | ||
380 | if (!_forceSimplePrimMeshing) | ||
381 | { | ||
382 | // m_log.DebugFormat("{0}: NeedsMeshing: simple mesh: profshape={1}, curve={2}", LogHeader, pbs.ProfileShape, pbs.PathCurve); | ||
383 | if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) | ||
384 | || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 | ||
385 | && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)) | ||
386 | { | ||
387 | |||
388 | if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 | ||
389 | && pbs.ProfileHollow == 0 | ||
390 | && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 | ||
391 | && pbs.PathBegin == 0 && pbs.PathEnd == 0 | ||
392 | && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 | ||
393 | && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 | ||
394 | && pbs.PathShearX == 0 && pbs.PathShearY == 0) | ||
395 | { | ||
396 | return false; | ||
397 | } | ||
398 | } | ||
399 | } | ||
400 | |||
401 | /* TODO: verify that the mesher will now do all these shapes | ||
402 | if (pbs.ProfileHollow != 0) | ||
403 | iPropertiesNotSupportedDefault++; | ||
404 | |||
405 | if ((pbs.PathBegin != 0) || pbs.PathEnd != 0) | ||
406 | iPropertiesNotSupportedDefault++; | ||
407 | |||
408 | if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0)) | ||
409 | iPropertiesNotSupportedDefault++; | ||
410 | |||
411 | if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0) | ||
412 | iPropertiesNotSupportedDefault++; | ||
413 | |||
414 | if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100)) | ||
415 | iPropertiesNotSupportedDefault++; | ||
416 | |||
417 | if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0)) | ||
418 | iPropertiesNotSupportedDefault++; | ||
419 | |||
420 | if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight) | ||
421 | iPropertiesNotSupportedDefault++; | ||
422 | |||
423 | if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 && (pbs.Scale.X != pbs.Scale.Y || pbs.Scale.Y != pbs.Scale.Z || pbs.Scale.Z != pbs.Scale.X)) | ||
424 | iPropertiesNotSupportedDefault++; | ||
425 | |||
426 | if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1) | ||
427 | iPropertiesNotSupportedDefault++; | ||
428 | |||
429 | // test for torus | ||
430 | if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square) | ||
431 | { | ||
432 | if (pbs.PathCurve == (byte)Extrusion.Curve1) | ||
433 | { | ||
434 | iPropertiesNotSupportedDefault++; | ||
435 | } | ||
436 | } | ||
437 | else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) | ||
438 | { | ||
439 | if (pbs.PathCurve == (byte)Extrusion.Straight) | ||
440 | { | ||
441 | iPropertiesNotSupportedDefault++; | ||
442 | } | ||
443 | // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits | ||
444 | else if (pbs.PathCurve == (byte)Extrusion.Curve1) | ||
445 | { | ||
446 | iPropertiesNotSupportedDefault++; | ||
447 | } | ||
448 | } | ||
449 | else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) | ||
450 | { | ||
451 | if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2) | ||
452 | { | ||
453 | iPropertiesNotSupportedDefault++; | ||
454 | } | ||
455 | } | ||
456 | else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) | ||
457 | { | ||
458 | if (pbs.PathCurve == (byte)Extrusion.Straight) | ||
459 | { | ||
460 | iPropertiesNotSupportedDefault++; | ||
461 | } | ||
462 | else if (pbs.PathCurve == (byte)Extrusion.Curve1) | ||
463 | { | ||
464 | iPropertiesNotSupportedDefault++; | ||
465 | } | ||
466 | } | ||
467 | if (iPropertiesNotSupportedDefault == 0) | ||
468 | { | ||
469 | return false; | ||
470 | } | ||
471 | */ | ||
472 | return true; | ||
473 | } | ||
474 | |||
475 | // The calls to the PhysicsActors can't directly call into the physics engine | ||
476 | // because it might be busy. We we delay changes to a known time. | ||
477 | // We rely on C#'s closure to save and restore the context for the delegate. | ||
478 | public void TaintedObject(TaintCallback callback) | ||
479 | { | ||
480 | lock (_taintLock) | ||
481 | _taintedObjects.Add(callback); | ||
482 | return; | ||
483 | } | ||
484 | |||
485 | // When someone tries to change a property on a BSPrim or BSCharacter, the object queues | ||
486 | // a callback into itself to do the actual property change. That callback is called | ||
487 | // here just before the physics engine is called to step the simulation. | ||
488 | public void ProcessTaints() | ||
489 | { | ||
490 | if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process | ||
491 | { | ||
492 | // swizzle a new list into the list location so we can process what's there | ||
493 | List<TaintCallback> oldList; | ||
494 | lock (_taintLock) | ||
495 | { | ||
496 | oldList = _taintedObjects; | ||
497 | _taintedObjects = new List<TaintCallback>(); | ||
498 | } | ||
499 | |||
500 | foreach (TaintCallback callback in oldList) | ||
501 | { | ||
502 | try | ||
503 | { | ||
504 | callback(); | ||
505 | } | ||
506 | catch (Exception e) | ||
507 | { | ||
508 | m_log.ErrorFormat("{0}: ProcessTaints: Exception: {1}", LogHeader, e); | ||
509 | } | ||
510 | } | ||
511 | oldList.Clear(); | ||
512 | } | ||
513 | } | ||
514 | |||
515 | #region Vehicles | ||
516 | // Make so the scene will call this prim for vehicle actions each tick. | ||
517 | // Safe to call if prim is already in the vehicle list. | ||
518 | public void AddVehiclePrim(BSPrim vehicle) | ||
519 | { | ||
520 | lock (m_vehicles) | ||
521 | { | ||
522 | if (!m_vehicles.Contains(vehicle)) | ||
523 | { | ||
524 | m_vehicles.Add(vehicle); | ||
525 | } | ||
526 | } | ||
527 | } | ||
528 | |||
529 | // Remove a prim from our list of vehicles. | ||
530 | // Safe to call if the prim is not in the vehicle list. | ||
531 | public void RemoveVehiclePrim(BSPrim vehicle) | ||
532 | { | ||
533 | lock (m_vehicles) | ||
534 | { | ||
535 | if (m_vehicles.Contains(vehicle)) | ||
536 | { | ||
537 | m_vehicles.Remove(vehicle); | ||
538 | } | ||
539 | } | ||
540 | } | ||
541 | |||
542 | // Some prims have extra vehicle actions | ||
543 | // no locking because only called when physics engine is not busy | ||
544 | private void ProcessVehicles(float timeStep) | ||
545 | { | ||
546 | foreach (BSPrim prim in m_vehicles) | ||
547 | { | ||
548 | prim.StepVehicle(timeStep); | ||
549 | } | ||
550 | } | ||
551 | #endregion Vehicles | ||
552 | } | ||
553 | } | ||