public void Empty() { Containers.Clear(); NearRoomList.Clear(); Portals.Clear(); Frustum.Clear(); Mesh = null; if (StaticMesh.Count > 0) { foreach(var sm in StaticMesh) { RigidBody body = null; if((body = sm.BtBody) != null) { if(body.MotionState != null) { body.MotionState.Dispose(); body.MotionState = null; } body.CollisionShape = null; BtEngineDynamicsWorld.RemoveRigidBody(body); body.Dispose(); body = null; sm.BtBody = null; } if(sm.Self != null) { sm.Self.Room = null; sm.Self = null; } } StaticMesh.Clear(); } if(BtBody != null) { BtBody.UserObject = null; if(BtBody.MotionState != null) { BtBody.MotionState.Dispose(); BtBody.MotionState = null; } if(BtBody.CollisionShape != null) { BtBody.CollisionShape.Dispose(); //BtBody.CollisionShape = null; } BtEngineDynamicsWorld.RemoveRigidBody(BtBody); BtBody = null; } Sectors.Clear(); SectorsX = 0; SectorsY = 0; Sprites.Clear(); Lights.Clear(); Self = null; }
/// <summary> /// Creates hair into allocated hair structure, using previously defined setup and entity index. /// </summary> public bool Create(HairSetup setup, Entity parentEntity) { // No setup or parent to link to - bypass function. if (parentEntity == null || setup == null || setup.LinkBody >= parentEntity.Bf.BoneTags.Count || parentEntity.Bt.BtBody[(int)setup.LinkBody] == null) return false; var model = EngineWorld.GetModelByID(setup.Model); // No model to link to - bypass function. if (model == null || model.MeshCount == 0) return false; // Setup engine container. FIXME: DOESN'T WORK PROPERLY ATM. Container = new EngineContainer(); Container.Room = parentEntity.Self.Room; Container.ObjectType = OBJECT_TYPE.Hair; Container.Object = this; // Setup initial hair parameters. OwnerChar = parentEntity; // Entity to refer to. OwnerBody = setup.LinkBody; // Entity body to refer to. // Setup initial position / angles. var ownerBodyTransform = parentEntity.Transform * parentEntity.Bf.BoneTags[(int) OwnerBody].FullTransform; // Number of elements (bodies) is equal to number of hair meshes. Elements = new List<HairElement>(); Elements.Resize(model.MeshCount, () => new HairElement()); // Root index should be always zero, as it is how engine determines that it is // connected to head and renders it properly. Tail index should be always the // last element of the hair, as it indicates absence of "child" constraint. RootIndex = 0; TailIndex = (byte)(Elements.Count - 1); // Weight step is needed to determine the weight of each hair body. // It is derived from root body weight and tail body weight. var weightStep = (setup.RootWeight - setup.TailWeight) / Elements.Count; var currentWeight = setup.RootWeight; for (var i = 0; i < Elements.Count; i++) { // Point to corresponding mesh. Elements[i].Mesh = model.MeshTree[i].MeshBase; // Begin creating ACTUAL physical hair mesh. var localInertia = BulletSharp.Math.Vector3.Zero; // Make collision shape out of mesh. Elements[i].Shape = BT_CSfromMesh(Elements[i].Mesh, true, true, false); Elements[i].Shape.CalculateLocalInertia(currentWeight * setup.HairInertia, out localInertia); // Decrease next body weight to weight_step parameter. currentWeight -= weightStep; // Initialize motion state for body. var startTransform = ownerBodyTransform; var motionState = new DefaultMotionState(((Matrix4)startTransform).ToBullet()); // Make rigid body. Elements[i].Body = new RigidBody(new RigidBodyConstructionInfo(currentWeight, motionState, Elements[i].Shape, localInertia)); // Damping makes body stop in space by itself, to prevent it from continous movement. Elements[i].Body.SetDamping(setup.HairDamping[0], setup.HairDamping[1]); // Restitution and friction parameters define "bounciness" and "dullness" of hair. Elements[i].Body.Restitution = setup.HairRestitution; Elements[i].Body.Friction = setup.HairFriction; // Since hair is always moving with Lara, even if she's in still state (like, hanging // on a ledge), hair bodies shouldn't deactivate over time. Elements[i].Body.ForceActivationState(ActivationState.DisableDeactivation); // Hair bodies must not collide with each other, and also collide ONLY with kinematic // bodies (e. g. animated meshes), or else Lara's ghost object or anything else will be able to // collide with hair! Elements[i].Body.UserObject = Container; BtEngineDynamicsWorld.AddRigidBody(Elements[i].Body, CollisionFilterGroups.CharacterFilter, CollisionFilterGroups.KinematicFilter); Elements[i].Body.Activate(); } // GENERATE CONSTRAINTS. // All constraints are generic 6-DOF type, as they seem perfect fit for hair. // Joint count is calculated from overall body amount multiplied by per-body constraint // count. Joints = new List<Generic6DofConstraint>(); Joints.Resize(Elements.Count); // If multiple joints per body is specified, joints are placed in circular manner, // with obvious step of (SIMD_2_PI) / joint count. It means that all joints will form // circle-like figure. var currJoint = 0; for (var i = 0; i < Elements.Count; i++) { float bodyLength; var localA = new Transform(); localA.SetIdentity(); var localB = new Transform(); localB.SetIdentity(); var jointX = 0.0f; var jointY = 0.0f; RigidBody prevBody; if(i == 0) // First joint group { // Adjust pivot point A to parent body. localA.Origin = setup.HeadOffset + new Vector3(jointX, 0.0f, jointY); Helper.SetEulerZYX(ref localA.Basis, setup.RootAngle.X, setup.RootAngle.Y, setup.RootAngle.Z); // Stealing this calculation because I need it for drawing OwnerBodyHairRoot = localA; localB.Origin = new Vector3(jointX, 0.0f, jointY); Helper.SetEulerZYX(ref localB.Basis, 0, -HalfPI, 0); prevBody = parentEntity.Bt.BtBody[(int) OwnerBody]; // Previous body is parent body. } else { // Adjust pivot point A to previous mesh's length, considering mesh overlap multiplier. bodyLength = Math.Abs(Elements[i - 1].Mesh.BBMax.Y - Elements[i - 1].Mesh.BBMin.Y) * setup.JointOverlap; localA.Origin = new Vector3(jointX, bodyLength, jointY); Helper.SetEulerZYX(ref localA.Basis, 0, -HalfPI, 0); // Pivot point B is automatically adjusted by Bullet. localB.Origin = new Vector3(jointX, 0.0f, jointY); Helper.SetEulerZYX(ref localB.Basis, 0, -HalfPI, 0); prevBody = Elements[i - 1].Body; // Previous body is preceding hair mesh. } // Create 6DOF constraint. Joints[currJoint] = new Generic6DofConstraint(prevBody, Elements[i].Body, ((Matrix4) localA).ToBullet(), ((Matrix4) localB).ToBullet(), true); // CFM and ERP parameters are critical for making joint "hard" and link // to Lara's head. With wrong values, constraints may become "elastic". for (var axis = 0; axis < 6; axis++) { Joints[currJoint].SetParam(ConstraintParam.StopCfm, setup.JointCfm, axis); Joints[currJoint].SetParam(ConstraintParam.StopErp, setup.JointErp, axis); } Joints[currJoint].LinearLowerLimit = BulletSharp.Math.Vector3.Zero; Joints[currJoint].LinearUpperLimit = BulletSharp.Math.Vector3.Zero; if(i == 0) { // First joint group should be more limited in motion, as it is connected // right to the head. NB: Should we make it scriptable as well? Joints[currJoint].AngularLowerLimit = new BulletSharp.Math.Vector3(-HalfPI, 0.0f, -HalfPI * 0.4f); Joints[currJoint].AngularLowerLimit = new BulletSharp.Math.Vector3(-HalfPI * 0.3f, 0.0f, HalfPI * 0.4f); // Increased solver iterations make constraint even more stable. Joints[currJoint].OverrideNumSolverIterations = 100; } else { // Normal joint with more movement freedom. Joints[currJoint].AngularLowerLimit = new BulletSharp.Math.Vector3(-HalfPI * 0.5f, 0.0f, -HalfPI * 0.5f); Joints[currJoint].AngularLowerLimit = new BulletSharp.Math.Vector3(HalfPI * 0.5f, 0.0f, HalfPI * 0.5f); } Joints[currJoint].DebugDrawSize = 5.0f; // Draw constraint axes. // Add constraint to the world. BtEngineDynamicsWorld.AddConstraint(Joints[currJoint], true); currJoint++; // Point to the next joint. } createHairMesh(model); return true; }
public void Dispose() { Bt.LastCollisions.Clear(); if (Bt.BtJoints.Count > 0) { DeleteRagdoll(); } foreach (var ghost in Bt.GhostObjects) { ghost.UserObject = null; BtEngineDynamicsWorld?.RemoveCollisionObject(ghost); } Bt.GhostObjects.Clear(); Bt.Shapes.Clear(); Bt.ManifoldArray?.Clear(); if (Bt.BtBody.Count > 0) { foreach (var body in Bt.BtBody) { if (body != null) { body.UserObject = null; if (body.MotionState != null) { body.MotionState.Dispose(); body.MotionState = null; } body.CollisionShape = null; BtEngineDynamicsWorld.RemoveRigidBody(body); } } Bt.BtBody.Clear(); } Self = null; Bf.BoneTags.Clear(); for (var ssAnim = Bf.Animations.Next; ssAnim != null;) { var ssAnimNext = ssAnim.Next; ssAnim.Next = null; ssAnim = ssAnimNext; } Bf.Animations.Next = null; }
public Entity(uint id) { ID = id; MoveType = MoveType.OnFloor; Self = new EngineContainer(); Transform = new Transform(); Transform.SetIdentity(); Self.Object = this; Self.ObjectType = OBJECT_TYPE.Entity; Self.Room = null; Self.CollisionType = COLLISION_TYPE.None; OBB = new OBB(); OBB.Transform = Transform; Bt = new BtEntityData(); Bt.BtBody = new List<RigidBody>(); Bt.BtJoints = new List<TypedConstraint>(); Bt.NoFixAll = false; Bt.NoFixBodyParts = 0x0000000; Bt.ManifoldArray = null; Bt.Shapes = new List<CollisionShape>(); Bt.GhostObjects = new List<PairCachingGhostObject>(); Bt.LastCollisions = new List<EntityCollisionNode>(); Bf = new SSBoneFrame(); Bf.Animations = new SSAnimation(); Bf.Animations.Model = null; Bf.Animations.ClearOnFrame(); Bf.Animations.FrameTime = 0.0f; Bf.Animations.LastState = TR_STATE.LaraWalkForward; Bf.Animations.NextState = TR_STATE.LaraWalkForward; Bf.Animations.Lerp = 0.0f; Bf.Animations.CurrentAnimation = TR_ANIMATION.LaraRun; Bf.Animations.CurrentFrame = 0; Bf.Animations.NextAnimation = TR_ANIMATION.LaraRun; Bf.Animations.NextFrame = 0; Bf.Animations.Next = null; Bf.BoneTags = new List<SSBoneTag>(); Bf.BBMax = Vector3.Zero; Bf.BBMin = Vector3.Zero; Bf.Centre = Vector3.Zero; Bf.Position = Vector3.Zero; Speed = Vector3.Zero; }
public BtEngineClosestConvexResultCallback(EngineContainer cont, bool skipGhost = false) : base(ref Helper.ZeroB, ref Helper.ZeroB) { Container = cont; SkipGhost = skipGhost; }
public static void SecondaryMouseDown() { var from = EngineCamera.Position; var to = from + EngineCamera.ViewDirection * 32768.0f; var camCont = new EngineContainer {Room = EngineCamera.CurrentRoom}; var cbc = new BtEngineClosestRayResultCallback(camCont); //cbc.CollisionFilterMask = CollisionFilterGroups.StaticFilter | CollisionFilterGroups.KinematicFilter; BtEngineDynamicsWorld.RayTest(from.ToBullet(), to.ToBullet(), cbc); if(cbc.HasHit) { var castRay = new float[6]; Vector3 place; Helper.SetInterpolate3(out place, from, to, cbc.ClosestHitFraction); place.CopyToArray(castRay, 0); (place + 100.0f * cbc.HitNormalWorld.ToOpenTK()).CopyToArray(castRay, 3); var c0 = (EngineContainer) cbc.CollisionObject.UserObject; if(c0 != null) { if(c0.ObjectType == OBJECT_TYPE.BulletMisc) { var obj = cbc.CollisionObject; var body = RigidBody.Upcast(obj); body?.MotionState?.Dispose(); body?.CollisionShape?.Dispose(); if(body != null) { body.UserObject = null; } c0.Room = null; c0 = null; BtEngineDynamicsWorld.RemoveCollisionObject(obj); obj.Dispose(); } else { LastContainer = c0; } } } }
public static void PrimaryMouseDown() { var cont = new EngineContainer(); var dbgR = 128.0f; var v = EngineCamera.Position; var dir = EngineCamera.ViewDirection; var localInertia = BulletSharp.Math.Vector3.Zero; var cshape = new SphereShape(dbgR); cshape.Margin = COLLISION_MARGIN_DEFAULT; var startTransform = new Transform(); startTransform.SetIdentity(); var newPos = v; startTransform.Origin = newPos; cshape.CalculateLocalInertia(12.0f, out localInertia); var motionState = new DefaultMotionState(((Matrix4)startTransform).ToBullet()); var body = new RigidBody(new RigidBodyConstructionInfo(12.0f, motionState, cshape, localInertia)); BtEngineDynamicsWorld.AddRigidBody(body); body.LinearVelocity = (dir * 6000).ToBullet(); cont.Room = Room.FindPosCogerrence(newPos, EngineCamera.CurrentRoom); cont.ObjectType = OBJECT_TYPE.BulletMisc; // bullet have to destroy this user pointer body.UserObject = cont; body.CcdMotionThreshold = dbgR; // disable tunneling effect body.CcdSweptSphereRadius = dbgR; }