From 8aa5fdb6a3f1e4b349757df5d9fcc06ab8dfdb64 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Mon, 3 Dec 2012 17:05:05 +0000 Subject: [PATCH] *TEST* diferent avatar collider --- .../Region/Framework/Scenes/ScenePresence.cs | 15 +- .../Physics/UbitOdePlugin/ODECharacter.cs | 371 ++++++++++++++---- .../Region/Physics/UbitOdePlugin/OdeScene.cs | 69 +++- 3 files changed, 351 insertions(+), 104 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 7f07d73e86..bacc9c90a3 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -571,7 +571,18 @@ namespace OpenSim.Region.Framework.Scenes set { m_bodyRot = value; -// m_log.DebugFormat("[SCENE PRESENCE]: Body rot for {0} set to {1}", Name, m_bodyRot); + // m_log.DebugFormat("[SCENE PRESENCE]: Body rot for {0} set to {1}", Name, m_bodyRot); + if (PhysicsActor != null) + { + try + { + PhysicsActor.Orientation = value; + } + catch (Exception e) + { + m_log.Error("[SCENE PRESENCE]: Orientation " + e.Message); + } + } } } @@ -3435,7 +3446,7 @@ namespace OpenSim.Region.Framework.Scenes PhysicsActor = scene.AddAvatar( LocalId, Firstname + "." + Lastname, pVec, - new Vector3(0f, 0f, Appearance.AvatarHeight), isFlying); + new Vector3(0.45f, 0.6f, Appearance.AvatarHeight), isFlying); //PhysicsActor.OnRequestTerseUpdate += SendTerseUpdateToAllClients; PhysicsActor.OnCollisionUpdate += PhysicsCollisionUpdate; diff --git a/OpenSim/Region/Physics/UbitOdePlugin/ODECharacter.cs b/OpenSim/Region/Physics/UbitOdePlugin/ODECharacter.cs index 94ed663cf9..3d5be3e590 100644 --- a/OpenSim/Region/Physics/UbitOdePlugin/ODECharacter.cs +++ b/OpenSim/Region/Physics/UbitOdePlugin/ODECharacter.cs @@ -79,6 +79,8 @@ namespace OpenSim.Region.Physics.OdePlugin private Vector3 _target_velocity; private Vector3 _acceleration; private Vector3 m_rotationalVelocity; + private Vector3 m_size; + private Quaternion m_orientation; private float m_mass = 80f; public float m_density = 60f; private bool m_pidControllerActive = true; @@ -86,10 +88,17 @@ namespace OpenSim.Region.Physics.OdePlugin public float PID_P = 900.0f; //private static float POSTURE_SERVO = 10000.0f; - public float CAPSULE_RADIUS = 0.37f; - public float CAPSULE_LENGTH = 2.140599f; - const float CAP_OFFSET = -.2f; // compensation of SL size offset plus spheric collision shape bottom + private float m_invElipSizeX; + private float m_invElipSizeY; + + private float feetOff = 0; + private float feetSZ = 0.5f; + const float feetScale = 0.9f; + const float invFeetScale = 1.0f / 0.9f; + const float sizeZAdjust = 0.15f; + private float boneOff = 0; + public float walkDivisor = 1.3f; public float runDivisor = 0.8f; @@ -127,10 +136,16 @@ namespace OpenSim.Region.Physics.OdePlugin // we do land collisions not ode | CollisionCategories.Land); public IntPtr Body = IntPtr.Zero; private OdeScene _parent_scene; - public IntPtr Shell = IntPtr.Zero; + public IntPtr topbox = IntPtr.Zero; + public IntPtr midbox = IntPtr.Zero; + public IntPtr feetbox = IntPtr.Zero; + public IntPtr bonebox = IntPtr.Zero; + public IntPtr Amotor = IntPtr.Zero; + public d.Mass ShellMass; -// public bool collidelock = false; + + public int m_eventsubscription = 0; private int m_cureventsubscription = 0; @@ -145,7 +160,7 @@ namespace OpenSim.Region.Physics.OdePlugin - public OdeCharacter(String avName, OdeScene parent_scene, Vector3 pos, Vector3 size, float pid_d, float pid_p, float capsule_radius, float density, float walk_divisor, float rundivisor) + public OdeCharacter(String avName, OdeScene parent_scene, Vector3 pos, Vector3 pSize, float pid_d, float pid_p, float density, float walk_divisor, float rundivisor) { m_uuid = UUID.Random(); @@ -171,9 +186,20 @@ namespace OpenSim.Region.Physics.OdePlugin PID_D = pid_d; PID_P = pid_p; - CAPSULE_RADIUS = capsule_radius; + + m_size.X = pSize.X; + m_size.Y = pSize.Y; + m_size.Z = pSize.Z; + + if(m_size.X <0.01f) + m_size.X = 0.01f; + if(m_size.Y <0.01f) + m_size.Y = 0.01f; + if(m_size.Z <0.01f) + m_size.Z = 0.01f; + + m_orientation = Quaternion.Identity; m_density = density; - m_mass = 80f; // sure we have a default // force lower density for testing m_density = 3.0f; @@ -183,8 +209,7 @@ namespace OpenSim.Region.Physics.OdePlugin walkDivisor = walk_divisor; runDivisor = rundivisor; - CAPSULE_LENGTH = size.Z - CAPSULE_RADIUS + CAP_OFFSET; - //m_log.Info("[SIZE]: " + CAPSULE_LENGTH.ToString()); + m_mass = m_density * m_size.X * m_size.Y * m_size.Z; ; // sure we have a default m_isPhysical = false; // current status: no ODE information exists @@ -426,14 +451,21 @@ namespace OpenSim.Region.Physics.OdePlugin /// public override Vector3 Size { - get { - float d = CAPSULE_RADIUS * 2; - return new Vector3(d, d, (CAPSULE_LENGTH + CAPSULE_RADIUS - CAP_OFFSET)); + get + { + return m_size; } set { if (value.IsFinite()) { + if(value.X <0.01f) + value.X = 0.01f; + if(value.Y <0.01f) + value.Y = 0.01f; + if(value.Z <0.01f) + value.Z = 0.01f; + AddChange(changes.Size, value); } else @@ -459,8 +491,7 @@ namespace OpenSim.Region.Physics.OdePlugin { get { - float AVvolume = (float)(Math.PI * CAPSULE_RADIUS * CAPSULE_RADIUS * (1.3333333333f * CAPSULE_RADIUS + CAPSULE_LENGTH)); - return m_density * AVvolume; + return m_density * m_size.X * m_size.Y * m_size.Z; } } public override void link(PhysicsActor obj) @@ -578,9 +609,14 @@ namespace OpenSim.Region.Physics.OdePlugin public override Quaternion Orientation { - get { return Quaternion.Identity; } + get { return m_orientation; } set { + // fakeori = value; + // givefakeori++; + + value.Normalize(); + AddChange(changes.Orientation, value); } } @@ -632,32 +668,65 @@ namespace OpenSim.Region.Physics.OdePlugin AddChange(changes.Momentum, momentum); } - - // WARNING: This MUST NOT be called outside of ProcessTaints, else we can have unsynchronized access - // to ODE internals. ProcessTaints is called from within thread-locked Simulate(), so it is the only - // place that is safe to call this routine AvatarGeomAndBodyCreation. private void AvatarGeomAndBodyCreation(float npositionX, float npositionY, float npositionZ) { + // sizes one day should came from visual parameters + float sz = m_size.Z + sizeZAdjust; + + m_invElipSizeX = 1.0f / m_size.X; + m_invElipSizeY = 1.0f / m_size.Y; + + float topsx = m_size.X; + float midsx = m_size.X; + float feetsx = m_size.X * feetScale; + float bonesx = feetsx * 0.2f; + + float topsy = m_size.Y * 0.5f; + float midsy = m_size.Y; + float feetsy = m_size.Y * feetScale; + float bonesy = feetsy * 0.2f; + + float topsz = sz * 0.15f; + float feetsz = sz * 0.3f; + if (feetsz > 0.6f) + feetsz = 0.6f; + + float midsz = sz - topsz - feetsz; + float bonesz = sz; + + float bot = -sz * 0.5f; + + boneOff = bot + 0.3f; + + float feetz = bot + feetsz * 0.5f; + bot += feetsz; + + feetOff = bot; + feetSZ = feetsz; + + float midz = bot + midsz * 0.5f; + bot += midsz; + float topz = bot + topsz * 0.5f; + _parent_scene.waitForSpaceUnlock(_parent_scene.ActiveSpace); - if (CAPSULE_LENGTH <= 0) - { - m_log.Warn("[PHYSICS]: The capsule size you specified in opensim.ini is invalid! Setting it to the smallest possible size!"); - CAPSULE_LENGTH = 0.01f; - } + feetbox = d.CreateBox(_parent_scene.ActiveSpace, feetsx, feetsy, feetsz); + d.GeomSetCategoryBits(feetbox, (uint)m_collisionCategories); + d.GeomSetCollideBits(feetbox, (uint)m_collisionFlags); - if (CAPSULE_RADIUS <= 0) - { - m_log.Warn("[PHYSICS]: The capsule size you specified in opensim.ini is invalid! Setting it to the smallest possible size!"); - CAPSULE_RADIUS = 0.01f; + midbox = d.CreateBox(_parent_scene.ActiveSpace, midsx, midsy, midsz); + d.GeomSetCategoryBits(midbox, (uint)m_collisionCategories); + d.GeomSetCollideBits(midbox, (uint)m_collisionFlags); - } - Shell = d.CreateCapsule(_parent_scene.ActiveSpace, CAPSULE_RADIUS, CAPSULE_LENGTH); + topbox = d.CreateBox(_parent_scene.ActiveSpace, topsx, topsy, topsz); + d.GeomSetCategoryBits(topbox, (uint)m_collisionCategories); + d.GeomSetCollideBits(topbox, (uint)m_collisionFlags); - d.GeomSetCategoryBits(Shell, (uint)m_collisionCategories); - d.GeomSetCollideBits(Shell, (uint)m_collisionFlags); + bonebox = d.CreateBox(_parent_scene.ActiveSpace, bonesx, bonesy, bonesz); + d.GeomSetCategoryBits(bonebox, (uint)m_collisionCategories); + d.GeomSetCollideBits(bonebox, (uint)m_collisionFlags); - d.MassSetCapsule(out ShellMass, m_density, 3, CAPSULE_RADIUS, CAPSULE_LENGTH); + d.MassSetBox(out ShellMass, m_density, m_size.X , m_size.Y, m_size.Z); m_mass = ShellMass.mass; // update mass @@ -688,7 +757,14 @@ namespace OpenSim.Region.Physics.OdePlugin _position.Z = npositionZ; d.BodySetMass(Body, ref ShellMass); - d.GeomSetBody(Shell, Body); + d.GeomSetBody(feetbox, Body); + d.GeomSetBody(midbox, Body); + d.GeomSetBody(topbox, Body); + d.GeomSetBody(bonebox, Body); + + d.GeomSetOffsetPosition(feetbox, 0, 0, feetz); + d.GeomSetOffsetPosition(midbox, 0, 0, midz); + d.GeomSetOffsetPosition(topbox, 0, 0, topz); // The purpose of the AMotor here is to keep the avatar's physical // surrogate from rotating while moving @@ -748,15 +824,152 @@ namespace OpenSim.Region.Physics.OdePlugin Body = IntPtr.Zero; } - //kill the Geometry - if (Shell != IntPtr.Zero) + //kill the Geoms + if (topbox != IntPtr.Zero) { -// _parent_scene.geom_name_map.Remove(Shell); - _parent_scene.actor_name_map.Remove(Shell); + _parent_scene.actor_name_map.Remove(topbox); _parent_scene.waitForSpaceUnlock(_parent_scene.ActiveSpace); - d.GeomDestroy(Shell); - Shell = IntPtr.Zero; + d.GeomDestroy(topbox); + topbox = IntPtr.Zero; } + if (midbox != IntPtr.Zero) + { + _parent_scene.actor_name_map.Remove(midbox); + _parent_scene.waitForSpaceUnlock(_parent_scene.ActiveSpace); + d.GeomDestroy(midbox); + midbox = IntPtr.Zero; + } + if (feetbox != IntPtr.Zero) + { + _parent_scene.actor_name_map.Remove(feetbox); + _parent_scene.waitForSpaceUnlock(_parent_scene.ActiveSpace); + d.GeomDestroy(feetbox); + feetbox = IntPtr.Zero; + } + + if (bonebox != IntPtr.Zero) + { + _parent_scene.actor_name_map.Remove(bonebox); + _parent_scene.waitForSpaceUnlock(_parent_scene.ActiveSpace); + d.GeomDestroy(bonebox); + bonebox = IntPtr.Zero; + } + + } + + public bool Collide(IntPtr me, bool reverse, ref d.ContactGeom contact) + { + + if (me == bonebox) // inner bone + { + if (contact.pos.Z - _position.Z < boneOff) + IsColliding = true; + return true; + } + + if (me == topbox) // keep a box head + return true; + + // rotate elipsoide assuming only rotation around Z + float ca = m_orientation.W * m_orientation.W - m_orientation.Z * m_orientation.Z; + float sa = 2 * m_orientation.W * m_orientation.Z; + + float isx; + float isy; + + if (me == feetbox) // feet have narrow bounds + { + + isx = m_invElipSizeX * invFeetScale; + isy = m_invElipSizeY * invFeetScale; + } + else + { + isx = m_invElipSizeX; + isy = m_invElipSizeY; + } + + float a = isx * ca - isy * sa; + float b = isx * sa + isy * ca; + + float offx = contact.pos.X - _position.X; + float er = offx * a; + er *= er; + + float offy = contact.pos.Y - _position.Y; + float ty = offy * b; + er += ty * ty; + + if (me == midbox) + { + if (er > 4.0f) // no collision + return false; + if (er < 0.2f) + return true; + + float t = offx * offx + offy * offy; + t = (float)Math.Sqrt(t); + t = 1 / t; + offx *= t; + offy *= t; + + if (reverse) + { + contact.normal.X = offx; + contact.normal.Y = offy; + } + else + { + contact.normal.X = -offx; + contact.normal.Y = -offy; + } + + contact.normal.Z = 0; + return true; + } + + else if (me == feetbox) + { + float c = feetSZ * 2; + float h = contact.pos.Z - _position.Z; + float offz = h - feetOff; // distance from top of feetbox + + float tz = offz / c; + er += tz * tz; + + if (er > 4.0f) // no collision + return false; + + if (er > 0.2f) + { + float t = offx * offx + offy * offy + offz * offz; + t = (float)Math.Sqrt(t); + t = 1 / t; + offx *= t; + offy *= t; + offz *= t; + + if (reverse) + { + contact.normal.X = offx; + contact.normal.Y = offy; + contact.normal.Z = offz; + } + else + { + contact.normal.X = -offx; + contact.normal.Y = -offy; + contact.normal.Z = -offz; + } + } + + if(h < boneOff) + IsColliding = true; + } + else + return false; + + return true; } /// @@ -776,10 +989,10 @@ namespace OpenSim.Region.Physics.OdePlugin // so force it back to identity d.Quaternion qtmp; - qtmp.W = 1; - qtmp.X = 0; - qtmp.Y = 0; - qtmp.Z = 0; + qtmp.W = m_orientation.W; + qtmp.X = m_orientation.X; + qtmp.Y = m_orientation.Y; + qtmp.Z = m_orientation.Z; d.BodySetQuaternion(Body, ref qtmp); if (m_pidControllerActive == false) @@ -843,7 +1056,7 @@ namespace OpenSim.Region.Physics.OdePlugin //****************************************** // colide with land d.AABB aabb; - d.GeomGetAABB(Shell, out aabb); + d.GeomGetAABB(feetbox, out aabb); float chrminZ = aabb.MinZ - 0.04f; // move up a bit Vector3 posch = localpos; @@ -1182,20 +1395,14 @@ namespace OpenSim.Region.Physics.OdePlugin { if (NewStatus) { - // Create avatar capsule and related ODE data - if ((Shell != IntPtr.Zero)) - { - // a lost shell ? - m_log.Warn("[PHYSICS]: re-creating the following avatar ODE data, even though it already exists - " - + (Shell != IntPtr.Zero ? "Shell " : "") - + (Body != IntPtr.Zero ? "Body " : "") - + (Amotor != IntPtr.Zero ? "Amotor " : "")); - AvatarGeomAndBodyDestroy(); - } + AvatarGeomAndBodyDestroy(); AvatarGeomAndBodyCreation(_position.X, _position.Y, _position.Z); - _parent_scene.actor_name_map[Shell] = (PhysicsActor)this; + _parent_scene.actor_name_map[topbox] = (PhysicsActor)this; + _parent_scene.actor_name_map[midbox] = (PhysicsActor)this; + _parent_scene.actor_name_map[feetbox] = (PhysicsActor)this; + _parent_scene.actor_name_map[bonebox] = (PhysicsActor)this; _parent_scene.AddCharacter(this); } else @@ -1224,37 +1431,29 @@ namespace OpenSim.Region.Physics.OdePlugin { } - private void changeSize(Vector3 Size) + private void changeSize(Vector3 pSize) { - if (Size.IsFinite()) + if (pSize.IsFinite()) { - float caplen = Size.Z; - - caplen = caplen - CAPSULE_RADIUS + CAP_OFFSET; - - if (caplen != CAPSULE_LENGTH) + // for now only look to Z changes since viewers also don't change X and Y + if (pSize.Z != m_size.Z) { - if (Shell != IntPtr.Zero && Body != IntPtr.Zero && Amotor != IntPtr.Zero) - { - AvatarGeomAndBodyDestroy(); + AvatarGeomAndBodyDestroy(); - float prevCapsule = CAPSULE_LENGTH; - CAPSULE_LENGTH = caplen; - AvatarGeomAndBodyCreation(_position.X, _position.Y, - _position.Z + (CAPSULE_LENGTH - prevCapsule) * 0.5f); + float oldsz = m_size.Z; + m_size = pSize; - Velocity = Vector3.Zero; - _parent_scene.actor_name_map[Shell] = (PhysicsActor)this; - } - else - { - m_log.Warn("[PHYSICS]: trying to change capsule size, but the following ODE data is missing - " - + (Shell == IntPtr.Zero ? "Shell " : "") - + (Body == IntPtr.Zero ? "Body " : "") - + (Amotor == IntPtr.Zero ? "Amotor " : "")); - } + AvatarGeomAndBodyCreation(_position.X, _position.Y, + _position.Z + (m_size.Z - oldsz) * 0.5f); + + Velocity = Vector3.Zero; + + _parent_scene.actor_name_map[topbox] = (PhysicsActor)this; + _parent_scene.actor_name_map[midbox] = (PhysicsActor)this; + _parent_scene.actor_name_map[feetbox] = (PhysicsActor)this; + _parent_scene.actor_name_map[bonebox] = (PhysicsActor)this; } m_freemove = false; m_pidControllerActive = true; @@ -1276,6 +1475,14 @@ namespace OpenSim.Region.Physics.OdePlugin private void changeOrientation(Quaternion newOri) { + d.Quaternion myrot = new d.Quaternion(); + myrot.X = newOri.X; + myrot.Y = newOri.Y; + myrot.Z = newOri.Z; + myrot.W = newOri.W; + float t = d.JointGetAMotorAngle(Amotor, 2); + d.BodySetQuaternion(Body,ref myrot); + m_orientation = newOri; } private void changeVelocity(Vector3 newVel) @@ -1365,7 +1572,7 @@ namespace OpenSim.Region.Physics.OdePlugin public bool DoAChange(changes what, object arg) { - if (Shell == IntPtr.Zero && what != changes.Add && what != changes.Remove) + if (topbox == IntPtr.Zero && what != changes.Add && what != changes.Remove) { return false; } diff --git a/OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs b/OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs index 54bc29f2f8..003a91c942 100644 --- a/OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/UbitOdePlugin/OdeScene.cs @@ -840,6 +840,8 @@ namespace OpenSim.Region.Physics.OdePlugin { case (int)ActorTypes.Agent: { + dop1foot = true; + AvanormOverride = true; Vector3 tmp = p2.Position - p1.Position; normoverride = p2.Velocity - p1.Velocity; @@ -883,6 +885,10 @@ namespace OpenSim.Region.Physics.OdePlugin switch (p2.PhysicsActorType) { case (int)ActorTypes.Agent: + + + dop2foot = true; + AvanormOverride = true; Vector3 tmp = p2.Position - p1.Position; @@ -1017,6 +1023,7 @@ namespace OpenSim.Region.Physics.OdePlugin IntPtr Joint; int i = 0; + int ncontacts = 0; while(true) { @@ -1031,7 +1038,28 @@ namespace OpenSim.Region.Physics.OdePlugin else { + if(dop1foot) + { + if (!(((OdeCharacter)p1).Collide(g1,false, ref curContact))) + { + if (++i >= count) + break; + else + continue; + } + } + else if(dop2foot) + { + if(!(((OdeCharacter) p2).Collide(g2,true,ref curContact))) + { + if (++i >= count) + break; + else + continue; + } + } +/* if (AvanormOverride) { if (curContact.depth > 0.3f) @@ -1081,34 +1109,31 @@ namespace OpenSim.Region.Physics.OdePlugin { float sz = p2.Size.Z; Vector3 vtmp = p2.Position; - float ppos = curContact.pos.Z - vtmp.Z + (sz - avCapRadius) * 0.5f; + vtmp.Z -= sz * 0.5f; + vtmp.Z += 0.5f; + float ppos = vtmp.Z - curContact.pos.Z; if (ppos > 0f) { if (!p2.Flying) { - d.AABB aabb; - d.GeomGetAABB(g1, out aabb); float tmp = vtmp.Z - sz * .18f; - - if (aabb.MaxZ < tmp) - { - vtmp.X = curContact.pos.X - vtmp.X; - vtmp.Y = curContact.pos.Y - vtmp.Y; - vtmp.Z = -0.2f; - vtmp.Normalize(); - curContact.normal.X = vtmp.X; - curContact.normal.Y = vtmp.Y; - curContact.normal.Z = vtmp.Z; - } + vtmp.X = curContact.pos.X - vtmp.X; + vtmp.Y = curContact.pos.Y - vtmp.Y; + vtmp.Z = curContact.pos.Z - vtmp.Z; + vtmp.Normalize(); + curContact.normal.X = vtmp.X; + curContact.normal.Y = vtmp.Y; + curContact.normal.Z = vtmp.Z; } } - else +// else p2.IsColliding = true; } } } - +*/ + ncontacts++; Joint = CreateContacJoint(ref curContact, mu, bounce, cfm, erpscale, dscale); d.JointAttach(Joint, b1, b2); @@ -1134,7 +1159,8 @@ namespace OpenSim.Region.Physics.OdePlugin } } - collision_accounting_events(p1, p2, maxDepthContact); + if(ncontacts > 0) + collision_accounting_events(p1, p2, maxDepthContact); /* if (notskipedcount > geomContactPointsStartthrottle) @@ -1234,14 +1260,17 @@ namespace OpenSim.Region.Physics.OdePlugin { foreach (OdeCharacter chr in _characters) { - if (chr == null || chr.Shell == IntPtr.Zero || chr.Body == IntPtr.Zero) + if (chr == null || chr.Body == IntPtr.Zero) continue; chr.IsColliding = false; // chr.CollidingGround = false; not done here chr.CollidingObj = false; // do colisions with static space - d.SpaceCollide2(StaticSpace, chr.Shell, IntPtr.Zero, nearCallback); + d.SpaceCollide2(StaticSpace, chr.topbox, IntPtr.Zero, nearCallback); + d.SpaceCollide2(StaticSpace, chr.midbox, IntPtr.Zero, nearCallback); + d.SpaceCollide2(StaticSpace, chr.feetbox, IntPtr.Zero, nearCallback); + d.SpaceCollide2(StaticSpace, chr.bonebox, IntPtr.Zero, nearCallback); // no coll with gnd } } @@ -1334,7 +1363,7 @@ namespace OpenSim.Region.Physics.OdePlugin pos.X = position.X; pos.Y = position.Y; pos.Z = position.Z; - OdeCharacter newAv = new OdeCharacter(avName, this, pos, size, avPIDD, avPIDP, avCapRadius, avDensity, avMovementDivisorWalk, avMovementDivisorRun); + OdeCharacter newAv = new OdeCharacter(avName, this, pos, size, avPIDD, avPIDP, avDensity, avMovementDivisorWalk, avMovementDivisorRun); newAv.Flying = isFlying; newAv.MinimumGroundFlightOffset = minimumGroundFlightOffset;