/// <summary> /// Creates a cloth using physical methods with particles. /// </summary> /// <param name="container"></param> /// <param name="particleTemplate">A template to use for the cloth particles</param> public MataliCloth(object container, string uniqueName, MataliObject particleTemplate) : base(container) { this.uniqueName = uniqueName; if (particleTemplate == null) { this.particleTemplate = new MataliObject(null); this.particleTemplate.Shape = ShapeType.Extra; this.particleTemplate.ExtraShape = ExtraShapeType.Point; this.particleTemplate.ShapeData.Add(0); this.particleTemplate.ShapeData.Add(0); this.particleTemplate.ShapeData.Add(0); this.particleTemplate.RelativeTransform = Matrix.CreateScale(0.1f); this.particleTemplate.ShapeCollisionMargin = 0.1f; this.particleTemplate.Density = 1; } else this.particleTemplate = particleTemplate; constraints = new List<ConstraintPair>(); particles = new List<MataliObject>(); Tearable = false; MinTearVelocity = 20; Streachable = true; Stiffness = 0.1f; }
/// <summary> /// Creates a cloth using physical methods with particles. /// </summary> /// <param name="container"></param> /// <param name="particleTemplate">A template to use for the cloth particles</param> public MataliCloth(object container, string uniqueName, MataliObject particleTemplate) : base(container) { this.uniqueName = uniqueName; if (particleTemplate == null) { this.particleTemplate = new MataliObject(null); this.particleTemplate.Shape = ShapeType.Extra; this.particleTemplate.ExtraShape = ExtraShapeType.Point; this.particleTemplate.ShapeData.Add(0); this.particleTemplate.ShapeData.Add(0); this.particleTemplate.ShapeData.Add(0); this.particleTemplate.RelativeTransform = Matrix.CreateScale(0.1f); this.particleTemplate.ShapeCollisionMargin = 0.1f; this.particleTemplate.Density = 1; } else { this.particleTemplate = particleTemplate; } constraints = new List <ConstraintPair>(); particles = new List <MataliObject>(); Tearable = false; MinTearVelocity = 20; Streachable = true; Stiffness = 0.1f; }
private void CheckCollision(CollisionMethodArgs args) { MataliPhysicsObject mataliPhysicsObj = scene.Factory.PhysicsObjectManager.Get(args.OwnerIndex); MataliObject mataliObj = (MataliObject)mataliPhysicsObj.UserTagObj; Dictionary <MataliPhysicsObject, bool> table = collisionTable[mataliPhysicsObj]; for (int i = 0; i < args.Collisions.Count; i++) { MataliPhysicsObject collidingObject = scene.Factory.PhysicsObjectManager.Get(args.Collisions[i]); if (table.ContainsKey(collidingObject)) { if (mataliObj.CollisionContinueCallback != null) { mataliObj.CollisionContinueCallback(mataliPhysicsObj, collidingObject); } } else { table.Add(collidingObject, true); if (mataliObj.CollisionStartCallback != null) { mataliObj.CollisionStartCallback(mataliPhysicsObj, collidingObject); } } } }
/// <summary> /// /// </summary> /// <remarks> /// Steering wheel is optional. You can still steer the car without this object. /// </remarks> /// <param name="steeringObject"></param> public void AddSteering(MataliObject steeringObject) { if (hasSteering) { throw new GoblinException("You already have steering for this vehicle"); } steeringObjects.Add(steeringObject); hasSteering = true; }
/// <summary> /// Add a body as a single object. /// </summary> /// <param name="bodyObject"></param> public void AddBody(MataliObject bodyObject) { if (hasBody) { throw new GoblinException("You already have a body for this vehicle"); } bodyObjects.Add(bodyObject); hasBody = true; }
/// <summary> /// Add doors that can fling. If you don't need certain doors, just pass null (e.g., if your car /// only has front doors, then pass rearLeftDoor and rearRightDoor as null). /// </summary> /// <remarks> /// Doors are optional. /// </remarks> /// <param name="frontLeftDoor"></param> /// <param name="frontRightDoor"></param> /// <param name="rearLeftDoor"></param> /// <param name="rearRightDoor"></param> public void AddDoors(MataliObject frontLeftDoor, MataliObject frontRightDoor, MataliObject rearLeftDoor, MataliObject rearRightDoor) { if (hasDoors) { throw new GoblinException("You already have doors for this vehicle"); } doorObjects[0].Add(frontLeftDoor); doorObjects[1].Add(frontRightDoor); doorObjects[2].Add(rearLeftDoor); doorObjects[3].Add(rearRightDoor); hasDoors = true; }
/// <summary> /// Add four wheels each with different graphical geometry and physical properties. /// </summary> /// <param name="frontLeftWheel"></param> /// <param name="frontRightWheel"></param> /// <param name="rearLeftWheel"></param> /// <param name="rearRightWheel"></param> public void AddWheels(MataliObject frontLeftWheel, MataliObject frontRightWheel, MataliObject rearLeftWheel, MataliObject rearRightWheel) { if (hasWheels) { throw new GoblinException("You already have wheels for this vehicle"); } wheelObjects[0].Add(frontLeftWheel); wheelObjects[1].Add(frontRightWheel); wheelObjects[2].Add(rearLeftWheel); wheelObjects[3].Add(rearRightWheel); hasWheels = true; }
public void Copy(MataliObject mataliObj) { // needs more to copy, but will implement later Model = mataliObj.Model; Shape = mataliObj.Shape; if (mataliObj.ShapeData.Count > 0) { ShapeData.AddRange(mataliObj.ShapeData); } ExtraShape = mataliObj.ExtraShape; Mass = mataliObj.Mass; Density = mataliObj.Density; RelativeTransform = mataliObj.RelativeTransform; ShapeCollisionMargin = mataliObj.ShapeCollisionMargin; ShapeOriginalMatrix = mataliObj.ShapeOriginalMatrix; StaticFriction = mataliObj.StaticFriction; DynamicFriction = mataliObj.DynamicFriction; Restitution = mataliObj.Restitution; }
public void Update(float elapsedTime) { if (pauseSimulation) { return; } AddConstraint(); scene.Simulate(elapsedTime); Dictionary <MataliPhysicsObject, bool> table = null; List <MataliPhysicsObject> removeList = new List <MataliPhysicsObject>(); foreach (MataliPhysicsObject objBase in collisionTable.Keys) { table = collisionTable[objBase]; MataliObject mataliObj = (MataliObject)objBase.UserTagObj; foreach (MataliPhysicsObject objCol in table.Keys) { if (!objBase.IsColliding(objCol)) { removeList.Add(objCol); if (mataliObj.CollisionEndCallback != null) { mataliObj.CollisionEndCallback(objBase, objCol); } } } foreach (MataliPhysicsObject obj in removeList) { table.Remove(obj); } removeList.Clear(); } }
private void ConstructClothParticles(List<Vector3> vertices, List<int> indices) { foreach (Vector3 vertex in vertices) { MataliObject particle = new MataliObject(null); particle.Copy(particleTemplate); particle.Collidable = true; particle.Interactable = true; particle.CompoundInitialWorldTransform = particle.RelativeTransform * RelativeTransform * Matrix.CreateTranslation(vertex); particles.Add(particle); } // use a hashmap to make sure that we don't create duplicate constraints between the same // two vertices Dictionary<string, bool> constraintTable = new Dictionary<string, bool>(); int id0, id1, s, l; string key; float distance; Vector3 position1 = Vector3.Zero, position2 = Vector3.Zero; for (int i = 0; i < indices.Count; i += 3) { for (int j = 0; j < 3; ++j) { id0 = indices[i + j]; id1 = indices[i + (j + 1) % 3]; s = Math.Min(id0, id1); l = Math.Max(id0, id1); key = s + "_" + l; if (!constraintTable.ContainsKey(key)) { constraintTable.Add(key, true); ConstraintPair pair = new ConstraintPair(); pair.Name = uniqueName + " Point Cloth Constraint " + key; pair.PhysicsObject1 = particles[s]; pair.PhysicsObject2 = particles[l]; pair.Callback = delegate(Constraint constraint) { constraint.PhysicsObject1.MainWorldTransform.GetPosition(ref position1); constraint.PhysicsObject2.MainWorldTransform.GetPosition(ref position2); constraint.SetAnchor1(position1); constraint.SetAnchor2(position2); distance = Vector3.Distance(position1, position2); constraint.Distance = Streachable ? distance : -distance; constraint.Force = Stiffness; constraint.EnableBreak = Tearable; constraint.MinBreakVelocity = MinTearVelocity; }; constraints.Add(pair); } } } constraintTable.Clear(); }
private void ConstructClothParticles(List <Vector3> vertices, List <int> indices) { foreach (Vector3 vertex in vertices) { MataliObject particle = new MataliObject(null); particle.Copy(particleTemplate); particle.Collidable = true; particle.Interactable = true; particle.CompoundInitialWorldTransform = particle.RelativeTransform * RelativeTransform * Matrix.CreateTranslation(vertex); particles.Add(particle); } // use a hashmap to make sure that we don't create duplicate constraints between the same // two vertices Dictionary <string, bool> constraintTable = new Dictionary <string, bool>(); int id0, id1, s, l; string key; float distance; Vector3 position1 = Vector3.Zero, position2 = Vector3.Zero; for (int i = 0; i < indices.Count; i += 3) { for (int j = 0; j < 3; ++j) { id0 = indices[i + j]; id1 = indices[i + (j + 1) % 3]; s = Math.Min(id0, id1); l = Math.Max(id0, id1); key = s + "_" + l; if (!constraintTable.ContainsKey(key)) { constraintTable.Add(key, true); ConstraintPair pair = new ConstraintPair(); pair.Name = uniqueName + " Point Cloth Constraint " + key; pair.PhysicsObject1 = particles[s]; pair.PhysicsObject2 = particles[l]; pair.Callback = delegate(Constraint constraint) { constraint.PhysicsObject1.MainWorldTransform.GetPosition(ref position1); constraint.PhysicsObject2.MainWorldTransform.GetPosition(ref position2); constraint.SetAnchor1(position1); constraint.SetAnchor2(position2); distance = Vector3.Distance(position1, position2); constraint.Distance = Streachable ? distance : -distance; constraint.Force = Stiffness; constraint.EnableBreak = Tearable; constraint.MinBreakVelocity = MinTearVelocity; }; constraints.Add(pair); } } } constraintTable.Clear(); }
/// <summary> /// Add four wheels that have the same graphical geometry and physical properties. /// </summary> /// <param name="wheelObject"></param> public void AddWheels(MataliObject wheelObject) { AddWheels(wheelObject, wheelObject, wheelObject, wheelObject); }
/// <summary> /// /// </summary> /// <remarks> /// For all of the shapes that has directions (e.g., Cylinder, Hemisphere), Y direction is /// used, so if you would like it to face other directions, use MataliObject.ShapeOriginalMatrix /// to orient them. /// /// For a cylinder with different bottom and top radius, IPhysicsObject.ShapeData are used. /// ShapeData[0] - bottom radius, ShapeData[1] - height, ShapeData[2] = top radius /// /// For Compound shape, an additional information can be set by using /// MataliObject.CompoundShape. /// /// For additional shape types such as Heightmap, Point, and so on, set Shape to ShapeType.Extra /// and define MataliObject.ExtraShape. /// </remarks> /// <param name="mataliPhysicsObj"></param> /// <param name="physObj"></param> private void SetShape(MataliPhysicsObject mataliPhysicsObj, IPhysicsObject physObj) { Vector3 boundingBox = Vector3.Zero; if (physObj.Model != null) { boundingBox = Vector3Helper.GetDimensions(physObj.Model.MinimumBoundingBox); } Vector3 size = Vector3.Zero; MataliObject mataliObj = null; if (physObj is MataliObject) { mataliObj = (MataliObject)physObj; } switch (physObj.Shape) { case ShapeType.Box: if (physObj.ShapeData.Count == 3) { size = new Vector3(physObj.ShapeData[0], physObj.ShapeData[1], physObj.ShapeData[2]); } else { size = boundingBox; } size /= 2; break; case ShapeType.Sphere: if (physObj.ShapeData.Count == 1) { size = new Vector3(physObj.ShapeData[0], 0, 0); } else { size = boundingBox / 2; } break; case ShapeType.Cone: case ShapeType.Cylinder: case ShapeType.Capsule: if (physObj.ShapeData.Count == 2) { size = new Vector3(physObj.ShapeData[0], physObj.ShapeData[1], physObj.ShapeData[0]); } else if (physObj.ShapeData.Count == 3) { size = new Vector3(physObj.ShapeData[0], physObj.ShapeData[1], physObj.ShapeData[2]); } else { size = new Vector3(boundingBox.X / 2, boundingBox.Y, boundingBox.X / 2); } break; case ShapeType.Compound: // size is used solely for naming, not used for the collision shape size = new Vector3(physObj.ShapeData.Count, physObj.ShapeData[0], physObj.ShapeData[physObj.ShapeData.Count - 1]); break; case ShapeType.ConvexHull: // size is used solely for naming, not used for the collision shape size = new Vector3(physObj.MeshProvider.Vertices.Count, physObj.MeshProvider.Indices.Count, physObj.MeshProvider.Vertices[0].X); break; case ShapeType.Extra: if (mataliObj == null) { throw new GoblinException("For extra shape type, you need to define the 'physObj' " + "as MataliObject instance"); } if (mataliObj.ExtraShape == ExtraShapeType.Undefined) { throw new GoblinException("Undefined type is not allowed if Extra shape type is specified"); } // size is used solely for naming, not used for the collision shape switch (mataliObj.ExtraShape) { case ExtraShapeType.Point: if (physObj.ShapeData.Count != 3) { throw new GoblinException("For Point shape type, you need to specify the position (x,y,z) in ShapeData"); } size = new Vector3(physObj.ShapeData[0], physObj.ShapeData[1], physObj.ShapeData[2]); break; case ExtraShapeType.Heightmap: if (physObj.ShapeData.Count < 2) { throw new GoblinException("There needs to be at least two floats specifying the " + "width and height"); } size = new Vector3(physObj.ShapeData[0], physObj.ShapeData[1], physObj.ShapeData.Count); break; case ExtraShapeType.Edge: if (physObj.ShapeData.Count != 6) { throw new GoblinException("For Edge shape type, you need to specify the start and " + "end positions (x,y,z) in ShapeData"); } size = new Vector3(physObj.ShapeData[0] + physObj.ShapeData[3], physObj.ShapeData[1] + physObj.ShapeData[4], physObj.ShapeData[2] + physObj.ShapeData[5]); break; default: throw new GoblinException(mataliObj.ExtraShape.ToString() + " not implemented yet"); } break; } String shapeName = physObj.Shape.ToString() + size.ToString(); String primitiveName = physObj.Shape.ToString() + size.ToString(); if (mataliObj != null) { String suffix = ""; if (physObj.Shape == ShapeType.Extra) { suffix += mataliObj.ExtraShape.ToString(); } else if (physObj.Shape == ShapeType.Compound) { suffix += mataliObj.CompoundShape.ToString(); } suffix += mataliObj.ShapeOriginalMatrix.ToString(); suffix += mataliObj.ShapeCollisionMargin; shapeName += suffix; primitiveName += suffix; } Shape shape = null; ShapePrimitive primitive = null; if (scene.Factory.ShapeManager.Contains(shapeName)) { shape = scene.Factory.ShapeManager.Find(shapeName); } else { if (physObj.Shape != ShapeType.Compound) { primitive = scene.Factory.ShapePrimitiveManager.Create(primitiveName); } shape = scene.Factory.ShapeManager.Create(shapeName); bool shapeSet = false; switch (physObj.Shape) { case ShapeType.Box: primitive.CreateBox(size.X, size.Y, size.Z); break; case ShapeType.Sphere: primitive.CreateSphere(size.X); break; case ShapeType.Cone: primitive.CreateConeY(size.Y, size.X); break; case ShapeType.Cylinder: if (size.X != size.Z) { primitive.CreateCylinder2RY(size.Y, size.X, size.Z); } else { primitive.CreateCylinderY(size.Y, size.X); } break; case ShapeType.Capsule: primitive.CreateCapsuleY(size.Y - size.X * 2, size.X); break; case ShapeType.Compound: ShapeCompoundType type = ShapeCompoundType.ConvexHull; if (mataliObj != null) { if (mataliObj.CompoundShape == CompoundShapeType.MinkowskiSum) { type = ShapeCompoundType.MinkowskiSum; } } int dataIndex = 0; Shape compoundShapePart = null; ShapePrimitive compoundPrimitive = null; float[] matrixVals = new float[16]; while (dataIndex < physObj.ShapeData.Count) { ShapeType shapeType = (ShapeType)Enum.ToObject(typeof(ShapeType), (int)physObj.ShapeData[dataIndex++]); switch (shapeType) { case ShapeType.Cylinder: size = new Vector3(physObj.ShapeData[dataIndex], physObj.ShapeData[dataIndex + 1], physObj.ShapeData[dataIndex]); dataIndex += 2; break; case ShapeType.Sphere: size = new Vector3(physObj.ShapeData[dataIndex], 0, 0); dataIndex++; break; } shapeName = shapeType.ToString() + size.ToString(); primitiveName = shapeType.ToString() + size.ToString(); if (scene.Factory.ShapeManager.Contains(shapeName)) { compoundShapePart = scene.Factory.ShapeManager.Find(shapeName); } else { compoundPrimitive = scene.Factory.ShapePrimitiveManager.Create(primitiveName); compoundShapePart = scene.Factory.ShapeManager.Create(shapeName); switch (shapeType) { case ShapeType.Cylinder: compoundPrimitive.CreateCylinderY(size.Y, size.X); break; case ShapeType.Sphere: compoundPrimitive.CreateSphere(size.X); break; default: throw new GoblinException(shape.ToString() + " is not supported yet as a compound part"); } compoundShapePart.Set(compoundPrimitive, Matrix.Identity, 0.0f); } for (int i = 0; i < 16; ++i) { matrixVals[i] = physObj.ShapeData[dataIndex + i]; } dataIndex += 16; shape.Add(compoundShapePart, MatrixHelper.FloatsToMatrix(matrixVals), 0.0f, type); } float margin = 0.0f; if (mataliObj != null) { margin = mataliObj.ShapeCollisionMargin; } shape.CreateMesh(margin); shapeSet = true; break; case ShapeType.ConvexHull: case ShapeType.TriangleMesh: float[] frictions = null; float[] restitutions = null; if (physObj.Shape == ShapeType.ConvexHull) { primitive.CreateConvex(physObj.MeshProvider.Vertices); } else { int triangleCount = physObj.MeshProvider.Indices.Count / 3; frictions = new float[triangleCount]; restitutions = new float[triangleCount]; for (int i = 0; i < frictions.Length; i++) { frictions[i] = 1.0f; restitutions[i] = 0.0f; } Vector3[] triVerts = new Vector3[physObj.MeshProvider.Indices.Count]; for (int i = 0; i < triVerts.Length; ++i) { triVerts[i] = physObj.MeshProvider.Vertices[physObj.MeshProvider.Indices[i]]; } bool flipTriangle = (physObj is MataliObject) ? ((MataliObject)physObj).FlipTriangleOrder : true; primitive.CreateTriangleMesh(triVerts, flipTriangle, 2, frictions, restitutions, 1.0f, 0.0f); } break; case ShapeType.Extra: switch (mataliObj.ExtraShape) { case ExtraShapeType.Heightmap: int width = (int)physObj.ShapeData[0]; int height = (int)physObj.ShapeData[1]; float[] heightData = new float[height * width]; float[] heightFrictions = new float[height * width]; float[] heightRestituions = new float[height * width]; if (physObj.ShapeData.Count < (2 + heightData.Length)) { throw new GoblinException("You also need to specify the hegith map data"); } Buffer.BlockCopy(physObj.ShapeData.ToArray(), 2 * sizeof(float), heightData, 0, heightData.Length * sizeof(float)); if (physObj.ShapeData.Count > (2 + heightData.Length * 2)) { Buffer.BlockCopy(physObj.ShapeData.ToArray(), (2 + heightData.Length) * sizeof(float), heightFrictions, 0, heightFrictions.Length * sizeof(float)); } else { for (int i = 0; i < heightFrictions.Length; ++i) { heightFrictions[i] = mataliObj.TriangleMeshFriction; } } if (physObj.ShapeData.Count > (2 + heightData.Length * 3)) { Buffer.BlockCopy(physObj.ShapeData.ToArray(), (2 + heightData.Length * 2) * sizeof(float), heightRestituions, 0, heightRestituions.Length * sizeof(float)); } else { for (int i = 0; i < heightRestituions.Length; ++i) { heightRestituions[i] = mataliObj.TriangleMeshRestitution; } } primitive.CreateHeightmap(0, 0, width, height, width, height, heightData, heightFrictions, heightRestituions, mataliObj.TriangleMeshFriction, mataliObj.TriangleMeshRestitution, mataliObj.IsDynamic); shape.Set(primitive, mataliObj.ShapeOriginalMatrix, mataliObj.ShapeCollisionMargin); shape.CreateMesh(0.0f); shapeSet = true; mataliPhysicsObj.InternalControllers.CreateHeightmapController(true); break; case ExtraShapeType.Point: primitive.CreatePoint(physObj.ShapeData[0], physObj.ShapeData[1], physObj.ShapeData[2]); break; case ExtraShapeType.Edge: primitive.CreateEdge(new Vector3(physObj.ShapeData[0], physObj.ShapeData[1], physObj.ShapeData[2]), new Vector3(physObj.ShapeData[3], physObj.ShapeData[4], physObj.ShapeData[5])); break; case ExtraShapeType.Plane: primitive.CreatePlaneY(physObj.ShapeData[0], (physObj.ShapeData[1] > 0)); break; case ExtraShapeType.Triangle: primitive.CreateTriangle( new Vector3(physObj.ShapeData[0], physObj.ShapeData[1], physObj.ShapeData[2]), new Vector3(physObj.ShapeData[3], physObj.ShapeData[4], physObj.ShapeData[5]), new Vector3(physObj.ShapeData[6], physObj.ShapeData[7], physObj.ShapeData[8])); break; case ExtraShapeType.Tetrahedron: primitive.CreateTetrahedron( new Vector3(physObj.ShapeData[0], physObj.ShapeData[1], physObj.ShapeData[2]), new Vector3(physObj.ShapeData[3], physObj.ShapeData[4], physObj.ShapeData[5]), new Vector3(physObj.ShapeData[6], physObj.ShapeData[7], physObj.ShapeData[8]), new Vector3(physObj.ShapeData[9], physObj.ShapeData[10], physObj.ShapeData[11])); break; case ExtraShapeType.Fluid: break; case ExtraShapeType.Hemisphere: primitive.CreateHemisphereY(physObj.ShapeData[0]); break; } break; } if (!shapeSet) { if (mataliObj != null) { shape.Set(primitive, mataliObj.ShapeOriginalMatrix, mataliObj.ShapeCollisionMargin); } else { shape.Set(primitive, Matrix.Identity, 0.0f); } if (buildCollisionMesh) { float margin = 0.0f; if (mataliObj != null) { margin = mataliObj.ShapeCollisionMargin; } shape.CreateMesh(margin); } } } mataliPhysicsObj.Shape = shape; }
public void Copy(MataliObject mataliObj) { // needs more to copy, but will implement later Model = mataliObj.Model; Shape = mataliObj.Shape; if (mataliObj.ShapeData.Count > 0) ShapeData.AddRange(mataliObj.ShapeData); ExtraShape = mataliObj.ExtraShape; Mass = mataliObj.Mass; Density = mataliObj.Density; RelativeTransform = mataliObj.RelativeTransform; ShapeCollisionMargin = mataliObj.ShapeCollisionMargin; ShapeOriginalMatrix = mataliObj.ShapeOriginalMatrix; StaticFriction = mataliObj.StaticFriction; DynamicFriction = mataliObj.DynamicFriction; Restitution = mataliObj.Restitution; }