/*
 * Copyright (c) Contributors, http://opensimulator.org/
 * See CONTRIBUTORS.TXT for a full list of copyright holders.
 *
 * 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 OpenSim Project 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 System.Runtime.InteropServices;
using System.Threading;
using log4net;
using OpenMetaverse;
using BulletDotNET;
using OpenSim.Framework;
using OpenSim.Region.Physics.Manager;

namespace OpenSim.Region.Physics.BulletDotNETPlugin
{
    public class BulletDotNETPrim : PhysicsActor
    {
        private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        private PhysicsVector _position;
        private PhysicsVector _velocity;
        private PhysicsVector _torque = new PhysicsVector(0, 0, 0);
        private PhysicsVector m_lastVelocity = new PhysicsVector(0.0f, 0.0f, 0.0f);
        private PhysicsVector m_lastposition = new PhysicsVector(0.0f, 0.0f, 0.0f);
        private Quaternion m_lastorientation = new Quaternion();
        private PhysicsVector m_rotationalVelocity;
        private PhysicsVector _size;
        private PhysicsVector _acceleration;
        // private d.Vector3 _zeroPosition = new d.Vector3(0.0f, 0.0f, 0.0f);
        private Quaternion _orientation;
        private PhysicsVector m_taintposition;
        private PhysicsVector m_taintsize;
        private PhysicsVector m_taintVelocity = new PhysicsVector(0, 0, 0);
        private PhysicsVector m_taintTorque = new PhysicsVector(0, 0, 0);
        private Quaternion m_taintrot;
        private PhysicsVector m_angularlock = new PhysicsVector(1f, 1f, 1f);
        private PhysicsVector m_taintAngularLock = new PhysicsVector(1f, 1f, 1f);
        private btGeneric6DofConstraint Amotor;

        private PhysicsVector m_PIDTarget = new PhysicsVector(0, 0, 0);
        private float m_PIDTau = 0f;
        private float m_PIDHoverHeight = 0f;
        private float m_PIDHoverTau = 0f;
        private bool m_useHoverPID = false;
        private PIDHoverType m_PIDHoverType = PIDHoverType.Ground;
        private float m_targetHoverHeight = 0f;
        private float m_groundHeight = 0f;
        private float m_waterHeight = 0f;
        private float PID_D = 35f;
        private float PID_G = 25f;
        private float m_tensor = 5f;
        private int body_autodisable_frames = 20;
        private IMesh primMesh = null;

        private bool m_usePID = false;


        private const CollisionCategories m_default_collisionFlags = (CollisionCategories.Geom
                                                        | CollisionCategories.Space
                                                        | CollisionCategories.Body
                                                        | CollisionCategories.Character
                                                     );

        private bool m_taintshape = false;
        private bool m_taintPhysics = false;
        private bool m_collidesLand = true;
        private bool m_collidesWater = false;
        public bool m_returnCollisions = false;

        // Default we're a Geometry
        private CollisionCategories m_collisionCategories = (CollisionCategories.Geom);

        // Default, Collide with Other Geometries, spaces and Bodies
        private CollisionCategories m_collisionFlags = m_default_collisionFlags;

        public bool m_taintremove = false;
        public bool m_taintdisable = false;
        public bool m_disabled = false;
        public bool m_taintadd = false;
        public bool m_taintselected = false;
        public bool m_taintCollidesWater = false;

        public uint m_localID = 0;

        //public GCHandle gc;
        private CollisionLocker ode;

        private bool m_taintforce = false;
        private bool m_taintaddangularforce = false;
        private PhysicsVector m_force = new PhysicsVector(0.0f, 0.0f, 0.0f);
        private List<PhysicsVector> m_forcelist = new List<PhysicsVector>();
        private List<PhysicsVector> m_angularforcelist = new List<PhysicsVector>();

        private IMesh _mesh;
        private PrimitiveBaseShape _pbs;
        private BulletDotNETScene _parent_scene;
        public btCollisionShape prim_geom;
        public IntPtr _triMeshData;

        private PhysicsActor _parent = null;
        private PhysicsActor m_taintparent = null;

        private List<BulletDotNETPrim> childrenPrim = new List<BulletDotNETPrim>();

        private bool iscolliding = false;
        private bool m_isphysical = false;
        private bool m_isSelected = false;

        internal bool m_isVolumeDetect = false; // If true, this prim only detects collisions but doesn't collide actively

        private bool m_throttleUpdates = false;
        private int throttleCounter = 0;
        public int m_interpenetrationcount = 0;
        public float m_collisionscore = 0;
        public int m_roundsUnderMotionThreshold = 0;
        private int m_crossingfailures = 0;

        public float m_buoyancy = 0f;

        public bool outofBounds = false;
        private float m_density = 10.000006836f; // Aluminum g/cm3;

        public bool _zeroFlag = false;
        private bool m_lastUpdateSent = false;


        private String m_primName;
        private PhysicsVector _target_velocity;

        public int m_eventsubscription = 0;
        private CollisionEventUpdate CollisionEventsThisFrame = null;

        public volatile bool childPrim = false;

        private btVector3 tempPosition1;
        private btVector3 tempPosition2;
        private btVector3 tempPosition3;
        private btVector3 tempSize1;
        private btVector3 tempSize2;
        private btVector3 tempLinearVelocity1;
        private btVector3 tempLinearVelocity2;
        private btVector3 tempAngularVelocity1;
        private btVector3 tempAngularVelocity2;
        private btVector3 tempInertia1;
        private btVector3 tempInertia2;
        private btQuaternion tempOrientation1;
        private btQuaternion tempOrientation2;
        private btMotionState tempMotionState1;
        private btMotionState tempMotionState2;
        private btMotionState tempMotionState3;
        private btTransform tempTransform1;
        private btTransform tempTransform2;
        private btTransform tempTransform3;
        private btTransform tempTransform4;
        private btTriangleIndexVertexArray btshapeArray;
        private bool forceenable = false;

        public btRigidBody Body;

        public BulletDotNETPrim(String primName, BulletDotNETScene parent_scene, PhysicsVector pos, PhysicsVector size,
                       Quaternion rotation, IMesh mesh, PrimitiveBaseShape pbs, bool pisPhysical)
        {
            tempPosition1 = new btVector3(0, 0, 0);
            tempPosition2 = new btVector3(0, 0, 0);
            tempPosition3 = new btVector3(0, 0, 0);
            tempSize1 = new btVector3(0, 0, 0);
            tempSize2 = new btVector3(0, 0, 0);
            tempLinearVelocity1 = new btVector3(0, 0, 0);
            tempLinearVelocity2 = new btVector3(0, 0, 0);
            tempAngularVelocity1 = new btVector3(0, 0, 0);
            tempAngularVelocity2 = new btVector3(0, 0, 0);
            tempInertia1 = new btVector3(0, 0, 0);
            tempInertia2 = new btVector3(0, 0, 0);
            tempOrientation1 = new btQuaternion(0,0,0,1);
            tempOrientation2 = new btQuaternion(0, 0, 0, 1);
            _parent_scene = parent_scene;
            tempTransform1 = new btTransform(_parent_scene.QuatIdentity, _parent_scene.VectorZero);
            tempTransform2 = new btTransform(_parent_scene.QuatIdentity, _parent_scene.VectorZero); ;
            tempTransform3 = new btTransform(_parent_scene.QuatIdentity, _parent_scene.VectorZero); ;
            tempTransform4 = new btTransform(_parent_scene.QuatIdentity, _parent_scene.VectorZero); ;

            tempMotionState1 = new btDefaultMotionState(_parent_scene.TransZero);
            tempMotionState2 = new btDefaultMotionState(_parent_scene.TransZero);
            tempMotionState3 = new btDefaultMotionState(_parent_scene.TransZero);

            _target_velocity = new PhysicsVector(0, 0, 0);
            _velocity = new PhysicsVector();
            _position = pos;
            m_taintposition = pos;
            PID_D = parent_scene.bodyPIDD;
            PID_G = parent_scene.bodyPIDG;
            m_density = parent_scene.geomDefaultDensity;
            m_tensor = parent_scene.bodyMotorJointMaxforceTensor;
            body_autodisable_frames = parent_scene.bodyFramesAutoDisable;

            prim_geom = null;
            Body = null;

            if (size.X <= 0) size.X = 0.01f;
            if (size.Y <= 0) size.Y = 0.01f;
            if (size.Z <= 0) size.Z = 0.01f;

            _size = size;
            m_taintsize = _size;
            _acceleration = new PhysicsVector();
            m_rotationalVelocity = PhysicsVector.Zero;
            _orientation = rotation;
            m_taintrot = _orientation;
            _mesh = mesh;
            _pbs = pbs;

            _parent_scene = parent_scene;

            if (pos.Z < 0)
                m_isphysical = false;
            else
            {
                m_isphysical = pisPhysical;
                // If we're physical, we need to be in the master space for now.
                // linksets *should* be in a space together..  but are not currently
            }
            m_primName = primName;
            m_taintadd = true;
            _parent_scene.AddPhysicsActorTaint(this);

        }

        #region PhysicsActor overrides

        public override bool Stopped
        {
            get { return _zeroFlag; }
        }

        public override PhysicsVector Size
        {
            get { return _size; }
            set { _size = value; }
        }

        public override PrimitiveBaseShape Shape
        {
            set
            {
                _pbs = value;
                m_taintshape = true;
            }
        }

        public override uint LocalID
        {
            set
            {
                //m_log.Info("[PHYSICS]: Setting TrackerID: " + value);
                m_localID = value;
            }
        }

        public override bool Grabbed
        {
            set { return; }
        }

        public override bool Selected
        {
            set
            {
                // This only makes the object not collidable if the object
                // is physical or the object is modified somehow *IN THE FUTURE*
                // without this, if an avatar selects prim, they can walk right
                // through it while it's selected
                m_collisionscore = 0;
                if ((m_isphysical && !_zeroFlag) || !value)
                {
                    m_taintselected = value;
                    _parent_scene.AddPhysicsActorTaint(this);
                }
                else
                {
                    m_taintselected = value;
                    m_isSelected = value;
                }
            }
        }

        public override void CrossingFailure()
        {
            m_crossingfailures++;
            if (m_crossingfailures > _parent_scene.geomCrossingFailuresBeforeOutofbounds)
            {
                base.RaiseOutOfBounds(_position);
                return;
            }
            else if (m_crossingfailures == _parent_scene.geomCrossingFailuresBeforeOutofbounds)
            {
                m_log.Warn("[PHYSICS]: Too many crossing failures for: " + m_primName);
            }
        }
        public override void link(PhysicsActor obj)
        {
            //TODO:
        }

        public override void delink()
        {
            //TODO:
        }

        public override void LockAngularMotion(PhysicsVector axis)
        {
            m_log.DebugFormat("[axislock]: <{0},{1},{2}>", axis.X, axis.Y, axis.Z);
            m_taintAngularLock = new PhysicsVector(axis.X, axis.Y, axis.Z);
        }

        public override PhysicsVector Position
        {
            get { return _position; }

            set
            {
                _position = value;
                //m_log.Info("[PHYSICS]: " + _position.ToString());
            }
        }

        public override float Mass
        {
            get { return CalculateMass(); }
        }

        public override PhysicsVector Force
        {
            //get { return PhysicsVector.Zero; }
            get { return m_force; }
            set { m_force = value; }
        }

        public override int VehicleType
        {
            get { return 0; }
            set { return; }
        }

        public override void VehicleFloatParam(int param, float value)
        {
            //TODO:
        }

        public override void VehicleVectorParam(int param, PhysicsVector value)
        {
            //TODO:
        }

        public override void VehicleRotationParam(int param, Quaternion rotation)
        {
            //TODO:
        }

        public override void SetVolumeDetect(int param)
        {
            //TODO: GhostObject
            m_isVolumeDetect = (param != 0);

        }

        public override PhysicsVector GeometricCenter
        {
            get { return PhysicsVector.Zero; }
        }

        public override PhysicsVector CenterOfMass
        {
            get { return PhysicsVector.Zero; }
        }

        public override PhysicsVector Velocity
        {
            get
            {
                // Averate previous velocity with the new one so
                // client object interpolation works a 'little' better
                PhysicsVector returnVelocity = new PhysicsVector();
                returnVelocity.X = (m_lastVelocity.X + _velocity.X) / 2;
                returnVelocity.Y = (m_lastVelocity.Y + _velocity.Y) / 2;
                returnVelocity.Z = (m_lastVelocity.Z + _velocity.Z) / 2;
                return returnVelocity;
            }
            set
            {
                _velocity = value;

                m_taintVelocity = value;
                _parent_scene.AddPhysicsActorTaint(this);
            }
        }

        public override PhysicsVector Torque
        {
            get
            {
                if (!m_isphysical || Body.Handle == IntPtr.Zero)
                    return new PhysicsVector(0, 0, 0);

                return _torque;
            }

            set
            {
                m_taintTorque = value;
                _parent_scene.AddPhysicsActorTaint(this);
            }
        }

        public override float CollisionScore
        {
            get { return m_collisionscore; }
            set { m_collisionscore = value; }
        }

        public override PhysicsVector Acceleration
        {
            get { return _acceleration; }
        }

        public override Quaternion Orientation
        {
            get { return _orientation; }
            set { _orientation = value; }
        }

        public override int PhysicsActorType
        {
            get { return (int)ActorTypes.Prim; }
            set { return; }
        }

        public override bool IsPhysical
        {
            get { return m_isphysical; }
            set { m_isphysical = value; }
        }

        public override bool Flying
        {
            // no flying prims for you
            get { return false; }
            set { }
        }

        public override bool SetAlwaysRun
        {
            get { return false; }
            set { return; }
        }

        public override bool ThrottleUpdates
        {
            get { return m_throttleUpdates; }
            set { m_throttleUpdates = value; }
        }

        public override bool IsColliding
        {
            get { return iscolliding; }
            set { iscolliding = value; }
        }

        public override bool CollidingGround
        {
            get { return false; }
            set { return; }
        }

        public override bool CollidingObj
        {
            get { return false; }
            set { return; }
        }

        public override bool FloatOnWater
        {
            set
            {
                m_taintCollidesWater = value;
                _parent_scene.AddPhysicsActorTaint(this);
            }
        }

        public override PhysicsVector RotationalVelocity
        {
            get
            {
                PhysicsVector pv = new PhysicsVector(0, 0, 0);
                if (_zeroFlag)
                    return pv;
                m_lastUpdateSent = false;

                if (m_rotationalVelocity.IsIdentical(pv, 0.2f))
                    return pv;

                return m_rotationalVelocity;
            }
            set { m_rotationalVelocity = value; }
        }

        public override bool Kinematic
        {
            get { return false; }
            set { }
        }

        public override float Buoyancy
        {
            get { return m_buoyancy; }
            set { m_buoyancy = value; }
        }

        public override PhysicsVector PIDTarget { set { m_PIDTarget = value; ; } }
        public override bool PIDActive { set { m_usePID = value; } }
        public override float PIDTau { set { m_PIDTau = value; } }

        public override float PIDHoverHeight { set { m_PIDHoverHeight = value; ; } }
        public override bool PIDHoverActive { set { m_useHoverPID = value; } }
        public override PIDHoverType PIDHoverType { set { m_PIDHoverType = value; } }
        public override float PIDHoverTau { set { m_PIDHoverTau = value; } }


        public override void AddForce(PhysicsVector force, bool pushforce)
        {
            m_forcelist.Add(force);
            m_taintforce = true;
            //m_log.Info("[PHYSICS]: Added Force:" + force.ToString() +  " to prim at " + Position.ToString());
        }

        public override void AddAngularForce(PhysicsVector force, bool pushforce)
        {
            m_angularforcelist.Add(force);
            m_taintaddangularforce = true;
        }

        public override void SetMomentum(PhysicsVector momentum)
        {
        }

        public override void SubscribeEvents(int ms)
        {
            m_eventsubscription = ms;
            _parent_scene.addCollisionEventReporting(this);
        }

        public override void UnSubscribeEvents()
        {
            _parent_scene.remCollisionEventReporting(this);
            m_eventsubscription = 0;
        }

        public override bool SubscribedEvents()
        {
            return (m_eventsubscription > 0);
        }

        #endregion



        internal void Dispose()
        {
            //TODO:
            DisposeOfBody();
            SetCollisionShape(null);

            if (tempMotionState3 != null && tempMotionState3.Handle != IntPtr.Zero)
            {
                tempMotionState3.Dispose();
                tempMotionState3 = null;
            }

            if (tempMotionState2 != null && tempMotionState2.Handle != IntPtr.Zero)
            {
                tempMotionState2.Dispose();
                tempMotionState2 = null;
            }

            if (tempMotionState1 != null && tempMotionState1.Handle != IntPtr.Zero)
            {
                tempMotionState1.Dispose();
                tempMotionState1 = null;
            }

            if (tempTransform4 != null && tempTransform4.Handle != IntPtr.Zero)
            {
                tempTransform4.Dispose();
                tempTransform4 = null;
            }

            if (tempTransform3 != null && tempTransform3.Handle != IntPtr.Zero)
            {
                tempTransform3.Dispose();
                tempTransform3 = null;
            }

            if (tempTransform2 != null && tempTransform2.Handle != IntPtr.Zero)
            {
                tempTransform2.Dispose();
                tempTransform2 = null;
            }

            if (tempTransform1 != null && tempTransform1.Handle != IntPtr.Zero)
            {
                tempTransform1.Dispose();
                tempTransform1 = null;
            }

            if (tempOrientation2 != null && tempOrientation2.Handle != IntPtr.Zero)
            {
                tempOrientation2.Dispose();
                tempOrientation2 = null;
            }

            if (tempOrientation1 != null && tempOrientation1.Handle != IntPtr.Zero)
            {
                tempOrientation1.Dispose();
                tempOrientation1 = null;
            }

            if (tempInertia1 != null && tempInertia1.Handle != IntPtr.Zero)
            {
                tempInertia1.Dispose();
                tempInertia1 = null;
            }

            if (tempInertia2 != null && tempInertia2.Handle != IntPtr.Zero)
            {
                tempInertia2.Dispose();
                tempInertia1 = null;
            }
            
            
            if (tempAngularVelocity2 != null && tempAngularVelocity2.Handle != IntPtr.Zero)
            {
                tempAngularVelocity2.Dispose();
                tempAngularVelocity2 = null;
            }

            if (tempAngularVelocity1 != null && tempAngularVelocity1.Handle != IntPtr.Zero)
            {
                tempAngularVelocity1.Dispose();
                tempAngularVelocity1 = null;
            }

            if (tempLinearVelocity2 != null && tempLinearVelocity2.Handle != IntPtr.Zero)
            {
                tempLinearVelocity2.Dispose();
                tempLinearVelocity2 = null;
            }

            if (tempLinearVelocity1 != null && tempLinearVelocity1.Handle != IntPtr.Zero)
            {
                tempLinearVelocity1.Dispose();
                tempLinearVelocity1 = null;
            }

            if (tempSize2 != null && tempSize2.Handle != IntPtr.Zero)
            {
                tempSize2.Dispose();
                tempSize2 = null;
            }

            if (tempSize1 != null && tempSize1.Handle != IntPtr.Zero)
            {
                tempSize1.Dispose();
                tempSize1 = null;
            }

            if (tempPosition3 != null && tempPosition3.Handle != IntPtr.Zero)
            {
                tempPosition3.Dispose();
                tempPosition3 = null;
            }

            if (tempPosition2 != null && tempPosition2.Handle != IntPtr.Zero)
            {
                tempPosition2.Dispose();
                tempPosition2 = null;
            }

            if (tempPosition1 != null && tempPosition1.Handle != IntPtr.Zero)
            {
                tempPosition1.Dispose();
                tempPosition1 = null;
            }

        }



        public void ProcessTaints(float timestep)
        {
            if (m_taintadd)
            {
                m_log.Debug("[PHYSICS]: TaintAdd");
                changeadd(timestep);
            }

            if (prim_geom.Handle == IntPtr.Zero)
            {
                CreateGeom(IntPtr.Zero, primMesh);

                if (IsPhysical)
                    SetBody(Mass);
                else
                    SetBody(0);
                m_log.Debug("[PHYSICS]: GEOM_DOESNT_EXSIT");

            }

            if (!_position.IsIdentical(m_taintposition, 0f))
            {
                m_log.Debug("[PHYSICS]: TaintMove");
                changemove(timestep);
            }
            if (m_taintrot != _orientation)
            {
                m_log.Debug("[PHYSICS]: TaintRotate");
                rotate(timestep);
            } //

            if (m_taintPhysics != m_isphysical && !(m_taintparent != _parent))
            {
                m_log.Debug("[PHYSICS]: TaintPhysics");
                changePhysicsStatus(timestep);
            }
            //

            if (!_size.IsIdentical(m_taintsize, 0))
            {
                m_log.Debug("[PHYSICS]: TaintSize");
                changesize(timestep);
            }

        //

            if (m_taintshape)
            {
                m_log.Debug("[PHYSICS]: TaintShape");
                changeshape(timestep);
            } //

            if (m_taintforce)
            {
                m_log.Debug("[PHYSICS]: TaintForce");
                changeAddForce(timestep);
            }
            if (m_taintaddangularforce)
            {
                m_log.Debug("[PHYSICS]: TaintAngularForce");
                changeAddAngularForce(timestep);
            }
            if (!m_taintTorque.IsIdentical(PhysicsVector.Zero, 0.001f))
            {
                m_log.Debug("[PHYSICS]: TaintTorque");
                changeSetTorque(timestep);
            }
            if (m_taintdisable)
            {
                m_log.Debug("[PHYSICS]: TaintDisable");
                changedisable(timestep);
            }
            if (m_taintselected != m_isSelected)
            {
                m_log.Debug("[PHYSICS]: TaintSelected");
                changeSelectedStatus(timestep);
            }
            if (!m_taintVelocity.IsIdentical(PhysicsVector.Zero, 0.001f))
            {
                m_log.Debug("[PHYSICS]: TaintVelocity");
                changevelocity(timestep);
            }
            if (m_taintparent != _parent)
            {
                m_log.Debug("[PHYSICS]: TaintLink");
                changelink(timestep);
            }
            if (m_taintCollidesWater != m_collidesWater)
            {
                changefloatonwater(timestep);
            }
            if (!m_angularlock.IsIdentical(m_taintAngularLock, 0))
            {
                m_log.Debug("[PHYSICS]: TaintAngularLock");
                changeAngularLock(timestep);
            }
            if (m_taintremove)
            {
                DisposeOfBody();
                Dispose();
            }

        }

        #region Physics Scene Change Action routines

        private void changeadd(float timestep)
        {
            //SetCollisionShape(null);
            // Construction of new prim
            if (Body != null)
            {
                if (Body.Handle != IntPtr.Zero)
                {
                    _parent_scene.removeFromWorld(this, Body);
                    //Body.Dispose();
                }
                //Body = null;
                // TODO: dispose parts that make up body
            }
            if (_parent_scene.needsMeshing(_pbs))
            {
                // Don't need to re-enable body..   it's done in SetMesh
                float meshlod = _parent_scene.meshSculptLOD;

                if (IsPhysical)
                    meshlod = _parent_scene.MeshSculptphysicalLOD;

                IMesh mesh = _parent_scene.mesher.CreateMesh(SOPName, _pbs, _size, meshlod, IsPhysical);
                // createmesh returns null when it doesn't mesh.
                CreateGeom(IntPtr.Zero, mesh);
            }
            else
            {
                _mesh = null;
                CreateGeom(IntPtr.Zero, null);
            }

            if (IsPhysical)
                SetBody(Mass);
            else
                SetBody(0);
            //changeSelectedStatus(timestep);
            m_taintadd = false;

        }

        private void changemove(float timestep)
        {

            m_log.Debug("[PHYSICS]: _________ChangeMove");
            tempTransform2 = Body.getWorldTransform();
            btQuaternion quat = tempTransform2.getRotation();
            tempPosition2.setValue(_position.X, _position.Y, _position.Z);
            tempTransform2.Dispose();
            tempTransform2 = new btTransform(quat, tempPosition2);
            Body.setWorldTransform(tempTransform2);

            changeSelectedStatus(timestep);

            resetCollisionAccounting();
            m_taintposition = _position;
        }

        private void rotate(float timestep)
        {
            m_log.Debug("[PHYSICS]: _________ChangeRotate");
            tempTransform2 = Body.getWorldTransform();
            tempOrientation2 = new btQuaternion(_orientation.X, _orientation.Y, _orientation.Z, _orientation.W);
            tempTransform2.setRotation(tempOrientation2);
            Body.setWorldTransform(tempTransform2);

            resetCollisionAccounting();
            m_taintrot = _orientation;
        }

        private void changePhysicsStatus(float timestep)
        {
            if (Body != null)
            {
                if (Body.Handle != IntPtr.Zero)
                {
                    _parent_scene.removeFromWorld(this, Body);
                    //Body.Dispose();
                }
                //Body = null;
                // TODO: dispose parts that make up body
            }
            m_log.Debug("[PHYSICS]: _________ChangePhysics");
            if (_parent_scene.needsMeshing(_pbs))
            {
                // Don't need to re-enable body..   it's done in SetMesh
                float meshlod = _parent_scene.meshSculptLOD;

                if (IsPhysical)
                    meshlod = _parent_scene.MeshSculptphysicalLOD;

                IMesh mesh = _parent_scene.mesher.CreateMesh(SOPName, _pbs, _size, meshlod, IsPhysical);
                // createmesh returns null when it doesn't mesh.
                CreateGeom(IntPtr.Zero, mesh);
            }
            else
            {
                _mesh = null;
                CreateGeom(IntPtr.Zero, null);
            }
            SetCollisionShape(prim_geom);
            if (m_isphysical)
                SetBody(Mass);
            else
                SetBody(0);
            changeSelectedStatus(timestep);
            
            resetCollisionAccounting();
            m_taintPhysics = m_isphysical;
        }

        private void changesize(float timestep)
        {
            if (Body != null)
            {
                if (Body.Handle != IntPtr.Zero)
                {
                    _parent_scene.removeFromWorld(this, Body);
                    //Body.Dispose();
                }
                //Body = null;
                // TODO: dispose parts that make up body
            }

            m_log.Debug("[PHYSICS]: _________ChangeSize");
            SetCollisionShape(null);
            // Construction of new prim
            if (_parent_scene.needsMeshing(_pbs))
            {
                // Don't need to re-enable body..   it's done in SetMesh
                float meshlod = _parent_scene.meshSculptLOD;

                if (IsPhysical)
                    meshlod = _parent_scene.MeshSculptphysicalLOD;

                IMesh mesh = _parent_scene.mesher.CreateMesh(SOPName, _pbs, _size, meshlod, IsPhysical);
                // createmesh returns null when it doesn't mesh.
                CreateGeom(IntPtr.Zero, mesh);
            }
            else
            {
                _mesh = null;
                CreateGeom(IntPtr.Zero, null);
            }
            
            if (IsPhysical)
                SetBody(Mass);
            else
                SetBody(0);
            
            m_taintsize = _size;

        }

        private void changeshape(float timestep)
        {
            if (Body != null)
            {
                if (Body.Handle != IntPtr.Zero)
                {
                    _parent_scene.removeFromWorld(this, Body);
                    //Body.Dispose();
                }
                //Body = null;
                // TODO: dispose parts that make up body
            }
            // Cleanup of old prim geometry and Bodies
            if (IsPhysical && Body != null && Body.Handle != IntPtr.Zero)
            {
                if (childPrim)
                {
                    if (_parent != null)
                    {
                        BulletDotNETPrim parent = (BulletDotNETPrim)_parent;
                        parent.ChildDelink(this);
                    }
                }
                else
                {
                    //disableBody();
                }
            }
            try
            {
                //SetCollisionShape(null);
            }
            catch (System.AccessViolationException)
            {
                //prim_geom = IntPtr.Zero;
                m_log.Error("[PHYSICS]: PrimGeom dead");
            }
            
            // we don't need to do space calculation because the client sends a position update also.
            if (_size.X <= 0) _size.X = 0.01f;
            if (_size.Y <= 0) _size.Y = 0.01f;
            if (_size.Z <= 0) _size.Z = 0.01f;
            // Construction of new prim

            if (_parent_scene.needsMeshing(_pbs))
            {
                // Don't need to re-enable body..   it's done in SetMesh
                float meshlod = _parent_scene.meshSculptLOD;

                if (IsPhysical)
                    meshlod = _parent_scene.MeshSculptphysicalLOD;

                IMesh mesh = _parent_scene.mesher.CreateMesh(SOPName, _pbs, _size, meshlod, IsPhysical);
                // createmesh returns null when it doesn't mesh.
                CreateGeom(IntPtr.Zero, mesh);
            }
            else
            {
                _mesh = null;
                CreateGeom(IntPtr.Zero, null);
            }
            tempPosition1.setValue(_position.X, _position.Y, _position.Z);
            if (tempOrientation1.Handle != IntPtr.Zero)
                tempOrientation1.Dispose();
            tempOrientation1 = new btQuaternion(_orientation.X, Orientation.Y, _orientation.Z, _orientation.W);
            if (tempTransform1 != null && tempTransform1.Handle != IntPtr.Zero)
                tempTransform1.Dispose();
            tempTransform1 = new btTransform(tempOrientation1, tempPosition1);

            
            

            //d.GeomBoxSetLengths(prim_geom, _size.X, _size.Y, _size.Z);
            if (IsPhysical)
            {
                SetBody(Mass);
                // Re creates body on size.
                // EnableBody also does setMass()
               
            }
            else
            {
                SetBody(0);
            }

            changeSelectedStatus(timestep);
            if (childPrim)
            {
                if (_parent is BulletDotNETPrim)
                {
                    BulletDotNETPrim parent = (BulletDotNETPrim)_parent;
                    parent.ChildSetGeom(this);
                }
            }
            resetCollisionAccounting();
            
            m_taintshape = false;
        }

        private void resetCollisionAccounting()
        {
            m_collisionscore = 0;
        }

        private void ChildSetGeom(BulletDotNETPrim bulletDotNETPrim)
        {
            // TODO: throw new NotImplementedException();
        }

        private void changeAddForce(float timestep)
        {
            // TODO: throw new NotImplementedException();
        }

        private void changeAddAngularForce(float timestep)
        {
            // TODO: throw new NotImplementedException();
        }

        private void changeSetTorque(float timestep)
        {
            // TODO: throw new NotImplementedException();
        }

        private void changedisable(float timestep)
        {
            // TODO: throw new NotImplementedException();
        }

        private void changeSelectedStatus(float timestep)
        {
            // TODO: throw new NotImplementedException();
            if (m_taintselected)
            {
                disableBodySoft();
            }
            else
            {
                enableBodySoft();
            }
            m_isSelected = m_taintselected;
            
        }

        private void changevelocity(float timestep)
        {
            // TODO: throw new NotImplementedException();
        }

        private void changelink(float timestep)
        {
            // TODO: throw new NotImplementedException();
        }

        private void changefloatonwater(float timestep)
        {
            // TODO: throw new NotImplementedException();
        }

        private void changeAngularLock(float timestep)
        {
            // TODO: throw new NotImplementedException();
        }
        #endregion




        internal void Move(float timestep)
        {
            //TODO:
            float fx = 0;
            float fy = 0;
            float fz = 0;

            if (IsPhysical && Body != null && Body.Handle != IntPtr.Zero && !m_isSelected)
            {
                float m_mass = CalculateMass();

                fz = 0f;
                //m_log.Info(m_collisionFlags.ToString());

                if (m_buoyancy != 0)
                {
                    if (m_buoyancy > 0)
                    {
                        fz = (((-1 * _parent_scene.gravityz) * m_buoyancy) * m_mass);

                        //d.Vector3 l_velocity = d.BodyGetLinearVel(Body);
                        //m_log.Info("Using Buoyancy: " + buoyancy + " G: " + (_parent_scene.gravityz * m_buoyancy) + "mass:" + m_mass + "  Pos: " + Position.ToString());
                    }
                    else
                    {
                        fz = (-1 * (((-1 * _parent_scene.gravityz) * (-1 * m_buoyancy)) * m_mass));
                    }
                }

                if (m_usePID)
                {
                    //if (!d.BodyIsEnabled(Body))
                    //d.BodySetForce(Body, 0f, 0f, 0f);
                    // If we're using the PID controller, then we have no gravity
                    fz = (-1 * _parent_scene.gravityz) * m_mass;

                    //  no lock; for now it's only called from within Simulate()

                    // If the PID Controller isn't active then we set our force
                    // calculating base velocity to the current position

                    if ((m_PIDTau < 1) && (m_PIDTau != 0))
                    {
                        //PID_G = PID_G / m_PIDTau;
                        m_PIDTau = 1;
                    }

                    if ((PID_G - m_PIDTau) <= 0)
                    {
                        PID_G = m_PIDTau + 1;
                    }

                    // TODO: NEED btVector3 for Linear Velocity
                    // NEED btVector3 for Position

                    PhysicsVector pos = new PhysicsVector(0, 0, 0); //TODO: Insert values gotten from bullet
                    PhysicsVector vel = new PhysicsVector(0, 0, 0);

                    _target_velocity =
                        new PhysicsVector(
                            (m_PIDTarget.X - pos.X) * ((PID_G - m_PIDTau) * timestep),
                            (m_PIDTarget.Y - pos.Y) * ((PID_G - m_PIDTau) * timestep),
                            (m_PIDTarget.Z - pos.Z) * ((PID_G - m_PIDTau) * timestep)
                            );

                    if (_target_velocity.IsIdentical(PhysicsVector.Zero, 0.1f))
                    {

                        /* TODO: Do Bullet equiv
                         * 
                        d.BodySetPosition(Body, m_PIDTarget.X, m_PIDTarget.Y, m_PIDTarget.Z);
                        d.BodySetLinearVel(Body, 0, 0, 0);
                        d.BodyAddForce(Body, 0, 0, fz);
                        return;
                        */
                    }
                    else
                    {
                        _zeroFlag = false;

                        fx = ((_target_velocity.X) - vel.X) * (PID_D);
                        fy = ((_target_velocity.Y) - vel.Y) * (PID_D);
                        fz = fz + ((_target_velocity.Z - vel.Z) * (PID_D) * m_mass);

                    }

                }

                if (m_useHoverPID && !m_usePID)
                {
                    // If we're using the PID controller, then we have no gravity
                    fz = (-1 * _parent_scene.gravityz) * m_mass;

                    //  no lock; for now it's only called from within Simulate()

                    // If the PID Controller isn't active then we set our force
                    // calculating base velocity to the current position

                    if ((m_PIDTau < 1))
                    {
                        PID_G = PID_G / m_PIDTau;
                    }

                    if ((PID_G - m_PIDTau) <= 0)
                    {
                        PID_G = m_PIDTau + 1;
                    }
                    PhysicsVector pos = new PhysicsVector(0, 0, 0); //TODO: Insert values gotten from bullet
                    PhysicsVector vel = new PhysicsVector(0, 0, 0);

                    // determine what our target height really is based on HoverType
                    switch (m_PIDHoverType)
                    {
                        case PIDHoverType.Absolute:
                            m_targetHoverHeight = m_PIDHoverHeight;
                            break;
                        case PIDHoverType.Ground:
                            m_groundHeight = _parent_scene.GetTerrainHeightAtXY(pos.X, pos.Y);
                            m_targetHoverHeight = m_groundHeight + m_PIDHoverHeight;
                            break;
                        case PIDHoverType.GroundAndWater:
                            m_groundHeight = _parent_scene.GetTerrainHeightAtXY(pos.X, pos.Y);
                            m_waterHeight = _parent_scene.GetWaterLevel();
                            if (m_groundHeight > m_waterHeight)
                            {
                                m_targetHoverHeight = m_groundHeight + m_PIDHoverHeight;
                            }
                            else
                            {
                                m_targetHoverHeight = m_waterHeight + m_PIDHoverHeight;
                            }
                            break;
                        case PIDHoverType.Water:
                            m_waterHeight = _parent_scene.GetWaterLevel();
                            m_targetHoverHeight = m_waterHeight + m_PIDHoverHeight;
                            break;
                    }


                    _target_velocity =
                        new PhysicsVector(0.0f, 0.0f,
                            (m_targetHoverHeight - pos.Z) * ((PID_G - m_PIDHoverTau) * timestep)
                            );

                    //  if velocity is zero, use position control; otherwise, velocity control

                    if (_target_velocity.IsIdentical(PhysicsVector.Zero, 0.1f))
                    {

                        /* TODO: Do Bullet Equiv
                        d.BodySetPosition(Body, pos.X, pos.Y, m_targetHoverHeight);
                        d.BodySetLinearVel(Body, vel.X, vel.Y, 0);
                        d.BodyAddForce(Body, 0, 0, fz);
                        */
                        return;
                    }
                    else
                    {
                        _zeroFlag = false;

                        // We're flying and colliding with something
                        fz = fz + ((_target_velocity.Z - vel.Z) * (PID_D) * m_mass);
                    }
                }

                fx *= m_mass;
                fy *= m_mass;
                //fz *= m_mass;

                fx += m_force.X;
                fy += m_force.Y;
                fz += m_force.Z;

                //m_log.Info("[OBJPID]: X:" + fx.ToString() + " Y:" + fy.ToString() + " Z:" + fz.ToString());
                if (fx != 0 || fy != 0 || fz != 0)
                {
                    /*
                     * TODO: Do Bullet Equiv
                    if (!d.BodyIsEnabled(Body))
                    {
                        d.BodySetLinearVel(Body, 0f, 0f, 0f);
                        d.BodySetForce(Body, 0, 0, 0);
                        enableBodySoft();
                    }
                    */
                    // 35x10 = 350n times the mass per second applied maximum.

                    float nmax = 35f * m_mass;
                    float nmin = -35f * m_mass;


                    if (fx > nmax)
                        fx = nmax;
                    if (fx < nmin)
                        fx = nmin;
                    if (fy > nmax)
                        fy = nmax;
                    if (fy < nmin)
                        fy = nmin;

                    // TODO: Do Bullet Equiv
                    // d.BodyAddForce(Body, fx, fy, fz);
                }
            }
            else
            {
                // _zeroPosition = d.BodyGetPosition(Body);
                return;
            }
        }




        #region Mass Calculation

        private float CalculateMass()
        {
            float volume = 0;

            // No material is passed to the physics engines yet..  soo..
            // we're using the m_density constant in the class definition

            float returnMass = 0;

            switch (_pbs.ProfileShape)
            {
                case ProfileShape.Square:
                    // Profile Volume

                    volume = _size.X * _size.Y * _size.Z;

                    // If the user has 'hollowed out'
                    // ProfileHollow is one of those 0 to 50000 values :P
                    // we like percentages better..   so turning into a percentage

                    if (((float)_pbs.ProfileHollow / 50000f) > 0.0)
                    {
                        float hollowAmount = (float)_pbs.ProfileHollow / 50000f;

                        // calculate the hollow volume by it's shape compared to the prim shape
                        float hollowVolume = 0;
                        switch (_pbs.HollowShape)
                        {
                            case HollowShape.Square:
                            case HollowShape.Same:
                                // Cube Hollow volume calculation
                                float hollowsizex = _size.X * hollowAmount;
                                float hollowsizey = _size.Y * hollowAmount;
                                float hollowsizez = _size.Z * hollowAmount;
                                hollowVolume = hollowsizex * hollowsizey * hollowsizez;
                                break;

                            case HollowShape.Circle:
                                // Hollow shape is a perfect cyllinder in respect to the cube's scale
                                // Cyllinder hollow volume calculation
                                float hRadius = _size.X / 2;
                                float hLength = _size.Z;

                                // pi * r2 * h
                                hollowVolume = ((float)(Math.PI * Math.Pow(hRadius, 2) * hLength) * hollowAmount);
                                break;

                            case HollowShape.Triangle:
                                // Equilateral Triangular Prism volume hollow calculation
                                // Triangle is an Equilateral Triangular Prism with aLength = to _size.Y

                                float aLength = _size.Y;
                                // 1/2 abh
                                hollowVolume = (float)((0.5 * aLength * _size.X * _size.Z) * hollowAmount);
                                break;

                            default:
                                hollowVolume = 0;
                                break;
                        }
                        volume = volume - hollowVolume;
                    }

                    break;
                case ProfileShape.Circle:
                    if (_pbs.PathCurve == (byte)Extrusion.Straight)
                    {
                        // Cylinder
                        float volume1 = (float)(Math.PI * Math.Pow(_size.X / 2, 2) * _size.Z);
                        float volume2 = (float)(Math.PI * Math.Pow(_size.Y / 2, 2) * _size.Z);

                        // Approximating the cylinder's irregularity.
                        if (volume1 > volume2)
                        {
                            volume = (float)volume1 - (volume1 - volume2);
                        }
                        else if (volume2 > volume1)
                        {
                            volume = (float)volume2 - (volume2 - volume1);
                        }
                        else
                        {
                            // Regular cylinder
                            volume = volume1;
                        }
                    }
                    else
                    {
                        // We don't know what the shape is yet, so use default
                        volume = _size.X * _size.Y * _size.Z;
                    }
                    // If the user has 'hollowed out'
                    // ProfileHollow is one of those 0 to 50000 values :P
                    // we like percentages better..   so turning into a percentage

                    if (((float)_pbs.ProfileHollow / 50000f) > 0.0)
                    {
                        float hollowAmount = (float)_pbs.ProfileHollow / 50000f;

                        // calculate the hollow volume by it's shape compared to the prim shape
                        float hollowVolume = 0;
                        switch (_pbs.HollowShape)
                        {
                            case HollowShape.Same:
                            case HollowShape.Circle:
                                // Hollow shape is a perfect cyllinder in respect to the cube's scale
                                // Cyllinder hollow volume calculation
                                float hRadius = _size.X / 2;
                                float hLength = _size.Z;

                                // pi * r2 * h
                                hollowVolume = ((float)(Math.PI * Math.Pow(hRadius, 2) * hLength) * hollowAmount);
                                break;

                            case HollowShape.Square:
                                // Cube Hollow volume calculation
                                float hollowsizex = _size.X * hollowAmount;
                                float hollowsizey = _size.Y * hollowAmount;
                                float hollowsizez = _size.Z * hollowAmount;
                                hollowVolume = hollowsizex * hollowsizey * hollowsizez;
                                break;

                            case HollowShape.Triangle:
                                // Equilateral Triangular Prism volume hollow calculation
                                // Triangle is an Equilateral Triangular Prism with aLength = to _size.Y

                                float aLength = _size.Y;
                                // 1/2 abh
                                hollowVolume = (float)((0.5 * aLength * _size.X * _size.Z) * hollowAmount);
                                break;

                            default:
                                hollowVolume = 0;
                                break;
                        }
                        volume = volume - hollowVolume;
                    }
                    break;

                case ProfileShape.HalfCircle:
                    if (_pbs.PathCurve == (byte)Extrusion.Curve1)
                    {
                        if (_size.X == _size.Y && _size.Z == _size.X)
                        {
                            // regular sphere
                            // v = 4/3 * pi * r^3
                            float sradius3 = (float)Math.Pow((_size.X / 2), 3);
                            volume = (float)((4 / 3f) * Math.PI * sradius3);
                        }
                        else
                        {
                            // we treat this as a box currently
                            volume = _size.X * _size.Y * _size.Z;
                        }
                    }
                    else
                    {
                        // We don't know what the shape is yet, so use default
                        volume = _size.X * _size.Y * _size.Z;
                    }
                    break;

                case ProfileShape.EquilateralTriangle:
                    /*
                        v = (abs((xB*yA-xA*yB)+(xC*yB-xB*yC)+(xA*yC-xC*yA))/2) * h

                        // seed mesh
                        Vertex MM = new Vertex(-0.25f, -0.45f, 0.0f);
                        Vertex PM = new Vertex(+0.5f, 0f, 0.0f);
                        Vertex PP = new Vertex(-0.25f, +0.45f, 0.0f);
                     */
                    float xA = -0.25f * _size.X;
                    float yA = -0.45f * _size.Y;

                    float xB = 0.5f * _size.X;
                    float yB = 0;

                    float xC = -0.25f * _size.X;
                    float yC = 0.45f * _size.Y;

                    volume = (float)((Math.Abs((xB * yA - xA * yB) + (xC * yB - xB * yC) + (xA * yC - xC * yA)) / 2) * _size.Z);

                    // If the user has 'hollowed out'
                    // ProfileHollow is one of those 0 to 50000 values :P
                    // we like percentages better..   so turning into a percentage
                    float fhollowFactor = ((float)_pbs.ProfileHollow / 1.9f);
                    if (((float)fhollowFactor / 50000f) > 0.0)
                    {
                        float hollowAmount = (float)fhollowFactor / 50000f;

                        // calculate the hollow volume by it's shape compared to the prim shape
                        float hollowVolume = 0;
                        switch (_pbs.HollowShape)
                        {
                            case HollowShape.Same:
                            case HollowShape.Triangle:
                                // Equilateral Triangular Prism volume hollow calculation
                                // Triangle is an Equilateral Triangular Prism with aLength = to _size.Y

                                float aLength = _size.Y;
                                // 1/2 abh
                                hollowVolume = (float)((0.5 * aLength * _size.X * _size.Z) * hollowAmount);
                                break;

                            case HollowShape.Square:
                                // Cube Hollow volume calculation
                                float hollowsizex = _size.X * hollowAmount;
                                float hollowsizey = _size.Y * hollowAmount;
                                float hollowsizez = _size.Z * hollowAmount;
                                hollowVolume = hollowsizex * hollowsizey * hollowsizez;
                                break;

                            case HollowShape.Circle:
                                // Hollow shape is a perfect cyllinder in respect to the cube's scale
                                // Cyllinder hollow volume calculation
                                float hRadius = _size.X / 2;
                                float hLength = _size.Z;

                                // pi * r2 * h
                                hollowVolume = ((float)((Math.PI * Math.Pow(hRadius, 2) * hLength) / 2) * hollowAmount);
                                break;

                            default:
                                hollowVolume = 0;
                                break;
                        }
                        volume = volume - hollowVolume;
                    }
                    break;

                default:
                    // we don't have all of the volume formulas yet so
                    // use the common volume formula for all
                    volume = _size.X * _size.Y * _size.Z;
                    break;
            }

            // Calculate Path cut effect on volume
            // Not exact, in the triangle hollow example
            // They should never be zero or less then zero..
            // we'll ignore it if it's less then zero

            // ProfileEnd and ProfileBegin are values
            // from 0 to 50000

            // Turning them back into percentages so that I can cut that percentage off the volume

            float PathCutEndAmount = _pbs.ProfileEnd;
            float PathCutStartAmount = _pbs.ProfileBegin;
            if (((PathCutStartAmount + PathCutEndAmount) / 50000f) > 0.0f)
            {
                float pathCutAmount = ((PathCutStartAmount + PathCutEndAmount) / 50000f);

                // Check the return amount for sanity
                if (pathCutAmount >= 0.99f)
                    pathCutAmount = 0.99f;

                volume = volume - (volume * pathCutAmount);
            }
            UInt16 taperX = _pbs.PathScaleX;
            UInt16 taperY = _pbs.PathScaleY;
            float taperFactorX = 0;
            float taperFactorY = 0;

            // Mass = density * volume
            if (taperX != 100)
            {
                if (taperX > 100)
                {
                    taperFactorX = 1.0f - ((float)taperX / 200);
                    //m_log.Warn("taperTopFactorX: " + extr.taperTopFactorX.ToString());
                }
                else
                {
                    taperFactorX = 1.0f - ((100 - (float)taperX) / 100);
                    //m_log.Warn("taperBotFactorX: " + extr.taperBotFactorX.ToString());
                }
                volume = (float)volume * ((taperFactorX / 3f) + 0.001f);
            }

            if (taperY != 100)
            {
                if (taperY > 100)
                {
                    taperFactorY = 1.0f - ((float)taperY / 200);
                    //m_log.Warn("taperTopFactorY: " + extr.taperTopFactorY.ToString());
                }
                else
                {
                    taperFactorY = 1.0f - ((100 - (float)taperY) / 100);
                    //m_log.Warn("taperBotFactorY: " + extr.taperBotFactorY.ToString());
                }
                volume = (float)volume * ((taperFactorY / 3f) + 0.001f);
            }
            returnMass = m_density * volume;
            if (returnMass <= 0) returnMass = 0.0001f;//ckrinke: Mass must be greater then zero.



            // Recursively calculate mass
            bool HasChildPrim = false;
            lock (childrenPrim)
            {
                if (childrenPrim.Count > 0)
                {
                    HasChildPrim = true;
                }

            }
            if (HasChildPrim)
            {
                BulletDotNETPrim[] childPrimArr = new BulletDotNETPrim[0];

                lock (childrenPrim)
                    childPrimArr = childrenPrim.ToArray();

                for (int i = 0; i < childPrimArr.Length; i++)
                {
                    if (childPrimArr[i] != null && !childPrimArr[i].m_taintremove)
                        returnMass += childPrimArr[i].CalculateMass();
                    // failsafe, this shouldn't happen but with OpenSim, you never know :)
                    if (i > 256)
                        break;
                }
            }





            return returnMass;
        }

        #endregion


        public void CreateGeom(IntPtr m_targetSpace, IMesh p_mesh)
        {
            m_log.Debug("[PHYSICS]: _________CreateGeom");
            if (p_mesh != null)
            {
                _mesh = _parent_scene.mesher.CreateMesh(m_primName, _pbs, _size, _parent_scene.meshSculptLOD, IsPhysical);
                setMesh(_parent_scene, _mesh);
                
            }
            else
            {
                if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1)
                {
                    if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z)
                    {
                        if (((_size.X / 2f) > 0f))
                        {
                            //SetGeom to a Regular Sphere
                            tempSize1.setValue(_size.X * 0.5f, _size.Y * 0.5f, _size.Z * 0.5f);
                            SetCollisionShape(new btSphereShape(_size.X*0.5f));
                        }
                        else
                        {
                            // uses halfextents
                            tempSize1.setValue(_size.X*0.5f, _size.Y*0.5f, _size.Z*0.5f);
                            SetCollisionShape(new btBoxShape(tempSize1));
                        }
                    }
                    else
                    {
                        // uses halfextents
                        tempSize1.setValue(_size.X * 0.5f, _size.Y * 0.5f, _size.Z * 0.5f);
                        SetCollisionShape(new btBoxShape(tempSize1));
                    }

                }
                else
                {
                    // uses halfextents
                    tempSize1.setValue(_size.X * 0.5f, _size.Y * 0.5f, _size.Z * 0.5f);
                    SetCollisionShape(new btBoxShape(tempSize1));
                }
            }
        }

        private void setMesh(BulletDotNETScene _parent_scene, IMesh mesh)
        {
            // TODO: Set Collision Body Mesh
            // This sleeper is there to moderate how long it takes between
            // setting up the mesh and pre-processing it when we get rapid fire mesh requests on a single object
            m_log.Debug("_________SetMesh");
            Thread.Sleep(10);

            //Kill Body so that mesh can re-make the geom
            if (IsPhysical && Body != null && Body.Handle != IntPtr.Zero)
            {
                if (childPrim)
                {
                    if (_parent != null)
                    {
                        BulletDotNETPrim parent = (BulletDotNETPrim)_parent;
                        parent.ChildDelink(this);
                    }
                }
                else
                {
                    //disableBody();
                }
            }

            IMesh oldMesh = primMesh;

            primMesh = mesh;
            
            float[] vertexList = primMesh.getVertexListAsFloatLocked(); // Note, that vertextList is pinned in memory
            int[] indexList = primMesh.getIndexListAsIntLocked(); // Also pinned, needs release after usage
            //Array.Reverse(indexList);
            primMesh.releaseSourceMeshData(); // free up the original mesh data to save memory

            int VertexCount = vertexList.GetLength(0) / 3;
            int IndexCount = indexList.GetLength(0);

            if (btshapeArray != null && btshapeArray.Handle != IntPtr.Zero)
                btshapeArray.Dispose();
            //Array.Reverse(indexList);
            btshapeArray = new btTriangleIndexVertexArray(IndexCount / 3, indexList, (3 * sizeof(int)),
                                                                                     VertexCount, vertexList, 3*sizeof (float));
            SetCollisionShape(new btGImpactMeshShape(btshapeArray));
            //((btGImpactMeshShape) prim_geom).updateBound();
            ((btGImpactMeshShape)prim_geom).setLocalScaling(new btVector3(1,1, 1));
            ((btGImpactMeshShape)prim_geom).updateBound();
            _parent_scene.SetUsingGImpact();
            if (oldMesh != null)
            {
                oldMesh.releasePinned();
                oldMesh = null;
            }

        }

        private void SetCollisionShape(btCollisionShape shape)
        {
            /*
            if (shape == null)
                m_log.Debug("[PHYSICS]:SetShape!Null");
            else
                m_log.Debug("[PHYSICS]:SetShape!");
            
            if (Body != null)
            {
                DisposeOfBody();
            }

            if (prim_geom != null)
            {
                prim_geom.Dispose();
                prim_geom = null;
            }
             */
            prim_geom = shape;
           
            //Body.set
        }

        public void SetBody(float mass)
        {
            //m_log.DebugFormat("[PHYSICS]: SetBody! {0}",mass);
            /*
            if (Body != null && Body.Handle != IntPtr.Zero)
            {
                DisposeOfBody();
            }
            */
            if (tempMotionState1 != null && tempMotionState1.Handle != IntPtr.Zero)
                tempMotionState1.Dispose();
            if (tempTransform2 != null && tempTransform2.Handle != IntPtr.Zero)
                tempTransform2.Dispose();
            if (tempOrientation2 != null && tempOrientation2.Handle != IntPtr.Zero)
                tempOrientation2.Dispose();

            if (tempPosition2 != null && tempPosition2.Handle != IntPtr.Zero)
                tempPosition2.Dispose();

            tempOrientation2 = new btQuaternion(_orientation.X, _orientation.Y, _orientation.Z, _orientation.W);
            tempPosition2 = new btVector3(_position.X, _position.Y, _position.Z);
            tempTransform2 = new btTransform(tempOrientation2, tempPosition2);
            tempMotionState1 = new btDefaultMotionState(tempTransform2, _parent_scene.TransZero);
            if (tempInertia1 != null && tempInertia1.Handle != IntPtr.Zero)
                tempInertia1.Dispose();
            tempInertia1 = new btVector3(0, 0, 0);
            /*
            if (prim_geom.Handle == IntPtr.Zero)
            {
                m_log.Warn("[PHYSICS]:PrimGeom is Disposed!");
                if (_parent_scene.needsMeshing(_pbs))
                {
                    // Don't need to re-enable body..   it's done in SetMesh
                    float meshlod = _parent_scene.meshSculptLOD;

                    if (IsPhysical)
                        meshlod = _parent_scene.MeshSculptphysicalLOD;

                    IMesh mesh = _parent_scene.mesher.CreateMesh(SOPName, _pbs, _size, meshlod, IsPhysical);
                    // createmesh returns null when it doesn't mesh.
                    CreateGeom(IntPtr.Zero, mesh);
                }
                else
                {
                    _mesh = null;
                    CreateGeom(IntPtr.Zero, null);
                }

            }
            */

            prim_geom.calculateLocalInertia(mass, tempInertia1);

            if (mass != 0)
                _parent_scene.addActivePrim(this);
            else
                _parent_scene.remActivePrim(this);

           //     Body = new btRigidBody(mass, tempMotionState1, prim_geom);
            //else
            Body = new btRigidBody(mass, tempMotionState1, prim_geom, tempInertia1);

            if (prim_geom is btGImpactMeshShape)
            {
                ((btGImpactMeshShape) prim_geom).setLocalScaling(new btVector3(1, 1, 1));
                ((btGImpactMeshShape) prim_geom).updateBound();
            }
            _parent_scene.AddPrimToScene(this);
        }

        private void DisposeOfBody()
        {
            if (Body != null)
            {
                if (Body.Handle != IntPtr.Zero)
                {
                    _parent_scene.removeFromWorld(this,Body);
                    Body.Dispose();
                }
                Body = null;
                // TODO: dispose parts that make up body
            }
        }

        private void ChildDelink(BulletDotNETPrim pPrim)
        {
            // Okay, we have a delinked child..   need to rebuild the body.
            lock (childrenPrim)
            {
                foreach (BulletDotNETPrim prm in childrenPrim)
                {
                    prm.childPrim = true;
                    prm.disableBody();

                }
            }
            disableBody();

            lock (childrenPrim)
            {
                childrenPrim.Remove(pPrim);
            }




            if (Body != null && Body.Handle != IntPtr.Zero)
            {
                _parent_scene.remActivePrim(this);
            }



            lock (childrenPrim)
            {
                foreach (BulletDotNETPrim prm in childrenPrim)
                {
                    ParentPrim(prm);
                }
            }

        }

        private void ParentPrim(BulletDotNETPrim prm)
        {
            // TODO: Parent Linking algorithm.   Use btComplexObject
        }

        public void disableBody()
        {
            //this kills the body so things like 'mesh' can re-create it.
            /*
            lock (this)
            {
                if (!childPrim)
                {
                    if (Body != null && Body.Handle != IntPtr.Zero)
                    {
                        _parent_scene.remActivePrim(this);

                        m_collisionCategories &= ~CollisionCategories.Body;
                        m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);

                        if (prim_geom != null && prim_geom.Handle != IntPtr.Zero)
                        {
                            // TODO: Set Category bits and Flags
                        }

                        // TODO: destroy body
                        DisposeOfBody();

                        lock (childrenPrim)
                        {
                            if (childrenPrim.Count > 0)
                            {
                                foreach (BulletDotNETPrim prm in childrenPrim)
                                {
                                    _parent_scene.remActivePrim(prm);
                                    prm.DisposeOfBody();
                                    prm.SetCollisionShape(null);
                                }
                            }

                        }

                        DisposeOfBody();
                    }
                }
                else
                {
                    _parent_scene.remActivePrim(this);
                    m_collisionCategories &= ~CollisionCategories.Body;
                    m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);

                    if (prim_geom != null && prim_geom.Handle != IntPtr.Zero)
                    {
                        // TODO: Set Category bits and Flags
                    }

                    DisposeOfBody();
                }
                
            }
            */
            m_disabled = true;
            m_collisionscore = 0;
        }

        public void disableBodySoft()
        {
            m_disabled = true;

            if (m_isphysical && Body.Handle != IntPtr.Zero)
            {
                Body.clearForces();
                Body.forceActivationState(0);
                
            }

        }

        public void enableBodySoft()
        {
            if (!childPrim)
            {
                if (m_isphysical && Body.Handle != IntPtr.Zero)
                {
                    Body.clearForces();
                    Body.forceActivationState(4);
                    forceenable = true;
                    
                }
                m_disabled = false;
            }
        }

        public void enableBody()
        {
            if (!childPrim)
            {
                //SetCollisionShape(prim_geom);
                if (IsPhysical)
                    SetBody(Mass);
                else
                    SetBody(0);
                
                // TODO: Set Collision Category Bits and Flags
                // TODO: Set Auto Disable data

                m_interpenetrationcount = 0;
                m_collisionscore = 0;
                m_disabled = false;
                // The body doesn't already have a finite rotation mode set here
                if ((!m_angularlock.IsIdentical(PhysicsVector.Zero, 0)) && _parent == null)
                {
                    // TODO: Create Angular Motor on Axis Lock!
                }
                _parent_scene.addActivePrim(this);
            }
        }

        public void UpdatePositionAndVelocity()
        {
            if (_parent == null)
            {
                PhysicsVector pv = new PhysicsVector(0, 0, 0);
                bool lastZeroFlag = _zeroFlag;
                if (tempPosition3 != null && tempPosition3.Handle != IntPtr.Zero)
                    tempPosition3.Dispose();
                if (tempTransform3 != null && tempTransform3.Handle != IntPtr.Zero)
                    tempTransform3.Dispose();

                if (tempOrientation2 != null && tempOrientation2.Handle != IntPtr.Zero)
                    tempOrientation2.Dispose();

                if (tempAngularVelocity1 != null && tempAngularVelocity1.Handle != IntPtr.Zero)
                    tempAngularVelocity1.Dispose();

                if (tempLinearVelocity1 != null && tempLinearVelocity1.Handle != IntPtr.Zero)
                    tempLinearVelocity1.Dispose();



                tempTransform3 = Body.getInterpolationWorldTransform();
                tempPosition3 = tempTransform3.getOrigin(); // vec
                tempOrientation2 = tempTransform3.getRotation(); // ori
                tempAngularVelocity1 = Body.getInterpolationAngularVelocity(); //rotvel
                tempLinearVelocity1 = Body.getInterpolationLinearVelocity(); // vel

                _torque.setValues(tempAngularVelocity1.getX(), tempAngularVelocity1.getX(), tempAngularVelocity1.getZ());
                PhysicsVector l_position = new PhysicsVector();
                Quaternion l_orientation = new Quaternion();
                m_lastposition = _position;
                m_lastorientation = _orientation;

                l_position.X = tempPosition3.getX();
                l_position.Y = tempPosition3.getY();
                l_position.Z = tempPosition3.getZ();
                l_orientation.X = tempOrientation2.getX();
                l_orientation.Y = tempOrientation2.getY();
                l_orientation.Z = tempOrientation2.getZ();
                l_orientation.W = tempOrientation2.getW();

                if (l_position.X > 255.95f || l_position.X < 0f || l_position.Y > 255.95f || l_position.Y < 0f)
                {
                    //base.RaiseOutOfBounds(l_position);

                    if (m_crossingfailures < _parent_scene.geomCrossingFailuresBeforeOutofbounds)
                    {
                        _position = l_position;
                        //_parent_scene.remActivePrim(this);
                        if (_parent == null)
                            base.RequestPhysicsterseUpdate();
                        return;
                    }
                    else
                    {
                        if (_parent == null)
                            base.RaiseOutOfBounds(l_position);
                        return;
                    }
                }

                if (l_position.Z < -200000f)
                {
                    // This is so prim that get lost underground don't fall forever and suck up
                    //
                    // Sim resources and memory.
                    // Disables the prim's movement physics....
                    // It's a hack and will generate a console message if it fails.

                    //IsPhysical = false;
                    //if (_parent == null)
                        //base.RaiseOutOfBounds(_position);

                    _acceleration.X = 0;
                    _acceleration.Y = 0;
                    _acceleration.Z = 0;

                    _velocity.X = 0;
                    _velocity.Y = 0;
                    _velocity.Z = 0;
                    m_rotationalVelocity.X = 0;
                    m_rotationalVelocity.Y = 0;
                    m_rotationalVelocity.Z = 0;

                    if (_parent == null)
                        base.RequestPhysicsterseUpdate();

                    m_throttleUpdates = false;
                    throttleCounter = 0;
                    _zeroFlag = true;
                    //outofBounds = true;
                }

                if ((Math.Abs(m_lastposition.X - l_position.X) < 0.02)
                    && (Math.Abs(m_lastposition.Y - l_position.Y) < 0.02)
                    && (Math.Abs(m_lastposition.Z - l_position.Z) < 0.02)
                    && (1.0 - Math.Abs(Quaternion.Dot(m_lastorientation, l_orientation)) < 0.01 ))
                {
                    _zeroFlag = true;
                    m_throttleUpdates = false;
                }
                else
                {
                    //m_log.Debug(Math.Abs(m_lastposition.X - l_position.X).ToString());
                    _zeroFlag = false;
                }

                if (_zeroFlag)
                {
                    _velocity.X = 0.0f;
                    _velocity.Y = 0.0f;
                    _velocity.Z = 0.0f;

                    _acceleration.X = 0;
                    _acceleration.Y = 0;
                    _acceleration.Z = 0;

                    //_orientation.w = 0f;
                    //_orientation.X = 0f;
                    //_orientation.Y = 0f;
                    //_orientation.Z = 0f;
                    m_rotationalVelocity.X = 0;
                    m_rotationalVelocity.Y = 0;
                    m_rotationalVelocity.Z = 0;
                    if (!m_lastUpdateSent)
                    {
                        m_throttleUpdates = false;
                        throttleCounter = 0;
                        m_rotationalVelocity = pv;

                        if (_parent == null)
                            base.RequestPhysicsterseUpdate();

                        m_lastUpdateSent = true;
                    }
                }
                else
                {
                    if (lastZeroFlag != _zeroFlag)
                    {
                        if (_parent == null)
                            base.RequestPhysicsterseUpdate();
                    }

                    m_lastVelocity = _velocity;

                    _position = l_position;

                    _velocity.X = tempLinearVelocity1.getX();
                    _velocity.Y = tempLinearVelocity1.getY();
                    _velocity.Z = tempLinearVelocity1.getZ();

                    _acceleration = ((_velocity - m_lastVelocity) / 0.1f);
                    _acceleration = new PhysicsVector(_velocity.X - m_lastVelocity.X / 0.1f, _velocity.Y - m_lastVelocity.Y / 0.1f, _velocity.Z - m_lastVelocity.Z / 0.1f);
                    //m_log.Info("[PHYSICS]: V1: " + _velocity + " V2: " + m_lastVelocity + " Acceleration: " + _acceleration.ToString());

                    if (_velocity.IsIdentical(pv, 0.5f))
                    {
                        m_rotationalVelocity = pv;
                    }
                    else
                    {
                        
                        m_rotationalVelocity.setValues(tempAngularVelocity1.getX(), tempAngularVelocity1.getY(), tempAngularVelocity1.getZ());
                    }

                    //m_log.Debug("ODE: " + m_rotationalVelocity.ToString());

                    _orientation.X = l_orientation.X;
                    _orientation.Y = l_orientation.Y;
                    _orientation.Z = l_orientation.Z;
                    _orientation.W = l_orientation.W;
                    m_lastUpdateSent = false;
                    
                    //if (!m_throttleUpdates || throttleCounter > _parent_scene.geomUpdatesPerThrottledUpdate)
                    //{
                        if (_parent == null)
                            base.RequestPhysicsterseUpdate();
                   // }
                   // else
                   // {
                   //     throttleCounter++;
                    //}
                   
                }
                m_lastposition = l_position;
                if (forceenable)
                {
                    Body.forceActivationState(1);
                    forceenable = false;
                }
            }
            else
            {
                // Not a body..   so Make sure the client isn't interpolating
                _velocity.X = 0;
                _velocity.Y = 0;
                _velocity.Z = 0;

                _acceleration.X = 0;
                _acceleration.Y = 0;
                _acceleration.Z = 0;

                m_rotationalVelocity.X = 0;
                m_rotationalVelocity.Y = 0;
                m_rotationalVelocity.Z = 0;
                _zeroFlag = true;
            }
        }


        internal void setPrimForRemoval()
        {
            m_taintremove = true;
        }
    }
}