diff --git a/source/src/Math/MathHelper.ts b/source/src/Math/MathHelper.ts index ecbaeee7..0b774ef4 100644 --- a/source/src/Math/MathHelper.ts +++ b/source/src/Math/MathHelper.ts @@ -27,6 +27,10 @@ class MathHelper { return rightMin + (value - leftMin) * (rightMax - rightMin) / (leftMax - leftMin); } + public static lerp(value1: number, value2: number, amount: number){ + return value1 + (value2 - value1) * amount; + } + public static clamp(value: number, min: number, max: number){ if (value < min) return min; diff --git a/source/src/Math/Vector2.ts b/source/src/Math/Vector2.ts index 87549ca3..304be0bd 100644 --- a/source/src/Math/Vector2.ts +++ b/source/src/Math/Vector2.ts @@ -8,9 +8,9 @@ class Vector2 { * @param x 二维空间中的x坐标 * @param y 二维空间的y坐标 */ - constructor(x: number, y: number){ + constructor(x: number, y?: number){ this.x = x; - this.y = y; + this.y = y ? y : x; } public static add(value1: Vector2, value2: Vector2){ @@ -78,6 +78,10 @@ class Vector2 { return (v1 * v1) + (v2 * v2); } + public static lerp(value1: Vector2, value2: Vector2, amount: number){ + return new Vector2(MathHelper.lerp(value1.x, value2.x, amount), MathHelper.lerp(value1.y, value2.y, amount)); + } + public static transform(position: Vector2, matrix: Matrix2D){ return new Vector2((position.x * matrix.m11) + (position.y * matrix.m21), (position.x * matrix.m12) + (position.y * matrix.m22)); } diff --git a/source/src/Physics/Physics.ts b/source/src/Physics/Physics.ts index 021132bb..237332f8 100644 --- a/source/src/Physics/Physics.ts +++ b/source/src/Physics/Physics.ts @@ -6,4 +6,8 @@ class Physics { public static overlapCircleAll(center: Vector2, randius: number, results: any[], layerMask = -1){ return this._spatialHash.overlapCircle(center, randius, results, layerMask); } + + public static boxcastBroadphase(rect: Rectangle, layerMask: number = this.allLayers){ + return this._spatialHash.aabbBroadphase(rect, null, layerMask); + } } \ No newline at end of file diff --git a/source/src/Physics/Shapes/Polygon.ts b/source/src/Physics/Shapes/Polygon.ts index b85f2114..b164105c 100644 --- a/source/src/Physics/Shapes/Polygon.ts +++ b/source/src/Physics/Shapes/Polygon.ts @@ -4,13 +4,40 @@ class Polygon extends Shape { public isUnrotated: boolean = true; private _polygonCenter: Vector2; private _areEdgeNormalsDirty = true; - private _originalPoint: Vector2[] + private _originalPoint: Vector2[]; + + public _edgeNormals: Vector2[]; + public get edgeNormals(){ + if (this._areEdgeNormalsDirty) + this.buildEdgeNormals(); + return this._edgeNormals; + } + public isBox: boolean; constructor(vertCount: number, radius: number) { super(); this.setPoints(Polygon.buildSymmertricalPolygon(vertCount, radius)); } + private buildEdgeNormals(){ + let totalEdges = this.isBox ? 2 : this.points.length; + if (this._edgeNormals == null || this._edgeNormals.length != totalEdges) + this._edgeNormals = new Vector2[totalEdges]; + + let p2: Vector2; + for (let i = 0; i < totalEdges; i ++){ + let p1 = this.points[i]; + if (i + 1 >= this.points.length) + p2 = this.points[0]; + else + p2 = this.points[i + 1]; + + let perp = Vector2Ext.perpendicular(p1, p2); + perp = Vector2.normalize(perp); + this._edgeNormals[i] = perp; + } + } + public setPoints(points: Vector2[]) { this.points = points; this.recalculateCenterAndEdgeNormals(); @@ -19,6 +46,11 @@ class Polygon extends Shape { this._originalPoint = points; } + public collidesWithShape(other: Shape){ + if (other instanceof Polygon) + return ShapeCollisions.polygonToPolygon(this, other); + } + public recalculateCenterAndEdgeNormals() { this._polygonCenter = Polygon.findPolygonCenter(this.points); this._areEdgeNormalsDirty = true; diff --git a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisions.ts b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisions.ts index 1e1317ca..02918df3 100644 --- a/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisions.ts +++ b/source/src/Physics/Shapes/ShapeCollisions/ShapeCollisions.ts @@ -1,4 +1,37 @@ class ShapeCollisions { + public static polygonToPolygon(first: Polygon, second: Polygon){ + let result = new CollisionResult(); + let isIntersecting = true; + // let firstEdges = first.ed + } + + public static circleToPolygon(circle: Circle, polygon: Polygon){ + let result = new CollisionResult(); + + let poly2Circle = Vector2.subtract(circle.position, polygon.position); + + let gpp = Polygon.getClosestPointOnPolygonToPoint(polygon.points, poly2Circle); + let closestPoint = gpp.closestPoint; + let distanceSquared: number = gpp.distanceSquared; + result.normal = gpp.edgeNormal; + + let circleCenterInsidePoly = polygon.containsPoint(circle.position); + if (distanceSquared > circle.radius * circle.radius && !circleCenterInsidePoly) + return result; + + let mtv: Vector2; + if (circleCenterInsidePoly){ + mtv = Vector2.multiply(result.normal, new Vector2(Math.sqrt(distanceSquared) - circle.radius, Math.sqrt(distanceSquared) - circle.radius)); + }else{ + if (distanceSquared == 0){ + mtv = Vector2.multiply(result.normal, new Vector2(circle.radius, circle.radius)); + }else{ + let distance = Math.sqrt(distanceSquared); + // mtv = Vector2.multiply( -Vector2.subtract(poly2Circle, closestPoint), new Vector2((circle.radius - distanceSquared) / distance)) + } + } + } + public static circleToRect(circle: Circle, box: Rect): CollisionResult{ let result = new CollisionResult(); let closestPointOnBounds = box.bounds.getClosestPointOnRectangleBorderToPoint(circle.position).res; diff --git a/source/src/Physics/Verlet/Constraint/DistanceConstraint.ts b/source/src/Physics/Verlet/Constraint/DistanceConstraint.ts index 9d29be64..d119d6dc 100644 --- a/source/src/Physics/Verlet/Constraint/DistanceConstraint.ts +++ b/source/src/Physics/Verlet/Constraint/DistanceConstraint.ts @@ -3,6 +3,8 @@ class DistanceConstraint extends Constraint { public stiffness: number = 0; public restingDistance: number = 0; public tearSensitivity = Number.POSITIVE_INFINITY; + public shouldApproximateCollisionWithPoints: boolean; + public totalPointsToApproximateCollisionsWith = 5; private _particleOne: Particle; private _particleTwo: Particle; @@ -28,11 +30,48 @@ class DistanceConstraint extends Constraint { } public handleCollisions(collidersWithLayers){ + if (this.shouldApproximateCollisionWithPoints){ + this.approximateCollisionWithPoints(collidersWithLayers) + return; + } + let minX = Math.min(this._particleOne.position.x, this._particleTwo.position.x); let maxX = Math.max(this._particleOne.position.x, this._particleTwo.position.x); let minY = Math.min(this._particleOne.position.y, this._particleTwo.position.y); let maxY = Math.max(this._particleOne.position.y, this._particleTwo.position.y); - // DistanceConstraint._polygon.bounds = Rectangle. + DistanceConstraint._polygon.bounds = Rectangle.fromMinMax(minX, minY, maxX, maxY); + + let midPoint: Vector2 = this.preparePolygonForCollisionChecks(); + let colliders = Physics.boxcastBroadphase(DistanceConstraint._polygon.bounds, collidersWithLayers); + colliders.forEach(collider => { + + }); + } + + private preparePolygonForCollisionChecks(){ + let midPoint = Vector2.lerp(this._particleOne.position, this._particleTwo.position, 0.5); + DistanceConstraint._polygon.position = midPoint; + DistanceConstraint._polygon.points[0] = Vector2.subtract(this._particleOne.position, DistanceConstraint._polygon.position); + DistanceConstraint._polygon.points[1] = Vector2.subtract(this._particleTwo.position, DistanceConstraint._polygon.position); + DistanceConstraint._polygon.recalculateCenterAndEdgeNormals(); + + return midPoint; + } + + private approximateCollisionWithPoints(collidersWithLayers: number){ + let pt; + for (let j = 0; j < this.totalPointsToApproximateCollisionsWith - 1; j ++){ + pt = Vector2.lerp(this._particleOne.position, this._particleTwo.position, (j + 1) / this.totalPointsToApproximateCollisionsWith); + let collidedCount = Physics.overlapCircleAll(pt, 3, VerletWorld.colliders, collidersWithLayers); + for (let i = 0; i < collidedCount; i ++){ + let collider = VerletWorld.colliders[i]; + let collisionResult: CollisionResult = collider.shape.pointCollidesWithShape(pt); + if (collisionResult){ + this._particleOne.position = Vector2.subtract(this._particleOne.position, collisionResult.minimumTranslationVector); + this._particleTwo.position = Vector2.subtract(this._particleTwo.position, collisionResult.minimumTranslationVector); + } + } + } } public solve() { diff --git a/source/src/Physics/Verlet/VerletWorld.ts b/source/src/Physics/Verlet/VerletWorld.ts index 443fc7b3..90d62291 100644 --- a/source/src/Physics/Verlet/VerletWorld.ts +++ b/source/src/Physics/Verlet/VerletWorld.ts @@ -14,7 +14,7 @@ class VerletWorld { private _composites: Composite[] = []; private _fixedDeltaTimeSq: number; - private static _colliders: Collider[] = new Array(4); + public static colliders: Collider[] = new Array(4); private _tempCircle: Circle = new Circle(1); constructor(simulationBounds?: Rectangle){ @@ -52,9 +52,9 @@ class VerletWorld { } private handleCollisions(p: Particle, collidesWithLayers: number){ - let collidedCount = Physics.overlapCircleAll(p.position, p.radius, VerletWorld._colliders, collidesWithLayers); + let collidedCount = Physics.overlapCircleAll(p.position, p.radius, VerletWorld.colliders, collidesWithLayers); for (let i = 0; i < collidedCount; i ++){ - let collider = VerletWorld._colliders[i]; + let collider = VerletWorld.colliders[i]; if (collider.isTrigger) continue; diff --git a/source/src/Utils/Vector2Ext.ts b/source/src/Utils/Vector2Ext.ts index 22a73979..6c07e431 100644 --- a/source/src/Utils/Vector2Ext.ts +++ b/source/src/Utils/Vector2Ext.ts @@ -17,4 +17,13 @@ class Vector2Ext { public static cross(u: Vector2, v: Vector2){ return u.y * v.x - u.x * v.y; } + + /** + * 返回与传入向量垂直的向量 + * @param first + * @param second + */ + public static perpendicular(first: Vector2, second: Vector2){ + return new Vector2(-1 * (second.y - first.y), second.x - first.x); + } } \ No newline at end of file