新增verlet物理引擎(实验性)
This commit is contained in:
47
source/src/Physics/Verlet/Constraints/AngleConstraint.ts
Normal file
47
source/src/Physics/Verlet/Constraints/AngleConstraint.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
///<reference path="./Constraint.ts" />
|
||||
module es {
|
||||
export class AngleConstraint extends Constraint {
|
||||
public stiffness: number;
|
||||
public angleInRadius: number;
|
||||
|
||||
_particleA: Particle;
|
||||
_centerParticle: Particle;
|
||||
_particleC: Particle;
|
||||
|
||||
constructor(a: Particle, center: Particle, c: Particle, stiffness: number) {
|
||||
super();
|
||||
this._particleA = a;
|
||||
this._centerParticle = center;
|
||||
this._particleC = c;
|
||||
this.stiffness = stiffness;
|
||||
|
||||
this.collidesWithColliders = false;
|
||||
|
||||
this.angleInRadius = this.angleBetweenParticles();
|
||||
}
|
||||
|
||||
angleBetweenParticles(): number {
|
||||
const first = this._particleA.position.sub(this._centerParticle.position);
|
||||
const second = this._particleC.position.sub(this._centerParticle.position);
|
||||
|
||||
return Math.atan2(first.x * second.y - first.y * second.x, first.x * second.x + first.y * second.y);
|
||||
}
|
||||
|
||||
public solve() {
|
||||
const angleBetween = this.angleBetweenParticles();
|
||||
let diff = angleBetween - this.angleInRadius;
|
||||
|
||||
if (diff <= -Math.PI)
|
||||
diff += 2 * Math.PI;
|
||||
else if(diff >= Math.PI)
|
||||
diff -= 2 * Math.PI;
|
||||
|
||||
diff *= this.stiffness;
|
||||
|
||||
this._particleA.position = MathHelper.rotateAround2(this._particleA.position, this._centerParticle.position, diff);
|
||||
this._particleC.position = MathHelper.rotateAround2(this._particleC.position, this._centerParticle.position, -diff);
|
||||
this._centerParticle.position = MathHelper.rotateAround2(this._centerParticle.position, this._particleA.position, diff);
|
||||
this._centerParticle.position = MathHelper.rotateAround2(this._centerParticle.position, this._particleC.position, -diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
source/src/Physics/Verlet/Constraints/Constraint.ts
Normal file
16
source/src/Physics/Verlet/Constraints/Constraint.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
module es {
|
||||
export abstract class Constraint {
|
||||
public composite: Composite;
|
||||
public collidesWithColliders: boolean = true;
|
||||
|
||||
public abstract solve(): void;
|
||||
|
||||
public handleCollisions(collidesWithLayers: number) {
|
||||
|
||||
}
|
||||
|
||||
public debugRender(batcher: IBatcher) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
123
source/src/Physics/Verlet/Constraints/DistanceConstraint.ts
Normal file
123
source/src/Physics/Verlet/Constraints/DistanceConstraint.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
module es {
|
||||
export class DistanceConstraint extends Constraint {
|
||||
public stiffness: number;
|
||||
public restingDistance: number;
|
||||
public tearSensitivity = Number.POSITIVE_INFINITY;
|
||||
public shouldApproximateCollisionsWithPoints: boolean;
|
||||
public totalPointsToApproximateCollisionsWith = 5;
|
||||
_particleOne: Particle;
|
||||
_particleTwo: Particle;
|
||||
static _polygon: Polygon = new Polygon([]);
|
||||
|
||||
constructor(first: Particle, second: Particle, stiffness: number, distance: number = -1) {
|
||||
super();
|
||||
|
||||
DistanceConstraint._polygon.create(2, 1);
|
||||
this._particleOne = first;
|
||||
this._particleTwo = second;
|
||||
this.stiffness = stiffness;
|
||||
|
||||
if (distance > -1)
|
||||
this.restingDistance = distance;
|
||||
else
|
||||
this.restingDistance = Vector2.distance(first.position, second.position);
|
||||
}
|
||||
|
||||
public static create(a: Particle, center: Particle, c: Particle, stiffness: number, angleInDegrees: number) {
|
||||
const aToCenter = Vector2.distance(a.position, center.position);
|
||||
const cToCenter = Vector2.distance(c.position, center.position);
|
||||
const distance = Math.sqrt(aToCenter * aToCenter + cToCenter * cToCenter - (2 * aToCenter * cToCenter * Math.cos(angleInDegrees * MathHelper.Deg2Rad)));
|
||||
|
||||
return new DistanceConstraint(a, c, stiffness, distance);
|
||||
}
|
||||
|
||||
public setTearSensitivity(tearSensitivity: number) {
|
||||
this.tearSensitivity = tearSensitivity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public setCollidesWithColliders(collidesWithColliders: boolean) {
|
||||
this.collidesWithColliders = collidesWithColliders;
|
||||
return this;
|
||||
}
|
||||
|
||||
public setShouldApproximateCollisionsWithPoints(shouldApproximateCollisionsWithPoints: boolean) {
|
||||
this.shouldApproximateCollisionsWithPoints = shouldApproximateCollisionsWithPoints;
|
||||
return this;
|
||||
}
|
||||
|
||||
public solve(): void {
|
||||
const diff = this._particleOne.position.sub(this._particleTwo.position);
|
||||
const d = diff.magnitude();
|
||||
|
||||
const difference = (this.restingDistance - d) / d;
|
||||
if (d / this.restingDistance > this.tearSensitivity) {
|
||||
this.composite.removeConstraint(this);
|
||||
return;
|
||||
}
|
||||
|
||||
const im1 = 1 / this._particleOne.mass;
|
||||
const im2 = 1 / this._particleTwo.mass;
|
||||
const scalarP1 = (im1 / (im1 + im2)) * this.stiffness;
|
||||
const scalarP2 = this.stiffness - scalarP1;
|
||||
|
||||
this._particleOne.position = this._particleOne.position.add(diff.scale(scalarP1 * difference));
|
||||
this._particleTwo.position = this._particleTwo.position.sub(diff.scale(scalarP2 * difference));
|
||||
}
|
||||
|
||||
public handleCollisions(collidesWithLayers: number) {
|
||||
if (this.shouldApproximateCollisionsWithPoints) {
|
||||
this.approximateCollisionsWithPoints(collidesWithLayers);
|
||||
return;
|
||||
}
|
||||
|
||||
const minX = Math.min(this._particleOne.position.x, this._particleTwo.position.x);
|
||||
const maxX = Math.max(this._particleOne.position.x, this._particleTwo.position.x);
|
||||
const minY = Math.min(this._particleOne.position.y, this._particleTwo.position.y);
|
||||
const maxY = Math.max(this._particleOne.position.y, this._particleTwo.position.y);
|
||||
DistanceConstraint._polygon.bounds = Rectangle.fromMinMax(minX, minY, maxX, maxY);
|
||||
|
||||
let midPoint: Vector2;
|
||||
this.preparePolygonForCollisionChecks(midPoint);
|
||||
|
||||
const colliders = Physics.boxcastBroadphase(DistanceConstraint._polygon.bounds, collidesWithLayers);
|
||||
for (let i = 0; i < colliders.length; i ++) {
|
||||
const collider = colliders[i];
|
||||
const result = new CollisionResult();
|
||||
if (DistanceConstraint._polygon.collidesWithShape(collider.shape, result)) {
|
||||
this._particleOne.position = this._particleOne.position.sub(result.minimumTranslationVector);
|
||||
this._particleTwo.position = this._particleTwo.position.sub(result.minimumTranslationVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
approximateCollisionsWithPoints(collidesWithLayers: number) {
|
||||
let pt: Vector2;
|
||||
for (let j = 0; j < this.totalPointsToApproximateCollisionsWith - 1; j ++) {
|
||||
pt = Vector2.lerp(this._particleOne.position, this._particleTwo.position, (j + 1) / this.totalPointsToApproximateCollisionsWith);
|
||||
const collidedCount = Physics.overlapCircleAll(pt, 3, VerletWorld._colliders, collidesWithLayers);
|
||||
for (let i = 0; i < collidedCount; i ++) {
|
||||
const collider = VerletWorld._colliders[i];
|
||||
const collisionResult = new CollisionResult();
|
||||
if (collider.shape.pointCollidesWithShape(pt, collisionResult)) {
|
||||
this._particleOne.position = this._particleOne.position.sub(collisionResult.minimumTranslationVector);
|
||||
this._particleTwo.position = this._particleTwo.position.sub(collisionResult.minimumTranslationVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preparePolygonForCollisionChecks(midPoint: Vector2) {
|
||||
const tempMidPoint = Vector2.lerp(this._particleOne.position, this._particleTwo.position, 0.5);
|
||||
midPoint.setTo(tempMidPoint.x, tempMidPoint.y);
|
||||
DistanceConstraint._polygon.position = midPoint;
|
||||
DistanceConstraint._polygon.points[0] = this._particleOne.position.sub(DistanceConstraint._polygon.position);
|
||||
DistanceConstraint._polygon.points[1] = this._particleTwo.position.sub(DistanceConstraint._polygon.position);
|
||||
DistanceConstraint._polygon.recalculateCenterAndEdgeNormals();
|
||||
}
|
||||
|
||||
public debugRender(batcher: IBatcher) {
|
||||
batcher.drawLine(this._particleOne.position, this._particleTwo.position, new Color(67, 62, 54), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user