From 83d67391f15962e45c1d1173439fa8d28de0628a Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Wed, 18 May 2011 08:30:59 -0700 Subject: [PATCH 1/7] Fix physical actor update by passing additional attributes when physics terse update happens --- .../RegionSyncModule/PhysEngineToSceneConnector.cs | 2 +- .../RegionSyncModule/SceneToPhysEngineConnector.cs | 6 +++--- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 7 ++++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/PhysEngineToSceneConnector.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/PhysEngineToSceneConnector.cs index d4919cdab3..6bb3e3187d 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/PhysEngineToSceneConnector.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/PhysEngineToSceneConnector.cs @@ -342,7 +342,7 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule { UUID uuid = data["uuid"].AsUUID(); string actorID = data["actorID"].AsString(); - m_log.DebugFormat("{0}: HandlPhysUpdateAttributes for {1}", LogHeader, uuid); + // m_log.DebugFormat("{0}: HandlPhysUpdateAttributes for {1}", LogHeader, uuid); PhysicsActor pa = FindPhysicsActor(uuid); if (pa != null) { diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SceneToPhysEngineConnector.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SceneToPhysEngineConnector.cs index e7115aaa28..2e0b19b737 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SceneToPhysEngineConnector.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SceneToPhysEngineConnector.cs @@ -373,7 +373,7 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule pa.Kinematic = data["kinematic"].AsBoolean(); // receive?? pa.Buoyancy = (float)(data["buoyancy"].AsReal()); pa.CollidingGround = data["isCollidingGround"].AsBoolean(); - pa.IsColliding = data["isCollidingGround"].AsBoolean(); + pa.IsColliding = data["isColliding"].AsBoolean(); pa.ChangingActorID = actorID; pa.RequestPhysicsterseUpdate(); // tell the system the values have changed @@ -431,8 +431,8 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule data["isPhysical"] = OSD.FromBoolean(pa.IsPhysical); data["flying"] = OSD.FromBoolean(pa.Flying); data["buoyancy"] = OSD.FromReal(pa.Buoyancy); - // data["isColliding"] = OSD.FromBoolean(pa.IsColliding); - // data["isCollidingGround"] = OSD.FromBoolean(pa.CollidingGround); + data["isColliding"] = OSD.FromBoolean(pa.IsColliding); + data["isCollidingGround"] = OSD.FromBoolean(pa.CollidingGround); RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.PhysUpdateAttributes, OSDParser.SerializeJsonString(data)); diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index a0c1b1d8f5..4651f47993 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2794,7 +2794,12 @@ namespace OpenSim.Region.Framework.Scenes //m_parentGroup.RootPart.m_groupPosition = newpos; } //ScheduleTerseUpdate(); - ScheduleTerseUpdate(new List(){SceneObjectPartSyncProperties.Position}); + ScheduleTerseUpdate(new List(){ + SceneObjectPartSyncProperties.Position, + SceneObjectPartSyncProperties.Orientation, + SceneObjectPartSyncProperties.Velocity, + SceneObjectPartSyncProperties.RotationalVelocity + }); //SendTerseUpdateToAllClients(); } From b0173de7ecbd616ee94e85aa3e9e38245f5ac863 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 19 May 2011 12:10:53 -0700 Subject: [PATCH 2/7] Add localID to prim and avatar physics creation calls --- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 2 ++ OpenSim/Region/Framework/Scenes/ScenePresence.cs | 2 +- OpenSim/Region/Physics/Manager/PhysicsScene.cs | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 4651f47993..d4e6ce4ad0 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -1627,6 +1627,7 @@ namespace OpenSim.Region.Framework.Scenes if (!isPhantom && !IsAttachment && !(Shape.PathCurve == (byte) Extrusion.Flexible)) { PhysActor = m_parentGroup.Scene.PhysicsScene.AddPrimShape( + LocalId, string.Format("{0}/{1}", Name, UUID), Shape, AbsolutePosition, @@ -4495,6 +4496,7 @@ namespace OpenSim.Region.Framework.Scenes { // It's not phantom anymore. So make sure the physics engine get's knowledge of it PhysActor = m_parentGroup.Scene.PhysicsScene.AddPrimShape( + LocalId, string.Format("{0}/{1}", Name, UUID), Shape, AbsolutePosition, diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 623e3dbcfe..e1e7283cb3 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -3405,7 +3405,7 @@ namespace OpenSim.Region.Framework.Scenes Vector3 pVec = AbsolutePosition; // Old bug where the height was in centimeters instead of meters - m_physicsActor = scene.AddAvatar(Firstname + "." + Lastname, pVec, + m_physicsActor = scene.AddAvatar(LocalId, Firstname + "." + Lastname, pVec, new Vector3(0f, 0f, m_appearance.AvatarHeight), isFlying); scene.AddPhysicsActorTaint(m_physicsActor); diff --git a/OpenSim/Region/Physics/Manager/PhysicsScene.cs b/OpenSim/Region/Physics/Manager/PhysicsScene.cs index 217d307443..c314293d5b 100644 --- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs +++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs @@ -66,6 +66,11 @@ namespace OpenSim.Region.Physics.Manager public abstract PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying); + public virtual PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying) + { + return AddAvatar(avName, position, size, isFlying); + } + public abstract void RemoveAvatar(PhysicsActor actor); public abstract void RemovePrim(PhysicsActor prim); @@ -75,6 +80,12 @@ namespace OpenSim.Region.Physics.Manager public abstract PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, Vector3 size, Quaternion rotation, bool isPhysical); + public virtual PhysicsActor AddPrimShape(uint localId, string primName, PrimitiveBaseShape pbs, Vector3 position, + Vector3 size, Quaternion rotation, bool isPhysical) + { + return AddPrimShape(primName, pbs, position, size, rotation, isPhysical); + } + public virtual float TimeDilation { get { return 1.0f; } From 05dbf3156773adc8647f8cabee80039fb12c683e Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 19 May 2011 12:50:29 -0700 Subject: [PATCH 3/7] Adding BulletSim module --- addon-modules/BulletSPlugin/BSCharacter.cs | 413 +++++++ addon-modules/BulletSPlugin/BSPlugin.cs | 67 ++ addon-modules/BulletSPlugin/BSPrim.cs | 1088 +++++++++++++++++++ addon-modules/BulletSPlugin/BSScene.cs | 471 ++++++++ addon-modules/BulletSPlugin/BulletSimAPI.cs | 171 +++ addon-modules/BulletSPlugin/prebuild.xml | 32 + 6 files changed, 2242 insertions(+) create mode 100644 addon-modules/BulletSPlugin/BSCharacter.cs create mode 100644 addon-modules/BulletSPlugin/BSPlugin.cs create mode 100644 addon-modules/BulletSPlugin/BSPrim.cs create mode 100644 addon-modules/BulletSPlugin/BSScene.cs create mode 100644 addon-modules/BulletSPlugin/BulletSimAPI.cs create mode 100644 addon-modules/BulletSPlugin/prebuild.xml diff --git a/addon-modules/BulletSPlugin/BSCharacter.cs b/addon-modules/BulletSPlugin/BSCharacter.cs new file mode 100644 index 0000000000..4194a49142 --- /dev/null +++ b/addon-modules/BulletSPlugin/BSCharacter.cs @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2011 Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSCharacter : PhysicsActor +{ + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[BULLETS CHAR]"; + + private BSScene _scene; + private String _avName; + private bool _stopped; + private Vector3 _size; + private Vector3 _scale; + private PrimitiveBaseShape _pbs; + private uint _localID = 0; + private bool _grabbed; + private bool _selected; + private Vector3 _position; + private float _mass = 80f; + public float _density = 60f; + public float CAPSULE_RADIUS = 0.37f; + public float CAPSULE_LENGTH = 2.140599f; + private Vector3 _force; + private Vector3 _velocity; + private Vector3 _torque; + private float _collisionScore; + private Vector3 _acceleration; + private Quaternion _orientation; + private int _physicsActorType; + private bool _isPhysical; + private bool _flying; + private bool _setAlwaysRun; + private bool _throttleUpdates; + private bool _isColliding; + private long _collidingStep; + private bool _collidingGround; + private long _collidingGroundStep; + private bool _collidingObj; + private bool _floatOnWater; + private Vector3 _rotationalVelocity; + private bool _kinematic; + private float _buoyancy; + + private int _subscribedEventsMs = 0; + + private Vector3 _PIDTarget; + private bool _usePID; + private float _PIDTau; + private bool _useHoverPID; + private float _PIDHoverHeight; + private PIDHoverType _PIDHoverType; + private float _PIDHoverTao; + + public BSCharacter(uint localID, String avName, BSScene parent_scene, Vector3 pos, Vector3 size, bool isFlying) + { + _localID = localID; + _avName = avName; + _scene = parent_scene; + _position = pos; + _size = size; + _orientation = Quaternion.Identity; + _velocity = Vector3.Zero; + _scale = new Vector3(1f, 1f, 1f); + float AVvolume = (float) (Math.PI*Math.Pow(CAPSULE_RADIUS, 2)*CAPSULE_LENGTH); + _mass = _density*AVvolume; + + ShapeData shapeData = new ShapeData(); + shapeData.ID = _localID; + shapeData.Type = ShapeData.PhysicsShapeType.SHAPE_AVATAR; + shapeData.Position = _position; + shapeData.Rotation = _orientation; + shapeData.Velocity = _velocity; + shapeData.Scale = _scale; + shapeData.Mass = _mass; + shapeData.Flying = isFlying ? ShapeData.numericTrue : ShapeData.numericFalse; + shapeData.Dynamic = ShapeData.numericFalse; + + BulletSimAPI.CreateObject(parent_scene.WorldID, shapeData); + + return; + } + + // called when this character is being destroyed and the resources should be released + public void Destroy() + { + _scene.TaintedObject(delegate() + { + BulletSimAPI.DestroyObject(_scene.WorldID, _localID); + }); + } + + public override void RequestPhysicsterseUpdate() + { + base.RequestPhysicsterseUpdate(); + } + + public override bool Stopped { + get { return _stopped; } + } + public override Vector3 Size { + get { return _size; } + set { _size = value; + } + } + public override PrimitiveBaseShape Shape { + set { _pbs = value; + } + } + public override uint LocalID { + set { _localID = value; + } + get { return _localID; } + } + public override bool Grabbed { + set { _grabbed = value; + } + } + public override bool Selected { + set { _selected = value; + } + } + public override void CrossingFailure() { return; } + public override void link(PhysicsActor obj) { return; } + public override void delink() { return; } + public override void LockAngularMotion(Vector3 axis) { return; } + + public override Vector3 Position { + get { + _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + return _position; + } + set { + _position = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + }); + } + } + public override float Mass { + get { + return _mass; + } + } + public override Vector3 Force { + get { return _force; } + set { + _force = value; + m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force); + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + }); + } + } + + public override int VehicleType { + get { return 0; } + set { return; } + } + public override void VehicleFloatParam(int param, float value) { } + public override void VehicleVectorParam(int param, Vector3 value) {} + public override void VehicleRotationParam(int param, Quaternion rotation) { } + public override void VehicleFlags(int param, bool remove) { } + + // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more + public override void SetVolumeDetect(int param) { return; } + + public override Vector3 GeometricCenter { get { return Vector3.Zero; } } + public override Vector3 CenterOfMass { get { return Vector3.Zero; } } + public override Vector3 Velocity { + get { return _velocity; } + set { + _velocity = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectVelocity(_scene.WorldID, _localID, _velocity); + }); + } + } + public override Vector3 Torque { + get { return _torque; } + set { _torque = value; + } + } + public override float CollisionScore { + get { return _collisionScore; } + set { _collisionScore = value; + } + } + public override Vector3 Acceleration { + get { return _acceleration; } + } + public override Quaternion Orientation { + get { return _orientation; } + set { + _orientation = value; + _scene.TaintedObject(delegate() + { + _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + }); + } + } + public override int PhysicsActorType { + get { return _physicsActorType; } + set { _physicsActorType = value; + } + } + public override bool IsPhysical { + get { return _isPhysical; } + set { _isPhysical = value; + } + } + public override bool Flying { + get { return _flying; } + set { + _flying = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectFlying(_scene.WorldID, LocalID, _flying); + }); + } + } + public override bool + SetAlwaysRun { + get { return _setAlwaysRun; } + set { _setAlwaysRun = value; } + } + public override bool ThrottleUpdates { + get { return _throttleUpdates; } + set { _throttleUpdates = value; } + } + public override bool IsColliding { + get { return (_collidingStep == _scene.SimulationStep); } + set { _isColliding = value; } + } + public override bool CollidingGround { + get { return (_collidingGroundStep == _scene.SimulationStep); } + set { _collidingGround = value; } + } + public override bool CollidingObj { + get { return _collidingObj; } + set { _collidingObj = value; } + } + public override bool FloatOnWater { + set { _floatOnWater = value; } + } + public override Vector3 RotationalVelocity { + get { return _rotationalVelocity; } + set { _rotationalVelocity = value; } + } + public override bool Kinematic { + get { return _kinematic; } + set { _kinematic = value; } + } + public override float Buoyancy { + get { return _buoyancy; } + set { _buoyancy = value; } + } + + // Used for MoveTo + public override Vector3 PIDTarget { + set { _PIDTarget = value; } + } + public override bool PIDActive { + set { _usePID = value; } + } + public override float PIDTau { + set { _PIDTau = value; } + } + + // Used for llSetHoverHeight and maybe vehicle height + // Hover Height will override MoveTo target's Z + public override bool PIDHoverActive { + set { _useHoverPID = value; } + } + public override float PIDHoverHeight { + set { _PIDHoverHeight = value; } + } + public override PIDHoverType PIDHoverType { + set { _PIDHoverType = value; } + } + public override float PIDHoverTau { + set { _PIDHoverTao = value; } + } + + // For RotLookAt + public override Quaternion APIDTarget { set { return; } } + public override bool APIDActive { set { return; } } + public override float APIDStrength { set { return; } } + public override float APIDDamping { set { return; } } + + public override void AddForce(Vector3 force, bool pushforce) { + if (force.IsFinite()) + { + _force.X += force.X; + _force.Y += force.Y; + _force.Z += force.Z; + } + else + { + m_log.WarnFormat("{0}: Got a NaN force applied to a Character", LogHeader); + } + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + }); + //m_lastUpdateSent = false; + } + public override void AddAngularForce(Vector3 force, bool pushforce) { + } + public override void SetMomentum(Vector3 momentum) { + } + public override void SubscribeEvents(int ms) { + _subscribedEventsMs = ms; + } + public override void UnSubscribeEvents() { + _subscribedEventsMs = 0; + } + public override bool SubscribedEvents() { + return (_subscribedEventsMs > 0); + } + + // The physics engine says that properties have updated. Update same and inform + // the world that things have changed. + public void UpdateProperties(EntityProperties entprop) + { + bool changed = false; + // we assign to the local variables so the normal set action does not happen + if (_position != entprop.Position) + { + _position = entprop.Position; + changed = true; + } + if (_orientation != entprop.Rotation) + { + _orientation = entprop.Rotation; + changed = true; + } + if (_velocity != entprop.Velocity) + { + _velocity = entprop.Velocity; + changed = true; + } + if (_acceleration != entprop.Acceleration) + { + _acceleration = entprop.Acceleration; + changed = true; + } + if (_rotationalVelocity != entprop.AngularVelocity) + { + _rotationalVelocity = entprop.AngularVelocity; + changed = true; + } + if (changed) + { + base.RequestPhysicsterseUpdate(); + } + } + + public void Collide(uint collidingWith, ActorTypes type, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth) + { + // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith); + if (_subscribedEventsMs == 0) return; // don't want collisions + + // The following says we're colliding this simulation step + _collidingStep = _scene.SimulationStep; + if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID) + { + _collidingGroundStep = _scene.SimulationStep; + } + + Dictionary contactPoints = new Dictionary(); + contactPoints.Add(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); + CollisionEventUpdate args = new CollisionEventUpdate(LocalID, (int)type, 1, contactPoints); + base.SendCollisionUpdate(args); + } + +} +} diff --git a/addon-modules/BulletSPlugin/BSPlugin.cs b/addon-modules/BulletSPlugin/BSPlugin.cs new file mode 100644 index 0000000000..0d53e8ed67 --- /dev/null +++ b/addon-modules/BulletSPlugin/BSPlugin.cs @@ -0,0 +1,67 @@ +/* + * Copyright (c) Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSPlugin : IPhysicsPlugin +{ + //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + private BSScene _mScene; + + public BSPlugin() + { + } + + public bool Init() + { + return true; + } + + public PhysicsScene GetScene(String sceneIdentifier) + { + if (_mScene == null) + { + _mScene = new BSScene(sceneIdentifier); + } + return (_mScene); + } + + public string GetName() + { + return ("BulletSim"); + } + + public void Dispose() + { + } +} +} diff --git a/addon-modules/BulletSPlugin/BSPrim.cs b/addon-modules/BulletSPlugin/BSPrim.cs new file mode 100644 index 0000000000..1c251a00f9 --- /dev/null +++ b/addon-modules/BulletSPlugin/BSPrim.cs @@ -0,0 +1,1088 @@ +/* + * Copyright (c) Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Xml; +using log4net; +using OMV = OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Physics.ConvexDecompositionDotNet; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + [Serializable] +public sealed class BSPrim : PhysicsActor +{ + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[BULLETS PRIM]"; + + private IMesh _mesh; + private PrimitiveBaseShape _pbs; + private ShapeData.PhysicsShapeType _shapeType; + private ulong _hullKey; + private List _hulls; + + private BSScene _scene; + private String _avName; + private uint _localID = 0; + + private OMV.Vector3 _size; + private OMV.Vector3 _scale; + private bool _stopped; + private bool _grabbed; + private bool _isSelected; + private OMV.Vector3 _position; + private float _mass; + private float _density; + private OMV.Vector3 _force; + private OMV.Vector3 _velocity; + private OMV.Vector3 _torque; + private float _collisionScore; + private OMV.Vector3 _acceleration; + private OMV.Quaternion _orientation; + private int _physicsActorType; + private bool _isPhysical; + private bool _flying; + private float _friction; + private bool _setAlwaysRun; + private bool _throttleUpdates; + private bool _isColliding; + private bool _collidingGround; + private bool _collidingObj; + private bool _floatOnWater; + private OMV.Vector3 _rotationalVelocity; + private bool _kinematic; + private float _buoyancy; + + private List _childrenPrims; + private BSPrim _parentPrim; + + private int _subscribedEventsMs = 0; + long _collidingStep; + long _collidingGroundStep; + + private OMV.Vector3 _PIDTarget; + private bool _usePID; + private float _PIDTau; + private bool _useHoverPID; + private float _PIDHoverHeight; + private PIDHoverType _PIDHoverType; + private float _PIDHoverTao; + + public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, + OMV.Quaternion rotation, IMesh mesh, PrimitiveBaseShape pbs, bool pisPhysical) + { + m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID); + _localID = localID; + _avName = primName; + _scene = parent_scene; + _position = pos; + _size = size; + _scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type + _orientation = rotation; + _mesh = mesh; + _hullKey = 0; + _pbs = pbs; + _isPhysical = pisPhysical; + _subscribedEventsMs = 0; + _friction = _scene.DefaultFriction; // TODO: compute based on object material + _density = _scene.DefaultDensity; // TODO: compute based on object material + _parentPrim = null; // not a child or a parent + _childrenPrims = new List(); + if (_isPhysical) + _mass = CalculateMass(); + else + _mass = 0f; + // do the actual object creation at taint time + _scene.TaintedObject(delegate() + { + CreateGeom(); + CreateObject(); + }); + } + + // called when this prim is being destroyed and we should free all the resources + public void Destroy() + { + // m_log.DebugFormat("{0}: Destroy", LogHeader); + _scene.TaintedObject(delegate() + { + BulletSimAPI.DestroyObject(_scene.WorldID, _localID); + }); + } + + public override bool Stopped { + get { return _stopped; } + } + public override OMV.Vector3 Size { + get { return _size; } + set { + _size = value; + m_log.DebugFormat("{0}: Size={1}", LogHeader, _size); + _scene.TaintedObject(delegate() + { + if (_isPhysical) _mass = CalculateMass(); // changing size changes the mass + BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, _mass, _isPhysical); + RecreateGeomAndObject(); + // SyncUpdated = true; + }); + } + } + public override PrimitiveBaseShape Shape { + set { + _pbs = value; + m_log.DebugFormat("{0}: set Shape", LogHeader); + _scene.TaintedObject(delegate() + { + if (_isPhysical) _mass = CalculateMass(); // changing the shape changes the mass + RecreateGeomAndObject(); + // SyncUpdated = true; + }); + } + } + public override uint LocalID { + set { _localID = value; } + get { return _localID; } + } + public override bool Grabbed { + set { _grabbed = value; + m_log.DebugFormat("{0}: Grabbed={1}", LogHeader, _grabbed); + } + } + public override bool Selected { + set { + _isSelected = value; + _scene.TaintedObject(delegate() + { + m_log.DebugFormat("{0}: Selected={1}, localID={2}", LogHeader, _isSelected, _localID); + SetObjectDynamic(); + // SyncUpdated = true; + }); + } + } + public override void CrossingFailure() { return; } + + // link me to the specified parent + public override void link(PhysicsActor obj) { + BSPrim parent = (BSPrim)obj; + m_log.DebugFormat("{0}: link {1} to {2}", LogHeader, _localID, obj.LocalID); + // TODO: decide if this parent checking needs to happen at taint time + if (_parentPrim == null) + { + if (parent != null) + { + // I don't have a parent so I am joining a linkset + parent.AddChildToLinkset(this); + } + } + else + { + // I already have a parent, is parenting changing? + if (parent != _parentPrim) + { + if (parent == null) + { + // we are being removed from a linkset + _parentPrim.RemoveChildFromLinkset(this); + } + else + { + // asking to reparent a prim should not happen + m_log.ErrorFormat("{0}: Reparenting a prim. ", LogHeader); + } + } + } + return; + } + + // delink me from my linkset + public override void delink() { + // TODO: decide if this parent checking needs to happen at taint time + m_log.DebugFormat("{0}: delink {1}", LogHeader, _localID); + if (_parentPrim != null) + { + _parentPrim.RemoveChildFromLinkset(this); + } + return; + } + + public void AddChildToLinkset(BSPrim pchild) + { + BSPrim child = pchild; + _scene.TaintedObject(delegate() + { + if (!_childrenPrims.Contains(child)) + { + _childrenPrims.Add(child); + child.ParentPrim = this; // the child has gained a parent + RecreateGeomAndObject(); // rebuild my shape with the new child added + } + }); + return; + } + + public void RemoveChildFromLinkset(BSPrim pchild) + { + BSPrim child = pchild; + _scene.TaintedObject(delegate() + { + if (_childrenPrims.Contains(child)) + { + _childrenPrims.Remove(child); + child.ParentPrim = null; // the child has lost its parent + RecreateGeomAndObject(); // rebuild my shape with the child removed + } + else + { + m_log.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset"); + } + }); + return; + } + + public BSPrim ParentPrim + { + set { _parentPrim = value; } + } + + public ulong HullKey + { + get { return _hullKey; } + } + + // return true if we are the root of a linkset (there are children to manage) + public bool IsRootOfLinkset + { + get { return (_parentPrim == null && _childrenPrims.Count != 0); } + } + + public override void LockAngularMotion(OMV.Vector3 axis) { return; } + + public override OMV.Vector3 Position { + get { + // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + return _position; + } + set { + _position = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + // SyncUpdated = true; + }); + } + } + public override float Mass { + get { return _mass; } + } + public override OMV.Vector3 Force { + get { return _force; } + set { + _force = value; + m_log.DebugFormat("{0}: set Force. f={1}", LogHeader, _force); + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + // SyncUpdated = true; + }); + } + } + + public override int VehicleType { + get { return 0; } + set { return; } + } + public override void VehicleFloatParam(int param, float value) { } + public override void VehicleVectorParam(int param, OMV.Vector3 value) {} + public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { } + public override void VehicleFlags(int param, bool remove) { } + + // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more + public override void SetVolumeDetect(int param) { return; } + + public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } } + public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } } + public override OMV.Vector3 Velocity { + get { return _velocity; } + set { _velocity = value; + // SyncUpdated = true; + } + } + public override OMV.Vector3 Torque { + get { return _torque; } + set { _torque = value; + // SyncUpdated = true; + } + } + public override float CollisionScore { + get { return _collisionScore; } + set { _collisionScore = value; + // SyncUpdated = true; + } + } + public override OMV.Vector3 Acceleration { + get { return _acceleration; } + } + public override OMV.Quaternion Orientation { + get { return _orientation; } + set { + _orientation = value; + _scene.TaintedObject(delegate() + { + _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); + // m_log.DebugFormat("{0}: set orientation: {1}", LogHeader, _orientation); + // SyncUpdated = true; + }); + } + } + public override int PhysicsActorType { + get { return _physicsActorType; } + set { _physicsActorType = value; + // SyncUpdated = true; + } + } + public override bool IsPhysical { + get { return _isPhysical; } + set { + _isPhysical = value; + _scene.TaintedObject(delegate() + { + SetObjectDynamic(); + m_log.DebugFormat("{0}: ID={1}, IsPhysical={2}, IsSelected={3}, mass={4}", LogHeader, _localID, _isPhysical, _isSelected, _mass); + // SyncUpdated = true; + }); + } + } + // make gravity work if the object is physical and not selected + // no locking here because only called when it is safe + private void SetObjectDynamic() + { + // a selected object is not physical + if (_isSelected || !_isPhysical) + { + _mass = 0f; // non-physical things work best with a mass of zero + BulletSimAPI.SetObjectDynamic(_scene.WorldID, _localID, false, _mass); + } + else + { + _mass = CalculateMass(); + BulletSimAPI.SetObjectDynamic(_scene.WorldID, _localID, true, _mass); + } + } + public override bool Flying { + get { return _flying; } + set { + _flying = value; + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectFlying(_scene.WorldID, LocalID, _flying); + // SyncUpdated = true; + }); + } + } + public override bool + SetAlwaysRun { + get { return _setAlwaysRun; } + set { _setAlwaysRun = value; } + } + public override bool ThrottleUpdates { + get { return _throttleUpdates; } + set { _throttleUpdates = value; } + } + public override bool IsColliding { + get { return _isColliding; } + set { _isColliding = value; } + } + public override bool CollidingGround { + get { return _collidingGround; } + set { _collidingGround = value; } + } + public override bool CollidingObj { + get { return _collidingObj; } + set { _collidingObj = value; } + } + public bool IsPhantom { + get { + // SceneObjectPart removes phantom objects from the physics scene + // so, although we could implement touching and such, we never + // are invoked as a phantom object + return false; + } + } + public override bool FloatOnWater { + set { _floatOnWater = value; } + } + public override OMV.Vector3 RotationalVelocity { + get { return _rotationalVelocity; } + set { _rotationalVelocity = value; + m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); + } + } + public override bool Kinematic { + get { return _kinematic; } + set { _kinematic = value; + m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic); + } + } + public override float Buoyancy { + get { return _buoyancy; } + set { _buoyancy = value; } + } + + // Used for MoveTo + public override OMV.Vector3 PIDTarget { + set { _PIDTarget = value; } + } + public override bool PIDActive { + set { _usePID = value; } + } + public override float PIDTau { + set { _PIDTau = value; } + } + + // Used for llSetHoverHeight and maybe vehicle height + // Hover Height will override MoveTo target's Z + public override bool PIDHoverActive { + set { _useHoverPID = value; } + } + public override float PIDHoverHeight { + set { _PIDHoverHeight = value; } + } + public override PIDHoverType PIDHoverType { + set { _PIDHoverType = value; } + } + public override float PIDHoverTau { + set { _PIDHoverTao = value; } + } + + // For RotLookAt + public override OMV.Quaternion APIDTarget { set { return; } } + public override bool APIDActive { set { return; } } + public override float APIDStrength { set { return; } } + public override float APIDDamping { set { return; } } + + public override void AddForce(OMV.Vector3 force, bool pushforce) { + if (force.IsFinite()) + { + _force.X += force.X; + _force.Y += force.Y; + _force.Z += force.Z; + } + else + { + m_log.WarnFormat("{0}: Got a NaN force applied to a Character", LogHeader); + } + _scene.TaintedObject(delegate() + { + BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + }); + } + + public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { + m_log.DebugFormat("{0}: AddAngularForce. f={1}, push={2}", LogHeader, force, pushforce); + } + public override void SetMomentum(OMV.Vector3 momentum) { + } + public override void SubscribeEvents(int ms) { + _subscribedEventsMs = ms; + } + public override void UnSubscribeEvents() { + _subscribedEventsMs = 0; + } + public override bool SubscribedEvents() { + return (_subscribedEventsMs > 0); + } + + #region Mass Calculation + + private float CalculateMass() + { + float volume = _size.X * _size.Y * _size.Z; // default + float tmp; + + float returnMass = 0; + float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f; + float hollowVolume = hollowAmount * hollowAmount; + + switch (_pbs.ProfileShape) + { + case ProfileShape.Square: + // default box + + if (_pbs.PathCurve == (byte)Extrusion.Straight) + { + if (hollowAmount > 0.0) + { + switch (_pbs.HollowShape) + { + case HollowShape.Square: + case HollowShape.Same: + break; + + case HollowShape.Circle: + + hollowVolume *= 0.78539816339f; + break; + + case HollowShape.Triangle: + + hollowVolume *= (0.5f * .5f); + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + + else if (_pbs.PathCurve == (byte)Extrusion.Curve1) + { + //a tube + + volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX); + tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY); + volume -= volume*tmp*tmp; + + if (hollowAmount > 0.0) + { + hollowVolume *= hollowAmount; + + switch (_pbs.HollowShape) + { + case HollowShape.Square: + case HollowShape.Same: + break; + + case HollowShape.Circle: + hollowVolume *= 0.78539816339f;; + break; + + case HollowShape.Triangle: + hollowVolume *= 0.5f * 0.5f; + break; + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + + break; + + case ProfileShape.Circle: + + if (_pbs.PathCurve == (byte)Extrusion.Straight) + { + volume *= 0.78539816339f; // elipse base + + if (hollowAmount > 0.0) + { + switch (_pbs.HollowShape) + { + case HollowShape.Same: + case HollowShape.Circle: + break; + + case HollowShape.Square: + hollowVolume *= 0.5f * 2.5984480504799f; + break; + + case HollowShape.Triangle: + hollowVolume *= .5f * 1.27323954473516f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + + else if (_pbs.PathCurve == (byte)Extrusion.Curve1) + { + volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX); + tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); + volume *= (1.0f - tmp * tmp); + + if (hollowAmount > 0.0) + { + + // calculate the hollow volume by it's shape compared to the prim shape + hollowVolume *= hollowAmount; + + switch (_pbs.HollowShape) + { + case HollowShape.Same: + case HollowShape.Circle: + break; + + case HollowShape.Square: + hollowVolume *= 0.5f * 2.5984480504799f; + break; + + case HollowShape.Triangle: + hollowVolume *= .5f * 1.27323954473516f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + break; + + case ProfileShape.HalfCircle: + if (_pbs.PathCurve == (byte)Extrusion.Curve1) + { + volume *= 0.52359877559829887307710723054658f; + } + break; + + case ProfileShape.EquilateralTriangle: + + if (_pbs.PathCurve == (byte)Extrusion.Straight) + { + volume *= 0.32475953f; + + if (hollowAmount > 0.0) + { + + // calculate the hollow volume by it's shape compared to the prim shape + switch (_pbs.HollowShape) + { + case HollowShape.Same: + case HollowShape.Triangle: + hollowVolume *= .25f; + break; + + case HollowShape.Square: + hollowVolume *= 0.499849f * 3.07920140172638f; + break; + + case HollowShape.Circle: + // Hollow shape is a perfect cyllinder in respect to the cube's scale + // Cyllinder hollow volume calculation + + hollowVolume *= 0.1963495f * 3.07920140172638f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + else if (_pbs.PathCurve == (byte)Extrusion.Curve1) + { + volume *= 0.32475953f; + volume *= 0.01f * (float)(200 - _pbs.PathScaleX); + tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); + volume *= (1.0f - tmp * tmp); + + if (hollowAmount > 0.0) + { + + hollowVolume *= hollowAmount; + + switch (_pbs.HollowShape) + { + case HollowShape.Same: + case HollowShape.Triangle: + hollowVolume *= .25f; + break; + + case HollowShape.Square: + hollowVolume *= 0.499849f * 3.07920140172638f; + break; + + case HollowShape.Circle: + + hollowVolume *= 0.1963495f * 3.07920140172638f; + break; + + default: + hollowVolume = 0; + break; + } + volume *= (1.0f - hollowVolume); + } + } + break; + + default: + break; + } + + + + float taperX1; + float taperY1; + float taperX; + float taperY; + float pathBegin; + float pathEnd; + float profileBegin; + float profileEnd; + + if (_pbs.PathCurve == (byte)Extrusion.Straight || _pbs.PathCurve == (byte)Extrusion.Flexible) + { + taperX1 = _pbs.PathScaleX * 0.01f; + if (taperX1 > 1.0f) + taperX1 = 2.0f - taperX1; + taperX = 1.0f - taperX1; + + taperY1 = _pbs.PathScaleY * 0.01f; + if (taperY1 > 1.0f) + taperY1 = 2.0f - taperY1; + taperY = 1.0f - taperY1; + } + else + { + taperX = _pbs.PathTaperX * 0.01f; + if (taperX < 0.0f) + taperX = -taperX; + taperX1 = 1.0f - taperX; + + taperY = _pbs.PathTaperY * 0.01f; + if (taperY < 0.0f) + taperY = -taperY; + taperY1 = 1.0f - taperY; + + } + + + volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY); + + pathBegin = (float)_pbs.PathBegin * 2.0e-5f; + pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f; + volume *= (pathEnd - pathBegin); + + // this is crude aproximation + profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f; + profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f; + volume *= (profileEnd - profileBegin); + + returnMass = _density * volume; + + if (returnMass <= 0) + returnMass = 0.0001f;//ckrinke: Mass must be greater then zero. + + if (IsRootOfLinkset) + { + foreach (BSPrim prim in _childrenPrims) + { + returnMass += prim.CalculateMass(); + } + } + + if (returnMass > _scene.maximumMassObject) + returnMass = _scene.maximumMassObject; + return returnMass; + }// end CalculateMass + #endregion Mass Calculation + + // Create the geometry information in Bullet for later use + // No locking here because this is done when we know physics is not simulating + private void CreateGeom() + { + if (_mesh == null) + { + // the mesher thought this was too simple to mesh. Use a native Bullet collision shape. + if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1) + { + if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z) + { + m_log.DebugFormat("{0}: CreateGeom: mesh null. Defaulting to sphere of size {1}", LogHeader, _size); + _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE; + // Bullet native objects are scaled by the Bullet engine so pass the size in + _scale = _size; + } + } + else + { + m_log.DebugFormat("{0}: CreateGeom: mesh null. Defaulting to box of size {1}", LogHeader, _size); + _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX; + _scale = _size; + } + } + else + { + if (_hullKey != 0) + { + m_log.DebugFormat("{0}: CreateGeom: deleting old hull. Key={1}", LogHeader, _hullKey); + BulletSimAPI.DestroyHull(_scene.WorldID, _hullKey); + _hullKey = 0; + _hulls.Clear(); + } + + int[] indices = _mesh.getIndexListAsInt(); + List vertices = _mesh.getVertexList(); + + //format conversion from IMesh format to DecompDesc format + List convIndices = new List(); + List convVertices = new List(); + for (int ii = 0; ii < indices.GetLength(0); ii++) + { + convIndices.Add(indices[ii]); + } + foreach (OMV.Vector3 vv in vertices) + { + convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); + } + + // setup and do convex hull conversion + _hulls = new List(); + DecompDesc dcomp = new DecompDesc(); + dcomp.mIndices = convIndices; + dcomp.mVertices = convVertices; + ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); + // create the hull into the _hulls variable + convexBuilder.process(dcomp); + + // Convert the vertices and indices for passing to unmanaged + // The hull information is passed as a large floating point array. + // The format is: + // convHulls[0] = number of hulls + // convHulls[1] = number of vertices in first hull + // convHulls[2] = hull centroid X coordinate + // convHulls[3] = hull centroid Y coordinate + // convHulls[4] = hull centroid Z coordinate + // convHulls[5] = first hull vertex X + // convHulls[6] = first hull vertex Y + // convHulls[7] = first hull vertex Z + // convHulls[8] = second hull vertex X + // ... + // convHulls[n] = number of vertices in second hull + // convHulls[n+1] = second hull centroid X coordinate + // ... + // + // TODO: is is very inefficient. Someday change the convex hull generator to return + // data structures that do not need to be converted in order to pass to Bullet. + // And maybe put the values directly into pinned memory rather than marshaling. + int hullCount = _hulls.Count; + int totalVertices = 1; // include one for the count of the hulls + foreach (ConvexResult cr in _hulls) + { + totalVertices += 4; // add four for the vertex count and centroid + totalVertices += cr.HullIndices.Count * 3; // we pass just triangles + } + float[] convHulls = new float[totalVertices]; + + convHulls[0] = (float)hullCount; + int jj = 1; + foreach (ConvexResult cr in _hulls) + { + // copy vertices for index access + float3[] verts = new float3[cr.HullVertices.Count]; + int kk = 0; + foreach (float3 ff in cr.HullVertices) + { + verts[kk++] = ff; + } + + // add to the array one hull's worth of data + convHulls[jj++] = cr.HullIndices.Count; + convHulls[jj++] = 0f; // centroid x,y,z + convHulls[jj++] = 0f; + convHulls[jj++] = 0f; + foreach (int ind in cr.HullIndices) + { + convHulls[jj++] = verts[ind].x; + convHulls[jj++] = verts[ind].y; + convHulls[jj++] = verts[ind].z; + } + } + + // create the hull definition in Bullet + _hullKey = (ulong)_pbs.GetHashCode(); + // m_log.DebugFormat("{0}: CreateGeom: calling CreateHull. key={1}, hulls={2}", LogHeader, _hullKey, hullCount); + BulletSimAPI.CreateHull(_scene.WorldID, _hullKey, hullCount, convHulls); + _shapeType = ShapeData.PhysicsShapeType.SHAPE_HULL; + // meshes are already scaled by the meshmerizer + _scale = new OMV.Vector3(1f, 1f, 1f); + } + return; + } + + private void HullReturn(ConvexResult result) + { + _hulls.Add(result); + return; + } + + // Create an object in Bullet + // No locking here because this is done when the physics engine is not simulating + private void CreateObject() + { + if (IsRootOfLinkset) + { + // Create a linkset around this object + // If I am the root prim of a linkset, replace my physical shape with all the + // pieces of the children. + // All of the children should have called CreateGeom so they have a hull + // in the physics engine already. Here we pull together all of those hulls + // into one shape. + m_log.DebugFormat("{0}: CreateLinkset", LogHeader); + int totalPrimsInLinkset = _childrenPrims.Count + 1; + ShapeData[] shapes = new ShapeData[totalPrimsInLinkset]; + FillShapeInfo(out shapes[0]); + int ii = 1; + foreach (BSPrim prim in _childrenPrims) + { + m_log.DebugFormat("{0}: CreateLinkset: adding prim {1}", LogHeader, prim.LocalID); + prim.FillShapeInfo(out shapes[ii]); + ii++; + } + BulletSimAPI.CreateLinkset(_scene.WorldID, totalPrimsInLinkset, shapes); + } + else + { + // simple object + ShapeData shape; + FillShapeInfo(out shape); + BulletSimAPI.CreateObject(_scene.WorldID, shape); + } + } + + // Copy prim's info into the BulletSim shape description structure + public void FillShapeInfo(out ShapeData shape) + { + shape.ID = _localID; + shape.Type = _shapeType; + shape.Position = _position; + shape.Rotation = _orientation; + shape.Velocity = _velocity; + shape.Scale = _scale; + shape.Mass = _isPhysical ? _mass : 0f; + shape.MeshKey = _hullKey; + shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse; + shape.Flying = _flying ? ShapeData.numericTrue : ShapeData.numericFalse; + shape.Friction = _friction; + shape.Dynamic = _isPhysical ? ShapeData.numericTrue : ShapeData.numericFalse; + } + + // Rebuild the geometry and object. + // This is called when the shape changes so we need to recreate the mesh/hull. + // No locking here because this is done when the physics engine is not simulating + private void RecreateGeomAndObject() + { + if (_hullKey != 0) + { + // if a hull already exists, delete the old one + BulletSimAPI.DestroyHull(_scene.WorldID, _hullKey); + _hullKey = 0; + } + // If this object is complex or we are the root of a linkset, build a mesh. + // The root of a linkset must be a mesh so we can create the linked compound object. + if (_scene.NeedsMeshing(_pbs) || IsRootOfLinkset ) + { + m_log.DebugFormat("{0}: RecreateGeomAndObject: creating mesh", LogHeader); + _mesh = _scene.mesher.CreateMesh(_avName, _pbs, _size, _scene.meshLOD, _isPhysical); + } + else + { + // it's a BulletSim native shape. + _mesh = null; + } + CreateGeom(); // create the geometry for this prim + CreateObject(); + return; + } + + // The physics engine says that properties have updated. Update same and inform + // the world that things have changed. + // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate() + public void UpdateProperties(EntityProperties entprop) + { + bool changed = false; + // we assign to the local variables so the normal set action does not happen + if (_position != entprop.Position) + { + _position = entprop.Position; + // m_log.DebugFormat("{0}: UpdateProperties: position = {1}", LogHeader, _position); + changed = true; + } + if (_orientation != entprop.Rotation) + { + _orientation = entprop.Rotation; + // m_log.DebugFormat("{0}: UpdateProperties: rotation = {1}", LogHeader, _orientation); + changed = true; + } + if (_velocity != entprop.Velocity) + { + _velocity = entprop.Velocity; + // m_log.DebugFormat("{0}: UpdateProperties: velocity = {1}", LogHeader, _velocity); + changed = true; + } + if (_acceleration != entprop.Acceleration) + { + _acceleration = entprop.Acceleration; + // m_log.DebugFormat("{0}: UpdateProperties: acceleration = {1}", LogHeader, _acceleration); + changed = true; + } + if (_rotationalVelocity != entprop.AngularVelocity) + { + _rotationalVelocity = entprop.AngularVelocity; + // m_log.DebugFormat("{0}: UpdateProperties: rotationalVelocity = {1}", LogHeader, _rotationalVelocity); + changed = true; + } + if (changed) + { + base.RequestPhysicsterseUpdate(); + } + } + + // I've collided with something + public void Collide(uint collidingWith, ActorTypes type, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) + { + // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith); + if (_subscribedEventsMs == 0) return; // nothing in the object is waiting for collision events + + // The following makes it so we can sense we're colliding this simulation step + _collidingStep = _scene.SimulationStep; + if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID) + { + _collidingGroundStep = _scene.SimulationStep; + } + + // create the event for the collision + Dictionary contactPoints = new Dictionary(); + contactPoints.Add(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth)); + CollisionEventUpdate args = new CollisionEventUpdate(LocalID, (int)type, 1, contactPoints); + base.SendCollisionUpdate(args); + } +} +} diff --git a/addon-modules/BulletSPlugin/BSScene.cs b/addon-modules/BulletSPlugin/BSScene.cs new file mode 100644 index 0000000000..de7a9ce754 --- /dev/null +++ b/addon-modules/BulletSPlugin/BSScene.cs @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2011 Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using Nini.Config; +using log4net; +using OpenSim.Framework; +using OpenSim.Region.Physics.Manager; +using OpenMetaverse; +using OpenSim.Region.Framework; + +// TODOs for BulletSim (both BSScene and BSPrim) +// Does NeedsMeshing() really need to exclude all the different shapes? +// Based on material, set density and friction +// More efficient memory usage in passing hull information from BSPrim to BulletSim +// Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly? +// In SL one can set both physical and phantom (gravity, does not effect others, makes collisions) +// Need three states for objects: sense and report collisions, have physical effects, affects other objects +// LinkSets +// Freeing of memory of linksets in BulletSim::DestroyObject +// Should prim.link() and prim.delink() be done at taint time? +// Pass collision enable flags to BulletSim code so collisions are not reported up unless they are really needed +// Set child prims phantom since the physicality is handled by the parent prim +// Linked children need rotation relative to parent (passed as world rotation) +// +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSScene : PhysicsScene +{ + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[BULLETS SCENE]"; + + private Dictionary m_avatars = new Dictionary(); + private Dictionary m_prims = new Dictionary(); + private float[] m_heightMap; + private uint m_worldID; + public uint WorldID { get { return m_worldID; } } + + public IMesher mesher; + public int meshLOD = 32; + + private int m_maxSubSteps = 10; + private float m_fixedTimeStep = 1f / 60f; + private long m_simulationStep = 0; + public long SimulationStep { get { return m_simulationStep; } } + + private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed + private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes + public float maximumMassObject = 10000.01f; + + public const uint TERRAIN_ID = 0; + public const uint GROUNDPLANE_ID = 1; + + public float DefaultFriction = 0.70f; + public float DefaultDensity = 10.000006836f; // Aluminum g/cm3; TODO: compute based on object material + + public delegate void TaintCallback(); + private List _taintedObjects; + + private BulletSimAPI.DebugLogCallback debugLogCallbackHandle; + + public BSScene(string identifier) + { + } + + public override void Initialise(IMesher meshmerizer, IConfigSource config) + { + if (config != null) + { + IConfig pConfig = config.Configs["BulletSim"]; + if (pConfig != null) + { + DefaultFriction = pConfig.GetFloat("Friction", DefaultFriction); + DefaultDensity = pConfig.GetFloat("Density", DefaultDensity); + } + } + // if Debug, enable logging from the unmanaged code + if (m_log.IsDebugEnabled) + { + m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); + debugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); + BulletSimAPI.SetDebugLogCallback(debugLogCallbackHandle); + } + + _meshSculptedPrim = true; // mesh sculpted prims + _forceSimplePrimMeshing = false; // use complex meshing if called for + + _taintedObjects = new List(); + + mesher = meshmerizer; + // m_log.DebugFormat("{0}: Initialize: Calling BulletSimAPI.Initialize.", LogHeader); + m_worldID = BulletSimAPI.Initialize(new Vector3(Constants.RegionSize, Constants.RegionSize, 1000f)); + } + + private void BulletLogger(string msg) + { + m_log.Debug("[BULLETS UNMANAGED]:" + msg); + } + + public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying) + { + m_log.ErrorFormat("{0}: CALL TO AddAvatar in BSScene. NOT IMPLEMENTED", LogHeader); + return null; + } + + public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying) + { + // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName); + BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); + lock (m_avatars) m_avatars.Add(localID, actor); + return actor; + } + + public override void RemoveAvatar(PhysicsActor actor) + { + // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader); + if (actor is BSCharacter) + { + ((BSCharacter)actor).Destroy(); + } + try + { + lock (m_avatars) m_avatars.Remove(actor.LocalID); + } + catch (Exception e) + { + m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e); + } + } + + public override void RemovePrim(PhysicsActor prim) + { + // m_log.DebugFormat("{0}: RemovePrim", LogHeader); + if (prim is BSPrim) + { + ((BSPrim)prim).Destroy(); + } + try + { + lock (m_prims) m_prims.Remove(prim.LocalID); + } + catch (Exception e) + { + m_log.WarnFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e); + } + } + + public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, + Vector3 size, Quaternion rotation) // deprecated + { + return null; + } + public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position, + Vector3 size, Quaternion rotation, bool isPhysical) + { + m_log.ErrorFormat("{0}: CALL TO AddPrimShape in BSScene. NOT IMPLEMENTED", LogHeader); + return null; + } + + public override PhysicsActor AddPrimShape(uint localID, string primName, PrimitiveBaseShape pbs, Vector3 position, + Vector3 size, Quaternion rotation, bool isPhysical) + { + // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName); + IMesh mesh = null; + if (NeedsMeshing(pbs)) + { + // if the prim is complex, create the mesh for it. + // If simple (box or sphere) leave 'mesh' null and physics will do a native shape. + // m_log.DebugFormat("{0}: AddPrimShape2: creating mesh", LogHeader); + mesh = mesher.CreateMesh(primName, pbs, size, this.meshLOD, isPhysical); + } + BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, mesh, pbs, isPhysical); + lock (m_prims) m_prims.Add(localID, prim); + return prim; + } + + public override void AddPhysicsActorTaint(PhysicsActor prim) { } + + public override float Simulate(float timeStep) + { + int updatedEntityCount; + IntPtr updatedEntitiesPtr; + IntPtr[] updatedEntities; + int collidersCount; + IntPtr collidersPtr; + int[] colliders; // should be uint but Marshal.Copy does not have that overload + + // update the prim states while we know the physics engine is not busy + ProcessTaints(); + + // step the physical world one interval + m_simulationStep++; + int numSubSteps = BulletSimAPI.PhysicsStep(m_worldID, timeStep, m_maxSubSteps, m_fixedTimeStep, + out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); + + // if there were collisions, they show up here + if (collidersCount > 0) + { + colliders = new int[collidersCount]; + Marshal.Copy(collidersPtr, colliders, 0, collidersCount); + for (int ii = 0; ii < collidersCount; ii+=2) + { + uint cA = (uint)colliders[ii]; + uint cB = (uint)colliders[ii+1]; + SendCollision(cA, cB); + SendCollision(cB, cA); + } + } + + // if any of the objects had updated properties, they are returned in the updatedEntity structure + if (updatedEntityCount > 0) + { + updatedEntities = new IntPtr[updatedEntityCount]; + Marshal.Copy(updatedEntitiesPtr, updatedEntities, 0, updatedEntityCount); + for (int ii = 0; ii < updatedEntityCount; ii++) + { + IntPtr updatePointer = updatedEntities[ii]; + EntityProperties entprop = (EntityProperties)Marshal.PtrToStructure(updatePointer, typeof(EntityProperties)); + // m_log.DebugFormat("{0}: entprop: id={1}, pos={2}", LogHeader, entprop.ID, entprop.Position); + if (m_avatars.ContainsKey(entprop.ID)) + { + BSCharacter actor = m_avatars[entprop.ID]; + actor.UpdateProperties(entprop); + } + if (m_prims.ContainsKey(entprop.ID)) + { + BSPrim prim = m_prims[entprop.ID]; + prim.UpdateProperties(entprop); + } + } + } + + return 11f; // returns frames per second + } + + // Something has collided + private void SendCollision(uint localID, uint collidingWith) + { + if (localID == TERRAIN_ID || localID == GROUNDPLANE_ID) + { + // we never send collisions to the terrain + return; + } + + ActorTypes type = ActorTypes.Prim; + if (m_avatars.ContainsKey(collidingWith)) + type = ActorTypes.Agent; + else if (collidingWith == TERRAIN_ID || collidingWith == GROUNDPLANE_ID) + type = ActorTypes.Ground; + + if (m_prims.ContainsKey(localID)) + { + BSPrim prim = m_prims[localID]; + prim.Collide(collidingWith, type, Vector3.Zero, Vector3.UnitZ, 0.01f); + return; + } + if (m_avatars.ContainsKey(localID)) + { + BSCharacter actor = m_avatars[localID]; + actor.Collide(collidingWith, type, Vector3.Zero, Vector3.UnitZ, 0.01f); + return; + } + return; + } + + public override void GetResults() { } + + public override void SetTerrain(float[] heightMap) { + m_log.DebugFormat("{0}: SetTerrain", LogHeader); + m_heightMap = heightMap; + this.TaintedObject(delegate() + { + BulletSimAPI.SetHeightmap(m_worldID, m_heightMap); + }); + } + + public override void SetWaterLevel(float baseheight) { } + + public override void DeleteTerrain() + { + m_log.DebugFormat("{0}: DeleteTerrain()", LogHeader); + } + + public override void Dispose() + { + m_log.DebugFormat("{0}: Dispose()", LogHeader); + } + + public override Dictionary GetTopColliders() + { + return new Dictionary(); + } + + public override bool IsThreaded { get { return false; } } + + /// + /// Routine to figure out if we need to mesh this prim with our mesher + /// + /// + /// true if the prim needs meshing + public bool NeedsMeshing(PrimitiveBaseShape pbs) + { + // most of this is redundant now as the mesher will return null if it cant mesh a prim + // but we still need to check for sculptie meshing being enabled so this is the most + // convenient place to do it for now... + + // int iPropertiesNotSupportedDefault = 0; + + if (pbs.SculptEntry && !_meshSculptedPrim) + { + // m_log.DebugFormat("{0}: NeedsMeshing: scultpy mesh", LogHeader); + return false; + } + + // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet + // can use an internal representation for the prim + if (!_forceSimplePrimMeshing) + { + // m_log.DebugFormat("{0}: NeedsMeshing: simple mesh: profshape={1}, curve={2}", LogHeader, pbs.ProfileShape, pbs.PathCurve); + if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) + || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 + && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)) + { + + if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 + && pbs.ProfileHollow == 0 + && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0 + && pbs.PathBegin == 0 && pbs.PathEnd == 0 + && pbs.PathTaperX == 0 && pbs.PathTaperY == 0 + && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 + && pbs.PathShearX == 0 && pbs.PathShearY == 0) + { + return false; + } + } + } + + /* TODO: verify that the mesher will now do all these shapes + if (pbs.ProfileHollow != 0) + iPropertiesNotSupportedDefault++; + + if ((pbs.PathBegin != 0) || pbs.PathEnd != 0) + iPropertiesNotSupportedDefault++; + + if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0)) + iPropertiesNotSupportedDefault++; + + if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0) + iPropertiesNotSupportedDefault++; + + if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100)) + iPropertiesNotSupportedDefault++; + + if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0)) + iPropertiesNotSupportedDefault++; + + if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight) + iPropertiesNotSupportedDefault++; + + 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)) + iPropertiesNotSupportedDefault++; + + if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1) + iPropertiesNotSupportedDefault++; + + // test for torus + if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square) + { + if (pbs.PathCurve == (byte)Extrusion.Curve1) + { + iPropertiesNotSupportedDefault++; + } + } + else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) + { + if (pbs.PathCurve == (byte)Extrusion.Straight) + { + iPropertiesNotSupportedDefault++; + } + // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits + else if (pbs.PathCurve == (byte)Extrusion.Curve1) + { + iPropertiesNotSupportedDefault++; + } + } + else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) + { + if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2) + { + iPropertiesNotSupportedDefault++; + } + } + else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) + { + if (pbs.PathCurve == (byte)Extrusion.Straight) + { + iPropertiesNotSupportedDefault++; + } + else if (pbs.PathCurve == (byte)Extrusion.Curve1) + { + iPropertiesNotSupportedDefault++; + } + } + if (iPropertiesNotSupportedDefault == 0) + { + return false; + } + */ + return true; + } + + // The calls to the PhysicsActors can't directly call into the physics engine + // because it might be busy. We we delay changes to a known time. + // We rely on C#'s closure to save and restore the context for the delegate. + public void TaintedObject(TaintCallback callback) + { + // do we need to lock? + _taintedObjects.Add(callback); + return; + } + + // When someone tries to change a property on a BSPrim or BSCharacter, the object queues + // a callback into itself to do the actual property change. That callback is called + // here just before the physics engine is called to step the simulation. + public void ProcessTaints() + { + // swizzle a new list into the list location so we can process what's there + List newList = new List(); + List oldList = (List)Interlocked.Exchange(ref _taintedObjects, newList); + + foreach (TaintCallback callback in oldList) + { + try + { + callback(); + } + catch (Exception e) + { + m_log.ErrorFormat("{0}: ProcessTaints: Exception: {1}", LogHeader, e); + } + } + oldList.Clear(); + } +} +} diff --git a/addon-modules/BulletSPlugin/BulletSimAPI.cs b/addon-modules/BulletSPlugin/BulletSimAPI.cs new file mode 100644 index 0000000000..5487e8e07c --- /dev/null +++ b/addon-modules/BulletSPlugin/BulletSimAPI.cs @@ -0,0 +1,171 @@ +/* +Copyright (c) 2011, Intel Corporation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors may be + used to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using System; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin { + +public struct ConvexHull +{ + Vector3 Offset; + int VertexCount; + Vector3[] Vertices; +} +public struct ShapeData +{ + public enum PhysicsShapeType + { + SHAPE_AVATAR = 0, + SHAPE_BOX = 1, + SHAPE_CONE = 2, + SHAPE_CYLINDER = 3, + SHAPE_SPHERE = 4, + SHAPE_HULL = 5 + }; + public const int numericTrue = 1; + public const int numericFalse = 0; + public uint ID; + public PhysicsShapeType Type; + public Vector3 Position; + public Quaternion Rotation; + public Vector3 Velocity; + public Vector3 Scale; + public float Mass; + public System.UInt64 MeshKey; + public int Collidable; + public int Flying; + public float Friction; + public int Dynamic; + // note that bools are passed as ints since bool size changes by language +} +public struct SweepHit +{ + public uint ID; + public float Fraction; + public Vector3 Normal; + public Vector3 Point; +} +public struct RaycastHit +{ + public uint ID; + public float Fraction; + public Vector3 Normal; +} + +public struct EntityProperties +{ + public uint ID; + public Vector3 Position; + public Quaternion Rotation; + public Vector3 Velocity; + public Vector3 Acceleration; + public Vector3 AngularVelocity; +} + +static class BulletSimAPI { + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern uint Initialize(Vector3 maxPosition); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetHeightmap(uint worldID, [MarshalAs(UnmanagedType.LPArray)] float[] heightMap); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void Shutdown(uint worldID); + + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern int PhysicsStep(uint worldID, float timeStep, int maxSubSteps, float fixedTimeStep, + out int updatedEntityCount, + out IntPtr updatedEntitiesPtr, + out int collidersCount, + out IntPtr collidersPtr); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool CreateHull(uint worldID, System.UInt64 meshKey, int hullCount, + [MarshalAs(UnmanagedType.LPArray)] float[] hulls + ); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool DestroyHull(uint worldID, System.UInt64 meshKey); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool CreateObject(uint worldID, ShapeData shapeData); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void CreateLinkset(uint worldID, int objectCount, ShapeData[] shapeDatas); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetObjectPosition(uint WorldID, uint id); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectTranslation(uint worldID, uint id, Vector3 position, Quaternion rotation); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectVelocity(uint worldID, uint id, Vector3 velocity); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectForce(uint worldID, uint id, Vector3 force); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectScaleMass(uint worldID, uint id, Vector3 scale, float mass, bool isDynamic); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectCollidable(uint worldID, uint id, bool phantom); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectDynamic(uint worldID, uint id, bool isDynamic, float mass); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectFlying(uint worldID, uint id, bool flying); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool HasObject(uint worldID, uint id); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool DestroyObject(uint worldID, uint id); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern SweepHit ConvexSweepTest(uint worldID, uint id, Vector3 to, float extraMargin); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern RaycastHit RayTest(uint worldID, uint id, Vector3 from, Vector3 to); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 RecoverFromPenetration(uint worldID, uint id); + +// Log a debug message +[UnmanagedFunctionPointer(CallingConvention.Cdecl)] +public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg); +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetDebugLogCallback(DebugLogCallback callback); +} +} diff --git a/addon-modules/BulletSPlugin/prebuild.xml b/addon-modules/BulletSPlugin/prebuild.xml new file mode 100644 index 0000000000..47ed1a1069 --- /dev/null +++ b/addon-modules/BulletSPlugin/prebuild.xml @@ -0,0 +1,32 @@ + + + + ../../bin/Physics/ + + + + + ../../bin/Physics/ + + + + ../../bin/ + + + + + + + + + + + + + + + + + + + From 105e5913f697213818783d2f54fd90a42e8d0b98 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 19 May 2011 12:58:03 -0700 Subject: [PATCH 4/7] Add the BulletSim.dll to the OpenSim binaries --- bin/BulletSim.dll | Bin 0 -> 638464 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 bin/BulletSim.dll diff --git a/bin/BulletSim.dll b/bin/BulletSim.dll new file mode 100755 index 0000000000000000000000000000000000000000..c7e1880c670248d5ba65c6759435a8575166ee9e GIT binary patch literal 638464 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%0A8+dW*v@ol&ZZ+c?LqY1Ye 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 Date: Thu, 19 May 2011 13:31:21 -0700 Subject: [PATCH 5/7] Added execute permission to runprebuild2010.bat --- runprebuild2010.bat | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 runprebuild2010.bat diff --git a/runprebuild2010.bat b/runprebuild2010.bat old mode 100644 new mode 100755 From 973d0fcdaae3f78b7b3cf9a71a937e110e881c1c Mon Sep 17 00:00:00 2001 From: Dan Lake Date: Thu, 19 May 2011 13:32:27 -0700 Subject: [PATCH 6/7] Prevent checking for border crossings for synced avatars. This causes border crossings to happen only on the client manager where the real client is connected and the result is synced to other actors. --- OpenSim/Region/Framework/Scenes/Scene.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 7184158a13..35d2a3df9e 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1751,7 +1751,8 @@ namespace OpenSim.Region.Framework.Scenes // Check that we have a physics actor or we're sitting on something if (sp.ParentID == 0 && sp.PhysicsActor != null || sp.ParentID != 0) { - sp.CheckForBorderCrossing(); + if(!sp.IsSyncedAvatar) + sp.CheckForBorderCrossing(); } } }); From 95ce0caa76382e22e602a447b47802bd4bd4f3fc Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 19 May 2011 13:36:01 -0700 Subject: [PATCH 7/7] Add ConvexDecompositionDotNet --- .../ConvexDecompositionDotNet/CTri.cs | 341 +++ .../ConvexDecompositionDotNet/Concavity.cs | 233 ++ .../ConvexBuilder.cs | 411 ++++ .../ConvexDecomposition.cs | 200 ++ .../ConvexDecompositionDotNet/ConvexResult.cs | 74 + .../ConvexDecompositionDotNet/HullClasses.cs | 171 ++ .../ConvexDecompositionDotNet/HullTriangle.cs | 99 + .../ConvexDecompositionDotNet/HullUtils.cs | 1868 +++++++++++++++++ .../ConvexDecompositionDotNet/LICENSE.txt | 28 + .../ConvexDecompositionDotNet/Plane.cs | 99 + .../ConvexDecompositionDotNet/PlaneTri.cs | 211 ++ .../ConvexDecompositionDotNet/Quaternion.cs | 209 ++ .../ConvexDecompositionDotNet/README.txt | 7 + .../ConvexDecompositionDotNet/SplitPlane.cs | 265 +++ .../ConvexDecompositionDotNet/VertexLookup.cs | 70 + .../ConvexDecompositionDotNet/float2.cs | 70 + .../ConvexDecompositionDotNet/float3.cs | 444 ++++ .../ConvexDecompositionDotNet/float3x3.cs | 195 ++ .../ConvexDecompositionDotNet/float4.cs | 170 ++ .../ConvexDecompositionDotNet/float4x4.cs | 284 +++ .../ConvexDecompositionDotNet/int3.cs | 128 ++ .../ConvexDecompositionDotNet/int4.cs | 66 + .../ConvexDecompositionDotNet/prebuild.xml | 24 + 23 files changed, 5667 insertions(+) create mode 100644 addon-modules/ConvexDecompositionDotNet/CTri.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/Concavity.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/ConvexBuilder.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/ConvexDecomposition.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/ConvexResult.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/HullClasses.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/HullTriangle.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/HullUtils.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/LICENSE.txt create mode 100644 addon-modules/ConvexDecompositionDotNet/Plane.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/PlaneTri.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/Quaternion.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/README.txt create mode 100644 addon-modules/ConvexDecompositionDotNet/SplitPlane.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/VertexLookup.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/float2.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/float3.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/float3x3.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/float4.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/float4x4.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/int3.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/int4.cs create mode 100644 addon-modules/ConvexDecompositionDotNet/prebuild.xml diff --git a/addon-modules/ConvexDecompositionDotNet/CTri.cs b/addon-modules/ConvexDecompositionDotNet/CTri.cs new file mode 100644 index 0000000000..4d84c44704 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/CTri.cs @@ -0,0 +1,341 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class Wpoint + { + public float3 mPoint; + public float mWeight; + + public Wpoint(float3 p, float w) + { + mPoint = p; + mWeight = w; + } + } + + public class CTri + { + private const int WSCALE = 4; + + public float3 mP1; + public float3 mP2; + public float3 mP3; + public float3 mNear1; + public float3 mNear2; + public float3 mNear3; + public float3 mNormal; + public float mPlaneD; + public float mConcavity; + public float mC1; + public float mC2; + public float mC3; + public int mI1; + public int mI2; + public int mI3; + public int mProcessed; // already been added... + + public CTri(float3 p1, float3 p2, float3 p3, int i1, int i2, int i3) + { + mProcessed = 0; + mI1 = i1; + mI2 = i2; + mI3 = i3; + + mP1 = new float3(p1); + mP2 = new float3(p2); + mP3 = new float3(p3); + + mNear1 = new float3(); + mNear2 = new float3(); + mNear3 = new float3(); + + mNormal = new float3(); + mPlaneD = mNormal.ComputePlane(mP1, mP2, mP3); + } + + public float Facing(CTri t) + { + return float3.dot(mNormal, t.mNormal); + } + + public bool clip(float3 start, ref float3 end) + { + float3 sect = new float3(); + bool hit = lineIntersectsTriangle(start, end, mP1, mP2, mP3, ref sect); + + if (hit) + end = sect; + return hit; + } + + public bool Concave(float3 p, ref float distance, ref float3 n) + { + n.NearestPointInTriangle(p, mP1, mP2, mP3); + distance = p.Distance(n); + return true; + } + + public void addTri(int[] indices, int i1, int i2, int i3, ref int tcount) + { + indices[tcount * 3 + 0] = i1; + indices[tcount * 3 + 1] = i2; + indices[tcount * 3 + 2] = i3; + tcount++; + } + + public float getVolume() + { + int[] indices = new int[8 * 3]; + + int tcount = 0; + + addTri(indices, 0, 1, 2, ref tcount); + addTri(indices, 3, 4, 5, ref tcount); + + addTri(indices, 0, 3, 4, ref tcount); + addTri(indices, 0, 4, 1, ref tcount); + + addTri(indices, 1, 4, 5, ref tcount); + addTri(indices, 1, 5, 2, ref tcount); + + addTri(indices, 0, 3, 5, ref tcount); + addTri(indices, 0, 5, 2, ref tcount); + + List vertices = new List { mP1, mP2, mP3, mNear1, mNear2, mNear3 }; + List indexList = new List(indices); + + float v = Concavity.computeMeshVolume(vertices, indexList); + return v; + } + + public float raySect(float3 p, float3 dir, ref float3 sect) + { + float4 plane = new float4(); + + plane.x = mNormal.x; + plane.y = mNormal.y; + plane.z = mNormal.z; + plane.w = mPlaneD; + + float3 dest = p + dir * 100000f; + + intersect(p, dest, ref sect, plane); + + return sect.Distance(p); // return the intersection distance + } + + public float planeDistance(float3 p) + { + float4 plane = new float4(); + + plane.x = mNormal.x; + plane.y = mNormal.y; + plane.z = mNormal.z; + plane.w = mPlaneD; + + return DistToPt(p, plane); + } + + public bool samePlane(CTri t) + { + const float THRESH = 0.001f; + float dd = Math.Abs(t.mPlaneD - mPlaneD); + if (dd > THRESH) + return false; + dd = Math.Abs(t.mNormal.x - mNormal.x); + if (dd > THRESH) + return false; + dd = Math.Abs(t.mNormal.y - mNormal.y); + if (dd > THRESH) + return false; + dd = Math.Abs(t.mNormal.z - mNormal.z); + if (dd > THRESH) + return false; + return true; + } + + public bool hasIndex(int i) + { + if (i == mI1 || i == mI2 || i == mI3) + return true; + return false; + } + + public bool sharesEdge(CTri t) + { + bool ret = false; + uint count = 0; + + if (t.hasIndex(mI1)) + count++; + if (t.hasIndex(mI2)) + count++; + if (t.hasIndex(mI3)) + count++; + + if (count >= 2) + ret = true; + + return ret; + } + + public float area() + { + float a = mConcavity * mP1.Area(mP2, mP3); + return a; + } + + public void addWeighted(List list) + { + Wpoint p1 = new Wpoint(mP1, mC1); + Wpoint p2 = new Wpoint(mP2, mC2); + Wpoint p3 = new Wpoint(mP3, mC3); + + float3 d1 = mNear1 - mP1; + float3 d2 = mNear2 - mP2; + float3 d3 = mNear3 - mP3; + + d1 *= WSCALE; + d2 *= WSCALE; + d3 *= WSCALE; + + d1 = d1 + mP1; + d2 = d2 + mP2; + d3 = d3 + mP3; + + Wpoint p4 = new Wpoint(d1, mC1); + Wpoint p5 = new Wpoint(d2, mC2); + Wpoint p6 = new Wpoint(d3, mC3); + + list.Add(p1); + list.Add(p2); + list.Add(p3); + + list.Add(p4); + list.Add(p5); + list.Add(p6); + } + + private static float DistToPt(float3 p, float4 plane) + { + float x = p.x; + float y = p.y; + float z = p.z; + float d = x*plane.x + y*plane.y + z*plane.z + plane.w; + return d; + } + + private static void intersect(float3 p1, float3 p2, ref float3 split, float4 plane) + { + float dp1 = DistToPt(p1, plane); + + float3 dir = new float3(); + dir.x = p2[0] - p1[0]; + dir.y = p2[1] - p1[1]; + dir.z = p2[2] - p1[2]; + + float dot1 = dir[0] * plane[0] + dir[1] * plane[1] + dir[2] * plane[2]; + float dot2 = dp1 - plane[3]; + + float t = -(plane[3] + dot2) / dot1; + + split.x = (dir[0] * t) + p1[0]; + split.y = (dir[1] * t) + p1[1]; + split.z = (dir[2] * t) + p1[2]; + } + + private static bool rayIntersectsTriangle(float3 p, float3 d, float3 v0, float3 v1, float3 v2, out float t) + { + t = 0f; + + float3 e1, e2, h, s, q; + float a, f, u, v; + + e1 = v1 - v0; + e2 = v2 - v0; + h = float3.cross(d, e2); + a = float3.dot(e1, h); + + if (a > -0.00001f && a < 0.00001f) + return false; + + f = 1f / a; + s = p - v0; + u = f * float3.dot(s, h); + + if (u < 0.0f || u > 1.0f) + return false; + + q = float3.cross(s, e1); + v = f * float3.dot(d, q); + if (v < 0.0f || u + v > 1.0f) + return false; + + // at this stage we can compute t to find out where + // the intersection point is on the line + t = f * float3.dot(e2, q); + if (t > 0f) // ray intersection + return true; + else // this means that there is a line intersection but not a ray intersection + return false; + } + + private static bool lineIntersectsTriangle(float3 rayStart, float3 rayEnd, float3 p1, float3 p2, float3 p3, ref float3 sect) + { + float3 dir = rayEnd - rayStart; + + float d = (float)Math.Sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]); + float r = 1.0f / d; + + dir *= r; + + float t; + bool ret = rayIntersectsTriangle(rayStart, dir, p1, p2, p3, out t); + + if (ret) + { + if (t > d) + { + sect.x = rayStart.x + dir.x * t; + sect.y = rayStart.y + dir.y * t; + sect.z = rayStart.z + dir.z * t; + } + else + { + ret = false; + } + } + + return ret; + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/Concavity.cs b/addon-modules/ConvexDecompositionDotNet/Concavity.cs new file mode 100644 index 0000000000..cc6383a9c6 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/Concavity.cs @@ -0,0 +1,233 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public static class Concavity + { + // compute's how 'concave' this object is and returns the total volume of the + // convex hull as well as the volume of the 'concavity' which was found. + public static float computeConcavity(List vertices, List indices, ref float4 plane, ref float volume) + { + float cret = 0f; + volume = 1f; + + HullResult result = new HullResult(); + HullDesc desc = new HullDesc(); + + desc.MaxFaces = 256; + desc.MaxVertices = 256; + desc.SetHullFlag(HullFlag.QF_TRIANGLES); + desc.Vertices = vertices; + + HullError ret = HullUtils.CreateConvexHull(desc, ref result); + + if (ret == HullError.QE_OK) + { + volume = computeMeshVolume2(result.OutputVertices, result.Indices); + + // ok..now..for each triangle on the original mesh.. + // we extrude the points to the nearest point on the hull. + List tris = new List(); + + for (int i = 0; i < result.Indices.Count / 3; i++) + { + int i1 = result.Indices[i * 3 + 0]; + int i2 = result.Indices[i * 3 + 1]; + int i3 = result.Indices[i * 3 + 2]; + + float3 p1 = result.OutputVertices[i1]; + float3 p2 = result.OutputVertices[i2]; + float3 p3 = result.OutputVertices[i3]; + + CTri t = new CTri(p1, p2, p3, i1, i2, i3); + tris.Add(t); + } + + // we have not pre-computed the plane equation for each triangle in the convex hull.. + float totalVolume = 0; + + List ftris = new List(); // 'feature' triangles. + List input_mesh = new List(); + + for (int i = 0; i < indices.Count / 3; i++) + { + int i1 = indices[i * 3 + 0]; + int i2 = indices[i * 3 + 1]; + int i3 = indices[i * 3 + 2]; + + float3 p1 = vertices[i1]; + float3 p2 = vertices[i2]; + float3 p3 = vertices[i3]; + + CTri t = new CTri(p1, p2, p3, i1, i2, i3); + input_mesh.Add(t); + } + + for (int i = 0; i < indices.Count / 3; i++) + { + int i1 = indices[i * 3 + 0]; + int i2 = indices[i * 3 + 1]; + int i3 = indices[i * 3 + 2]; + + float3 p1 = vertices[i1]; + float3 p2 = vertices[i2]; + float3 p3 = vertices[i3]; + + CTri t = new CTri(p1, p2, p3, i1, i2, i3); + + featureMatch(t, tris, input_mesh); + + if (t.mConcavity > 0.05f) + { + float v = t.getVolume(); + totalVolume += v; + ftris.Add(t); + } + } + + SplitPlane.computeSplitPlane(vertices, indices, ref plane); + cret = totalVolume; + } + + return cret; + } + + public static bool featureMatch(CTri m, List tris, List input_mesh) + { + bool ret = false; + float neardot = 0.707f; + m.mConcavity = 0; + + for (int i = 0; i < tris.Count; i++) + { + CTri t = tris[i]; + + if (t.samePlane(m)) + { + ret = false; + break; + } + + float dot = float3.dot(t.mNormal, m.mNormal); + + if (dot > neardot) + { + float d1 = t.planeDistance(m.mP1); + float d2 = t.planeDistance(m.mP2); + float d3 = t.planeDistance(m.mP3); + + if (d1 > 0.001f || d2 > 0.001f || d3 > 0.001f) // can't be near coplaner! + { + neardot = dot; + + t.raySect(m.mP1, m.mNormal, ref m.mNear1); + t.raySect(m.mP2, m.mNormal, ref m.mNear2); + t.raySect(m.mP3, m.mNormal, ref m.mNear3); + + ret = true; + } + } + } + + if (ret) + { + m.mC1 = m.mP1.Distance(m.mNear1); + m.mC2 = m.mP2.Distance(m.mNear2); + m.mC3 = m.mP3.Distance(m.mNear3); + + m.mConcavity = m.mC1; + + if (m.mC2 > m.mConcavity) + m.mConcavity = m.mC2; + if (m.mC3 > m.mConcavity) + m.mConcavity = m.mC3; + } + + return ret; + } + + private static float det(float3 p1, float3 p2, float3 p3) + { + return p1.x * p2.y * p3.z + p2.x * p3.y * p1.z + p3.x * p1.y * p2.z - p1.x * p3.y * p2.z - p2.x * p1.y * p3.z - p3.x * p2.y * p1.z; + } + + public static float computeMeshVolume(List vertices, List indices) + { + float volume = 0f; + + for (int i = 0; i < indices.Count / 3; i++) + { + float3 p1 = vertices[indices[i * 3 + 0]]; + float3 p2 = vertices[indices[i * 3 + 1]]; + float3 p3 = vertices[indices[i * 3 + 2]]; + + volume += det(p1, p2, p3); // compute the volume of the tetrahedran relative to the origin. + } + + volume *= (1.0f / 6.0f); + if (volume < 0f) + return -volume; + return volume; + } + + public static float computeMeshVolume2(List vertices, List indices) + { + float volume = 0f; + + float3 p0 = vertices[0]; + for (int i = 0; i < indices.Count / 3; i++) + { + float3 p1 = vertices[indices[i * 3 + 0]]; + float3 p2 = vertices[indices[i * 3 + 1]]; + float3 p3 = vertices[indices[i * 3 + 2]]; + + volume += tetVolume(p0, p1, p2, p3); // compute the volume of the tetrahedron relative to the root vertice + } + + return volume * (1.0f / 6.0f); + } + + private static float tetVolume(float3 p0, float3 p1, float3 p2, float3 p3) + { + float3 a = p1 - p0; + float3 b = p2 - p0; + float3 c = p3 - p0; + + float3 cross = float3.cross(b, c); + float volume = float3.dot(a, cross); + + if (volume < 0f) + return -volume; + return volume; + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/ConvexBuilder.cs b/addon-modules/ConvexDecompositionDotNet/ConvexBuilder.cs new file mode 100644 index 0000000000..dfaede1176 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/ConvexBuilder.cs @@ -0,0 +1,411 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class DecompDesc + { + public List mVertices; + public List mIndices; + + // options + public uint mDepth; // depth to split, a maximum of 10, generally not over 7. + public float mCpercent; // the concavity threshold percentage. 0=20 is reasonable. + public float mPpercent; // the percentage volume conservation threshold to collapse hulls. 0-30 is reasonable. + + // hull output limits. + public uint mMaxVertices; // maximum number of vertices in the output hull. Recommended 32 or less. + public float mSkinWidth; // a skin width to apply to the output hulls. + + public ConvexDecompositionCallback mCallback; // the interface to receive back the results. + + public DecompDesc() + { + mDepth = 5; + mCpercent = 5; + mPpercent = 5; + mMaxVertices = 32; + } + } + + public class CHull + { + public float[] mMin = new float[3]; + public float[] mMax = new float[3]; + public float mVolume; + public float mDiagonal; + public ConvexResult mResult; + + public CHull(ConvexResult result) + { + mResult = new ConvexResult(result); + mVolume = Concavity.computeMeshVolume(result.HullVertices, result.HullIndices); + + mDiagonal = getBoundingRegion(result.HullVertices, mMin, mMax); + + float dx = mMax[0] - mMin[0]; + float dy = mMax[1] - mMin[1]; + float dz = mMax[2] - mMin[2]; + + dx *= 0.1f; // inflate 1/10th on each edge + dy *= 0.1f; // inflate 1/10th on each edge + dz *= 0.1f; // inflate 1/10th on each edge + + mMin[0] -= dx; + mMin[1] -= dy; + mMin[2] -= dz; + + mMax[0] += dx; + mMax[1] += dy; + mMax[2] += dz; + } + + public void Dispose() + { + mResult = null; + } + + public bool overlap(CHull h) + { + return overlapAABB(mMin, mMax, h.mMin, h.mMax); + } + + // returns the d1Giagonal distance + private static float getBoundingRegion(List points, float[] bmin, float[] bmax) + { + float3 first = points[0]; + + bmin[0] = first.x; + bmin[1] = first.y; + bmin[2] = first.z; + + bmax[0] = first.x; + bmax[1] = first.y; + bmax[2] = first.z; + + for (int i = 1; i < points.Count; i++) + { + float3 p = points[i]; + + if (p[0] < bmin[0]) bmin[0] = p[0]; + if (p[1] < bmin[1]) bmin[1] = p[1]; + if (p[2] < bmin[2]) bmin[2] = p[2]; + + if (p[0] > bmax[0]) bmax[0] = p[0]; + if (p[1] > bmax[1]) bmax[1] = p[1]; + if (p[2] > bmax[2]) bmax[2] = p[2]; + } + + float dx = bmax[0] - bmin[0]; + float dy = bmax[1] - bmin[1]; + float dz = bmax[2] - bmin[2]; + + return (float)Math.Sqrt(dx * dx + dy * dy + dz * dz); + } + + // return true if the two AABB's overlap. + private static bool overlapAABB(float[] bmin1, float[] bmax1, float[] bmin2, float[] bmax2) + { + if (bmax2[0] < bmin1[0]) return false; // if the maximum is less than our minimum on any axis + if (bmax2[1] < bmin1[1]) return false; + if (bmax2[2] < bmin1[2]) return false; + + if (bmin2[0] > bmax1[0]) return false; // if the minimum is greater than our maximum on any axis + if (bmin2[1] > bmax1[1]) return false; // if the minimum is greater than our maximum on any axis + if (bmin2[2] > bmax1[2]) return false; // if the minimum is greater than our maximum on any axis + + return true; // the extents overlap + } + } + + public class ConvexBuilder + { + public List mChulls = new List(); + private ConvexDecompositionCallback mCallback; + + private int MAXDEPTH = 8; + private float CONCAVE_PERCENT = 1f; + private float MERGE_PERCENT = 2f; + + public ConvexBuilder(ConvexDecompositionCallback callback) + { + mCallback = callback; + } + + public void Dispose() + { + int i; + for (i = 0; i < mChulls.Count; i++) + { + CHull cr = mChulls[i]; + cr.Dispose(); + } + } + + public bool isDuplicate(uint i1, uint i2, uint i3, uint ci1, uint ci2, uint ci3) + { + uint dcount = 0; + + Debug.Assert(i1 != i2 && i1 != i3 && i2 != i3); + Debug.Assert(ci1 != ci2 && ci1 != ci3 && ci2 != ci3); + + if (i1 == ci1 || i1 == ci2 || i1 == ci3) + dcount++; + if (i2 == ci1 || i2 == ci2 || i2 == ci3) + dcount++; + if (i3 == ci1 || i3 == ci2 || i3 == ci3) + dcount++; + + return dcount == 3; + } + + public void getMesh(ConvexResult cr, VertexPool vc, List indices) + { + List src = cr.HullIndices; + + for (int i = 0; i < src.Count / 3; i++) + { + int i1 = src[i * 3 + 0]; + int i2 = src[i * 3 + 1]; + int i3 = src[i * 3 + 2]; + + float3 p1 = cr.HullVertices[i1]; + float3 p2 = cr.HullVertices[i2]; + float3 p3 = cr.HullVertices[i3]; + + i1 = vc.getIndex(p1); + i2 = vc.getIndex(p2); + i3 = vc.getIndex(p3); + } + } + + public CHull canMerge(CHull a, CHull b) + { + if (!a.overlap(b)) // if their AABB's (with a little slop) don't overlap, then return. + return null; + + CHull ret = null; + + // ok..we are going to combine both meshes into a single mesh + // and then we are going to compute the concavity... + + VertexPool vc = new VertexPool(); + + List indices = new List(); + + getMesh(a.mResult, vc, indices); + getMesh(b.mResult, vc, indices); + + int vcount = vc.GetSize(); + List vertices = vc.GetVertices(); + int tcount = indices.Count / 3; + + //don't do anything if hull is empty + if (tcount == 0) + { + vc.Clear(); + return null; + } + + HullResult hresult = new HullResult(); + HullDesc desc = new HullDesc(); + + desc.SetHullFlag(HullFlag.QF_TRIANGLES); + desc.Vertices = vertices; + + HullError hret = HullUtils.CreateConvexHull(desc, ref hresult); + + if (hret == HullError.QE_OK) + { + float combineVolume = Concavity.computeMeshVolume(hresult.OutputVertices, hresult.Indices); + float sumVolume = a.mVolume + b.mVolume; + + float percent = (sumVolume * 100) / combineVolume; + if (percent >= (100.0f - MERGE_PERCENT)) + { + ConvexResult cr = new ConvexResult(hresult.OutputVertices, hresult.Indices); + ret = new CHull(cr); + } + } + + vc.Clear(); + return ret; + } + + public bool combineHulls() + { + bool combine = false; + + sortChulls(mChulls); // sort the convex hulls, largest volume to least... + + List output = new List(); // the output hulls... + + int i; + for (i = 0; i < mChulls.Count && !combine; ++i) + { + CHull cr = mChulls[i]; + + int j; + for (j = 0; j < mChulls.Count; j++) + { + CHull match = mChulls[j]; + + if (cr != match) // don't try to merge a hull with itself, that be stoopid + { + + CHull merge = canMerge(cr, match); // if we can merge these two.... + + if (merge != null) + { + output.Add(merge); + + ++i; + while (i != mChulls.Count) + { + CHull cr2 = mChulls[i]; + if (cr2 != match) + { + output.Add(cr2); + } + i++; + } + + cr.Dispose(); + match.Dispose(); + combine = true; + break; + } + } + } + + if (combine) + { + break; + } + else + { + output.Add(cr); + } + } + + if (combine) + { + mChulls.Clear(); + mChulls = output; + output.Clear(); + } + + return combine; + } + + public int process(DecompDesc desc) + { + int ret = 0; + + MAXDEPTH = (int)desc.mDepth; + CONCAVE_PERCENT = desc.mCpercent; + MERGE_PERCENT = desc.mPpercent; + + ConvexDecomposition.calcConvexDecomposition(desc.mVertices, desc.mIndices, ConvexDecompResult, 0f, 0, MAXDEPTH, CONCAVE_PERCENT, MERGE_PERCENT); + + while (combineHulls()) // keep combinging hulls until I can't combine any more... + ; + + int i; + for (i = 0; i < mChulls.Count; i++) + { + CHull cr = mChulls[i]; + + // before we hand it back to the application, we need to regenerate the hull based on the + // limits given by the user. + + ConvexResult c = cr.mResult; // the high resolution hull... + + HullResult result = new HullResult(); + HullDesc hdesc = new HullDesc(); + + hdesc.SetHullFlag(HullFlag.QF_TRIANGLES); + + hdesc.Vertices = c.HullVertices; + hdesc.MaxVertices = desc.mMaxVertices; // maximum number of vertices allowed in the output + + if (desc.mSkinWidth != 0f) + { + hdesc.SkinWidth = desc.mSkinWidth; + hdesc.SetHullFlag(HullFlag.QF_SKIN_WIDTH); // do skin width computation. + } + + HullError ret2 = HullUtils.CreateConvexHull(hdesc, ref result); + + if (ret2 == HullError.QE_OK) + { + ConvexResult r = new ConvexResult(result.OutputVertices, result.Indices); + + r.mHullVolume = Concavity.computeMeshVolume(result.OutputVertices, result.Indices); // the volume of the hull. + + // compute the best fit OBB + //computeBestFitOBB(result.mNumOutputVertices, result.mOutputVertices, sizeof(float) * 3, r.mOBBSides, r.mOBBTransform); + + //r.mOBBVolume = r.mOBBSides[0] * r.mOBBSides[1] * r.mOBBSides[2]; // compute the OBB volume. + + //fm_getTranslation(r.mOBBTransform, r.mOBBCenter); // get the translation component of the 4x4 matrix. + + //fm_matrixToQuat(r.mOBBTransform, r.mOBBOrientation); // extract the orientation as a quaternion. + + //r.mSphereRadius = computeBoundingSphere(result.mNumOutputVertices, result.mOutputVertices, r.mSphereCenter); + //r.mSphereVolume = fm_sphereVolume(r.mSphereRadius); + + mCallback(r); + } + + result = null; + cr.Dispose(); + } + + ret = mChulls.Count; + + mChulls.Clear(); + + return ret; + } + + public void ConvexDecompResult(ConvexResult result) + { + CHull ch = new CHull(result); + mChulls.Add(ch); + } + + public void sortChulls(List hulls) + { + hulls.Sort(delegate(CHull a, CHull b) { return a.mVolume.CompareTo(b.mVolume); }); + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/ConvexDecomposition.cs b/addon-modules/ConvexDecompositionDotNet/ConvexDecomposition.cs new file mode 100644 index 0000000000..2e2bb70c7a --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/ConvexDecomposition.cs @@ -0,0 +1,200 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public delegate void ConvexDecompositionCallback(ConvexResult result); + + public class FaceTri + { + public float3 P1; + public float3 P2; + public float3 P3; + + public FaceTri() { } + + public FaceTri(List vertices, int i1, int i2, int i3) + { + P1 = new float3(vertices[i1]); + P2 = new float3(vertices[i2]); + P3 = new float3(vertices[i3]); + } + } + + public static class ConvexDecomposition + { + private static void addTri(VertexPool vl, List list, float3 p1, float3 p2, float3 p3) + { + int i1 = vl.getIndex(p1); + int i2 = vl.getIndex(p2); + int i3 = vl.getIndex(p3); + + // do *not* process degenerate triangles! + if ( i1 != i2 && i1 != i3 && i2 != i3 ) + { + list.Add(i1); + list.Add(i2); + list.Add(i3); + } + } + + public static void calcConvexDecomposition(List vertices, List indices, ConvexDecompositionCallback callback, float masterVolume, int depth, + int maxDepth, float concavePercent, float mergePercent) + { + float4 plane = new float4(); + bool split = false; + + if (depth < maxDepth) + { + float volume = 0f; + float c = Concavity.computeConcavity(vertices, indices, ref plane, ref volume); + + if (depth == 0) + { + masterVolume = volume; + } + + float percent = (c * 100.0f) / masterVolume; + + if (percent > concavePercent) // if great than 5% of the total volume is concave, go ahead and keep splitting. + { + split = true; + } + } + + if (depth >= maxDepth || !split) + { + HullResult result = new HullResult(); + HullDesc desc = new HullDesc(); + + desc.SetHullFlag(HullFlag.QF_TRIANGLES); + + desc.Vertices = vertices; + + HullError ret = HullUtils.CreateConvexHull(desc, ref result); + + if (ret == HullError.QE_OK) + { + ConvexResult r = new ConvexResult(result.OutputVertices, result.Indices); + callback(r); + } + + return; + } + + List ifront = new List(); + List iback = new List(); + + VertexPool vfront = new VertexPool(); + VertexPool vback = new VertexPool(); + + // ok..now we are going to 'split' all of the input triangles against this plane! + for (int i = 0; i < indices.Count / 3; i++) + { + int i1 = indices[i * 3 + 0]; + int i2 = indices[i * 3 + 1]; + int i3 = indices[i * 3 + 2]; + + FaceTri t = new FaceTri(vertices, i1, i2, i3); + + float3[] front = new float3[4]; + float3[] back = new float3[4]; + + int fcount = 0; + int bcount = 0; + + PlaneTriResult result = PlaneTri.planeTriIntersection(plane, t, 0.00001f, ref front, out fcount, ref back, out bcount); + + if (fcount > 4 || bcount > 4) + { + result = PlaneTri.planeTriIntersection(plane, t, 0.00001f, ref front, out fcount, ref back, out bcount); + } + + switch (result) + { + case PlaneTriResult.PTR_FRONT: + Debug.Assert(fcount == 3); + addTri(vfront, ifront, front[0], front[1], front[2]); + break; + case PlaneTriResult.PTR_BACK: + Debug.Assert(bcount == 3); + addTri(vback, iback, back[0], back[1], back[2]); + break; + case PlaneTriResult.PTR_SPLIT: + Debug.Assert(fcount >= 3 && fcount <= 4); + Debug.Assert(bcount >= 3 && bcount <= 4); + + addTri(vfront, ifront, front[0], front[1], front[2]); + addTri(vback, iback, back[0], back[1], back[2]); + + if (fcount == 4) + { + addTri(vfront, ifront, front[0], front[2], front[3]); + } + + if (bcount == 4) + { + addTri(vback, iback, back[0], back[2], back[3]); + } + + break; + } + } + + // ok... here we recursively call + if (ifront.Count > 0) + { + int vcount = vfront.GetSize(); + List vertices2 = vfront.GetVertices(); + for (int i = 0; i < vertices2.Count; i++) + vertices2[i] = new float3(vertices2[i]); + int tcount = ifront.Count / 3; + + calcConvexDecomposition(vertices2, ifront, callback, masterVolume, depth + 1, maxDepth, concavePercent, mergePercent); + } + + ifront.Clear(); + vfront.Clear(); + + if (iback.Count > 0) + { + int vcount = vback.GetSize(); + List vertices2 = vback.GetVertices(); + int tcount = iback.Count / 3; + + calcConvexDecomposition(vertices2, iback, callback, masterVolume, depth + 1, maxDepth, concavePercent, mergePercent); + } + + iback.Clear(); + vback.Clear(); + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/ConvexResult.cs b/addon-modules/ConvexDecompositionDotNet/ConvexResult.cs new file mode 100644 index 0000000000..87758b5971 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/ConvexResult.cs @@ -0,0 +1,74 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class ConvexResult + { + public List HullVertices; + public List HullIndices; + + public float mHullVolume; // the volume of the convex hull. + + //public float[] OBBSides = new float[3]; // the width, height and breadth of the best fit OBB + //public float[] OBBCenter = new float[3]; // the center of the OBB + //public float[] OBBOrientation = new float[4]; // the quaternion rotation of the OBB. + //public float[] OBBTransform = new float[16]; // the 4x4 transform of the OBB. + //public float OBBVolume; // the volume of the OBB + + //public float SphereRadius; // radius and center of best fit sphere + //public float[] SphereCenter = new float[3]; + //public float SphereVolume; // volume of the best fit sphere + + public ConvexResult() + { + HullVertices = new List(); + HullIndices = new List(); + } + + public ConvexResult(List hvertices, List hindices) + { + HullVertices = hvertices; + HullIndices = hindices; + } + + public ConvexResult(ConvexResult r) + { + HullVertices = new List(r.HullVertices); + HullIndices = new List(r.HullIndices); + } + + public void Dispose() + { + HullVertices = null; + HullIndices = null; + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/HullClasses.cs b/addon-modules/ConvexDecompositionDotNet/HullClasses.cs new file mode 100644 index 0000000000..d81df262ae --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/HullClasses.cs @@ -0,0 +1,171 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class HullResult + { + public bool Polygons = true; // true if indices represents polygons, false indices are triangles + public List OutputVertices = new List(); + public List Indices; + + // If triangles, then indices are array indexes into the vertex list. + // If polygons, indices are in the form (number of points in face) (p1, p2, p3, ..) etc.. + } + + public class PHullResult + { + public List Vertices = new List(); + public List Indices = new List(); + } + + [Flags] + public enum HullFlag : int + { + QF_DEFAULT = 0, + QF_TRIANGLES = (1 << 0), // report results as triangles, not polygons. + QF_SKIN_WIDTH = (1 << 2) // extrude hull based on this skin width + } + + public enum HullError : int + { + QE_OK, // success! + QE_FAIL // failed. + } + + public class HullDesc + { + public HullFlag Flags; // flags to use when generating the convex hull. + public List Vertices; + public float NormalEpsilon; // the epsilon for removing duplicates. This is a normalized value, if normalized bit is on. + public float SkinWidth; + public uint MaxVertices; // maximum number of vertices to be considered for the hull! + public uint MaxFaces; + + public HullDesc() + { + Flags = HullFlag.QF_DEFAULT; + Vertices = new List(); + NormalEpsilon = 0.001f; + MaxVertices = 4096; + MaxFaces = 4096; + SkinWidth = 0.01f; + } + + public HullDesc(HullFlag flags, List vertices) + { + Flags = flags; + Vertices = new List(vertices); + NormalEpsilon = 0.001f; + MaxVertices = 4096; + MaxFaces = 4096; + SkinWidth = 0.01f; + } + + public bool HasHullFlag(HullFlag flag) + { + return (Flags & flag) != 0; + } + + public void SetHullFlag(HullFlag flag) + { + Flags |= flag; + } + + public void ClearHullFlag(HullFlag flag) + { + Flags &= ~flag; + } + } + + public class ConvexH + { + public struct HalfEdge + { + public short ea; // the other half of the edge (index into edges list) + public byte v; // the vertex at the start of this edge (index into vertices list) + public byte p; // the facet on which this edge lies (index into facets list) + + public HalfEdge(short _ea, byte _v, byte _p) + { + ea = _ea; + v = _v; + p = _p; + } + + public HalfEdge(HalfEdge e) + { + ea = e.ea; + v = e.v; + p = e.p; + } + } + + public List vertices = new List(); + public List edges = new List(); + public List facets = new List(); + + public ConvexH(int vertices_size, int edges_size, int facets_size) + { + vertices = new List(vertices_size); + edges = new List(edges_size); + facets = new List(facets_size); + } + } + + public class VertFlag + { + public byte planetest; + public byte junk; + public byte undermap; + public byte overmap; + } + + public class EdgeFlag + { + public byte planetest; + public byte fixes; + public short undermap; + public short overmap; + } + + public class PlaneFlag + { + public byte undermap; + public byte overmap; + } + + public class Coplanar + { + public ushort ea; + public byte v0; + public byte v1; + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/HullTriangle.cs b/addon-modules/ConvexDecompositionDotNet/HullTriangle.cs new file mode 100644 index 0000000000..1119a75e91 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/HullTriangle.cs @@ -0,0 +1,99 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class HullTriangle : int3 + { + public int3 n = new int3(); + public int id; + public int vmax; + public float rise; + private List tris; + + public HullTriangle(int a, int b, int c, List tris) + : base(a, b, c) + { + this.tris = tris; + + n = new int3(-1, -1, -1); + id = tris.Count; + tris.Add(this); + vmax = -1; + rise = 0.0f; + } + + public void Dispose() + { + Debug.Assert(tris[id] == this); + tris[id] = null; + } + + public int neib(int a, int b) + { + int i; + + for (i = 0; i < 3; i++) + { + int i1 = (i + 1) % 3; + int i2 = (i + 2) % 3; + if ((this)[i] == a && (this)[i1] == b) + return n[i2]; + if ((this)[i] == b && (this)[i1] == a) + return n[i2]; + } + + Debug.Assert(false); + return -1; + } + + public void setneib(int a, int b, int value) + { + int i; + + for (i = 0; i < 3; i++) + { + int i1 = (i + 1) % 3; + int i2 = (i + 2) % 3; + if ((this)[i] == a && (this)[i1] == b) + { + n[i2] = value; + return; + } + if ((this)[i] == b && (this)[i1] == a) + { + n[i2] = value; + return; + } + } + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/HullUtils.cs b/addon-modules/ConvexDecompositionDotNet/HullUtils.cs new file mode 100644 index 0000000000..c9ccfe2c61 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/HullUtils.cs @@ -0,0 +1,1868 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public static class HullUtils + { + public static int argmin(float[] a, int n) + { + int r = 0; + for (int i = 1; i < n; i++) + { + if (a[i] < a[r]) + { + r = i; + } + } + return r; + } + + public static float clampf(float a) + { + return Math.Min(1.0f, Math.Max(0.0f, a)); + } + + public static float Round(float a, float precision) + { + return (float)Math.Floor(0.5f + a / precision) * precision; + } + + public static float Interpolate(float f0, float f1, float alpha) + { + return f0 * (1 - alpha) + f1 * alpha; + } + + public static void Swap(ref T a, ref T b) + { + T tmp = a; + a = b; + b = tmp; + } + + public static bool above(List vertices, int3 t, float3 p, float epsilon) + { + float3 vtx = vertices[t.x]; + float3 n = TriNormal(vtx, vertices[t.y], vertices[t.z]); + return (float3.dot(n, p - vtx) > epsilon); // EPSILON??? + } + + public static int hasedge(int3 t, int a, int b) + { + for (int i = 0; i < 3; i++) + { + int i1 = (i + 1) % 3; + if (t[i] == a && t[i1] == b) + return 1; + } + return 0; + } + + public static bool hasvert(int3 t, int v) + { + return (t[0] == v || t[1] == v || t[2] == v); + } + + public static int shareedge(int3 a, int3 b) + { + int i; + for (i = 0; i < 3; i++) + { + int i1 = (i + 1) % 3; + if (hasedge(a, b[i1], b[i]) != 0) + return 1; + } + return 0; + } + + public static void b2bfix(HullTriangle s, HullTriangle t, List tris) + { + int i; + for (i = 0; i < 3; i++) + { + int i1 = (i + 1) % 3; + int i2 = (i + 2) % 3; + int a = (s)[i1]; + int b = (s)[i2]; + Debug.Assert(tris[s.neib(a, b)].neib(b, a) == s.id); + Debug.Assert(tris[t.neib(a, b)].neib(b, a) == t.id); + tris[s.neib(a, b)].setneib(b, a, t.neib(b, a)); + tris[t.neib(b, a)].setneib(a, b, s.neib(a, b)); + } + } + + public static void removeb2b(HullTriangle s, HullTriangle t, List tris) + { + b2bfix(s, t, tris); + s.Dispose(); + t.Dispose(); + } + + public static void checkit(HullTriangle t, List tris) + { + int i; + Debug.Assert(tris[t.id] == t); + for (i = 0; i < 3; i++) + { + int i1 = (i + 1) % 3; + int i2 = (i + 2) % 3; + int a = (t)[i1]; + int b = (t)[i2]; + Debug.Assert(a != b); + Debug.Assert(tris[t.n[i]].neib(b, a) == t.id); + } + } + + public static void extrude(HullTriangle t0, int v, List tris) + { + int3 t = t0; + int n = tris.Count; + HullTriangle ta = new HullTriangle(v, t[1], t[2], tris); + ta.n = new int3(t0.n[0], n + 1, n + 2); + tris[t0.n[0]].setneib(t[1], t[2], n + 0); + HullTriangle tb = new HullTriangle(v, t[2], t[0], tris); + tb.n = new int3(t0.n[1], n + 2, n + 0); + tris[t0.n[1]].setneib(t[2], t[0], n + 1); + HullTriangle tc = new HullTriangle(v, t[0], t[1], tris); + tc.n = new int3(t0.n[2], n + 0, n + 1); + tris[t0.n[2]].setneib(t[0], t[1], n + 2); + checkit(ta, tris); + checkit(tb, tris); + checkit(tc, tris); + if (hasvert(tris[ta.n[0]], v)) + removeb2b(ta, tris[ta.n[0]], tris); + if (hasvert(tris[tb.n[0]], v)) + removeb2b(tb, tris[tb.n[0]], tris); + if (hasvert(tris[tc.n[0]], v)) + removeb2b(tc, tris[tc.n[0]], tris); + t0.Dispose(); + } + + public static HullTriangle extrudable(float epsilon, List tris) + { + int i; + HullTriangle t = null; + for (i = 0; i < tris.Count; i++) + { + if (t == null || (tris.Count > i && (object)tris[i] != null && t.rise < tris[i].rise)) + { + t = tris[i]; + } + } + return (t.rise > epsilon) ? t : null; + } + + public static Quaternion RotationArc(float3 v0, float3 v1) + { + Quaternion q = new Quaternion(); + v0 = float3.normalize(v0); // Comment these two lines out if you know its not needed. + v1 = float3.normalize(v1); // If vector is already unit length then why do it again? + float3 c = float3.cross(v0, v1); + float d = float3.dot(v0, v1); + if (d <= -1.0f) // 180 about x axis + { + return new Quaternion(1f, 0f, 0f, 0f); + } + float s = (float)Math.Sqrt((1 + d) * 2f); + q.x = c.x / s; + q.y = c.y / s; + q.z = c.z / s; + q.w = s / 2.0f; + return q; + } + + public static float3 PlaneLineIntersection(Plane plane, float3 p0, float3 p1) + { + // returns the point where the line p0-p1 intersects the plane n&d + float3 dif = p1 - p0; + float dn = float3.dot(plane.normal, dif); + float t = -(plane.dist + float3.dot(plane.normal, p0)) / dn; + return p0 + (dif * t); + } + + public static float3 LineProject(float3 p0, float3 p1, float3 a) + { + float3 w = new float3(); + w = p1 - p0; + float t = float3.dot(w, (a - p0)) / (w.x * w.x + w.y * w.y + w.z * w.z); + return p0 + w * t; + } + + public static float3 PlaneProject(Plane plane, float3 point) + { + return point - plane.normal * (float3.dot(point, plane.normal) + plane.dist); + } + + public static float LineProjectTime(float3 p0, float3 p1, float3 a) + { + float3 w = new float3(); + w = p1 - p0; + float t = float3.dot(w, (a - p0)) / (w.x * w.x + w.y * w.y + w.z * w.z); + return t; + } + + public static float3 ThreePlaneIntersection(Plane p0, Plane p1, Plane p2) + { + float3x3 mp = float3x3.Transpose(new float3x3(p0.normal, p1.normal, p2.normal)); + float3x3 mi = float3x3.Inverse(mp); + float3 b = new float3(p0.dist, p1.dist, p2.dist); + return -b * mi; + } + + public static bool PolyHit(List vert, float3 v0, float3 v1) + { + float3 impact = new float3(); + float3 normal = new float3(); + return PolyHit(vert, v0, v1, out impact, out normal); + } + + public static bool PolyHit(List vert, float3 v0, float3 v1, out float3 impact) + { + float3 normal = new float3(); + return PolyHit(vert, v0, v1, out impact, out normal); + } + + public static bool PolyHit(List vert, float3 v0, float3 v1, out float3 impact, out float3 normal) + { + float3 the_point = new float3(); + + impact = null; + normal = null; + + int i; + float3 nrml = new float3(0, 0, 0); + for (i = 0; i < vert.Count; i++) + { + int i1 = (i + 1) % vert.Count; + int i2 = (i + 2) % vert.Count; + nrml = nrml + float3.cross(vert[i1] - vert[i], vert[i2] - vert[i1]); + } + + float m = float3.magnitude(nrml); + if (m == 0.0) + { + return false; + } + nrml = nrml * (1.0f / m); + float dist = -float3.dot(nrml, vert[0]); + float d0; + float d1; + if ((d0 = float3.dot(v0, nrml) + dist) < 0 || (d1 = float3.dot(v1, nrml) + dist) > 0) + { + return false; + } + + // By using the cached plane distances d0 and d1 + // we can optimize the following: + // the_point = planelineintersection(nrml,dist,v0,v1); + float a = d0 / (d0 - d1); + the_point = v0 * (1 - a) + v1 * a; + + + bool inside = true; + for (int j = 0; inside && j < vert.Count; j++) + { + // let inside = 0 if outside + float3 pp1 = new float3(); + float3 pp2 = new float3(); + float3 side = new float3(); + pp1 = vert[j]; + pp2 = vert[(j + 1) % vert.Count]; + side = float3.cross((pp2 - pp1), (the_point - pp1)); + inside = (float3.dot(nrml, side) >= 0.0); + } + if (inside) + { + if (normal != null) + { + normal = nrml; + } + if (impact != null) + { + impact = the_point; + } + } + return inside; + } + + public static bool BoxInside(float3 p, float3 bmin, float3 bmax) + { + return (p.x >= bmin.x && p.x <= bmax.x && p.y >= bmin.y && p.y <= bmax.y && p.z >= bmin.z && p.z <= bmax.z); + } + + public static bool BoxIntersect(float3 v0, float3 v1, float3 bmin, float3 bmax, float3 impact) + { + if (BoxInside(v0, bmin, bmax)) + { + impact = v0; + return true; + } + if (v0.x <= bmin.x && v1.x >= bmin.x) + { + float a = (bmin.x - v0.x) / (v1.x - v0.x); + //v.x = bmin.x; + float vy = (1 - a) * v0.y + a * v1.y; + float vz = (1 - a) * v0.z + a * v1.z; + if (vy >= bmin.y && vy <= bmax.y && vz >= bmin.z && vz <= bmax.z) + { + impact.x = bmin.x; + impact.y = vy; + impact.z = vz; + return true; + } + } + else if (v0.x >= bmax.x && v1.x <= bmax.x) + { + float a = (bmax.x - v0.x) / (v1.x - v0.x); + //v.x = bmax.x; + float vy = (1 - a) * v0.y + a * v1.y; + float vz = (1 - a) * v0.z + a * v1.z; + if (vy >= bmin.y && vy <= bmax.y && vz >= bmin.z && vz <= bmax.z) + { + impact.x = bmax.x; + impact.y = vy; + impact.z = vz; + return true; + } + } + if (v0.y <= bmin.y && v1.y >= bmin.y) + { + float a = (bmin.y - v0.y) / (v1.y - v0.y); + float vx = (1 - a) * v0.x + a * v1.x; + //v.y = bmin.y; + float vz = (1 - a) * v0.z + a * v1.z; + if (vx >= bmin.x && vx <= bmax.x && vz >= bmin.z && vz <= bmax.z) + { + impact.x = vx; + impact.y = bmin.y; + impact.z = vz; + return true; + } + } + else if (v0.y >= bmax.y && v1.y <= bmax.y) + { + float a = (bmax.y - v0.y) / (v1.y - v0.y); + float vx = (1 - a) * v0.x + a * v1.x; + // vy = bmax.y; + float vz = (1 - a) * v0.z + a * v1.z; + if (vx >= bmin.x && vx <= bmax.x && vz >= bmin.z && vz <= bmax.z) + { + impact.x = vx; + impact.y = bmax.y; + impact.z = vz; + return true; + } + } + if (v0.z <= bmin.z && v1.z >= bmin.z) + { + float a = (bmin.z - v0.z) / (v1.z - v0.z); + float vx = (1 - a) * v0.x + a * v1.x; + float vy = (1 - a) * v0.y + a * v1.y; + // v.z = bmin.z; + if (vy >= bmin.y && vy <= bmax.y && vx >= bmin.x && vx <= bmax.x) + { + impact.x = vx; + impact.y = vy; + impact.z = bmin.z; + return true; + } + } + else if (v0.z >= bmax.z && v1.z <= bmax.z) + { + float a = (bmax.z - v0.z) / (v1.z - v0.z); + float vx = (1 - a) * v0.x + a * v1.x; + float vy = (1 - a) * v0.y + a * v1.y; + // v.z = bmax.z; + if (vy >= bmin.y && vy <= bmax.y && vx >= bmin.x && vx <= bmax.x) + { + impact.x = vx; + impact.y = vy; + impact.z = bmax.z; + return true; + } + } + return false; + } + + public static float DistanceBetweenLines(float3 ustart, float3 udir, float3 vstart, float3 vdir, float3 upoint) + { + return DistanceBetweenLines(ustart, udir, vstart, vdir, upoint, null); + } + + public static float DistanceBetweenLines(float3 ustart, float3 udir, float3 vstart, float3 vdir) + { + return DistanceBetweenLines(ustart, udir, vstart, vdir, null, null); + } + + public static float DistanceBetweenLines(float3 ustart, float3 udir, float3 vstart, float3 vdir, float3 upoint, float3 vpoint) + { + float3 cp = float3.normalize(float3.cross(udir, vdir)); + + float distu = -float3.dot(cp, ustart); + float distv = -float3.dot(cp, vstart); + float dist = (float)Math.Abs(distu - distv); + if (upoint != null) + { + Plane plane = new Plane(); + plane.normal = float3.normalize(float3.cross(vdir, cp)); + plane.dist = -float3.dot(plane.normal, vstart); + upoint = PlaneLineIntersection(plane, ustart, ustart + udir); + } + if (vpoint != null) + { + Plane plane = new Plane(); + plane.normal = float3.normalize(float3.cross(udir, cp)); + plane.dist = -float3.dot(plane.normal, ustart); + vpoint = PlaneLineIntersection(plane, vstart, vstart + vdir); + } + return dist; + } + + public static float3 TriNormal(float3 v0, float3 v1, float3 v2) + { + // return the normal of the triangle + // inscribed by v0, v1, and v2 + float3 cp = float3.cross(v1 - v0, v2 - v1); + float m = float3.magnitude(cp); + if (m == 0) + return new float3(1, 0, 0); + return cp * (1.0f / m); + } + + public static int PlaneTest(Plane p, float3 v, float planetestepsilon) + { + float a = float3.dot(v, p.normal) + p.dist; + int flag = (a > planetestepsilon) ? (2) : ((a < -planetestepsilon) ? (1) : (0)); + return flag; + } + + public static int SplitTest(ref ConvexH convex, Plane plane, float planetestepsilon) + { + int flag = 0; + for (int i = 0; i < convex.vertices.Count; i++) + { + flag |= PlaneTest(plane, convex.vertices[i], planetestepsilon); + } + return flag; + } + + public static Quaternion VirtualTrackBall(float3 cop, float3 cor, float3 dir1, float3 dir2) + { + // routine taken from game programming gems. + // Implement track ball functionality to spin stuf on the screen + // cop center of projection + // cor center of rotation + // dir1 old mouse direction + // dir2 new mouse direction + // pretend there is a sphere around cor. Then find the points + // where dir1 and dir2 intersect that sphere. Find the + // rotation that takes the first point to the second. + float m; + // compute plane + float3 nrml = cor - cop; + float fudgefactor = 1.0f / (float3.magnitude(nrml) * 0.25f); // since trackball proportional to distance from cop + nrml = float3.normalize(nrml); + float dist = -float3.dot(nrml, cor); + float3 u = PlaneLineIntersection(new Plane(nrml, dist), cop, cop + dir1); + u = u - cor; + u = u * fudgefactor; + m = float3.magnitude(u); + if (m > 1) + { + u /= m; + } + else + { + u = u - (nrml * (float)Math.Sqrt(1 - m * m)); + } + float3 v = PlaneLineIntersection(new Plane(nrml, dist), cop, cop + dir2); + v = v - cor; + v = v * fudgefactor; + m = float3.magnitude(v); + if (m > 1) + { + v /= m; + } + else + { + v = v - (nrml * (float)Math.Sqrt(1 - m * m)); + } + return RotationArc(u, v); + } + + public static bool AssertIntact(ConvexH convex, float planetestepsilon) + { + int i; + int estart = 0; + for (i = 0; i < convex.edges.Count; i++) + { + if (convex.edges[estart].p != convex.edges[i].p) + { + estart = i; + } + int inext = i + 1; + if (inext >= convex.edges.Count || convex.edges[inext].p != convex.edges[i].p) + { + inext = estart; + } + Debug.Assert(convex.edges[inext].p == convex.edges[i].p); + int nb = convex.edges[i].ea; + Debug.Assert(nb != 255); + if (nb == 255 || nb == -1) + return false; + Debug.Assert(nb != -1); + Debug.Assert(i == convex.edges[nb].ea); + } + for (i = 0; i < convex.edges.Count; i++) + { + Debug.Assert((0) == PlaneTest(convex.facets[convex.edges[i].p], convex.vertices[convex.edges[i].v], planetestepsilon)); + if ((0) != PlaneTest(convex.facets[convex.edges[i].p], convex.vertices[convex.edges[i].v], planetestepsilon)) + return false; + if (convex.edges[estart].p != convex.edges[i].p) + { + estart = i; + } + int i1 = i + 1; + if (i1 >= convex.edges.Count || convex.edges[i1].p != convex.edges[i].p) + { + i1 = estart; + } + int i2 = i1 + 1; + if (i2 >= convex.edges.Count || convex.edges[i2].p != convex.edges[i].p) + { + i2 = estart; + } + if (i == i2) // i sliced tangent to an edge and created 2 meaningless edges + continue; + float3 localnormal = TriNormal(convex.vertices[convex.edges[i].v], convex.vertices[convex.edges[i1].v], convex.vertices[convex.edges[i2].v]); + Debug.Assert(float3.dot(localnormal, convex.facets[convex.edges[i].p].normal) > 0); + if (float3.dot(localnormal, convex.facets[convex.edges[i].p].normal) <= 0) + return false; + } + return true; + } + + public static ConvexH test_btbq(float planetestepsilon) + { + // back to back quads + ConvexH convex = new ConvexH(4, 8, 2); + convex.vertices[0] = new float3(0, 0, 0); + convex.vertices[1] = new float3(1, 0, 0); + convex.vertices[2] = new float3(1, 1, 0); + convex.vertices[3] = new float3(0, 1, 0); + convex.facets[0] = new Plane(new float3(0, 0, 1), 0); + convex.facets[1] = new Plane(new float3(0, 0, -1), 0); + convex.edges[0] = new ConvexH.HalfEdge(7, 0, 0); + convex.edges[1] = new ConvexH.HalfEdge(6, 1, 0); + convex.edges[2] = new ConvexH.HalfEdge(5, 2, 0); + convex.edges[3] = new ConvexH.HalfEdge(4, 3, 0); + + convex.edges[4] = new ConvexH.HalfEdge(3, 0, 1); + convex.edges[5] = new ConvexH.HalfEdge(2, 3, 1); + convex.edges[6] = new ConvexH.HalfEdge(1, 2, 1); + convex.edges[7] = new ConvexH.HalfEdge(0, 1, 1); + AssertIntact(convex, planetestepsilon); + return convex; + } + + public static ConvexH test_cube() + { + ConvexH convex = new ConvexH(8, 24, 6); + convex.vertices[0] = new float3(0, 0, 0); + convex.vertices[1] = new float3(0, 0, 1); + convex.vertices[2] = new float3(0, 1, 0); + convex.vertices[3] = new float3(0, 1, 1); + convex.vertices[4] = new float3(1, 0, 0); + convex.vertices[5] = new float3(1, 0, 1); + convex.vertices[6] = new float3(1, 1, 0); + convex.vertices[7] = new float3(1, 1, 1); + + convex.facets[0] = new Plane(new float3(-1, 0, 0), 0); + convex.facets[1] = new Plane(new float3(1, 0, 0), -1); + convex.facets[2] = new Plane(new float3(0, -1, 0), 0); + convex.facets[3] = new Plane(new float3(0, 1, 0), -1); + convex.facets[4] = new Plane(new float3(0, 0, -1), 0); + convex.facets[5] = new Plane(new float3(0, 0, 1), -1); + + convex.edges[0] = new ConvexH.HalfEdge(11, 0, 0); + convex.edges[1] = new ConvexH.HalfEdge(23, 1, 0); + convex.edges[2] = new ConvexH.HalfEdge(15, 3, 0); + convex.edges[3] = new ConvexH.HalfEdge(16, 2, 0); + + convex.edges[4] = new ConvexH.HalfEdge(13, 6, 1); + convex.edges[5] = new ConvexH.HalfEdge(21, 7, 1); + convex.edges[6] = new ConvexH.HalfEdge(9, 5, 1); + convex.edges[7] = new ConvexH.HalfEdge(18, 4, 1); + + convex.edges[8] = new ConvexH.HalfEdge(19, 0, 2); + convex.edges[9] = new ConvexH.HalfEdge(6, 4, 2); + convex.edges[10] = new ConvexH.HalfEdge(20, 5, 2); + convex.edges[11] = new ConvexH.HalfEdge(0, 1, 2); + + convex.edges[12] = new ConvexH.HalfEdge(22, 3, 3); + convex.edges[13] = new ConvexH.HalfEdge(4, 7, 3); + convex.edges[14] = new ConvexH.HalfEdge(17, 6, 3); + convex.edges[15] = new ConvexH.HalfEdge(2, 2, 3); + + convex.edges[16] = new ConvexH.HalfEdge(3, 0, 4); + convex.edges[17] = new ConvexH.HalfEdge(14, 2, 4); + convex.edges[18] = new ConvexH.HalfEdge(7, 6, 4); + convex.edges[19] = new ConvexH.HalfEdge(8, 4, 4); + + convex.edges[20] = new ConvexH.HalfEdge(10, 1, 5); + convex.edges[21] = new ConvexH.HalfEdge(5, 5, 5); + convex.edges[22] = new ConvexH.HalfEdge(12, 7, 5); + convex.edges[23] = new ConvexH.HalfEdge(1, 3, 5); + + return convex; + } + + public static ConvexH ConvexHMakeCube(float3 bmin, float3 bmax) + { + ConvexH convex = test_cube(); + convex.vertices[0] = new float3(bmin.x, bmin.y, bmin.z); + convex.vertices[1] = new float3(bmin.x, bmin.y, bmax.z); + convex.vertices[2] = new float3(bmin.x, bmax.y, bmin.z); + convex.vertices[3] = new float3(bmin.x, bmax.y, bmax.z); + convex.vertices[4] = new float3(bmax.x, bmin.y, bmin.z); + convex.vertices[5] = new float3(bmax.x, bmin.y, bmax.z); + convex.vertices[6] = new float3(bmax.x, bmax.y, bmin.z); + convex.vertices[7] = new float3(bmax.x, bmax.y, bmax.z); + + convex.facets[0] = new Plane(new float3(-1, 0, 0), bmin.x); + convex.facets[1] = new Plane(new float3(1, 0, 0), -bmax.x); + convex.facets[2] = new Plane(new float3(0, -1, 0), bmin.y); + convex.facets[3] = new Plane(new float3(0, 1, 0), -bmax.y); + convex.facets[4] = new Plane(new float3(0, 0, -1), bmin.z); + convex.facets[5] = new Plane(new float3(0, 0, 1), -bmax.z); + return convex; + } + + public static ConvexH ConvexHCrop(ref ConvexH convex, Plane slice, float planetestepsilon) + { + int i; + int vertcountunder = 0; + int vertcountover = 0; + List vertscoplanar = new List(); // existing vertex members of convex that are coplanar + List edgesplit = new List(); // existing edges that members of convex that cross the splitplane + + Debug.Assert(convex.edges.Count < 480); + + EdgeFlag[] edgeflag = new EdgeFlag[512]; + VertFlag[] vertflag = new VertFlag[256]; + PlaneFlag[] planeflag = new PlaneFlag[128]; + ConvexH.HalfEdge[] tmpunderedges = new ConvexH.HalfEdge[512]; + Plane[] tmpunderplanes = new Plane[128]; + Coplanar[] coplanaredges = new Coplanar[512]; + int coplanaredges_num = 0; + + List createdverts = new List(); + + // do the side-of-plane tests + for (i = 0; i < convex.vertices.Count; i++) + { + vertflag[i].planetest = (byte)PlaneTest(slice, convex.vertices[i], planetestepsilon); + if (vertflag[i].planetest == (0)) + { + // ? vertscoplanar.Add(i); + vertflag[i].undermap = (byte)vertcountunder++; + vertflag[i].overmap = (byte)vertcountover++; + } + else if (vertflag[i].planetest == (1)) + { + vertflag[i].undermap = (byte)vertcountunder++; + } + else + { + Debug.Assert(vertflag[i].planetest == (2)); + vertflag[i].overmap = (byte)vertcountover++; + vertflag[i].undermap = 255; // for debugging purposes + } + } + int vertcountunderold = vertcountunder; // for debugging only + + int under_edge_count = 0; + int underplanescount = 0; + int e0 = 0; + + for (int currentplane = 0; currentplane < convex.facets.Count; currentplane++) + { + int estart = e0; + int enextface = 0; + int planeside = 0; + int e1 = e0 + 1; + int vout = -1; + int vin = -1; + int coplanaredge = -1; + do + { + + if (e1 >= convex.edges.Count || convex.edges[e1].p != currentplane) + { + enextface = e1; + e1 = estart; + } + ConvexH.HalfEdge edge0 = convex.edges[e0]; + ConvexH.HalfEdge edge1 = convex.edges[e1]; + ConvexH.HalfEdge edgea = convex.edges[edge0.ea]; + + planeside |= vertflag[edge0.v].planetest; + //if((vertflag[edge0.v].planetest & vertflag[edge1.v].planetest) == COPLANAR) { + // assert(ecop==-1); + // ecop=e; + //} + + if (vertflag[edge0.v].planetest == (2) && vertflag[edge1.v].planetest == (2)) + { + // both endpoints over plane + edgeflag[e0].undermap = -1; + } + else if ((vertflag[edge0.v].planetest | vertflag[edge1.v].planetest) == (1)) + { + // at least one endpoint under, the other coplanar or under + + edgeflag[e0].undermap = (short)under_edge_count; + tmpunderedges[under_edge_count].v = vertflag[edge0.v].undermap; + tmpunderedges[under_edge_count].p = (byte)underplanescount; + if (edge0.ea < e0) + { + // connect the neighbors + Debug.Assert(edgeflag[edge0.ea].undermap != -1); + tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap; + tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count; + } + under_edge_count++; + } + else if ((vertflag[edge0.v].planetest | vertflag[edge1.v].planetest) == (0)) + { + // both endpoints coplanar + // must check a 3rd point to see if UNDER + int e2 = e1 + 1; + if (e2 >= convex.edges.Count || convex.edges[e2].p != currentplane) + { + e2 = estart; + } + Debug.Assert(convex.edges[e2].p == currentplane); + ConvexH.HalfEdge edge2 = convex.edges[e2]; + if (vertflag[edge2.v].planetest == (1)) + { + + edgeflag[e0].undermap = (short)under_edge_count; + tmpunderedges[under_edge_count].v = vertflag[edge0.v].undermap; + tmpunderedges[under_edge_count].p = (byte)underplanescount; + tmpunderedges[under_edge_count].ea = -1; + // make sure this edge is added to the "coplanar" list + coplanaredge = under_edge_count; + vout = vertflag[edge0.v].undermap; + vin = vertflag[edge1.v].undermap; + under_edge_count++; + } + else + { + edgeflag[e0].undermap = -1; + } + } + else if (vertflag[edge0.v].planetest == (1) && vertflag[edge1.v].planetest == (2)) + { + // first is under 2nd is over + + edgeflag[e0].undermap = (short)under_edge_count; + tmpunderedges[under_edge_count].v = vertflag[edge0.v].undermap; + tmpunderedges[under_edge_count].p = (byte)underplanescount; + if (edge0.ea < e0) + { + Debug.Assert(edgeflag[edge0.ea].undermap != -1); + // connect the neighbors + tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap; + tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count; + vout = tmpunderedges[edgeflag[edge0.ea].undermap].v; + } + else + { + Plane p0 = convex.facets[edge0.p]; + Plane pa = convex.facets[edgea.p]; + createdverts.Add(ThreePlaneIntersection(p0, pa, slice)); + //createdverts.Add(PlaneProject(slice,PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v]))); + //createdverts.Add(PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v])); + vout = vertcountunder++; + } + under_edge_count++; + /// hmmm something to think about: i might be able to output this edge regarless of + // wheter or not we know v-in yet. ok i;ll try this now: + tmpunderedges[under_edge_count].v = (byte)vout; + tmpunderedges[under_edge_count].p = (byte)underplanescount; + tmpunderedges[under_edge_count].ea = -1; + coplanaredge = under_edge_count; + under_edge_count++; + + if (vin != -1) + { + // we previously processed an edge where we came under + // now we know about vout as well + + // ADD THIS EDGE TO THE LIST OF EDGES THAT NEED NEIGHBOR ON PARTITION PLANE!! + } + + } + else if (vertflag[edge0.v].planetest == (0) && vertflag[edge1.v].planetest == (2)) + { + // first is coplanar 2nd is over + + edgeflag[e0].undermap = -1; + vout = vertflag[edge0.v].undermap; + // I hate this but i have to make sure part of this face is UNDER before ouputting this vert + int k = estart; + Debug.Assert(edge0.p == currentplane); + while (!((planeside & 1) != 0) && k < convex.edges.Count && convex.edges[k].p == edge0.p) + { + planeside |= vertflag[convex.edges[k].v].planetest; + k++; + } + if ((planeside & 1) != 0) + { + tmpunderedges[under_edge_count].v = (byte)vout; + tmpunderedges[under_edge_count].p = (byte)underplanescount; + tmpunderedges[under_edge_count].ea = -1; + coplanaredge = under_edge_count; // hmmm should make a note of the edge # for later on + under_edge_count++; + + } + } + else if (vertflag[edge0.v].planetest == (2) && vertflag[edge1.v].planetest == (1)) + { + // first is over next is under + // new vertex!!! + Debug.Assert(vin == -1); + if (e0 < edge0.ea) + { + Plane p0 = convex.facets[edge0.p]; + Plane pa = convex.facets[edgea.p]; + createdverts.Add(ThreePlaneIntersection(p0, pa, slice)); + //createdverts.Add(PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v])); + //createdverts.Add(PlaneProject(slice,PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v]))); + vin = vertcountunder++; + } + else + { + // find the new vertex that was created by edge[edge0.ea] + int nea = edgeflag[edge0.ea].undermap; + Debug.Assert(tmpunderedges[nea].p == tmpunderedges[nea + 1].p); + vin = tmpunderedges[nea + 1].v; + Debug.Assert(vin < vertcountunder); + Debug.Assert(vin >= vertcountunderold); // for debugging only + } + if (vout != -1) + { + // we previously processed an edge where we went over + // now we know vin too + // ADD THIS EDGE TO THE LIST OF EDGES THAT NEED NEIGHBOR ON PARTITION PLANE!! + } + // output edge + tmpunderedges[under_edge_count].v = (byte)vin; + tmpunderedges[under_edge_count].p = (byte)underplanescount; + edgeflag[e0].undermap = (short)under_edge_count; + if (e0 > edge0.ea) + { + Debug.Assert(edgeflag[edge0.ea].undermap != -1); + // connect the neighbors + tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap; + tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count; + } + Debug.Assert(edgeflag[e0].undermap == under_edge_count); + under_edge_count++; + } + else if (vertflag[edge0.v].planetest == (2) && vertflag[edge1.v].planetest == (0)) + { + // first is over next is coplanar + + edgeflag[e0].undermap = -1; + vin = vertflag[edge1.v].undermap; + Debug.Assert(vin != -1); + if (vout != -1) + { + // we previously processed an edge where we came under + // now we know both endpoints + // ADD THIS EDGE TO THE LIST OF EDGES THAT NEED NEIGHBOR ON PARTITION PLANE!! + } + + } + else + { + Debug.Assert(false); + } + + + e0 = e1; + e1++; // do the modulo at the beginning of the loop + + } while (e0 != estart); + e0 = enextface; + if ((planeside & 1) != 0) + { + planeflag[currentplane].undermap = (byte)underplanescount; + tmpunderplanes[underplanescount] = convex.facets[currentplane]; + underplanescount++; + } + else + { + planeflag[currentplane].undermap = 0; + } + if (vout >= 0 && (planeside & 1) != 0) + { + Debug.Assert(vin >= 0); + Debug.Assert(coplanaredge >= 0); + Debug.Assert(coplanaredge != 511); + coplanaredges[coplanaredges_num].ea = (ushort)coplanaredge; + coplanaredges[coplanaredges_num].v0 = (byte)vin; + coplanaredges[coplanaredges_num].v1 = (byte)vout; + coplanaredges_num++; + } + } + + // add the new plane to the mix: + if (coplanaredges_num > 0) + { + tmpunderplanes[underplanescount++] = slice; + } + for (i = 0; i < coplanaredges_num - 1; i++) + { + if (coplanaredges[i].v1 != coplanaredges[i + 1].v0) + { + int j = 0; + for (j = i + 2; j < coplanaredges_num; j++) + { + if (coplanaredges[i].v1 == coplanaredges[j].v0) + { + Coplanar tmp = coplanaredges[i + 1]; + coplanaredges[i + 1] = coplanaredges[j]; + coplanaredges[j] = tmp; + break; + } + } + if (j >= coplanaredges_num) + { + Debug.Assert(j < coplanaredges_num); + return null; + } + } + } + + ConvexH punder = new ConvexH(vertcountunder, under_edge_count + coplanaredges_num, underplanescount); + ConvexH under = punder; + + { + int k = 0; + for (i = 0; i < convex.vertices.Count; i++) + { + if (vertflag[i].planetest != (2)) + { + under.vertices[k++] = convex.vertices[i]; + } + } + i = 0; + while (k < vertcountunder) + { + under.vertices[k++] = createdverts[i++]; + } + Debug.Assert(i == createdverts.Count); + } + + for (i = 0; i < coplanaredges_num; i++) + { + ConvexH.HalfEdge edge = under.edges[under_edge_count + i]; + edge.p = (byte)(underplanescount - 1); + edge.ea = (short)coplanaredges[i].ea; + edge.v = (byte)coplanaredges[i].v0; + under.edges[under_edge_count + i] = edge; + + tmpunderedges[coplanaredges[i].ea].ea = (short)(under_edge_count + i); + } + + under.edges = new List(tmpunderedges); + under.facets = new List(tmpunderplanes); + return punder; + } + + public static ConvexH ConvexHDup(ConvexH src) + { + ConvexH dst = new ConvexH(src.vertices.Count, src.edges.Count, src.facets.Count); + dst.vertices = new List(src.vertices.Count); + foreach (float3 f in src.vertices) + dst.vertices.Add(new float3(f)); + dst.edges = new List(src.edges.Count); + foreach (ConvexH.HalfEdge e in src.edges) + dst.edges.Add(new ConvexH.HalfEdge(e)); + dst.facets = new List(src.facets.Count); + foreach (Plane p in src.facets) + dst.facets.Add(new Plane(p)); + return dst; + } + + public static int candidateplane(List planes, int planes_count, ConvexH convex, float epsilon) + { + int p = 0; + float md = 0; + int i; + for (i = 0; i < planes_count; i++) + { + float d = 0; + for (int j = 0; j < convex.vertices.Count; j++) + { + d = Math.Max(d, float3.dot(convex.vertices[j], planes[i].normal) + planes[i].dist); + } + if (i == 0 || d > md) + { + p = i; + md = d; + } + } + return (md > epsilon) ? p : -1; + } + + public static float3 orth(float3 v) + { + float3 a = float3.cross(v, new float3(0f, 0f, 1f)); + float3 b = float3.cross(v, new float3(0f, 1f, 0f)); + return float3.normalize((float3.magnitude(a) > float3.magnitude(b)) ? a : b); + } + + public static int maxdir(List p, int count, float3 dir) + { + Debug.Assert(count != 0); + int m = 0; + float currDotm = float3.dot(p[0], dir); + for (int i = 1; i < count; i++) + { + float currDoti = float3.dot(p[i], dir); + if (currDoti > currDotm) + { + currDotm = currDoti; + m = i; + } + } + return m; + } + + public static int maxdirfiltered(List p, int count, float3 dir, byte[] allow) + { + //Debug.Assert(count != 0); + int m = 0; + float currDotm = float3.dot(p[0], dir); + float currDoti; + + while (allow[m] == 0) + m++; + + for (int i = 1; i < count; i++) + { + if (allow[i] != 0) + { + currDoti = float3.dot(p[i], dir); + if (currDoti > currDotm) + { + currDotm = currDoti; + m = i; + } + } + } + //Debug.Assert(m != -1); + return m; + } + + public static int maxdirsterid(List p, int count, float3 dir, byte[] allow) + { + int m = -1; + while (m == -1) + { + m = maxdirfiltered(p, count, dir, allow); + if (allow[m] == 3) + return m; + float3 u = orth(dir); + float3 v = float3.cross(u, dir); + int ma = -1; + for (float x = 0.0f; x <= 360.0f; x += 45.0f) + { + int mb; + { + float s = (float)Math.Sin((3.14159264f / 180.0f) * (x)); + float c = (float)Math.Cos((3.14159264f / 180.0f) * (x)); + mb = maxdirfiltered(p, count, dir + (u * s + v * c) * 0.025f, allow); + } + if (ma == m && mb == m) + { + allow[m] = 3; + return m; + } + if (ma != -1 && ma != mb) // Yuck - this is really ugly + { + int mc = ma; + for (float xx = x - 40.0f; xx <= x; xx += 5.0f) + { + float s = (float)Math.Sin((3.14159264f / 180.0f) * (xx)); + float c = (float)Math.Cos((3.14159264f / 180.0f) * (xx)); + int md = maxdirfiltered(p, count, dir + (u * s + v * c) * 0.025f, allow); + if (mc == m && md == m) + { + allow[m] = 3; + return m; + } + mc = md; + } + } + ma = mb; + } + allow[m] = 0; + m = -1; + } + + Debug.Assert(false); + return m; + } + + public static int4 FindSimplex(List verts, byte[] allow) + { + float3[] basis = new float3[3]; + basis[0] = new float3(0.01f, 0.02f, 1.0f); + int p0 = maxdirsterid(verts, verts.Count, basis[0], allow); + int p1 = maxdirsterid(verts, verts.Count, -basis[0], allow); + basis[0] = verts[p0] - verts[p1]; + if (p0 == p1 || basis[0] == new float3(0, 0, 0)) + return new int4(-1, -1, -1, -1); + basis[1] = float3.cross(new float3(1, 0.02f, 0), basis[0]); + basis[2] = float3.cross(new float3(-0.02f, 1, 0), basis[0]); + basis[1] = float3.normalize((float3.magnitude(basis[1]) > float3.magnitude(basis[2])) ? basis[1] : basis[2]); + int p2 = maxdirsterid(verts, verts.Count, basis[1], allow); + if (p2 == p0 || p2 == p1) + { + p2 = maxdirsterid(verts, verts.Count, -basis[1], allow); + } + if (p2 == p0 || p2 == p1) + return new int4(-1, -1, -1, -1); + basis[1] = verts[p2] - verts[p0]; + basis[2] = float3.normalize(float3.cross(basis[1], basis[0])); + int p3 = maxdirsterid(verts, verts.Count, basis[2], allow); + if (p3 == p0 || p3 == p1 || p3 == p2) + p3 = maxdirsterid(verts, verts.Count, -basis[2], allow); + if (p3 == p0 || p3 == p1 || p3 == p2) + return new int4(-1, -1, -1, -1); + Debug.Assert(!(p0 == p1 || p0 == p2 || p0 == p3 || p1 == p2 || p1 == p3 || p2 == p3)); + if (float3.dot(verts[p3] - verts[p0], float3.cross(verts[p1] - verts[p0], verts[p2] - verts[p0])) < 0) + { + Swap(ref p2, ref p3); + } + return new int4(p0, p1, p2, p3); + } + + public static float GetDist(float px, float py, float pz, float3 p2) + { + float dx = px - p2.x; + float dy = py - p2.y; + float dz = pz - p2.z; + + return dx * dx + dy * dy + dz * dz; + } + + public static void ReleaseHull(PHullResult result) + { + if (result.Indices != null) + result.Indices = null; + if (result.Vertices != null) + result.Vertices = null; + } + + public static int calchullgen(List verts, int vlimit, List tris) + { + if (verts.Count < 4) + return 0; + if (vlimit == 0) + vlimit = 1000000000; + int j; + float3 bmin = new float3(verts[0]); + float3 bmax = new float3(verts[0]); + List isextreme = new List(verts.Count); + byte[] allow = new byte[verts.Count]; + for (j = 0; j < verts.Count; j++) + { + allow[j] = 1; + isextreme.Add(0); + bmin = float3.VectorMin(bmin, verts[j]); + bmax = float3.VectorMax(bmax, verts[j]); + } + float epsilon = float3.magnitude(bmax - bmin) * 0.001f; + + int4 p = FindSimplex(verts, allow); + if (p.x == -1) // simplex failed + return 0; + + float3 center = (verts[p[0]] + verts[p[1]] + verts[p[2]] + verts[p[3]]) / 4.0f; // a valid interior point + HullTriangle t0 = new HullTriangle(p[2], p[3], p[1], tris); + t0.n = new int3(2, 3, 1); + HullTriangle t1 = new HullTriangle(p[3], p[2], p[0], tris); + t1.n = new int3(3, 2, 0); + HullTriangle t2 = new HullTriangle(p[0], p[1], p[3], tris); + t2.n = new int3(0, 1, 3); + HullTriangle t3 = new HullTriangle(p[1], p[0], p[2], tris); + t3.n = new int3(1, 0, 2); + isextreme[p[0]] = isextreme[p[1]] = isextreme[p[2]] = isextreme[p[3]] = 1; + checkit(t0, tris); + checkit(t1, tris); + checkit(t2, tris); + checkit(t3, tris); + + for (j = 0; j < tris.Count; j++) + { + HullTriangle t = tris[j]; + Debug.Assert((object)t != null); + Debug.Assert(t.vmax < 0); + float3 n = TriNormal(verts[(t)[0]], verts[(t)[1]], verts[(t)[2]]); + t.vmax = maxdirsterid(verts, verts.Count, n, allow); + t.rise = float3.dot(n, verts[t.vmax] - verts[(t)[0]]); + } + HullTriangle te; + vlimit -= 4; + while (vlimit > 0 && (te = extrudable(epsilon, tris)) != null) + { + int3 ti = te; + int v = te.vmax; + Debug.Assert(isextreme[v] == 0); // wtf we've already done this vertex + isextreme[v] = 1; + //if(v==p0 || v==p1 || v==p2 || v==p3) continue; // done these already + j = tris.Count; + while (j-- != 0) + { + if (tris.Count <= j || (object)tris[j] == null) + continue; + int3 t = tris[j]; + if (above(verts, t, verts[v], 0.01f * epsilon)) + { + extrude(tris[j], v, tris); + } + } + // now check for those degenerate cases where we have a flipped triangle or a really skinny triangle + j = tris.Count; + while (j-- != 0) + { + if (tris.Count <= j || (object)tris[j] == null) + continue; + if (!hasvert(tris[j], v)) + break; + int3 nt = tris[j]; + if (above(verts, nt, center, 0.01f * epsilon) || float3.magnitude(float3.cross(verts[nt[1]] - verts[nt[0]], verts[nt[2]] - verts[nt[1]])) < epsilon * epsilon * 0.1f) + { + HullTriangle nb = tris[tris[j].n[0]]; + Debug.Assert(nb != null); + Debug.Assert(!hasvert(nb, v)); + Debug.Assert(nb.id < j); + extrude(nb, v, tris); + j = tris.Count; + } + } + j = tris.Count; + while (j-- != 0) + { + HullTriangle t = tris[j]; + if (t == null) + continue; + if (t.vmax >= 0) + break; + float3 n = TriNormal(verts[(t)[0]], verts[(t)[1]], verts[(t)[2]]); + t.vmax = maxdirsterid(verts, verts.Count, n, allow); + if (isextreme[t.vmax] != 0) + { + t.vmax = -1; // already done that vertex - algorithm needs to be able to terminate. + } + else + { + t.rise = float3.dot(n, verts[t.vmax] - verts[(t)[0]]); + } + } + vlimit--; + } + return 1; + } + + public static bool calchull(List verts, out List tris_out, int vlimit, List tris) + { + tris_out = null; + + int rc = calchullgen(verts, vlimit, tris); + if (rc == 0) + return false; + List ts = new List(); + for (int i = 0; i < tris.Count; i++) + { + if ((object)tris[i] != null) + { + for (int j = 0; j < 3; j++) + ts.Add((tris[i])[j]); + tris[i] = null; + } + } + + tris_out = ts; + tris.Clear(); + return true; + } + + public static int calchullpbev(List verts, int vlimit, out List planes, float bevangle, List tris) + { + int i; + int j; + planes = new List(); + int rc = calchullgen(verts, vlimit, tris); + if (rc == 0) + return 0; + for (i = 0; i < tris.Count; i++) + { + if (tris[i] != null) + { + Plane p = new Plane(); + HullTriangle t = tris[i]; + p.normal = TriNormal(verts[(t)[0]], verts[(t)[1]], verts[(t)[2]]); + p.dist = -float3.dot(p.normal, verts[(t)[0]]); + planes.Add(p); + for (j = 0; j < 3; j++) + { + if (t.n[j] < t.id) + continue; + HullTriangle s = tris[t.n[j]]; + float3 snormal = TriNormal(verts[(s)[0]], verts[(s)[1]], verts[(s)[2]]); + if (float3.dot(snormal, p.normal) >= Math.Cos(bevangle * (3.14159264f / 180.0f))) + continue; + float3 n = float3.normalize(snormal + p.normal); + planes.Add(new Plane(n, -float3.dot(n, verts[maxdir(verts, verts.Count, n)]))); + } + } + } + + tris.Clear(); + return 1; + } + + public static int overhull(List planes, List verts, int maxplanes, out List verts_out, out List faces_out, float inflate) + { + verts_out = null; + faces_out = null; + + int i; + int j; + if (verts.Count < 4) + return 0; + maxplanes = Math.Min(maxplanes, planes.Count); + float3 bmin = new float3(verts[0]); + float3 bmax = new float3(verts[0]); + for (i = 0; i < verts.Count; i++) + { + bmin = float3.VectorMin(bmin, verts[i]); + bmax = float3.VectorMax(bmax, verts[i]); + } + // float diameter = magnitude(bmax-bmin); + // inflate *=diameter; // RELATIVE INFLATION + bmin -= new float3(inflate, inflate, inflate); + bmax += new float3(inflate, inflate, inflate); + for (i = 0; i < planes.Count; i++) + { + planes[i].dist -= inflate; + } + float3 emin = new float3(bmin); + float3 emax = new float3(bmax); + float epsilon = float3.magnitude(emax - emin) * 0.025f; + float planetestepsilon = float3.magnitude(emax - emin) * (0.001f); + // todo: add bounding cube planes to force bevel. or try instead not adding the diameter expansion ??? must think. + // ConvexH *convex = ConvexHMakeCube(bmin - float3(diameter,diameter,diameter),bmax+float3(diameter,diameter,diameter)); + ConvexH c = ConvexHMakeCube(new float3(bmin), new float3(bmax)); + int k; + while (maxplanes-- != 0 && (k = candidateplane(planes, planes.Count, c, epsilon)) >= 0) + { + ConvexH tmp = c; + c = ConvexHCrop(ref tmp, planes[k], planetestepsilon); + if (c == null) // might want to debug this case better!!! + { + c = tmp; + break; + } + if (AssertIntact(c, planetestepsilon) == false) // might want to debug this case better too!!! + { + c = tmp; + break; + } + tmp.edges = null; + tmp.facets = null; + tmp.vertices = null; + } + + Debug.Assert(AssertIntact(c, planetestepsilon)); + //return c; + //C++ TO C# CONVERTER TODO TASK: The memory management function 'malloc' has no equivalent in C#: + faces_out = new List(); //(int)malloc(sizeof(int) * (1 + c.facets.Count + c.edges.Count)); // new int[1+c->facets.count+c->edges.count]; + int faces_count_out = 0; + i = 0; + faces_out[faces_count_out++] = -1; + k = 0; + while (i < c.edges.Count) + { + j = 1; + while (j + i < c.edges.Count && c.edges[i].p == c.edges[i + j].p) + { + j++; + } + faces_out[faces_count_out++] = j; + while (j-- != 0) + { + faces_out[faces_count_out++] = c.edges[i].v; + i++; + } + k++; + } + faces_out[0] = k; // number of faces. + Debug.Assert(k == c.facets.Count); + Debug.Assert(faces_count_out == 1 + c.facets.Count + c.edges.Count); + verts_out = c.vertices; // new float3[c->vertices.count]; + int verts_count_out = c.vertices.Count; + for (i = 0; i < c.vertices.Count; i++) + { + verts_out[i] = new float3(c.vertices[i]); + } + + c.edges = null; + c.facets = null; + c.vertices = null; + return 1; + } + + public static int overhullv(List verts, int maxplanes, out List verts_out, out List faces_out, float inflate, float bevangle, int vlimit, List tris) + { + verts_out = null; + faces_out = null; + + if (verts.Count == 0) + return 0; + List planes = new List(); + int rc = calchullpbev(verts, vlimit, out planes, bevangle, tris); + if (rc == 0) + return 0; + return overhull(planes, verts, maxplanes, out verts_out, out faces_out, inflate); + } + + public static void addPoint(ref uint vcount, List p, float x, float y, float z) + { + p.Add(new float3(x, y, z)); + vcount++; + } + + public static bool ComputeHull(List vertices, ref PHullResult result, int vlimit, float inflate) + { + List tris = new List(); + List faces; + List verts_out; + + if (inflate == 0.0f) + { + List tris_out; + bool ret = calchull(vertices, out tris_out, vlimit, tris); + if (ret == false) + return false; + + result.Indices = tris_out; + result.Vertices = vertices; + return true; + } + else + { + int ret = overhullv(vertices, 35, out verts_out, out faces, inflate, 120.0f, vlimit, tris); + if (ret == 0) + return false; + + List tris2 = new List(); + int n = faces[0]; + int k = 1; + for (int i = 0; i < n; i++) + { + int pn = faces[k++]; + for (int j = 2; j < pn; j++) + tris2.Add(new int3(faces[k], faces[k + j - 1], faces[k + j])); + k += pn; + } + Debug.Assert(tris2.Count == faces.Count - 1 - (n * 3)); + + result.Indices = new List(tris2.Count * 3); + for (int i = 0; i < tris2.Count; i++) + { + result.Indices.Add(tris2[i].x); + result.Indices.Add(tris2[i].y); + result.Indices.Add(tris2[i].z); + } + result.Vertices = verts_out; + + return true; + } + } + + private static bool CleanupVertices(List svertices, out List vertices, float normalepsilon, out float3 scale) + { + const float EPSILON = 0.000001f; + + vertices = new List(); + scale = new float3(1f, 1f, 1f); + + if (svertices.Count == 0) + return false; + + uint vcount = 0; + + float[] recip = new float[3]; + + float[] bmin = { Single.MaxValue, Single.MaxValue, Single.MaxValue }; + float[] bmax = { Single.MinValue, Single.MinValue, Single.MinValue }; + + for (int i = 0; i < svertices.Count; i++) + { + float3 p = svertices[i]; + + for (int j = 0; j < 3; j++) + { + if (p[j] < bmin[j]) + bmin[j] = p[j]; + if (p[j] > bmax[j]) + bmax[j] = p[j]; + } + } + + float dx = bmax[0] - bmin[0]; + float dy = bmax[1] - bmin[1]; + float dz = bmax[2] - bmin[2]; + + float3 center = new float3(); + + center.x = dx * 0.5f + bmin[0]; + center.y = dy * 0.5f + bmin[1]; + center.z = dz * 0.5f + bmin[2]; + + if (dx < EPSILON || dy < EPSILON || dz < EPSILON || svertices.Count < 3) + { + float len = Single.MaxValue; + + if (dx > EPSILON && dx < len) + len = dx; + if (dy > EPSILON && dy < len) + len = dy; + if (dz > EPSILON && dz < len) + len = dz; + + if (len == Single.MaxValue) + { + dx = dy = dz = 0.01f; // one centimeter + } + else + { + if (dx < EPSILON) // 1/5th the shortest non-zero edge. + dx = len * 0.05f; + if (dy < EPSILON) + dy = len * 0.05f; + if (dz < EPSILON) + dz = len * 0.05f; + } + + float x1 = center[0] - dx; + float x2 = center[0] + dx; + + float y1 = center[1] - dy; + float y2 = center[1] + dy; + + float z1 = center[2] - dz; + float z2 = center[2] + dz; + + addPoint(ref vcount, vertices, x1, y1, z1); + addPoint(ref vcount, vertices, x2, y1, z1); + addPoint(ref vcount, vertices, x2, y2, z1); + addPoint(ref vcount, vertices, x1, y2, z1); + addPoint(ref vcount, vertices, x1, y1, z2); + addPoint(ref vcount, vertices, x2, y1, z2); + addPoint(ref vcount, vertices, x2, y2, z2); + addPoint(ref vcount, vertices, x1, y2, z2); + + return true; // return cube + } + else + { + scale.x = dx; + scale.y = dy; + scale.z = dz; + + recip[0] = 1f / dx; + recip[1] = 1f / dy; + recip[2] = 1f / dz; + + center.x *= recip[0]; + center.y *= recip[1]; + center.z *= recip[2]; + } + + for (int i = 0; i < svertices.Count; i++) + { + float3 p = svertices[i]; + + float px = p[0]; + float py = p[1]; + float pz = p[2]; + + px = px * recip[0]; // normalize + py = py * recip[1]; // normalize + pz = pz * recip[2]; // normalize + + if (true) + { + int j; + + for (j = 0; j < vcount; j++) + { + float3 v = vertices[j]; + + float x = v[0]; + float y = v[1]; + float z = v[2]; + + float dx1 = Math.Abs(x - px); + float dy1 = Math.Abs(y - py); + float dz1 = Math.Abs(z - pz); + + if (dx1 < normalepsilon && dy1 < normalepsilon && dz1 < normalepsilon) + { + // ok, it is close enough to the old one + // now let us see if it is further from the center of the point cloud than the one we already recorded. + // in which case we keep this one instead. + float dist1 = GetDist(px, py, pz, center); + float dist2 = GetDist(v[0], v[1], v[2], center); + + if (dist1 > dist2) + { + v.x = px; + v.y = py; + v.z = pz; + } + + break; + } + } + + if (j == vcount) + { + float3 dest = new float3(px, py, pz); + vertices.Add(dest); + vcount++; + } + } + } + + // ok..now make sure we didn't prune so many vertices it is now invalid. + if (true) + { + float[] bmin2 = { Single.MaxValue, Single.MaxValue, Single.MaxValue }; + float[] bmax2 = { Single.MinValue, Single.MinValue, Single.MinValue }; + + for (int i = 0; i < vcount; i++) + { + float3 p = vertices[i]; + for (int j = 0; j < 3; j++) + { + if (p[j] < bmin2[j]) + bmin2[j] = p[j]; + if (p[j] > bmax2[j]) + bmax2[j] = p[j]; + } + } + + float dx2 = bmax2[0] - bmin2[0]; + float dy2 = bmax2[1] - bmin2[1]; + float dz2 = bmax2[2] - bmin2[2]; + + if (dx2 < EPSILON || dy2 < EPSILON || dz2 < EPSILON || vcount < 3) + { + float cx = dx2 * 0.5f + bmin2[0]; + float cy = dy2 * 0.5f + bmin2[1]; + float cz = dz2 * 0.5f + bmin2[2]; + + float len = Single.MaxValue; + + if (dx2 >= EPSILON && dx2 < len) + len = dx2; + if (dy2 >= EPSILON && dy2 < len) + len = dy2; + if (dz2 >= EPSILON && dz2 < len) + len = dz2; + + if (len == Single.MaxValue) + { + dx2 = dy2 = dz2 = 0.01f; // one centimeter + } + else + { + if (dx2 < EPSILON) // 1/5th the shortest non-zero edge. + dx2 = len * 0.05f; + if (dy2 < EPSILON) + dy2 = len * 0.05f; + if (dz2 < EPSILON) + dz2 = len * 0.05f; + } + + float x1 = cx - dx2; + float x2 = cx + dx2; + + float y1 = cy - dy2; + float y2 = cy + dy2; + + float z1 = cz - dz2; + float z2 = cz + dz2; + + vcount = 0; // add box + + addPoint(ref vcount, vertices, x1, y1, z1); + addPoint(ref vcount, vertices, x2, y1, z1); + addPoint(ref vcount, vertices, x2, y2, z1); + addPoint(ref vcount, vertices, x1, y2, z1); + addPoint(ref vcount, vertices, x1, y1, z2); + addPoint(ref vcount, vertices, x2, y1, z2); + addPoint(ref vcount, vertices, x2, y2, z2); + addPoint(ref vcount, vertices, x1, y2, z2); + + return true; + } + } + + return true; + } + + private static void BringOutYourDead(List verts, out List overts, List indices) + { + int[] used = new int[verts.Count]; + int ocount = 0; + + overts = new List(); + + for (int i = 0; i < indices.Count; i++) + { + int v = indices[i]; // original array index + + Debug.Assert(v >= 0 && v < verts.Count); + + if (used[v] != 0) // if already remapped + { + indices[i] = used[v] - 1; // index to new array + } + else + { + indices[i] = ocount; // new index mapping + + overts.Add(verts[v]); // copy old vert to new vert array + + ocount++; // increment output vert count + + Debug.Assert(ocount >= 0 && ocount <= verts.Count); + + used[v] = ocount; // assign new index remapping + } + } + } + + public static HullError CreateConvexHull(HullDesc desc, ref HullResult result) + { + HullError ret = HullError.QE_FAIL; + + PHullResult hr = new PHullResult(); + + uint vcount = (uint)desc.Vertices.Count; + if (vcount < 8) + vcount = 8; + + List vsource; + float3 scale = new float3(); + + bool ok = CleanupVertices(desc.Vertices, out vsource, desc.NormalEpsilon, out scale); // normalize point cloud, remove duplicates! + + if (ok) + { + if (true) // scale vertices back to their original size. + { + for (int i = 0; i < vsource.Count; i++) + { + float3 v = vsource[i]; + v.x *= scale[0]; + v.y *= scale[1]; + v.z *= scale[2]; + } + } + + float skinwidth = 0; + if (desc.HasHullFlag(HullFlag.QF_SKIN_WIDTH)) + skinwidth = desc.SkinWidth; + + ok = ComputeHull(vsource, ref hr, (int)desc.MaxVertices, skinwidth); + + if (ok) + { + List vscratch; + BringOutYourDead(hr.Vertices, out vscratch, hr.Indices); + + ret = HullError.QE_OK; + + if (desc.HasHullFlag(HullFlag.QF_TRIANGLES)) // if he wants the results as triangle! + { + result.Polygons = false; + result.Indices = hr.Indices; + result.OutputVertices = vscratch; + } + else + { + result.Polygons = true; + result.OutputVertices = vscratch; + + if (true) + { + List source = hr.Indices; + List dest = new List(); + for (int i = 0; i < hr.Indices.Count / 3; i++) + { + dest.Add(3); + dest.Add(source[i * 3 + 0]); + dest.Add(source[i * 3 + 1]); + dest.Add(source[i * 3 + 2]); + } + + result.Indices = dest; + } + } + } + } + + return ret; + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/LICENSE.txt b/addon-modules/ConvexDecompositionDotNet/LICENSE.txt new file mode 100644 index 0000000000..714ae898d8 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/LICENSE.txt @@ -0,0 +1,28 @@ +ConvexDecompositionDotNet +------------------------- + +The MIT License + +Copyright (c) 2010 Intel Corporation. +All rights reserved. + +Based on the convexdecomposition library from + by John W. Ratcliff and Stan Melax. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/addon-modules/ConvexDecompositionDotNet/Plane.cs b/addon-modules/ConvexDecompositionDotNet/Plane.cs new file mode 100644 index 0000000000..d099676208 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/Plane.cs @@ -0,0 +1,99 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class Plane + { + public float3 normal = new float3(); + public float dist; // distance below origin - the D from plane equasion Ax+By+Cz+D=0 + + public Plane(float3 n, float d) + { + normal = new float3(n); + dist = d; + } + + public Plane(Plane p) + { + normal = new float3(p.normal); + dist = p.dist; + } + + public Plane() + { + dist = 0; + } + + public void Transform(float3 position, Quaternion orientation) + { + // Transforms the plane to the space defined by the + // given position/orientation + float3 newNormal = Quaternion.Inverse(orientation) * normal; + float3 origin = Quaternion.Inverse(orientation) * (-normal * dist - position); + + normal = newNormal; + dist = -float3.dot(newNormal, origin); + } + + public override int GetHashCode() + { + return normal.GetHashCode() ^ dist.GetHashCode(); + } + + public override bool Equals(object obj) + { + Plane p = obj as Plane; + if (p == null) + return false; + + return this == p; + } + + public static bool operator ==(Plane a, Plane b) + { + return (a.normal == b.normal && a.dist == b.dist); + } + + public static bool operator !=(Plane a, Plane b) + { + return !(a == b); + } + + public static Plane PlaneFlip(Plane plane) + { + return new Plane(-plane.normal, -plane.dist); + } + + public static bool coplanar(Plane a, Plane b) + { + return (a == b || a == PlaneFlip(b)); + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/PlaneTri.cs b/addon-modules/ConvexDecompositionDotNet/PlaneTri.cs new file mode 100644 index 0000000000..31f0182520 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/PlaneTri.cs @@ -0,0 +1,211 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public enum PlaneTriResult : int + { + PTR_FRONT, + PTR_BACK, + PTR_SPLIT + } + + public static class PlaneTri + { + private static float DistToPt(float3 p, float4 plane) + { + return p.x * plane.x + p.y * plane.y + p.z * plane.z + plane.w; + } + + private static PlaneTriResult getSidePlane(float3 p, float4 plane, float epsilon) + { + float d = DistToPt(p, plane); + + if ((d + epsilon) > 0f) + return PlaneTriResult.PTR_FRONT; // it is 'in front' within the provided epsilon value. + + return PlaneTriResult.PTR_BACK; + } + + private static void add(float3 p, float3[] dest, ref int pcount) + { + dest[pcount++] = new float3(p); + Debug.Assert(pcount <= 4); + } + + // assumes that the points are on opposite sides of the plane! + private static void intersect(float3 p1, float3 p2, float3 split, float4 plane) + { + float dp1 = DistToPt(p1, plane); + float[] dir = new float[3]; + + dir[0] = p2[0] - p1[0]; + dir[1] = p2[1] - p1[1]; + dir[2] = p2[2] - p1[2]; + + float dot1 = dir[0] * plane[0] + dir[1] * plane[1] + dir[2] * plane[2]; + float dot2 = dp1 - plane[3]; + + float t = -(plane[3] + dot2) / dot1; + + split.x = (dir[0] * t) + p1[0]; + split.y = (dir[1] * t) + p1[1]; + split.z = (dir[2] * t) + p1[2]; + } + + public static PlaneTriResult planeTriIntersection(float4 plane, FaceTri triangle, float epsilon, ref float3[] front, out int fcount, ref float3[] back, out int bcount) + { + fcount = 0; + bcount = 0; + + // get the three vertices of the triangle. + float3 p1 = triangle.P1; + float3 p2 = triangle.P2; + float3 p3 = triangle.P3; + + PlaneTriResult r1 = getSidePlane(p1, plane, epsilon); // compute the side of the plane each vertex is on + PlaneTriResult r2 = getSidePlane(p2, plane, epsilon); + PlaneTriResult r3 = getSidePlane(p3, plane, epsilon); + + if (r1 == r2 && r1 == r3) // if all three vertices are on the same side of the plane. + { + if (r1 == PlaneTriResult.PTR_FRONT) // if all three are in front of the plane, then copy to the 'front' output triangle. + { + add(p1, front, ref fcount); + add(p2, front, ref fcount); + add(p3, front, ref fcount); + } + else + { + add(p1, back, ref bcount); // if all three are in 'back' then copy to the 'back' output triangle. + add(p2, back, ref bcount); + add(p3, back, ref bcount); + } + return r1; // if all three points are on the same side of the plane return result + } + + // ok.. we need to split the triangle at the plane. + + // First test ray segment P1 to P2 + if (r1 == r2) // if these are both on the same side... + { + if (r1 == PlaneTriResult.PTR_FRONT) + { + add(p1, front, ref fcount); + add(p2, front, ref fcount); + } + else + { + add(p1, back, ref bcount); + add(p2, back, ref bcount); + } + } + else + { + float3 split = new float3(); + intersect(p1, p2, split, plane); + + if (r1 == PlaneTriResult.PTR_FRONT) + { + + add(p1, front, ref fcount); + add(split, front, ref fcount); + + add(split, back, ref bcount); + add(p2, back, ref bcount); + + } + else + { + add(p1, back, ref bcount); + add(split, back, ref bcount); + + add(split, front, ref fcount); + add(p2, front, ref fcount); + } + + } + + // Next test ray segment P2 to P3 + if (r2 == r3) // if these are both on the same side... + { + if (r3 == PlaneTriResult.PTR_FRONT) + { + add(p3, front, ref fcount); + } + else + { + add(p3, back, ref bcount); + } + } + else + { + float3 split = new float3(); // split the point + intersect(p2, p3, split, plane); + + if (r3 == PlaneTriResult.PTR_FRONT) + { + add(split, front, ref fcount); + add(split, back, ref bcount); + + add(p3, front, ref fcount); + } + else + { + add(split, front, ref fcount); + add(split, back, ref bcount); + + add(p3, back, ref bcount); + } + } + + // Next test ray segment P3 to P1 + if (r3 != r1) // if these are both on the same side... + { + float3 split = new float3(); // split the point + intersect(p3, p1, split, plane); + + if (r1 == PlaneTriResult.PTR_FRONT) + { + add(split, front, ref fcount); + add(split, back, ref bcount); + } + else + { + add(split, front, ref fcount); + add(split, back, ref bcount); + } + } + + return PlaneTriResult.PTR_SPLIT; + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/Quaternion.cs b/addon-modules/ConvexDecompositionDotNet/Quaternion.cs new file mode 100644 index 0000000000..0ba8f17a9c --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/Quaternion.cs @@ -0,0 +1,209 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class Quaternion : float4 + { + public Quaternion() + { + x = y = z = 0.0f; + w = 1.0f; + } + + public Quaternion(float3 v, float t) + { + v = float3.normalize(v); + w = (float)Math.Cos(t / 2.0f); + v = v * (float)Math.Sin(t / 2.0f); + x = v.x; + y = v.y; + z = v.z; + } + + public Quaternion(float _x, float _y, float _z, float _w) + { + x = _x; + y = _y; + z = _z; + w = _w; + } + + public float angle() + { + return (float)Math.Acos(w) * 2.0f; + } + + public float3 axis() + { + float3 a = new float3(x, y, z); + if (Math.Abs(angle()) < 0.0000001f) + return new float3(1f, 0f, 0f); + return a * (1 / (float)Math.Sin(angle() / 2.0f)); + } + + public float3 xdir() + { + return new float3(1 - 2 * (y * y + z * z), 2 * (x * y + w * z), 2 * (x * z - w * y)); + } + + public float3 ydir() + { + return new float3(2 * (x * y - w * z), 1 - 2 * (x * x + z * z), 2 * (y * z + w * x)); + } + + public float3 zdir() + { + return new float3(2 * (x * z + w * y), 2 * (y * z - w * x), 1 - 2 * (x * x + y * y)); + } + + public float3x3 getmatrix() + { + return new float3x3(xdir(), ydir(), zdir()); + } + + public static implicit operator float3x3(Quaternion q) + { + return q.getmatrix(); + } + + public static Quaternion operator *(Quaternion a, Quaternion b) + { + Quaternion c = new Quaternion(); + c.w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z; + c.x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y; + c.y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x; + c.z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w; + return c; + } + + public static float3 operator *(Quaternion q, float3 v) + { + // The following is equivalent to: + //return (q.getmatrix() * v); + float qx2 = q.x * q.x; + float qy2 = q.y * q.y; + float qz2 = q.z * q.z; + + float qxqy = q.x * q.y; + float qxqz = q.x * q.z; + float qxqw = q.x * q.w; + float qyqz = q.y * q.z; + float qyqw = q.y * q.w; + float qzqw = q.z * q.w; + return new float3((1 - 2 * (qy2 + qz2)) * v.x + (2 * (qxqy - qzqw)) * v.y + (2 * (qxqz + qyqw)) * v.z, (2 * (qxqy + qzqw)) * v.x + (1 - 2 * (qx2 + qz2)) * v.y + (2 * (qyqz - qxqw)) * v.z, (2 * (qxqz - qyqw)) * v.x + (2 * (qyqz + qxqw)) * v.y + (1 - 2 * (qx2 + qy2)) * v.z); + } + + public static Quaternion operator +(Quaternion a, Quaternion b) + { + return new Quaternion(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); + } + + public static Quaternion operator *(Quaternion a, float b) + { + return new Quaternion(a.x *b, a.y *b, a.z *b, a.w *b); + } + + public static Quaternion normalize(Quaternion a) + { + float m = (float)Math.Sqrt(a.w * a.w + a.x * a.x + a.y * a.y + a.z * a.z); + if (m < 0.000000001f) + { + a.w = 1; + a.x = a.y = a.z = 0; + return a; + } + return a * (1f / m); + } + + public static float dot(Quaternion a, Quaternion b) + { + return (a.w * b.w + a.x * b.x + a.y * b.y + a.z * b.z); + } + + public static Quaternion slerp(Quaternion a, Quaternion b, float interp) + { + if (dot(a, b) < 0.0) + { + a.w = -a.w; + a.x = -a.x; + a.y = -a.y; + a.z = -a.z; + } + float d = dot(a, b); + if (d >= 1.0) + { + return a; + } + float theta = (float)Math.Acos(d); + if (theta == 0.0f) + { + return (a); + } + return a * ((float)Math.Sin(theta - interp * theta) / (float)Math.Sin(theta)) + b * ((float)Math.Sin(interp * theta) / (float)Math.Sin(theta)); + } + + public static Quaternion Interpolate(Quaternion q0, Quaternion q1, float alpha) + { + return slerp(q0, q1, alpha); + } + + public static Quaternion Inverse(Quaternion q) + { + return new Quaternion(-q.x, -q.y, -q.z, q.w); + } + + public static Quaternion YawPitchRoll(float yaw, float pitch, float roll) + { + roll *= (3.14159264f / 180.0f); + yaw *= (3.14159264f / 180.0f); + pitch *= (3.14159264f / 180.0f); + return new Quaternion(new float3(0.0f, 0.0f, 1.0f), yaw) * new Quaternion(new float3(1.0f, 0.0f, 0.0f), pitch) * new Quaternion(new float3(0.0f, 1.0f, 0.0f), roll); + } + + public static float Yaw(Quaternion q) + { + float3 v = q.ydir(); + return (v.y == 0.0 && v.x == 0.0) ? 0.0f : (float)Math.Atan2(-v.x, v.y) * (180.0f / 3.14159264f); + } + + public static float Pitch(Quaternion q) + { + float3 v = q.ydir(); + return (float)Math.Atan2(v.z, Math.Sqrt(v.x * v.x + v.y * v.y)) * (180.0f / 3.14159264f); + } + + public static float Roll(Quaternion q) + { + q = new Quaternion(new float3(0.0f, 0.0f, 1.0f), -Yaw(q) * (3.14159264f / 180.0f)) * q; + q = new Quaternion(new float3(1.0f, 0.0f, 0.0f), -Pitch(q) * (3.14159264f / 180.0f)) * q; + return (float)Math.Atan2(-q.xdir().z, q.xdir().x) * (180.0f / 3.14159264f); + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/README.txt b/addon-modules/ConvexDecompositionDotNet/README.txt new file mode 100644 index 0000000000..fc53ae75c8 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/README.txt @@ -0,0 +1,7 @@ +ConvexDecompositionDotNet +========================= + +A C# port of the ConvexDecomposition library by John W. Ratcliff and Stan Melax. +The original C++ version is available at . +See the blog post at +for a thorough explanation of generating convex hulls from concave meshes. diff --git a/addon-modules/ConvexDecompositionDotNet/SplitPlane.cs b/addon-modules/ConvexDecompositionDotNet/SplitPlane.cs new file mode 100644 index 0000000000..9f06a9a23f --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/SplitPlane.cs @@ -0,0 +1,265 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class Rect3d + { + public float[] mMin = new float[3]; + public float[] mMax = new float[3]; + + public Rect3d() + { + } + + public Rect3d(float[] bmin, float[] bmax) + { + mMin[0] = bmin[0]; + mMin[1] = bmin[1]; + mMin[2] = bmin[2]; + + mMax[0] = bmax[0]; + mMax[1] = bmax[1]; + mMax[2] = bmax[2]; + } + + public void SetMin(float[] bmin) + { + mMin[0] = bmin[0]; + mMin[1] = bmin[1]; + mMin[2] = bmin[2]; + } + + public void SetMax(float[] bmax) + { + mMax[0] = bmax[0]; + mMax[1] = bmax[1]; + mMax[2] = bmax[2]; + } + + public void SetMin(float x, float y, float z) + { + mMin[0] = x; + mMin[1] = y; + mMin[2] = z; + } + + public void SetMax(float x, float y, float z) + { + mMax[0] = x; + mMax[1] = y; + mMax[2] = z; + } + } + + public static class SplitPlane + { + public static bool computeSplitPlane(List vertices, List indices, ref float4 plane) + { + float[] bmin = { Single.MaxValue, Single.MaxValue, Single.MaxValue }; + float[] bmax = { Single.MinValue, Single.MinValue, Single.MinValue }; + + for (int i = 0; i < vertices.Count; i++) + { + float3 p = vertices[i]; + + if (p[0] < bmin[0]) + bmin[0] = p[0]; + if (p[1] < bmin[1]) + bmin[1] = p[1]; + if (p[2] < bmin[2]) + bmin[2] = p[2]; + + if (p[0] > bmax[0]) + bmax[0] = p[0]; + if (p[1] > bmax[1]) + bmax[1] = p[1]; + if (p[2] > bmax[2]) + bmax[2] = p[2]; + } + + float dx = bmax[0] - bmin[0]; + float dy = bmax[1] - bmin[1]; + float dz = bmax[2] - bmin[2]; + + float laxis = dx; + + int axis = 0; + + if (dy > dx) + { + axis = 1; + laxis = dy; + } + + if (dz > dx && dz > dy) + { + axis = 2; + laxis = dz; + } + + float[] p1 = new float[3]; + float[] p2 = new float[3]; + float[] p3 = new float[3]; + + p3[0] = p2[0] = p1[0] = bmin[0] + dx * 0.5f; + p3[1] = p2[1] = p1[1] = bmin[1] + dy * 0.5f; + p3[2] = p2[2] = p1[2] = bmin[2] + dz * 0.5f; + + Rect3d b = new Rect3d(bmin, bmax); + + Rect3d b1 = new Rect3d(); + Rect3d b2 = new Rect3d(); + + splitRect(axis, b, b1, b2, p1); + + switch (axis) + { + case 0: + p2[1] = bmin[1]; + p2[2] = bmin[2]; + + if (dz > dy) + { + p3[1] = bmax[1]; + p3[2] = bmin[2]; + } + else + { + p3[1] = bmin[1]; + p3[2] = bmax[2]; + } + + break; + case 1: + p2[0] = bmin[0]; + p2[2] = bmin[2]; + + if (dx > dz) + { + p3[0] = bmax[0]; + p3[2] = bmin[2]; + } + else + { + p3[0] = bmin[0]; + p3[2] = bmax[2]; + } + + break; + case 2: + p2[0] = bmin[0]; + p2[1] = bmin[1]; + + if (dx > dy) + { + p3[0] = bmax[0]; + p3[1] = bmin[1]; + } + else + { + p3[0] = bmin[0]; + p3[1] = bmax[1]; + } + + break; + } + + computePlane(p1, p2, p3, plane); + + return true; + } + + internal static void computePlane(float[] A, float[] B, float[] C, float4 plane) + { + float vx = (B[0] - C[0]); + float vy = (B[1] - C[1]); + float vz = (B[2] - C[2]); + + float wx = (A[0] - B[0]); + float wy = (A[1] - B[1]); + float wz = (A[2] - B[2]); + + float vw_x = vy * wz - vz * wy; + float vw_y = vz * wx - vx * wz; + float vw_z = vx * wy - vy * wx; + + float mag = (float)Math.Sqrt((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z)); + + if (mag < 0.000001f) + { + mag = 0; + } + else + { + mag = 1.0f / mag; + } + + float x = vw_x * mag; + float y = vw_y * mag; + float z = vw_z * mag; + + float D = 0.0f - ((x * A[0]) + (y * A[1]) + (z * A[2])); + + plane.x = x; + plane.y = y; + plane.z = z; + plane.w = D; + } + + public static void splitRect(int axis, Rect3d source, Rect3d b1, Rect3d b2, float[] midpoint) + { + switch (axis) + { + case 0: + b1.SetMin(source.mMin); + b1.SetMax(midpoint[0], source.mMax[1], source.mMax[2]); + + b2.SetMin(midpoint[0], source.mMin[1], source.mMin[2]); + b2.SetMax(source.mMax); + break; + case 1: + b1.SetMin(source.mMin); + b1.SetMax(source.mMax[0], midpoint[1], source.mMax[2]); + + b2.SetMin(source.mMin[0], midpoint[1], source.mMin[2]); + b2.SetMax(source.mMax); + break; + case 2: + b1.SetMin(source.mMin); + b1.SetMax(source.mMax[0], source.mMax[1], midpoint[2]); + + b2.SetMin(source.mMin[0], source.mMin[1], midpoint[2]); + b2.SetMax(source.mMax); + break; + } + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/VertexLookup.cs b/addon-modules/ConvexDecompositionDotNet/VertexLookup.cs new file mode 100644 index 0000000000..6f17c9f711 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/VertexLookup.cs @@ -0,0 +1,70 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class VertexPool + { + private List mVertices = new List(); + private Dictionary mIndices = new Dictionary(); + + public int getIndex(float3 vtx) + { + int idx; + if (mIndices.TryGetValue(vtx, out idx)) + return idx; + + idx = mVertices.Count; + mVertices.Add(vtx); + mIndices.Add(vtx, idx); + return idx; + } + + public float3 Get(int idx) + { + return mVertices[idx]; + } + + public int GetSize() + { + return mVertices.Count; + } + + public List GetVertices() + { + return mVertices; + } + + public void Clear() + { + mVertices.Clear(); + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/float2.cs b/addon-modules/ConvexDecompositionDotNet/float2.cs new file mode 100644 index 0000000000..ce88fc826e --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/float2.cs @@ -0,0 +1,70 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class float2 + { + public float x; + public float y; + + public float2() + { + } + + public float2(float _x, float _y) + { + x = _x; + y = _y; + } + + public float this[int i] + { + get + { + switch (i) + { + case 0: return x; + case 1: return y; + } + throw new ArgumentOutOfRangeException(); + } + } + + public static float2 operator -(float2 a, float2 b) + { + return new float2(a.x - b.x, a.y - b.y); + } + + public static float2 operator +(float2 a, float2 b) + { + return new float2(a.x + b.x, a.y + b.y); + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/float3.cs b/addon-modules/ConvexDecompositionDotNet/float3.cs new file mode 100644 index 0000000000..4389114848 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/float3.cs @@ -0,0 +1,444 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class float3 : IEquatable + { + public float x; + public float y; + public float z; + + public float3() + { + x = 0; + y = 0; + z = 0; + } + + public float3(float _x, float _y, float _z) + { + x = _x; + y = _y; + z = _z; + } + + public float3(float3 f) + { + x = f.x; + y = f.y; + z = f.z; + } + + public float this[int i] + { + get + { + switch (i) + { + case 0: return x; + case 1: return y; + case 2: return z; + } + throw new ArgumentOutOfRangeException(); + } + } + + public float Distance(float3 a) + { + float3 d = new float3(a.x - x, a.y - y, a.z - z); + return d.Length(); + } + + public float Distance2(float3 a) + { + float dx = a.x - x; + float dy = a.y - y; + float dz = a.z - z; + return dx * dx + dy * dy + dz * dz; + } + + public float Length() + { + return (float)Math.Sqrt(x * x + y * y + z * z); + } + + public float Area(float3 p1, float3 p2) + { + float A = Partial(p1); + A += p1.Partial(p2); + A += p2.Partial(this); + return A * 0.5f; + } + + public float Partial(float3 p) + { + return (x * p.y) - (p.x * y); + } + + // Given a point and a line (defined by two points), compute the closest point + // in the line. (The line is treated as infinitely long.) + public void NearestPointInLine(float3 point, float3 line0, float3 line1) + { + float3 nearestPoint = new float3(); + float3 lineDelta = line1 - line0; + + // Handle degenerate lines + if (lineDelta == float3.Zero) + { + nearestPoint = line0; + } + else + { + float delta = float3.dot(point - line0, lineDelta) / float3.dot(lineDelta, lineDelta); + nearestPoint = line0 + lineDelta * delta; + } + + this.x = nearestPoint.x; + this.y = nearestPoint.y; + this.z = nearestPoint.z; + } + + // Given a point and a line segment (defined by two points), compute the closest point + // in the line. Cap the point at the endpoints of the line segment. + public void NearestPointInLineSegment(float3 point, float3 line0, float3 line1) + { + float3 nearestPoint = new float3(); + float3 lineDelta = line1 - line0; + + // Handle degenerate lines + if (lineDelta == Zero) + { + nearestPoint = line0; + } + else + { + float delta = float3.dot(point - line0, lineDelta) / float3.dot(lineDelta, lineDelta); + + // Clamp the point to conform to the segment's endpoints + if (delta < 0) + delta = 0; + else if (delta > 1) + delta = 1; + + nearestPoint = line0 + lineDelta * delta; + } + + this.x = nearestPoint.x; + this.y = nearestPoint.y; + this.z = nearestPoint.z; + } + + // Given a point and a triangle (defined by three points), compute the closest point + // in the triangle. Clamp the point so it's confined to the area of the triangle. + public void NearestPointInTriangle(float3 point, float3 triangle0, float3 triangle1, float3 triangle2) + { + float3 nearestPoint = new float3(); + + float3 lineDelta0 = triangle1 - triangle0; + float3 lineDelta1 = triangle2 - triangle0; + + // Handle degenerate triangles + if ((lineDelta0 == Zero) || (lineDelta1 == Zero)) + { + nearestPoint.NearestPointInLineSegment(point, triangle1, triangle2); + } + else if (lineDelta0 == lineDelta1) + { + nearestPoint.NearestPointInLineSegment(point, triangle0, triangle1); + } + else + { + float3[] axis = new float3[3] { new float3(), new float3(), new float3() }; + axis[0].NearestPointInLine(triangle0, triangle1, triangle2); + axis[1].NearestPointInLine(triangle1, triangle0, triangle2); + axis[2].NearestPointInLine(triangle2, triangle0, triangle1); + + float3 axisDot = new float3(); + axisDot.x = dot(triangle0 - axis[0], point - axis[0]); + axisDot.y = dot(triangle1 - axis[1], point - axis[1]); + axisDot.z = dot(triangle2 - axis[2], point - axis[2]); + + bool bForce = true; + float bestMagnitude2 = 0; + float closeMagnitude2; + float3 closePoint = new float3(); + + if (axisDot.x < 0f) + { + closePoint.NearestPointInLineSegment(point, triangle1, triangle2); + closeMagnitude2 = point.Distance2(closePoint); + if (bForce || (bestMagnitude2 > closeMagnitude2)) + { + bForce = false; + bestMagnitude2 = closeMagnitude2; + nearestPoint = closePoint; + } + } + if (axisDot.y < 0f) + { + closePoint.NearestPointInLineSegment(point, triangle0, triangle2); + closeMagnitude2 = point.Distance2(closePoint); + if (bForce || (bestMagnitude2 > closeMagnitude2)) + { + bForce = false; + bestMagnitude2 = closeMagnitude2; + nearestPoint = closePoint; + } + } + if (axisDot.z < 0f) + { + closePoint.NearestPointInLineSegment(point, triangle0, triangle1); + closeMagnitude2 = point.Distance2(closePoint); + if (bForce || (bestMagnitude2 > closeMagnitude2)) + { + bForce = false; + bestMagnitude2 = closeMagnitude2; + nearestPoint = closePoint; + } + } + + // If bForce is true at this point, it means the nearest point lies + // inside the triangle; use the nearest-point-on-a-plane equation + if (bForce) + { + float3 normal; + + // Get the normal of the polygon (doesn't have to be a unit vector) + normal = float3.cross(lineDelta0, lineDelta1); + + float3 pointDelta = point - triangle0; + float delta = float3.dot(normal, pointDelta) / float3.dot(normal, normal); + + nearestPoint = point - normal * delta; + } + } + + this.x = nearestPoint.x; + this.y = nearestPoint.y; + this.z = nearestPoint.z; + } + + public static float3 operator +(float3 a, float3 b) + { + return new float3(a.x + b.x, a.y + b.y, a.z + b.z); + } + + public static float3 operator -(float3 a, float3 b) + { + return new float3(a.x - b.x, a.y - b.y, a.z - b.z); + } + + public static float3 operator -(float3 a, float s) + { + return new float3(a.x - s, a.y - s, a.z - s); + } + + public static float3 operator -(float3 v) + { + return new float3(-v.x, -v.y, -v.z); + } + + public static float3 operator *(float3 v, float s) + { + return new float3(v.x * s, v.y * s, v.z * s); + } + + public static float3 operator *(float s, float3 v) + { + return new float3(v.x * s, v.y * s, v.z * s); + } + + public static float3 operator *(float3 v, float3x3 m) + { + return new float3((m.x.x * v.x + m.y.x * v.y + m.z.x * v.z), (m.x.y * v.x + m.y.y * v.y + m.z.y * v.z), (m.x.z * v.x + m.y.z * v.y + m.z.z * v.z)); + } + + public static float3 operator *(float3x3 m, float3 v) + { + return new float3(dot(m.x, v), dot(m.y, v), dot(m.z, v)); + } + + public static float3 operator /(float3 v, float s) + { + float sinv = 1.0f / s; + return new float3(v.x * sinv, v.y * sinv, v.z * sinv); + } + + public bool Equals(float3 other) + { + return this == other; + } + + public override bool Equals(object obj) + { + float3 f = obj as float3; + if (f == null) + return false; + + return this == f; + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode(); + } + + public static bool operator ==(float3 a, float3 b) + { + // If both are null, or both are same instance, return true. + if (System.Object.ReferenceEquals(a, b)) + return true; + // If one is null, but not both, return false. + if (((object)a == null) || ((object)b == null)) + return false; + + return (a.x == b.x && a.y == b.y && a.z == b.z); + } + + public static bool operator !=(float3 a, float3 b) + { + return (a.x != b.x || a.y != b.y || a.z != b.z); + } + + public static float dot(float3 a, float3 b) + { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + public static float3 cmul(float3 v1, float3 v2) + { + return new float3(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z); + } + + public static float3 cross(float3 a, float3 b) + { + return new float3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); + } + + public static float3 Interpolate(float3 v0, float3 v1, float alpha) + { + return v0 * (1 - alpha) + v1 * alpha; + } + + public static float3 Round(float3 a, int digits) + { + return new float3((float)Math.Round(a.x, digits), (float)Math.Round(a.y, digits), (float)Math.Round(a.z, digits)); + } + + public static float3 VectorMax(float3 a, float3 b) + { + return new float3(Math.Max(a.x, b.x), Math.Max(a.y, b.y), Math.Max(a.z, b.z)); + } + + public static float3 VectorMin(float3 a, float3 b) + { + return new float3(Math.Min(a.x, b.x), Math.Min(a.y, b.y), Math.Min(a.z, b.z)); + } + + public static float3 vabs(float3 v) + { + return new float3(Math.Abs(v.x), Math.Abs(v.y), Math.Abs(v.z)); + } + + public static float magnitude(float3 v) + { + return (float)Math.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z); + } + + public static float3 normalize(float3 v) + { + float d = magnitude(v); + if (d == 0) + d = 0.1f; + d = 1 / d; + return new float3(v.x * d, v.y * d, v.z * d); + } + + public static float3 safenormalize(float3 v) + { + if (magnitude(v) <= 0.0f) + return new float3(1, 0, 0); + else + return normalize(v); + } + + public static float Yaw(float3 v) + { + return (v.y == 0.0 && v.x == 0.0) ? 0.0f : (float)Math.Atan2(-v.x, v.y) * (180.0f / 3.14159264f); + } + + public static float Pitch(float3 v) + { + return (float)Math.Atan2(v.z, Math.Sqrt(v.x * v.x + v.y * v.y)) * (180.0f / 3.14159264f); + } + + public float ComputePlane(float3 A, float3 B, float3 C) + { + float vx, vy, vz, wx, wy, wz, vw_x, vw_y, vw_z, mag; + + vx = (B.x - C.x); + vy = (B.y - C.y); + vz = (B.z - C.z); + + wx = (A.x - B.x); + wy = (A.y - B.y); + wz = (A.z - B.z); + + vw_x = vy * wz - vz * wy; + vw_y = vz * wx - vx * wz; + vw_z = vx * wy - vy * wx; + + mag = (float)Math.Sqrt((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z)); + + if (mag < 0.000001f) + { + mag = 0; + } + else + { + mag = 1.0f / mag; + } + + x = vw_x * mag; + y = vw_y * mag; + z = vw_z * mag; + + float D = 0.0f - ((x * A.x) + (y * A.y) + (z * A.z)); + return D; + } + + public override string ToString() + { + return String.Format("<{0}, {1}, {2}>", x, y, z); + } + + public static readonly float3 Zero = new float3(); + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/float3x3.cs b/addon-modules/ConvexDecompositionDotNet/float3x3.cs new file mode 100644 index 0000000000..76cf06347a --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/float3x3.cs @@ -0,0 +1,195 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class float3x3 + { + public float3 x = new float3(); + public float3 y = new float3(); + public float3 z = new float3(); + + public float3x3() + { + } + + public float3x3(float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz) + { + x = new float3(xx, xy, xz); + y = new float3(yx, yy, yz); + z = new float3(zx, zy, zz); + } + + public float3x3(float3 _x, float3 _y, float3 _z) + { + x = new float3(_x); + y = new float3(_y); + z = new float3(_z); + } + + public float3 this[int i] + { + get + { + switch (i) + { + case 0: return x; + case 1: return y; + case 2: return z; + } + throw new ArgumentOutOfRangeException(); + } + } + + public float this[int i, int j] + { + get + { + switch (i) + { + case 0: + switch (j) + { + case 0: return x.x; + case 1: return x.y; + case 2: return x.z; + } + break; + case 1: + switch (j) + { + case 0: return y.x; + case 1: return y.y; + case 2: return y.z; + } + break; + case 2: + switch (j) + { + case 0: return z.x; + case 1: return z.y; + case 2: return z.z; + } + break; + } + throw new ArgumentOutOfRangeException(); + } + set + { + switch (i) + { + case 0: + switch (j) + { + case 0: x.x = value; return; + case 1: x.y = value; return; + case 2: x.z = value; return; + } + break; + case 1: + switch (j) + { + case 0: y.x = value; return; + case 1: y.y = value; return; + case 2: y.z = value; return; + } + break; + case 2: + switch (j) + { + case 0: z.x = value; return; + case 1: z.y = value; return; + case 2: z.z = value; return; + } + break; + } + throw new ArgumentOutOfRangeException(); + } + } + + public static float3x3 Transpose(float3x3 m) + { + return new float3x3(new float3(m.x.x, m.y.x, m.z.x), new float3(m.x.y, m.y.y, m.z.y), new float3(m.x.z, m.y.z, m.z.z)); + } + + public static float3x3 operator *(float3x3 a, float3x3 b) + { + return new float3x3(a.x * b, a.y * b, a.z * b); + } + + public static float3x3 operator *(float3x3 a, float s) + { + return new float3x3(a.x * s, a.y * s, a.z * s); + } + + public static float3x3 operator /(float3x3 a, float s) + { + float t = 1f / s; + return new float3x3(a.x * t, a.y * t, a.z * t); + } + + public static float3x3 operator +(float3x3 a, float3x3 b) + { + return new float3x3(a.x + b.x, a.y + b.y, a.z + b.z); + } + + public static float3x3 operator -(float3x3 a, float3x3 b) + { + return new float3x3(a.x - b.x, a.y - b.y, a.z - b.z); + } + + public static float Determinant(float3x3 m) + { + return m.x.x * m.y.y * m.z.z + m.y.x * m.z.y * m.x.z + m.z.x * m.x.y * m.y.z - m.x.x * m.z.y * m.y.z - m.y.x * m.x.y * m.z.z - m.z.x * m.y.y * m.x.z; + } + + public static float3x3 Inverse(float3x3 a) + { + float3x3 b = new float3x3(); + float d = Determinant(a); + Debug.Assert(d != 0); + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + int i1 = (i + 1) % 3; + int i2 = (i + 2) % 3; + int j1 = (j + 1) % 3; + int j2 = (j + 2) % 3; + + // reverse indexs i&j to take transpose + b[i, j] = (a[i1][j1] * a[i2][j2] - a[i1][j2] * a[i2][j1]) / d; + } + } + return b; + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/float4.cs b/addon-modules/ConvexDecompositionDotNet/float4.cs new file mode 100644 index 0000000000..fa6087679f --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/float4.cs @@ -0,0 +1,170 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class float4 + { + public float x; + public float y; + public float z; + public float w; + + public float4() + { + x = 0; + y = 0; + z = 0; + w = 0; + } + + public float4(float _x, float _y, float _z, float _w) + { + x = _x; + y = _y; + z = _z; + w = _w; + } + + public float4(float3 v, float _w) + { + x = v.x; + y = v.y; + z = v.z; + w = _w; + } + + public float4(float4 f) + { + x = f.x; + y = f.y; + z = f.z; + w = f.w; + } + + public float this[int i] + { + get + { + switch (i) + { + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + } + throw new ArgumentOutOfRangeException(); + } + } + + public float3 xyz() + { + return new float3(x, y, z); + } + + public void setxyz(float3 xyz) + { + x = xyz.x; + y = xyz.y; + z = xyz.z; + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + } + + public override bool Equals(object obj) + { + float4 f = obj as float4; + if (f == null) + return false; + + return this == f; + } + + public static float4 Homogenize(float3 v3) + { + return Homogenize(v3, 1.0f); + } + + //C++ TO C# CONVERTER NOTE: C# does not allow default values for parameters. Overloaded methods are inserted above. + //ORIGINAL LINE: float4 Homogenize(const float3 &v3, const float &w =1.0f) + public static float4 Homogenize(float3 v3, float w) + { + return new float4(v3.x, v3.y, v3.z, w); + } + + public static float4 cmul(float4 a, float4 b) + { + return new float4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); + } + + public static float4 operator +(float4 a, float4 b) + { + return new float4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); + } + public static float4 operator -(float4 a, float4 b) + { + return new float4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); + } + + public static float4 operator *(float4 v, float4x4 m) + { + return v.x * m.x + v.y * m.y + v.z * m.z + v.w * m.w; // yes this actually works + } + + public static bool operator ==(float4 a, float4 b) + { + // If both are null, or both are same instance, return true. + if (System.Object.ReferenceEquals(a, b)) + return true; + // If one is null, but not both, return false. + if (((object)a == null) || ((object)b == null)) + return false; + + return (a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w); + } + + public static bool operator !=(float4 a, float4 b) + { + return !(a == b); + } + + public static float4 operator *(float4 v, float s) + { + return new float4(v.x * s, v.y * s, v.z * s, v.w * s); + } + + public static float4 operator *(float s, float4 v) + { + return new float4(v.x * s, v.y * s, v.z * s, v.w * s); + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/float4x4.cs b/addon-modules/ConvexDecompositionDotNet/float4x4.cs new file mode 100644 index 0000000000..7d1592f00c --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/float4x4.cs @@ -0,0 +1,284 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class float4x4 + { + public float4 x = new float4(); + public float4 y = new float4(); + public float4 z = new float4(); + public float4 w = new float4(); + + public float4x4() + { + } + + public float4x4(float4 _x, float4 _y, float4 _z, float4 _w) + { + x = new float4(_x); + y = new float4(_y); + z = new float4(_z); + w = new float4(_w); + } + + public float4x4( + float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) + { + x = new float4(m00, m01, m02, m03); + y = new float4(m10, m11, m12, m13); + z = new float4(m20, m21, m22, m23); + w = new float4(m30, m31, m32, m33); + } + + public float4x4(float4x4 m) + { + x = new float4(m.x); + y = new float4(m.y); + z = new float4(m.z); + w = new float4(m.w); + } + + public float4 this[int i] + { + get + { + switch (i) + { + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + } + throw new ArgumentOutOfRangeException(); + } + set + { + switch (i) + { + case 0: x = value; return; + case 1: y = value; return; + case 2: z = value; return; + case 3: w = value; return; + } + throw new ArgumentOutOfRangeException(); + } + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode(); + } + + public override bool Equals(object obj) + { + float4x4 m = obj as float4x4; + if (m == null) + return false; + + return this == m; + } + + public static float4x4 operator *(float4x4 a, float4x4 b) + { + return new float4x4(a.x * b, a.y * b, a.z * b, a.w * b); + } + + public static bool operator ==(float4x4 a, float4x4 b) + { + return (a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w); + } + + public static bool operator !=(float4x4 a, float4x4 b) + { + return !(a == b); + } + + public static float4x4 Inverse(float4x4 m) + { + float4x4 d = new float4x4(); + //float dst = d.x.x; + float[] tmp = new float[12]; // temp array for pairs + float[] src = new float[16]; // array of transpose source matrix + float det; // determinant + // transpose matrix + for (int i = 0; i < 4; i++) + { + src[i] = m[i].x; + src[i + 4] = m[i].y; + src[i + 8] = m[i].z; + src[i + 12] = m[i].w; + } + // calculate pairs for first 8 elements (cofactors) + tmp[0] = src[10] * src[15]; + tmp[1] = src[11] * src[14]; + tmp[2] = src[9] * src[15]; + tmp[3] = src[11] * src[13]; + tmp[4] = src[9] * src[14]; + tmp[5] = src[10] * src[13]; + tmp[6] = src[8] * src[15]; + tmp[7] = src[11] * src[12]; + tmp[8] = src[8] * src[14]; + tmp[9] = src[10] * src[12]; + tmp[10] = src[8] * src[13]; + tmp[11] = src[9] * src[12]; + // calculate first 8 elements (cofactors) + d.x.x = tmp[0]*src[5] + tmp[3]*src[6] + tmp[4]*src[7]; + d.x.x -= tmp[1]*src[5] + tmp[2]*src[6] + tmp[5]*src[7]; + d.x.y = tmp[1]*src[4] + tmp[6]*src[6] + tmp[9]*src[7]; + d.x.y -= tmp[0]*src[4] + tmp[7]*src[6] + tmp[8]*src[7]; + d.x.z = tmp[2]*src[4] + tmp[7]*src[5] + tmp[10]*src[7]; + d.x.z -= tmp[3]*src[4] + tmp[6]*src[5] + tmp[11]*src[7]; + d.x.w = tmp[5]*src[4] + tmp[8]*src[5] + tmp[11]*src[6]; + d.x.w -= tmp[4]*src[4] + tmp[9]*src[5] + tmp[10]*src[6]; + d.y.x = tmp[1]*src[1] + tmp[2]*src[2] + tmp[5]*src[3]; + d.y.x -= tmp[0]*src[1] + tmp[3]*src[2] + tmp[4]*src[3]; + d.y.y = tmp[0]*src[0] + tmp[7]*src[2] + tmp[8]*src[3]; + d.y.y -= tmp[1]*src[0] + tmp[6]*src[2] + tmp[9]*src[3]; + d.y.z = tmp[3]*src[0] + tmp[6]*src[1] + tmp[11]*src[3]; + d.y.z -= tmp[2]*src[0] + tmp[7]*src[1] + tmp[10]*src[3]; + d.y.w = tmp[4]*src[0] + tmp[9]*src[1] + tmp[10]*src[2]; + d.y.w -= tmp[5]*src[0] + tmp[8]*src[1] + tmp[11]*src[2]; + // calculate pairs for second 8 elements (cofactors) + tmp[0] = src[2]*src[7]; + tmp[1] = src[3]*src[6]; + tmp[2] = src[1]*src[7]; + tmp[3] = src[3]*src[5]; + tmp[4] = src[1]*src[6]; + tmp[5] = src[2]*src[5]; + tmp[6] = src[0]*src[7]; + tmp[7] = src[3]*src[4]; + tmp[8] = src[0]*src[6]; + tmp[9] = src[2]*src[4]; + tmp[10] = src[0]*src[5]; + tmp[11] = src[1]*src[4]; + // calculate second 8 elements (cofactors) + d.z.x = tmp[0]*src[13] + tmp[3]*src[14] + tmp[4]*src[15]; + d.z.x -= tmp[1]*src[13] + tmp[2]*src[14] + tmp[5]*src[15]; + d.z.y = tmp[1]*src[12] + tmp[6]*src[14] + tmp[9]*src[15]; + d.z.y -= tmp[0]*src[12] + tmp[7]*src[14] + tmp[8]*src[15]; + d.z.z = tmp[2]*src[12] + tmp[7]*src[13] + tmp[10]*src[15]; + d.z.z -= tmp[3]*src[12] + tmp[6]*src[13] + tmp[11]*src[15]; + d.z.w = tmp[5]*src[12] + tmp[8]*src[13] + tmp[11]*src[14]; + d.z.w-= tmp[4]*src[12] + tmp[9]*src[13] + tmp[10]*src[14]; + d.w.x = tmp[2]*src[10] + tmp[5]*src[11] + tmp[1]*src[9]; + d.w.x-= tmp[4]*src[11] + tmp[0]*src[9] + tmp[3]*src[10]; + d.w.y = tmp[8]*src[11] + tmp[0]*src[8] + tmp[7]*src[10]; + d.w.y-= tmp[6]*src[10] + tmp[9]*src[11] + tmp[1]*src[8]; + d.w.z = tmp[6]*src[9] + tmp[11]*src[11] + tmp[3]*src[8]; + d.w.z-= tmp[10]*src[11] + tmp[2]*src[8] + tmp[7]*src[9]; + d.w.w = tmp[10]*src[10] + tmp[4]*src[8] + tmp[9]*src[9]; + d.w.w-= tmp[8]*src[9] + tmp[11]*src[10] + tmp[5]*src[8]; + // calculate determinant + det = src[0] * d.x.x + src[1] * d.x.y + src[2] * d.x.z + src[3] * d.x.w; + // calculate matrix inverse + det = 1/det; + for (int j = 0; j < 4; j++) + d[j] *= det; + return d; + } + + public static float4x4 MatrixRigidInverse(float4x4 m) + { + float4x4 trans_inverse = MatrixTranslation(-m.w.xyz()); + float4x4 rot = new float4x4(m); + rot.w = new float4(0f, 0f, 0f, 1f); + return trans_inverse * MatrixTranspose(rot); + } + public static float4x4 MatrixTranspose(float4x4 m) + { + return new float4x4(m.x.x, m.y.x, m.z.x, m.w.x, m.x.y, m.y.y, m.z.y, m.w.y, m.x.z, m.y.z, m.z.z, m.w.z, m.x.w, m.y.w, m.z.w, m.w.w); + } + public static float4x4 MatrixPerspectiveFov(float fovy, float aspect, float zn, float zf) + { + float h = 1.0f / (float)Math.Tan(fovy / 2.0f); // view space height + float w = h / aspect; // view space width + return new float4x4(w, 0, 0, 0, 0, h, 0, 0, 0, 0, zf / (zn - zf), -1, 0, 0, zn * zf / (zn - zf), 0); + } + public static float4x4 MatrixTranslation(float3 t) + { + return new float4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, t.x, t.y, t.z, 1); + } + public static float4x4 MatrixRotationZ(float angle_radians) + { + float s = (float)Math.Sin(angle_radians); + float c = (float)Math.Cos(angle_radians); + return new float4x4(c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + } + public static float4x4 MatrixLookAt(float3 eye, float3 at, float3 up) + { + float4x4 m = new float4x4(); + m.w.w = 1.0f; + m.w.setxyz(eye); + m.z.setxyz(float3.normalize(eye - at)); + m.x.setxyz(float3.normalize(float3.cross(up, m.z.xyz()))); + m.y.setxyz(float3.cross(m.z.xyz(), m.x.xyz())); + return MatrixRigidInverse(m); + } + + public static float4x4 MatrixFromQuatVec(Quaternion q, float3 v) + { + // builds a 4x4 transformation matrix based on orientation q and translation v + float qx2 = q.x * q.x; + float qy2 = q.y * q.y; + float qz2 = q.z * q.z; + + float qxqy = q.x * q.y; + float qxqz = q.x * q.z; + float qxqw = q.x * q.w; + float qyqz = q.y * q.z; + float qyqw = q.y * q.w; + float qzqw = q.z * q.w; + + return new float4x4( + 1 - 2 * (qy2 + qz2), + 2 * (qxqy + qzqw), + 2 * (qxqz - qyqw), + 0, + 2 * (qxqy - qzqw), + 1 - 2 * (qx2 + qz2), + 2 * (qyqz + qxqw), + 0, + 2 * (qxqz + qyqw), + 2 * (qyqz - qxqw), + 1 - 2 * (qx2 + qy2), + 0, + v.x, + v.y, + v.z, + 1.0f); + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/int3.cs b/addon-modules/ConvexDecompositionDotNet/int3.cs new file mode 100644 index 0000000000..9c5760d7e9 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/int3.cs @@ -0,0 +1,128 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class int3 + { + public int x; + public int y; + public int z; + + public int3() + { + } + + public int3(int _x, int _y, int _z) + { + x = _x; + y = _y; + z = _z; + } + + public int this[int i] + { + get + { + switch (i) + { + case 0: return x; + case 1: return y; + case 2: return z; + } + throw new ArgumentOutOfRangeException(); + } + set + { + switch (i) + { + case 0: x = value; return; + case 1: y = value; return; + case 2: z = value; return; + } + throw new ArgumentOutOfRangeException(); + } + } + + public override int GetHashCode() + { + return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode(); + } + + public override bool Equals(object obj) + { + int3 i = obj as int3; + if (i == null) + return false; + + return this == i; + } + + public static bool operator ==(int3 a, int3 b) + { + // If both are null, or both are same instance, return true. + if (System.Object.ReferenceEquals(a, b)) + return true; + // If one is null, but not both, return false. + if (((object)a == null) || ((object)b == null)) + return false; + + for (int i = 0; i < 3; i++) + { + if (a[i] != b[i]) + return false; + } + return true; + } + + public static bool operator !=(int3 a, int3 b) + { + return !(a == b); + } + + public static int3 roll3(int3 a) + { + int tmp = a[0]; + a[0] = a[1]; + a[1] = a[2]; + a[2] = tmp; + return a; + } + + public static bool isa(int3 a, int3 b) + { + return (a == b || roll3(a) == b || a == roll3(b)); + } + + public static bool b2b(int3 a, int3 b) + { + return isa(a, new int3(b[2], b[1], b[0])); + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/int4.cs b/addon-modules/ConvexDecompositionDotNet/int4.cs new file mode 100644 index 0000000000..c2b32e5a4d --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/int4.cs @@ -0,0 +1,66 @@ +/* The MIT License + * + * Copyright (c) 2010 Intel Corporation. + * All rights reserved. + * + * Based on the convexdecomposition library from + * by John W. Ratcliff and Stan Melax. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +using System; + +namespace OpenSim.Region.Physics.ConvexDecompositionDotNet +{ + public class int4 + { + public int x; + public int y; + public int z; + public int w; + + public int4() + { + } + + public int4(int _x, int _y, int _z, int _w) + { + x = _x; + y = _y; + z = _z; + w = _w; + } + + public int this[int i] + { + get + { + switch (i) + { + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + } + throw new ArgumentOutOfRangeException(); + } + } + } +} diff --git a/addon-modules/ConvexDecompositionDotNet/prebuild.xml b/addon-modules/ConvexDecompositionDotNet/prebuild.xml new file mode 100644 index 0000000000..aaf2695c67 --- /dev/null +++ b/addon-modules/ConvexDecompositionDotNet/prebuild.xml @@ -0,0 +1,24 @@ + + + + ../../bin/Physics/ + + + + + ../../bin/Physics/ + + + + ../../bin/ + + + + + + + + + + +