public override CollisionShape GetCollisionShape() { if (collisionShapePtr == null) { Terrain t = GetComponent<Terrain>(); if (t == null) { Debug.LogError("Needs to be attached to a game object with a Terrain component." + name); return null; } BTerrainCollisionObject tco = GetComponent<BTerrainCollisionObject>(); if (tco == null) { Debug.LogError("Needs to be attached to a game object with a BTerrainCollisionObject." + name); } TerrainData td = t.terrainData; int width = td.heightmapWidth; int length = td.heightmapHeight; float maxHeight = td.size.y; //generate procedural data byte[] terr = new byte[width * length * sizeof(float)]; System.IO.MemoryStream file = new System.IO.MemoryStream(terr); System.IO.BinaryWriter writer = new System.IO.BinaryWriter(file); for (int i = 0; i < length; i++) { float[,] row = td.GetHeights(0, i, width, 1); for (int j = 0; j < width; j++) { writer.Write((float)row[0, j] * maxHeight); } } writer.Flush(); file.Position = 0; pinnedTerrainData = GCHandle.Alloc(terr,GCHandleType.Pinned); collisionShapePtr = new HeightfieldTerrainShape(width, length, pinnedTerrainData.AddrOfPinnedObject(), 1f, 0f, maxHeight, upIndex, scalarType, false); ((HeightfieldTerrainShape)collisionShapePtr).SetUseDiamondSubdivision(true); ((HeightfieldTerrainShape)collisionShapePtr).LocalScaling = new BulletSharp.Math.Vector3(td.heightmapScale.x, 1f, td.heightmapScale.z); //just allocated several hundred float arrays. Garbage collect now since 99% likely we just loaded the scene GC.Collect(); } return collisionShapePtr; }
protected override CollisionShape CreateShape() { if (this.ms == null) { byte[] terr = new byte[this.w * this.l * 4]; ms = new MemoryStream(terr); BinaryWriter writer = new BinaryWriter(ms); for (int i = 0; i < this.w * this.l; i++) { writer.Write(this.h[i]); } writer.Flush(); } ms.Position = 0; HeightfieldTerrainShape hs = new HeightfieldTerrainShape(w, l, ms, 0.0f, minh, maxh, 1, PhyScalarType.PhyFloat, false); hs.SetUseDiamondSubdivision(true); //hs.LocalScaling = new Vector3(this.sx, 1.0f, this.sz); return hs; }
float wheelFriction = 1000; //BT_LARGE_FLOAT; #endregion Fields #region Constructors public Physics(VehicleDemo game) { CollisionShape groundShape = new BoxShape(50, 3, 50); CollisionShapes.Add(groundShape); CollisionConf = new DefaultCollisionConfiguration(); Dispatcher = new CollisionDispatcher(CollisionConf); Solver = new SequentialImpulseConstraintSolver(); Vector3 worldMin = new Vector3(-10000, -10000, -10000); Vector3 worldMax = new Vector3(10000, 10000, 10000); Broadphase = new AxisSweep3(worldMin, worldMax); //Broadphase = new DbvtBroadphase(); World = new DiscreteDynamicsWorld(Dispatcher, Broadphase, Solver, CollisionConf); int i; Matrix tr; Matrix vehicleTr; if (UseTrimeshGround) { const float scale = 20.0f; //create a triangle-mesh ground int vertStride = Vector3.SizeInBytes; int indexStride = 3 * sizeof(int); const int NUM_VERTS_X = 20; const int NUM_VERTS_Y = 20; const int totalVerts = NUM_VERTS_X * NUM_VERTS_Y; const int totalTriangles = 2 * (NUM_VERTS_X - 1) * (NUM_VERTS_Y - 1); TriangleIndexVertexArray vertexArray = new TriangleIndexVertexArray(); IndexedMesh mesh = new IndexedMesh(); mesh.Allocate(totalVerts, vertStride, totalTriangles, indexStride); BulletSharp.DataStream data = mesh.LockVerts(); for (i = 0; i < NUM_VERTS_X; i++) { for (int j = 0; j < NUM_VERTS_Y; j++) { float wl = .2f; float height = 20.0f * (float)(Math.Sin(i * wl) * Math.Cos(j * wl)); data.Write((i - NUM_VERTS_X * 0.5f) * scale); data.Write(height); data.Write((j - NUM_VERTS_Y * 0.5f) * scale); } } int index = 0; IntArray idata = mesh.TriangleIndices; for (i = 0; i < NUM_VERTS_X - 1; i++) { for (int j = 0; j < NUM_VERTS_Y - 1; j++) { idata[index++] = j * NUM_VERTS_X + i; idata[index++] = j * NUM_VERTS_X + i + 1; idata[index++] = (j + 1) * NUM_VERTS_X + i + 1; idata[index++] = j * NUM_VERTS_X + i; idata[index++] = (j + 1) * NUM_VERTS_X + i + 1; idata[index++] = (j + 1) * NUM_VERTS_X + i; } } vertexArray.AddIndexedMesh(mesh); groundShape = new BvhTriangleMeshShape(vertexArray, true); tr = Matrix.Identity; vehicleTr = Matrix.Translation(0, -2, 0); } else { // Use HeightfieldTerrainShape int width = 40, length = 40; //int width = 128, length = 128; // Debugging is too slow for this float maxHeight = 10.0f; float heightScale = maxHeight / 256.0f; Vector3 scale = new Vector3(20.0f, maxHeight, 20.0f); //PhyScalarType scalarType = PhyScalarType.PhyUChar; //FileStream file = new FileStream(heightfieldFile, FileMode.Open, FileAccess.Read); // Use float data PhyScalarType scalarType = PhyScalarType.PhyFloat; byte[] terr = new byte[width * length * 4]; MemoryStream file = new MemoryStream(terr); BinaryWriter writer = new BinaryWriter(file); for (i = 0; i < width; i++) for (int j = 0; j < length; j++) writer.Write((float)((maxHeight / 2) + 4 * Math.Sin(j * 0.5f) * Math.Cos(i))); writer.Flush(); file.Position = 0; HeightfieldTerrainShape heightterrainShape = new HeightfieldTerrainShape(width, length, file, heightScale, 0, maxHeight, upIndex, scalarType, false); heightterrainShape.SetUseDiamondSubdivision(true); groundShape = heightterrainShape; groundShape.LocalScaling = new Vector3(scale.X, 1, scale.Z); tr = Matrix.Translation(new Vector3(-scale.X / 2, scale.Y / 2, -scale.Z / 2)); vehicleTr = Matrix.Translation(new Vector3(20, 3, -3)); // Create graphics object file.Position = 0; BinaryReader reader = new BinaryReader(file); int totalTriangles = (width - 1) * (length - 1) * 2; int totalVerts = width * length; game.groundMesh = new Mesh(game.Device, totalTriangles, totalVerts, MeshFlags.SystemMemory | MeshFlags.Use32Bit, VertexFormat.Position | VertexFormat.Normal); SlimDX.DataStream data = game.groundMesh.LockVertexBuffer(LockFlags.None); for (i = 0; i < width; i++) { for (int j = 0; j < length; j++) { float height; if (scalarType == PhyScalarType.PhyFloat) { // heightScale isn't applied internally for float data height = reader.ReadSingle(); } else if (scalarType == PhyScalarType.PhyUChar) { height = file.ReadByte() * heightScale; } else { height = 0.0f; } data.Write((j - length * 0.5f) * scale.X); data.Write(height); data.Write((i - width * 0.5f) * scale.Z); // Normals will be calculated later data.Position += 12; } } game.groundMesh.UnlockVertexBuffer(); file.Close(); data = game.groundMesh.LockIndexBuffer(LockFlags.None); for (i = 0; i < width - 1; i++) { for (int j = 0; j < length - 1; j++) { // Using diamond subdivision if ((j + i) % 2 == 0) { data.Write(j * width + i); data.Write((j + 1) * width + i + 1); data.Write(j * width + i + 1); data.Write(j * width + i); data.Write((j + 1) * width + i); data.Write((j + 1) * width + i + 1); } else { data.Write(j * width + i); data.Write((j + 1) * width + i); data.Write(j * width + i + 1); data.Write(j * width + i + 1); data.Write((j + 1) * width + i); data.Write((j + 1) * width + i + 1); } /* // Not using diamond subdivision data.Write(j * width + i); data.Write((j + 1) * width + i); data.Write(j * width + i + 1); data.Write(j * width + i + 1); data.Write((j + 1) * width + i); data.Write((j + 1) * width + i + 1); */ } } game.groundMesh.UnlockIndexBuffer(); game.groundMesh.ComputeNormals(); } CollisionShapes.Add(groundShape); //create ground object RigidBody ground = LocalCreateRigidBody(0, tr, groundShape); ground.UserObject = "Ground"; CollisionShape chassisShape = new BoxShape(1.0f, 0.5f, 2.0f); CollisionShapes.Add(chassisShape); CompoundShape compound = new CompoundShape(); CollisionShapes.Add(compound); //localTrans effectively shifts the center of mass with respect to the chassis Matrix localTrans = Matrix.Translation(Vector3.UnitY); compound.AddChildShape(localTrans, chassisShape); RigidBody carChassis = LocalCreateRigidBody(800, Matrix.Identity, compound); carChassis.UserObject = "Chassis"; //carChassis.SetDamping(0.2f, 0.2f); //CylinderShapeX wheelShape = new CylinderShapeX(wheelWidth, wheelRadius, wheelRadius); // clientResetScene(); // create vehicle RaycastVehicle.VehicleTuning tuning = new RaycastVehicle.VehicleTuning(); IVehicleRaycaster vehicleRayCaster = new DefaultVehicleRaycaster(World); vehicle = new RaycastVehicle(tuning, carChassis, vehicleRayCaster); carChassis.ActivationState = ActivationState.DisableDeactivation; World.AddAction(vehicle); float connectionHeight = 1.2f; bool isFrontWheel = true; // choose coordinate system vehicle.SetCoordinateSystem(rightIndex, upIndex, forwardIndex); Vector3 connectionPointCS0 = new Vector3(CUBE_HALF_EXTENTS - (0.3f * wheelWidth), connectionHeight, 2 * CUBE_HALF_EXTENTS - wheelRadius); WheelInfo a = vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel); connectionPointCS0 = new Vector3(-CUBE_HALF_EXTENTS + (0.3f * wheelWidth), connectionHeight, 2 * CUBE_HALF_EXTENTS - wheelRadius); vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel); isFrontWheel = false; connectionPointCS0 = new Vector3(-CUBE_HALF_EXTENTS + (0.3f * wheelWidth), connectionHeight, -2 * CUBE_HALF_EXTENTS + wheelRadius); vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel); connectionPointCS0 = new Vector3(CUBE_HALF_EXTENTS - (0.3f * wheelWidth), connectionHeight, -2 * CUBE_HALF_EXTENTS + wheelRadius); vehicle.AddWheel(connectionPointCS0, wheelDirectionCS0, wheelAxleCS, suspensionRestLength, wheelRadius, tuning, isFrontWheel); for (i = 0; i < vehicle.NumWheels; i++) { WheelInfo wheel = vehicle.GetWheelInfo(i); wheel.SuspensionStiffness = suspensionStiffness; wheel.WheelsDampingRelaxation = suspensionDamping; wheel.WheelsDampingCompression = suspensionCompression; wheel.FrictionSlip = wheelFriction; wheel.RollInfluence = rollInfluence; } vehicle.RigidBody.WorldTransform = vehicleTr; }
/// <summary> /// Creates a collision shape for a shape component /// </summary> private CollisionShape CreateShapeForComponent(ShapeComponent component) { switch (component.Type) { case ThingEnum.Box: return new BoxShape(component.Dimensions); case ThingEnum.Cylinder: return new CylinderShape(component.Dimensions); case ThingEnum.Cone: var cone = new ConeShape(component.Radius, component.Height); cone.ConeUpIndex = 1; return cone; case ThingEnum.Capsule: return new CapsuleShape(component.Radius, component.Height); case ThingEnum.Sphere: return new SphereShape(component.Radius); case ThingEnum.Hull: { CollisionShape shape; string name = Path.GetFileNameWithoutExtension(component.Mesh); string bulletFilePath = GetBulletFile(name); if (bulletFilePath != null) { // so it has a file shape = ImportCollisionShape(name); } else { /*var sceneMgr = LKernel.GetG<SceneManager>(); // get our entity if we have one, create it if we don't Entity ent = sceneMgr.HasEntity(component.Mesh) ? sceneMgr.GetEntity(component.Mesh) : sceneMgr.CreateEntity(component.Mesh, component.Mesh); ConvexHullShape hull = OgreToBulletMesh.ConvertToHull( ent.GetMesh(), component.Transform.GetTrans(), component.Transform.ExtractQuaternion(), Vector3.UNIT_SCALE); shape = hull; // TODO: figure out how to deal with concave triangle mesh shapes since apparently they aren't being exported SerializeShape(shape, name);*/ throw new FileNotFoundException("Your \"Mesh\" property did not point to an existing .bullet file!", component.Mesh); } return shape; } case ThingEnum.Mesh: { CollisionShape shape; // example // physics/example.bullet string name = Path.GetFileNameWithoutExtension(component.Mesh); string bulletFilePath = GetBulletFile(name); // right, so what we do is test to see if this shape has a .bullet file, and if it doesn't, create one if (bulletFilePath != null) { // so it has a file shape = ImportCollisionShape(name); } else { /*Launch.Log("[CollisionShapeManager] " + bulletFilePath + " does not exist, converting Ogre mesh into physics trimesh and exporting new .bullet file..."); // it does not have a file, so we need to convert our ogre mesh var sceneMgr = LKernel.GetG<SceneManager>(); Entity ent = sceneMgr.HasEntity(component.Mesh) ? sceneMgr.GetEntity(component.Mesh) : sceneMgr.CreateEntity(component.Mesh, component.Mesh); shape = new BvhTriangleMeshShape( OgreToBulletMesh.Convert( ent.GetMesh(), component.Transform.GetTrans(), component.Transform.ExtractQuaternion(), Vector3.UNIT_SCALE), true, true); (shape as BvhTriangleMeshShape).BuildOptimizedBvh(); // and then export it as a .bullet file SerializeShape(shape, name);*/ throw new FileNotFoundException("Your \"Mesh\" property did not point to an existing .bullet file!", component.Mesh); } return shape; } case ThingEnum.Heightmap: { string filename = "media/" + component.Mesh; //FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read); Bitmap bitmap = new Bitmap(filename); int width = 256; int length = 256; byte[] terr = new byte[width * length * 4]; MemoryStream file = new MemoryStream(terr); BinaryWriter writer = new BinaryWriter(file); for (int i = 0; i < width; i++) { for (int j = 0; j < length; j++) { writer.Write(bitmap.GetPixel((int) (((float) i / width) * bitmap.Width), (int) (((float) j / length) * bitmap.Height)).R / 255f); //writer.Write(bitmap.GetPixel(i, j).R / 255f); } } writer.Flush(); file.Position = 0; float heightScale = component.MaxHeight - component.MinHeight / 255f; Vector3 scale = component.Dimensions; var heightfield = new HeightfieldTerrainShape(width, length, file, heightScale, component.MinHeight, component.MaxHeight, 1, PhyScalarType.PhyFloat, false); //heightfield.SetUseDiamondSubdivision(true); //heightfield.LocalScaling = new Vector3(scale.x / width, scale.y, scale.z / length); //Matrix4 trans = new Matrix4(); //trans.MakeTransform(new Vector3(-scale.x / 2f, scale.y / 2f, -scale.z / 2f), new Vector3(scale.x, 1, scale.z), Quaternion.IDENTITY); //component.Transform = trans; return heightfield; } default: throw new ApplicationException("ShapeComponent's Type was invalid!"); } }
public void Load() { Stream entryStream = new MemoryStream(Resources.GetFile(HeightfieldName)); if (entryStream != null) { // Generate heightfield mesh Vector3[] points = new Vector3[Size * Size]; entryStream.Position = 0; byte[] b = new byte[4]; Vector2[] texturecoords = new Vector2[Size * Size]; for (long i = 0; i < entryStream.Length; i += 4) { entryStream.Read(b, 0, 4); float h = BitConverter.ToSingle(b, 0); points[i / 4] = new Vector3((i / 4) % Size, h, ((i / 4) / Size)) * Scale; texturecoords[i / 4] = new Vector2((i / 4) % Size, ((i / 4) / Size)) * Scale.Xz; texturecoords[i / 4].X /= TextureScale.X; texturecoords[i / 4].Y /= TextureScale.Y; if (h > Maximum) Maximum = h; if (h < Minimum) Minimum = h; } int[] indicies = new int[(Size - 1) * (Size - 1) * 6]; int c = 0; for (int i = 0; i < points.Length - Size; i++) { if ((i % Size) < Size - 1) { indicies[c++] = i; indicies[c++] = i + 1; indicies[c++] = i + 1 + Size; indicies[c++] = i + 1 + Size; indicies[c++] = i + Size; indicies[c++] = i; } } HeightfieldMesh.Load(points, indicies, TextureName, texturecoords); if (!Graphics.StaticView) { // Generate rigid body entryStream.Position = 0; float Extreme = (Math.Abs(Minimum) > Math.Abs(Maximum)) ? Math.Abs(Minimum) : Maximum; HeightfieldTerrainShape heightfield = new HeightfieldTerrainShape(Size, Size, entryStream, 1, -Extreme, Extreme, 1, PhyScalarType.PhyFloat, true); heightfield.LocalScaling = Scale; GroundBody = World.CreateRigidBody(0, Matrix4.CreateTranslation(new Vector3(Size / 2, 0, Size / 2)), heightfield); GroundBody.CollisionFlags |= CollisionFlags.DisableVisualizeObject; } entryStream.Close(); } }