everything i found in leaked code

This commit is contained in:
Tubix
2024-10-05 12:31:02 +01:00
parent 6679f44200
commit 28441faac3
2960 changed files with 394479 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.DS_Store

28
0.0.0.18/pom.xml Normal file
View File

@@ -0,0 +1,28 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>platform.clients.fp9.libraries</groupId>
<artifactId>AlternativaPhysics</artifactId>
<packaging>swc</packaging>
<version>0.0.0.18</version>
<parent>
<groupId>platform.tools.maven</groupId>
<artifactId>FlashBasePom</artifactId>
<version>1.0</version>
</parent>
<dependencies>
<dependency>
<groupId>platform.clients.fp9.libraries</groupId>
<artifactId>AlternativaTypes</artifactId>
<version>[1.1.0.1,2.0.0.0)</version>
<type>swc</type>
<scope>external</scope>
</dependency>
<dependency>
<groupId>platform.clients.fp9.libraries</groupId>
<artifactId>AlternativaUtils</artifactId>
<version>[1.2.0.0,2.0.0.0)</version>
<type>swc</type>
<scope>external</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,5 @@
K 25
svn:wc:ra_dav:version-url
V 74
/!svn/ver/5193/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src
END

31
0.0.0.18/src/.svn/entries Normal file
View File

@@ -0,0 +1,31 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src
http://svndev.alternativaplatform.com
2008-12-17T11:38:42.040147Z
5193
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
alternativa
dir

1
0.0.0.18/src/.svn/format Normal file
View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,5 @@
K 25
svn:wc:ra_dav:version-url
V 86
/!svn/ver/5193/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa
END

View File

@@ -0,0 +1,31 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa
http://svndev.alternativaplatform.com
2008-12-17T11:38:42.040147Z
5193
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
physics
dir

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,11 @@
K 25
svn:wc:ra_dav:version-url
V 94
/!svn/ver/5193/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics
END
altphysics.as
K 25
svn:wc:ra_dav:version-url
V 108
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/altphysics.as
END

View File

@@ -0,0 +1,55 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics
http://svndev.alternativaplatform.com
2008-12-17T11:38:42.040147Z
5193
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
particle
dir
altphysics.as
file
2010-10-28T04:34:31.000000Z
44fa0a3979af103ba658ab05e4428212
2008-11-24T06:53:44.272574Z
4196
mike
registry
dir
collision
dir
force
dir
rigid
dir

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,3 @@
package alternativa.physics {
public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
}

View File

@@ -0,0 +1,3 @@
package alternativa.physics {
public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
}

View File

@@ -0,0 +1,29 @@
K 25
svn:wc:ra_dav:version-url
V 104
/!svn/ver/4398/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision
END
CollisionBox.as
K 25
svn:wc:ra_dav:version-url
V 120
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision/CollisionBox.as
END
CollisionPrimitive.as
K 25
svn:wc:ra_dav:version-url
V 126
/!svn/ver/4375/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision/CollisionPrimitive.as
END
CollisionData.as
K 25
svn:wc:ra_dav:version-url
V 121
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision/CollisionData.as
END
CollisionDetector.as
K 25
svn:wc:ra_dav:version-url
V 125
/!svn/ver/4398/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision/CollisionDetector.as
END

View File

@@ -0,0 +1,76 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/collision
http://svndev.alternativaplatform.com
2008-11-26T15:04:50.319495Z
4398
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
CollisionBox.as
file
2010-10-28T04:34:31.000000Z
777502bd075948da11606f32afba6679
2008-11-24T06:53:44.272574Z
4196
mike
CollisionPrimitive.as
file
2010-10-28T04:34:31.000000Z
1e73f056a418e5c664977b4fafad70e5
2008-11-26T12:04:03.108808Z
4375
mike
CollisionData.as
file
2010-10-28T04:34:31.000000Z
a8943fcaceb0836d59e7f4c2b2d3f4df
2008-11-24T06:53:44.272574Z
4196
mike
CollisionDetector.as
file
2010-10-28T04:34:31.000000Z
ca727662c3fe996a487323977c4a67bb
2008-11-26T15:04:50.319495Z
4398
mike

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,15 @@
package alternativa.physics.collision {
import alternativa.physics.rigid.RigidBody;
import alternativa.types.Point3D;
public class CollisionBox extends CollisionPrimitive {
public var halfSize:Point3D = new Point3D();
public function CollisionBox(body:RigidBody, halfSize:Point3D) {
super(body);
this.halfSize.copy(halfSize);
}
}
}

View File

@@ -0,0 +1,18 @@
package alternativa.physics.collision {
import alternativa.types.Point3D;
public class CollisionData {
public var primitive1:CollisionPrimitive;
public var primitive2:CollisionPrimitive;
public var collisionNormal:Point3D = new Point3D();
public var collisionPoint:Point3D = new Point3D();
public var penetration:Number;
public var pointAxisCode1:int;
public var pointAxisCode2:int;
public var timeStamp:int;
}
}

View File

@@ -0,0 +1,342 @@
package alternativa.physics.collision {
import alternativa.physics.altphysics;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
use namespace altphysics;
public class CollisionDetector {
private var epsilon:Number;
private var bestAxisIndex:int;
private var minOverlap:Number;
private var vectorToBox1:Point3D = new Point3D();
private var axis:Point3D = new Point3D();
private var axis10:Point3D = new Point3D();
private var axis11:Point3D = new Point3D();
private var axis12:Point3D = new Point3D();
private var axis20:Point3D = new Point3D();
private var axis21:Point3D = new Point3D();
private var axis22:Point3D = new Point3D();
private var collisionAxis:Point3D = new Point3D();
private var tmpAxis:Point3D = new Point3D();
private var point1:Point3D = new Point3D();
private var point2:Point3D = new Point3D();
private var vector:Point3D = new Point3D();
public function CollisionDetector(epsilon:Number = 0.01) {
this.epsilon = epsilon;
}
/**
* Проверяет столкновение двух боксов, используя теорему о разделяющих плоскостях.
*
* @param box1
* @param box2
* @param collisionData
*
* @return
*/
public function boxToBoxCollision(box1:CollisionBox, box2:CollisionBox, collisionData:CollisionData):Boolean {
if (box1.body.inverseMass == 0 && box2.body.inverseMass == 0) {
// Нет необходимости проверять столкновения между неподвижными объектами
return false;
}
minOverlap = Number.MAX_VALUE;
// Вектор из центра второго бокса в центр первого
vectorToBox1.difference(box1.position, box2.position);
// Проверка пересечения по основным осям
box1.transform.getAxis(0, axis10);
if (!testAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
box1.transform.getAxis(1, axis11);
if (!testAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
box1.transform.getAxis(2, axis12);
if (!testAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
box2.transform.getAxis(0, axis20);
if (!testAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
box2.transform.getAxis(1, axis21);
if (!testAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
box2.transform.getAxis(2, axis22);
if (!testAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
// Сохраняем ось с наименьшим перекрытием на случай обнаружения контакта ребро-грань
var bestMainAxis:int = bestAxisIndex;
// Проверка производных осей
axis.cross2(axis10, axis20);
if (!testAxis(box1, box2, axis, 6, vectorToBox1)) return false;
axis.cross2(axis10, axis21);
if (!testAxis(box1, box2, axis, 7, vectorToBox1)) return false;
axis.cross2(axis10, axis22);
if (!testAxis(box1, box2, axis, 8, vectorToBox1)) return false;
axis.cross2(axis11, axis20);
if (!testAxis(box1, box2, axis, 9, vectorToBox1)) return false;
axis.cross2(axis11, axis21);
if (!testAxis(box1, box2, axis, 10, vectorToBox1)) return false;
axis.cross2(axis11, axis22);
if (!testAxis(box1, box2, axis, 11, vectorToBox1)) return false;
axis.cross2(axis12, axis20);
if (!testAxis(box1, box2, axis, 12, vectorToBox1)) return false;
axis.cross2(axis12, axis21);
if (!testAxis(box1, box2, axis, 13, vectorToBox1)) return false;
axis.cross2(axis12, axis22);
if (!testAxis(box1, box2, axis, 14, vectorToBox1)) return false;
if (bestAxisIndex < 3) {
// Пересечение вершины второго бокса с гранью первого
fillPointBoxPenetrationData(box1, box2, vectorToBox1, bestAxisIndex, minOverlap, collisionData);
collisionData.primitive1 = box1;
collisionData.primitive2 = box2;
return true;
} else if (bestAxisIndex < 6) {
// Пересечение вершины первого бокса с гранью второго
vectorToBox1.invert();
fillPointBoxPenetrationData(box2, box1, vectorToBox1, bestAxisIndex - 3, minOverlap, collisionData);
collisionData.primitive1 = box1;
collisionData.primitive2 = box2;
collisionData.collisionNormal.invert();
var tmp:Number = collisionData.pointAxisCode1;
collisionData.pointAxisCode1 = collisionData.pointAxisCode2;
collisionData.pointAxisCode2 = tmp;
return true;
} else {
// Пересечение рёбер
bestAxisIndex -= 6;
var axisIndex1:int = int(bestAxisIndex/3);
var axisIndex2:int = bestAxisIndex%3;
box1.transform.getAxis(axisIndex1, axis10);
box2.transform.getAxis(axisIndex2, axis20);
collisionAxis.cross2(axis10, axis20);
collisionAxis.normalize();
// Разворот оси в сторону первого бокса
if (collisionAxis.dot(vectorToBox1) < 0) {
collisionAxis.invert();
}
var edgeHalfLength1:Number;
var edgeHalfLength2:Number;
var pointEdgeCode1:int;
var pointEdgeCode2:int;
point1.copy(box1.halfSize);
point2.copy(box2.halfSize);
// x
if (axisIndex1 == 0) {
point1.x = 0;
edgeHalfLength1 = box1.halfSize.x;
pointEdgeCode1 |= 1;
} else {
box1.transform.getAxis(0, tmpAxis);
if (tmpAxis.dot(collisionAxis) > 0) {
point1.x = -point1.x;
pointEdgeCode1 |= 1;
}
}
if (axisIndex2 == 0) {
point2.x = 0;
edgeHalfLength2 = box2.halfSize.x;
pointEdgeCode2 |= 1;
} else {
box2.transform.getAxis(0, tmpAxis);
if (tmpAxis.dot(collisionAxis) < 0) {
point2.x = -point2.x;
pointEdgeCode2 |= 1;
}
}
// y
if (axisIndex1 == 1) {
point1.y = 0;
edgeHalfLength1 = box1.halfSize.y;
pointEdgeCode1 |= 2;
} else {
box1.transform.getAxis(1, tmpAxis);
if (tmpAxis.dot(collisionAxis) > 0) {
point1.y = -point1.y;
pointEdgeCode1 |= 2;
}
}
if (axisIndex2 == 1) {
point2.y = 0;
edgeHalfLength2 = box2.halfSize.y;
pointEdgeCode2 |= 2;
} else {
box2.transform.getAxis(1, tmpAxis);
if (tmpAxis.dot(collisionAxis) < 0) {
point2.y = -point2.y;
pointEdgeCode2 |= 2;
}
}
// z
if (axisIndex1 == 2) {
point1.z = 0;
edgeHalfLength1 = box1.halfSize.z;
pointEdgeCode1 |= 4;
} else {
box1.transform.getAxis(2, tmpAxis);
if (tmpAxis.dot(collisionAxis) > 0) {
point1.z = -point1.z;
pointEdgeCode1 |= 4;
}
}
if (axisIndex2 == 2) {
point2.z = 0;
edgeHalfLength2 = box2.halfSize.z;
pointEdgeCode2 |= 4;
} else {
box2.transform.getAxis(2, tmpAxis);
if (tmpAxis.dot(collisionAxis) < 0) {
point2.z = -point2.z;
pointEdgeCode2 |= 4;
}
}
point1.transform(box1.transform);
point2.transform(box2.transform);
// Находим точку пересечения рёбер
var k:Number = axis10.dot(axis20);
var det:Number = k*k - 1;
if (det > -0.0001 && det < 0.0001) {
vector.copy(bestMainAxis > 2 ? point1 : point2);
} else {
vector.difference(point2, point1);
var c1:Number = axis10.dot(vector);
var c2:Number = axis20.dot(vector);
var t1:Number = (c2*k - c1)/det;
if (t1 < -edgeHalfLength1 || t1 > edgeHalfLength1) {
vector.copy(bestMainAxis > 2 ? point1 : point2);
} else {
var t2:Number = (c2 - c1*k)/det;
if (t2 < -edgeHalfLength2 || t2 > edgeHalfLength2) {
vector.copy(bestMainAxis > 2 ? point1 : point2);
} else {
vector.x = 0.5*(point1.x + point2.x + axis10.x*t1 + axis20.x*t2);
vector.y = 0.5*(point1.y + point2.y + axis10.y*t1 + axis20.y*t2);
vector.z = 0.5*(point1.z + point2.z + axis10.z*t1 + axis20.z*t2);
}
}
}
collisionData.primitive1 = box1;
collisionData.primitive2 = box2;
collisionData.penetration = minOverlap;
collisionData.collisionPoint.copy(vector);
collisionData.collisionNormal.copy(collisionAxis);
collisionData.pointAxisCode1 = pointEdgeCode1 | (axisIndex1 << 3) | 32;
collisionData.pointAxisCode2 = pointEdgeCode2 | (axisIndex2 << 3) | 32;
}
return true;
}
/**
*
* @param box1
* @param box2
* @param axis
* @param axisIndex
* @param vectorToBox1
* @param bestAxis
* @return
*
*/
private function testAxis(box1:CollisionBox, box2:CollisionBox, axis:Point3D, axisIndex:int, vectorToBox1:Point3D):Boolean {
if (axis.lengthSqr < 0.0001) {
return true;
}
axis.normalize();
var overlap:Number = overlapOnAxis(box1, box2, axis, vectorToBox1);
if (overlap <= 0) {
return false;
}
if (overlap < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
*
* @param box1
* @param box2
* @param axis
* @param vectorToBox1
* @return
*
*/
public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Point3D, vectorToBox1:Point3D):Number {
var m:Matrix3D = box1.transform;
var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box1.halfSize.x;
var projection:Number = d < 0 ? -d : d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box1.halfSize.y;
projection += d < 0 ? -d : d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box1.halfSize.z;
projection += d < 0 ? -d : d;
m = box2.transform;
d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box2.halfSize.x;
projection += d < 0 ? -d : d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box2.halfSize.y;
projection += d < 0 ? -d : d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box2.halfSize.z;
projection += d < 0 ? -d : d;
d = vectorToBox1.x*axis.x + vectorToBox1.y*axis.y + vectorToBox1.z*axis.z;
if (d < 0) {
d = -d;
}
return projection - d;
}
/**
* Заполняет данные о контакте грани первого бокса с вершиной второго.
*
* @param box1
* @param box2
* @param vectorToBox1
* @param axisIndex
* @param overlap
* @param collisionData
* @param swapBoxes поменять примитивы местами при записи данных
*/
private function fillPointBoxPenetrationData(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Point3D, axisIndex:int, overlap:Number, collisionData:CollisionData):void {
box1.transform.getAxis(axisIndex, collisionData.collisionNormal);
if (collisionData.collisionNormal.dot(vectorToBox1) < 0) {
collisionData.collisionNormal.invert();
}
var pointCode:int;
axis10.copy(box2.halfSize);
box2.transform.getAxis(0, axis20);
if (axis20.dot(collisionData.collisionNormal) < 0) {
axis10.x = -axis10.x;
pointCode |= 1;
}
box2.transform.getAxis(1, axis20);
if (axis20.dot(collisionData.collisionNormal) < 0) {
axis10.y = -axis10.y;
pointCode |= 2;
}
box2.transform.getAxis(2, axis20);
if (axis20.dot(collisionData.collisionNormal) < 0) {
axis10.z = -axis10.z;
pointCode |= 4;
}
axis10.transform(box2.transform);
collisionData.collisionPoint.copy(axis10);
collisionData.penetration = overlap;
collisionData.pointAxisCode1 = (axisIndex << 3) | 32;
collisionData.pointAxisCode2 = pointCode;
}
}
}

View File

@@ -0,0 +1,31 @@
package alternativa.physics.collision {
import alternativa.physics.altphysics;
import alternativa.physics.rigid.RigidBody;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
use namespace altphysics;
public class CollisionPrimitive {
public var position:Point3D = new Point3D();
public var transform:Matrix3D = new Matrix3D();
public var body:RigidBody;
public function CollisionPrimitive(body:RigidBody) {
this.body = body;
}
public function updateTransform():void {
transform.copy(body.transformMatrix);
position.x = body.position.x;
position.y = body.position.y;
position.z = body.position.z;
transform.d = position.x;
transform.h = position.y;
transform.l = position.z;
}
}
}

View File

@@ -0,0 +1,15 @@
package alternativa.physics.collision {
import alternativa.physics.rigid.RigidBody;
import alternativa.types.Point3D;
public class CollisionBox extends CollisionPrimitive {
public var halfSize:Point3D = new Point3D();
public function CollisionBox(body:RigidBody, halfSize:Point3D) {
super(body);
this.halfSize.copy(halfSize);
}
}
}

View File

@@ -0,0 +1,18 @@
package alternativa.physics.collision {
import alternativa.types.Point3D;
public class CollisionData {
public var primitive1:CollisionPrimitive;
public var primitive2:CollisionPrimitive;
public var collisionNormal:Point3D = new Point3D();
public var collisionPoint:Point3D = new Point3D();
public var penetration:Number;
public var pointAxisCode1:int;
public var pointAxisCode2:int;
public var timeStamp:int;
}
}

View File

@@ -0,0 +1,342 @@
package alternativa.physics.collision {
import alternativa.physics.altphysics;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
use namespace altphysics;
public class CollisionDetector {
private var epsilon:Number;
private var bestAxisIndex:int;
private var minOverlap:Number;
private var vectorToBox1:Point3D = new Point3D();
private var axis:Point3D = new Point3D();
private var axis10:Point3D = new Point3D();
private var axis11:Point3D = new Point3D();
private var axis12:Point3D = new Point3D();
private var axis20:Point3D = new Point3D();
private var axis21:Point3D = new Point3D();
private var axis22:Point3D = new Point3D();
private var collisionAxis:Point3D = new Point3D();
private var tmpAxis:Point3D = new Point3D();
private var point1:Point3D = new Point3D();
private var point2:Point3D = new Point3D();
private var vector:Point3D = new Point3D();
public function CollisionDetector(epsilon:Number = 0.01) {
this.epsilon = epsilon;
}
/**
* Проверяет столкновение двух боксов, используя теорему о разделяющих плоскостях.
*
* @param box1
* @param box2
* @param collisionData
*
* @return
*/
public function boxToBoxCollision(box1:CollisionBox, box2:CollisionBox, collisionData:CollisionData):Boolean {
if (box1.body.inverseMass == 0 && box2.body.inverseMass == 0) {
// Нет необходимости проверять столкновения между неподвижными объектами
return false;
}
minOverlap = Number.MAX_VALUE;
// Вектор из центра второго бокса в центр первого
vectorToBox1.difference(box1.position, box2.position);
// Проверка пересечения по основным осям
box1.transform.getAxis(0, axis10);
if (!testAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
box1.transform.getAxis(1, axis11);
if (!testAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
box1.transform.getAxis(2, axis12);
if (!testAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
box2.transform.getAxis(0, axis20);
if (!testAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
box2.transform.getAxis(1, axis21);
if (!testAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
box2.transform.getAxis(2, axis22);
if (!testAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
// Сохраняем ось с наименьшим перекрытием на случай обнаружения контакта ребро-грань
var bestMainAxis:int = bestAxisIndex;
// Проверка производных осей
axis.cross2(axis10, axis20);
if (!testAxis(box1, box2, axis, 6, vectorToBox1)) return false;
axis.cross2(axis10, axis21);
if (!testAxis(box1, box2, axis, 7, vectorToBox1)) return false;
axis.cross2(axis10, axis22);
if (!testAxis(box1, box2, axis, 8, vectorToBox1)) return false;
axis.cross2(axis11, axis20);
if (!testAxis(box1, box2, axis, 9, vectorToBox1)) return false;
axis.cross2(axis11, axis21);
if (!testAxis(box1, box2, axis, 10, vectorToBox1)) return false;
axis.cross2(axis11, axis22);
if (!testAxis(box1, box2, axis, 11, vectorToBox1)) return false;
axis.cross2(axis12, axis20);
if (!testAxis(box1, box2, axis, 12, vectorToBox1)) return false;
axis.cross2(axis12, axis21);
if (!testAxis(box1, box2, axis, 13, vectorToBox1)) return false;
axis.cross2(axis12, axis22);
if (!testAxis(box1, box2, axis, 14, vectorToBox1)) return false;
if (bestAxisIndex < 3) {
// Пересечение вершины второго бокса с гранью первого
fillPointBoxPenetrationData(box1, box2, vectorToBox1, bestAxisIndex, minOverlap, collisionData);
collisionData.primitive1 = box1;
collisionData.primitive2 = box2;
return true;
} else if (bestAxisIndex < 6) {
// Пересечение вершины первого бокса с гранью второго
vectorToBox1.invert();
fillPointBoxPenetrationData(box2, box1, vectorToBox1, bestAxisIndex - 3, minOverlap, collisionData);
collisionData.primitive1 = box1;
collisionData.primitive2 = box2;
collisionData.collisionNormal.invert();
var tmp:Number = collisionData.pointAxisCode1;
collisionData.pointAxisCode1 = collisionData.pointAxisCode2;
collisionData.pointAxisCode2 = tmp;
return true;
} else {
// Пересечение рёбер
bestAxisIndex -= 6;
var axisIndex1:int = int(bestAxisIndex/3);
var axisIndex2:int = bestAxisIndex%3;
box1.transform.getAxis(axisIndex1, axis10);
box2.transform.getAxis(axisIndex2, axis20);
collisionAxis.cross2(axis10, axis20);
collisionAxis.normalize();
// Разворот оси в сторону первого бокса
if (collisionAxis.dot(vectorToBox1) < 0) {
collisionAxis.invert();
}
var edgeHalfLength1:Number;
var edgeHalfLength2:Number;
var pointEdgeCode1:int;
var pointEdgeCode2:int;
point1.copy(box1.halfSize);
point2.copy(box2.halfSize);
// x
if (axisIndex1 == 0) {
point1.x = 0;
edgeHalfLength1 = box1.halfSize.x;
pointEdgeCode1 |= 1;
} else {
box1.transform.getAxis(0, tmpAxis);
if (tmpAxis.dot(collisionAxis) > 0) {
point1.x = -point1.x;
pointEdgeCode1 |= 1;
}
}
if (axisIndex2 == 0) {
point2.x = 0;
edgeHalfLength2 = box2.halfSize.x;
pointEdgeCode2 |= 1;
} else {
box2.transform.getAxis(0, tmpAxis);
if (tmpAxis.dot(collisionAxis) < 0) {
point2.x = -point2.x;
pointEdgeCode2 |= 1;
}
}
// y
if (axisIndex1 == 1) {
point1.y = 0;
edgeHalfLength1 = box1.halfSize.y;
pointEdgeCode1 |= 2;
} else {
box1.transform.getAxis(1, tmpAxis);
if (tmpAxis.dot(collisionAxis) > 0) {
point1.y = -point1.y;
pointEdgeCode1 |= 2;
}
}
if (axisIndex2 == 1) {
point2.y = 0;
edgeHalfLength2 = box2.halfSize.y;
pointEdgeCode2 |= 2;
} else {
box2.transform.getAxis(1, tmpAxis);
if (tmpAxis.dot(collisionAxis) < 0) {
point2.y = -point2.y;
pointEdgeCode2 |= 2;
}
}
// z
if (axisIndex1 == 2) {
point1.z = 0;
edgeHalfLength1 = box1.halfSize.z;
pointEdgeCode1 |= 4;
} else {
box1.transform.getAxis(2, tmpAxis);
if (tmpAxis.dot(collisionAxis) > 0) {
point1.z = -point1.z;
pointEdgeCode1 |= 4;
}
}
if (axisIndex2 == 2) {
point2.z = 0;
edgeHalfLength2 = box2.halfSize.z;
pointEdgeCode2 |= 4;
} else {
box2.transform.getAxis(2, tmpAxis);
if (tmpAxis.dot(collisionAxis) < 0) {
point2.z = -point2.z;
pointEdgeCode2 |= 4;
}
}
point1.transform(box1.transform);
point2.transform(box2.transform);
// Находим точку пересечения рёбер
var k:Number = axis10.dot(axis20);
var det:Number = k*k - 1;
if (det > -0.0001 && det < 0.0001) {
vector.copy(bestMainAxis > 2 ? point1 : point2);
} else {
vector.difference(point2, point1);
var c1:Number = axis10.dot(vector);
var c2:Number = axis20.dot(vector);
var t1:Number = (c2*k - c1)/det;
if (t1 < -edgeHalfLength1 || t1 > edgeHalfLength1) {
vector.copy(bestMainAxis > 2 ? point1 : point2);
} else {
var t2:Number = (c2 - c1*k)/det;
if (t2 < -edgeHalfLength2 || t2 > edgeHalfLength2) {
vector.copy(bestMainAxis > 2 ? point1 : point2);
} else {
vector.x = 0.5*(point1.x + point2.x + axis10.x*t1 + axis20.x*t2);
vector.y = 0.5*(point1.y + point2.y + axis10.y*t1 + axis20.y*t2);
vector.z = 0.5*(point1.z + point2.z + axis10.z*t1 + axis20.z*t2);
}
}
}
collisionData.primitive1 = box1;
collisionData.primitive2 = box2;
collisionData.penetration = minOverlap;
collisionData.collisionPoint.copy(vector);
collisionData.collisionNormal.copy(collisionAxis);
collisionData.pointAxisCode1 = pointEdgeCode1 | (axisIndex1 << 3) | 32;
collisionData.pointAxisCode2 = pointEdgeCode2 | (axisIndex2 << 3) | 32;
}
return true;
}
/**
*
* @param box1
* @param box2
* @param axis
* @param axisIndex
* @param vectorToBox1
* @param bestAxis
* @return
*
*/
private function testAxis(box1:CollisionBox, box2:CollisionBox, axis:Point3D, axisIndex:int, vectorToBox1:Point3D):Boolean {
if (axis.lengthSqr < 0.0001) {
return true;
}
axis.normalize();
var overlap:Number = overlapOnAxis(box1, box2, axis, vectorToBox1);
if (overlap <= 0) {
return false;
}
if (overlap < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
*
* @param box1
* @param box2
* @param axis
* @param vectorToBox1
* @return
*
*/
public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Point3D, vectorToBox1:Point3D):Number {
var m:Matrix3D = box1.transform;
var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box1.halfSize.x;
var projection:Number = d < 0 ? -d : d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box1.halfSize.y;
projection += d < 0 ? -d : d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box1.halfSize.z;
projection += d < 0 ? -d : d;
m = box2.transform;
d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box2.halfSize.x;
projection += d < 0 ? -d : d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box2.halfSize.y;
projection += d < 0 ? -d : d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box2.halfSize.z;
projection += d < 0 ? -d : d;
d = vectorToBox1.x*axis.x + vectorToBox1.y*axis.y + vectorToBox1.z*axis.z;
if (d < 0) {
d = -d;
}
return projection - d;
}
/**
* Заполняет данные о контакте грани первого бокса с вершиной второго.
*
* @param box1
* @param box2
* @param vectorToBox1
* @param axisIndex
* @param overlap
* @param collisionData
* @param swapBoxes поменять примитивы местами при записи данных
*/
private function fillPointBoxPenetrationData(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Point3D, axisIndex:int, overlap:Number, collisionData:CollisionData):void {
box1.transform.getAxis(axisIndex, collisionData.collisionNormal);
if (collisionData.collisionNormal.dot(vectorToBox1) < 0) {
collisionData.collisionNormal.invert();
}
var pointCode:int;
axis10.copy(box2.halfSize);
box2.transform.getAxis(0, axis20);
if (axis20.dot(collisionData.collisionNormal) < 0) {
axis10.x = -axis10.x;
pointCode |= 1;
}
box2.transform.getAxis(1, axis20);
if (axis20.dot(collisionData.collisionNormal) < 0) {
axis10.y = -axis10.y;
pointCode |= 2;
}
box2.transform.getAxis(2, axis20);
if (axis20.dot(collisionData.collisionNormal) < 0) {
axis10.z = -axis10.z;
pointCode |= 4;
}
axis10.transform(box2.transform);
collisionData.collisionPoint.copy(axis10);
collisionData.penetration = overlap;
collisionData.pointAxisCode1 = (axisIndex << 3) | 32;
collisionData.pointAxisCode2 = pointCode;
}
}
}

View File

@@ -0,0 +1,31 @@
package alternativa.physics.collision {
import alternativa.physics.altphysics;
import alternativa.physics.rigid.RigidBody;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
use namespace altphysics;
public class CollisionPrimitive {
public var position:Point3D = new Point3D();
public var transform:Matrix3D = new Matrix3D();
public var body:RigidBody;
public function CollisionPrimitive(body:RigidBody) {
this.body = body;
}
public function updateTransform():void {
transform.copy(body.transformMatrix);
position.x = body.position.x;
position.y = body.position.y;
position.z = body.position.z;
transform.d = position.x;
transform.h = position.y;
transform.l = position.z;
}
}
}

View File

@@ -0,0 +1,77 @@
K 25
svn:wc:ra_dav:version-url
V 100
/!svn/ver/4732/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force
END
ParticleAnchoredBungee.as
K 25
svn:wc:ra_dav:version-url
V 126
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleAnchoredBungee.as
END
ParticleBuoyancy.as
K 25
svn:wc:ra_dav:version-url
V 120
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleBuoyancy.as
END
RigidBodySpring.as
K 25
svn:wc:ra_dav:version-url
V 119
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/RigidBodySpring.as
END
ParticleDrag.as
K 25
svn:wc:ra_dav:version-url
V 116
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleDrag.as
END
IParticelForceGenerator.as
K 25
svn:wc:ra_dav:version-url
V 127
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/IParticelForceGenerator.as
END
RigidBodyGravity.as
K 25
svn:wc:ra_dav:version-url
V 120
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/RigidBodyGravity.as
END
ParticleSpring.as
K 25
svn:wc:ra_dav:version-url
V 118
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleSpring.as
END
RigidBodyAnchoredBungee.as
K 25
svn:wc:ra_dav:version-url
V 127
/!svn/ver/4732/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/RigidBodyAnchoredBungee.as
END
ParticleBungee.as
K 25
svn:wc:ra_dav:version-url
V 118
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleBungee.as
END
ParticleGravity.as
K 25
svn:wc:ra_dav:version-url
V 119
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleGravity.as
END
ParticleAnchoredSpring.as
K 25
svn:wc:ra_dav:version-url
V 126
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/ParticleAnchoredSpring.as
END
IRigidBodyForceGenerator.as
K 25
svn:wc:ra_dav:version-url
V 128
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force/IRigidBodyForceGenerator.as
END

View File

@@ -0,0 +1,172 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/force
http://svndev.alternativaplatform.com
2008-12-03T05:15:16.843707Z
4732
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
ParticleAnchoredBungee.as
file
2010-10-28T04:34:31.000000Z
ed65cd81f1a99df1cc3c04e061cc15fd
2008-11-24T06:53:44.272574Z
4196
mike
ParticleBuoyancy.as
file
2010-10-28T04:34:31.000000Z
4479f6ebbce6c7326410485e5c62b9a9
2008-11-24T06:53:44.272574Z
4196
mike
RigidBodySpring.as
file
2010-10-28T04:34:31.000000Z
3f0de100aebf0d3250e608872e6f3863
2008-11-24T06:53:44.272574Z
4196
mike
ParticleDrag.as
file
2010-10-28T04:34:31.000000Z
1a8e8b326a47d4771d157259e43deb53
2008-11-24T06:53:44.272574Z
4196
mike
IParticelForceGenerator.as
file
2010-10-28T04:34:31.000000Z
645a997b618f0708a8311054767340a7
2008-11-24T06:53:44.272574Z
4196
mike
RigidBodyGravity.as
file
2010-10-28T04:34:31.000000Z
b206b142e125e9fe4a1775f8c4768b17
2008-11-24T06:53:44.272574Z
4196
mike
ParticleSpring.as
file
2010-10-28T04:34:31.000000Z
fb5cb524f90e27812074bc2668c548d1
2008-11-24T06:53:44.272574Z
4196
mike
RigidBodyAnchoredBungee.as
file
2010-10-28T04:34:31.000000Z
a64b67f4f850a73a3ffc226ab8fb0774
2008-12-03T05:15:16.843707Z
4732
mike
ParticleBungee.as
file
2010-10-28T04:34:31.000000Z
d79408ff66b4c9d92f9a2be73417ba3b
2008-11-24T06:53:44.272574Z
4196
mike
ParticleGravity.as
file
2010-10-28T04:34:31.000000Z
3b0d1310430d785b3ec7ffebf8ea7ea9
2008-11-24T06:53:44.272574Z
4196
mike
ParticleAnchoredSpring.as
file
2010-10-28T04:34:31.000000Z
2a0eee0933c8973ec42bb28a8b0b7160
2008-11-24T06:53:44.272574Z
4196
mike
IRigidBodyForceGenerator.as
file
2010-10-28T04:34:31.000000Z
824ea9b7a68dda215974882d6f32f892
2008-11-24T06:53:44.272574Z
4196
mike

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,7 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
public interface IParticelForceGenerator {
function updateForce(particle:Particle, time:Number):void;
}
}

View File

@@ -0,0 +1,10 @@
package alternativa.physics.force {
import alternativa.physics.rigid.RigidBody;
/**
*
*/
public interface IRigidBodyForceGenerator {
function updateForce(body:RigidBody, time:Number):void;
}
}

View File

@@ -0,0 +1,37 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleAnchoredBungee implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var anchor:Point3D;
private var springCoeff:Number;
private var restLength:Number;
public var relaxed:Boolean;
public function ParticleAnchoredBungee(anchor:Point3D, springCoeff:Number, restLength:Number) {
this.anchor = anchor;
this.springCoeff = springCoeff;
this.restLength = restLength;
}
public function updateForce(particle:Particle, time:Number):void {
force.x = particle.position.x - anchor.x;
force.y = particle.position.y - anchor.y;
force.z = particle.position.z - anchor.z;
var len:Number = force.length;
if (len < restLength) {
relaxed = true;
return;
}
relaxed = false;
var k:Number = (restLength - len)*springCoeff/len;
force.x *= k;
force.y *= k;
force.z *= k;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,31 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleAnchoredSpring implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var anchor:Point3D;
private var springCoeff:Number;
private var restLength:Number;
public function ParticleAnchoredSpring(anchor:Point3D, springCoeff:Number, restLength:Number) {
this.anchor = anchor;
this.springCoeff = springCoeff;
this.restLength = restLength;
}
public function updateForce(particle:Particle, time:Number):void {
force.x = particle.position.x - anchor.x;
force.y = particle.position.y - anchor.y;
force.z = particle.position.z - anchor.z;
var len:Number = force.length;
var k:Number = (restLength - len)*springCoeff/len;
force.x *= k;
force.y *= k;
force.z *= k;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,38 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleBungee implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var otherParticle:Particle;
private var springCoeff:Number;
private var restLength:Number;
public var relaxed:Boolean;
public function ParticleBungee(otherParticle:Particle, springCoeff:Number, restLength:Number) {
this.otherParticle = otherParticle;
this.springCoeff = springCoeff;
this.restLength = restLength;
}
public function updateForce(particle:Particle, time:Number):void {
force.x = particle.position.x - otherParticle.position.x;
force.y = particle.position.y - otherParticle.position.y;
force.z = particle.position.z - otherParticle.position.z;
var len:Number = force.length;
if (len < restLength) {
relaxed = true;
return;
}
relaxed = false;
var k:Number = (restLength - len)*springCoeff/len;
force.x *= k;
force.y *= k;
force.z *= k;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,37 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleBuoyancy implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var maxDepth:Number;
private var waterHeight:Number;
private var volume:Number;
private var liquidDensity:Number;
public function ParticleBuoyancy(maxDepth:Number, waterHeight:Number, volume:Number, liquidDensity:Number = 1000) {
this.maxDepth = maxDepth;
this.waterHeight = waterHeight;
this.volume = volume;
this.liquidDensity = liquidDensity;
}
public function updateForce(particle:Particle, time:Number):void {
var depth:Number = particle.position.y;
if (depth > waterHeight + maxDepth) {
return;
}
force.x = 0;
force.y = 0;
if (depth <= waterHeight - maxDepth) {
force.z = liquidDensity*volume;
} else {
force.z = liquidDensity*volume*(waterHeight + maxDepth - depth)/maxDepth*0.5;
}
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,29 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleDrag implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var k1:Number;
private var k2:Number;
public function ParticleDrag(k1:Number, k2:Number) {
this.k1 = k1;
this.k2 = k2;
}
public function updateForce(particle:Particle, time:Number):void {
force.x = particle.velocity.x;
force.y = particle.velocity.y;
force.z = particle.velocity.z;
var len:Number = force.length;
var coeff:Number = len;
coeff = -(k1*coeff + k2*coeff*coeff)/len;
force.x *= coeff;
force.y *= coeff;
force.z *= coeff;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,24 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleGravity implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var _gravity:Point3D;
public function ParticleGravity(gravity:Point3D) {
_gravity = gravity.clone();
}
public function updateForce(particle:Particle, time:Number):void {
if (!particle.hasFiniteMass()) {
return;
}
force.x = _gravity.x*particle.mass;
force.y = _gravity.y*particle.mass;
force.z = _gravity.z*particle.mass;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,31 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleSpring implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var otherParticle:Particle;
private var springCoeff:Number;
private var restLength:Number;
public function ParticleSpring(otherParticle:Particle, springCoeff:Number, restLength:Number) {
this.otherParticle = otherParticle;
this.springCoeff = springCoeff;
this.restLength = restLength;
}
public function updateForce(particle:Particle, time:Number):void {
force.x = particle.position.x - otherParticle.position.x;
force.y = particle.position.y - otherParticle.position.y;
force.z = particle.position.z - otherParticle.position.z;
var len:Number = force.length;
var k:Number = (restLength - len)*springCoeff/len;
force.x *= k;
force.y *= k;
force.z *= k;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,45 @@
package alternativa.physics.force {
import alternativa.physics.*;
import alternativa.physics.rigid.RigidBody;
import alternativa.types.Point3D;
use namespace altphysics;
public class RigidBodyAnchoredBungee implements IRigidBodyForceGenerator {
private var vector1:Point3D = new Point3D();
private var vector2:Point3D = new Point3D();
private var _connectionPoint:Point3D = new Point3D();
private var _anchorPoint:Point3D = new Point3D();
private var _springConstant:Number;
private var _restLength:Number;
public function RigidBodyAnchoredBungee(connectionPoint:Point3D, anchorPoint:Point3D, springConstant:Number, restLength:Number) {
_connectionPoint.copy(connectionPoint);
_anchorPoint.copy(anchorPoint);
_springConstant = springConstant;
_restLength = restLength;
}
public function updateForce(body:RigidBody, time:Number):void {
// TODO: Оптимизировать!!!
vector1.copy(_connectionPoint);
vector1.transform(body.transformMatrix);
vector2.copy(_anchorPoint);
vector2.subtract(vector1);
var len:Number = vector2.length;
var magnitude:Number = len - _restLength;
if (magnitude > 0) {
magnitude *= _springConstant;
vector2.multiply(magnitude/len);
body.addForceAtPoint(vector2, vector1);
}
}
public function set anchorPoint(value:Point3D):void {
_anchorPoint.copy(value);
}
}
}

View File

@@ -0,0 +1,27 @@
package alternativa.physics.force {
import alternativa.physics.*;
import alternativa.physics.rigid.RigidBody;
import alternativa.types.Point3D;
use namespace altphysics;
/**
*
*/
public class RigidBodyGravity implements IRigidBodyForceGenerator {
private var _gravity:Point3D = new Point3D();
public function RigidBodyGravity(g:Point3D) {
_gravity.copy(g);
}
public function updateForce(body:RigidBody, time:Number):void {
if (body.inverseMass == 0) {
return;
}
body.addForceComponents(_gravity.x*body.mass, _gravity.y*body.mass, _gravity.z*body.mass);
}
}
}

View File

@@ -0,0 +1,45 @@
package alternativa.physics.force {
import alternativa.physics.*;
import alternativa.physics.rigid.RigidBody;
import alternativa.types.Point3D;
use namespace altphysics;
public class RigidBodySpring implements IRigidBodyForceGenerator {
private var vector1:Point3D = new Point3D();
private var vector2:Point3D = new Point3D();
private var _connectionPoint:Point3D = new Point3D();
private var _otherConnectionPoint:Point3D = new Point3D();
private var _springConstant:Number;
private var _restLength:Number;
private var _otherBody:RigidBody;
public function RigidBodySpring(connectionPoint:Point3D, otherBody:RigidBody, otherConnectionPoint:Point3D, springConstant:Number, restLength:Number) {
_connectionPoint.copy(connectionPoint);
_otherConnectionPoint = otherConnectionPoint;
_otherBody = otherBody;
_springConstant = springConstant;
_restLength = restLength;
}
public function updateForce(body:RigidBody, time:Number):void {
// TODO: Оптимизировать!!!
vector1.copy(_connectionPoint);
vector1.transform(body.transformMatrix);
vector2.copy(_otherConnectionPoint);
vector2.transform(_otherBody.transformMatrix);
vector2.subtract(vector1);
var len:Number = vector2.length;
if (len == 0) {
trace("[RigidBodySpring::updateForce] spring length is zero");
}
var magnitude:Number = len - _restLength;
magnitude *= _springConstant;
vector2.multiply(magnitude/len);
body.addForceAtPoint(vector2, vector1);
}
}
}

View File

@@ -0,0 +1,7 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
public interface IParticelForceGenerator {
function updateForce(particle:Particle, time:Number):void;
}
}

View File

@@ -0,0 +1,10 @@
package alternativa.physics.force {
import alternativa.physics.rigid.RigidBody;
/**
*
*/
public interface IRigidBodyForceGenerator {
function updateForce(body:RigidBody, time:Number):void;
}
}

View File

@@ -0,0 +1,37 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleAnchoredBungee implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var anchor:Point3D;
private var springCoeff:Number;
private var restLength:Number;
public var relaxed:Boolean;
public function ParticleAnchoredBungee(anchor:Point3D, springCoeff:Number, restLength:Number) {
this.anchor = anchor;
this.springCoeff = springCoeff;
this.restLength = restLength;
}
public function updateForce(particle:Particle, time:Number):void {
force.x = particle.position.x - anchor.x;
force.y = particle.position.y - anchor.y;
force.z = particle.position.z - anchor.z;
var len:Number = force.length;
if (len < restLength) {
relaxed = true;
return;
}
relaxed = false;
var k:Number = (restLength - len)*springCoeff/len;
force.x *= k;
force.y *= k;
force.z *= k;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,31 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleAnchoredSpring implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var anchor:Point3D;
private var springCoeff:Number;
private var restLength:Number;
public function ParticleAnchoredSpring(anchor:Point3D, springCoeff:Number, restLength:Number) {
this.anchor = anchor;
this.springCoeff = springCoeff;
this.restLength = restLength;
}
public function updateForce(particle:Particle, time:Number):void {
force.x = particle.position.x - anchor.x;
force.y = particle.position.y - anchor.y;
force.z = particle.position.z - anchor.z;
var len:Number = force.length;
var k:Number = (restLength - len)*springCoeff/len;
force.x *= k;
force.y *= k;
force.z *= k;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,38 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleBungee implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var otherParticle:Particle;
private var springCoeff:Number;
private var restLength:Number;
public var relaxed:Boolean;
public function ParticleBungee(otherParticle:Particle, springCoeff:Number, restLength:Number) {
this.otherParticle = otherParticle;
this.springCoeff = springCoeff;
this.restLength = restLength;
}
public function updateForce(particle:Particle, time:Number):void {
force.x = particle.position.x - otherParticle.position.x;
force.y = particle.position.y - otherParticle.position.y;
force.z = particle.position.z - otherParticle.position.z;
var len:Number = force.length;
if (len < restLength) {
relaxed = true;
return;
}
relaxed = false;
var k:Number = (restLength - len)*springCoeff/len;
force.x *= k;
force.y *= k;
force.z *= k;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,37 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleBuoyancy implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var maxDepth:Number;
private var waterHeight:Number;
private var volume:Number;
private var liquidDensity:Number;
public function ParticleBuoyancy(maxDepth:Number, waterHeight:Number, volume:Number, liquidDensity:Number = 1000) {
this.maxDepth = maxDepth;
this.waterHeight = waterHeight;
this.volume = volume;
this.liquidDensity = liquidDensity;
}
public function updateForce(particle:Particle, time:Number):void {
var depth:Number = particle.position.y;
if (depth > waterHeight + maxDepth) {
return;
}
force.x = 0;
force.y = 0;
if (depth <= waterHeight - maxDepth) {
force.z = liquidDensity*volume;
} else {
force.z = liquidDensity*volume*(waterHeight + maxDepth - depth)/maxDepth*0.5;
}
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,29 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleDrag implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var k1:Number;
private var k2:Number;
public function ParticleDrag(k1:Number, k2:Number) {
this.k1 = k1;
this.k2 = k2;
}
public function updateForce(particle:Particle, time:Number):void {
force.x = particle.velocity.x;
force.y = particle.velocity.y;
force.z = particle.velocity.z;
var len:Number = force.length;
var coeff:Number = len;
coeff = -(k1*coeff + k2*coeff*coeff)/len;
force.x *= coeff;
force.y *= coeff;
force.z *= coeff;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,24 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleGravity implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var _gravity:Point3D;
public function ParticleGravity(gravity:Point3D) {
_gravity = gravity.clone();
}
public function updateForce(particle:Particle, time:Number):void {
if (!particle.hasFiniteMass()) {
return;
}
force.x = _gravity.x*particle.mass;
force.y = _gravity.y*particle.mass;
force.z = _gravity.z*particle.mass;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,31 @@
package alternativa.physics.force {
import alternativa.physics.particle.Particle;
import alternativa.types.Point3D;
public class ParticleSpring implements IParticelForceGenerator {
private var force:Point3D = new Point3D();
private var otherParticle:Particle;
private var springCoeff:Number;
private var restLength:Number;
public function ParticleSpring(otherParticle:Particle, springCoeff:Number, restLength:Number) {
this.otherParticle = otherParticle;
this.springCoeff = springCoeff;
this.restLength = restLength;
}
public function updateForce(particle:Particle, time:Number):void {
force.x = particle.position.x - otherParticle.position.x;
force.y = particle.position.y - otherParticle.position.y;
force.z = particle.position.z - otherParticle.position.z;
var len:Number = force.length;
var k:Number = (restLength - len)*springCoeff/len;
force.x *= k;
force.y *= k;
force.z *= k;
particle.addForce(force);
}
}
}

View File

@@ -0,0 +1,45 @@
package alternativa.physics.force {
import alternativa.physics.*;
import alternativa.physics.rigid.RigidBody;
import alternativa.types.Point3D;
use namespace altphysics;
public class RigidBodyAnchoredBungee implements IRigidBodyForceGenerator {
private var vector1:Point3D = new Point3D();
private var vector2:Point3D = new Point3D();
private var _connectionPoint:Point3D = new Point3D();
private var _anchorPoint:Point3D = new Point3D();
private var _springConstant:Number;
private var _restLength:Number;
public function RigidBodyAnchoredBungee(connectionPoint:Point3D, anchorPoint:Point3D, springConstant:Number, restLength:Number) {
_connectionPoint.copy(connectionPoint);
_anchorPoint.copy(anchorPoint);
_springConstant = springConstant;
_restLength = restLength;
}
public function updateForce(body:RigidBody, time:Number):void {
// TODO: Оптимизировать!!!
vector1.copy(_connectionPoint);
vector1.transform(body.transformMatrix);
vector2.copy(_anchorPoint);
vector2.subtract(vector1);
var len:Number = vector2.length;
var magnitude:Number = len - _restLength;
if (magnitude > 0) {
magnitude *= _springConstant;
vector2.multiply(magnitude/len);
body.addForceAtPoint(vector2, vector1);
}
}
public function set anchorPoint(value:Point3D):void {
_anchorPoint.copy(value);
}
}
}

View File

@@ -0,0 +1,27 @@
package alternativa.physics.force {
import alternativa.physics.*;
import alternativa.physics.rigid.RigidBody;
import alternativa.types.Point3D;
use namespace altphysics;
/**
*
*/
public class RigidBodyGravity implements IRigidBodyForceGenerator {
private var _gravity:Point3D = new Point3D();
public function RigidBodyGravity(g:Point3D) {
_gravity.copy(g);
}
public function updateForce(body:RigidBody, time:Number):void {
if (body.inverseMass == 0) {
return;
}
body.addForceComponents(_gravity.x*body.mass, _gravity.y*body.mass, _gravity.z*body.mass);
}
}
}

View File

@@ -0,0 +1,45 @@
package alternativa.physics.force {
import alternativa.physics.*;
import alternativa.physics.rigid.RigidBody;
import alternativa.types.Point3D;
use namespace altphysics;
public class RigidBodySpring implements IRigidBodyForceGenerator {
private var vector1:Point3D = new Point3D();
private var vector2:Point3D = new Point3D();
private var _connectionPoint:Point3D = new Point3D();
private var _otherConnectionPoint:Point3D = new Point3D();
private var _springConstant:Number;
private var _restLength:Number;
private var _otherBody:RigidBody;
public function RigidBodySpring(connectionPoint:Point3D, otherBody:RigidBody, otherConnectionPoint:Point3D, springConstant:Number, restLength:Number) {
_connectionPoint.copy(connectionPoint);
_otherConnectionPoint = otherConnectionPoint;
_otherBody = otherBody;
_springConstant = springConstant;
_restLength = restLength;
}
public function updateForce(body:RigidBody, time:Number):void {
// TODO: Оптимизировать!!!
vector1.copy(_connectionPoint);
vector1.transform(body.transformMatrix);
vector2.copy(_otherConnectionPoint);
vector2.transform(_otherBody.transformMatrix);
vector2.subtract(vector1);
var len:Number = vector2.length;
if (len == 0) {
trace("[RigidBodySpring::updateForce] spring length is zero");
}
var magnitude:Number = len - _restLength;
magnitude *= _springConstant;
vector2.multiply(magnitude/len);
body.addForceAtPoint(vector2, vector1);
}
}
}

View File

@@ -0,0 +1,53 @@
K 25
svn:wc:ra_dav:version-url
V 103
/!svn/ver/4378/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle
END
ParticleContact.as
K 25
svn:wc:ra_dav:version-url
V 122
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleContact.as
END
ParticleContactResolver.as
K 25
svn:wc:ra_dav:version-url
V 130
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleContactResolver.as
END
ParticleLink.as
K 25
svn:wc:ra_dav:version-url
V 119
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleLink.as
END
Particle.as
K 25
svn:wc:ra_dav:version-url
V 115
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/Particle.as
END
ParticleContactGenerator.as
K 25
svn:wc:ra_dav:version-url
V 131
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleContactGenerator.as
END
ParticleRod.as
K 25
svn:wc:ra_dav:version-url
V 118
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleRod.as
END
ParticleCable.as
K 25
svn:wc:ra_dav:version-url
V 120
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleCable.as
END
ParticleWorld.as
K 25
svn:wc:ra_dav:version-url
V 120
/!svn/ver/4378/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle/ParticleWorld.as
END

View File

@@ -0,0 +1,124 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/particle
http://svndev.alternativaplatform.com
2008-11-26T12:12:57.063201Z
4378
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
ParticleContact.as
file
2010-10-28T04:34:31.000000Z
af51b49cb3918bc31be32dca4a9ddff2
2008-11-24T06:53:44.272574Z
4196
mike
ParticleContactResolver.as
file
2010-10-28T04:34:31.000000Z
09a37756afe614b640d36b5e474ae709
2008-11-24T06:53:44.272574Z
4196
mike
ParticleLink.as
file
2010-10-28T04:34:31.000000Z
6313230f57ccdba2b80c3ab4306d9d02
2008-11-24T06:53:44.272574Z
4196
mike
Particle.as
file
2010-10-28T04:34:31.000000Z
36a3a8de3fce54e29395a591388b0fd9
2008-11-24T06:53:44.272574Z
4196
mike
ParticleContactGenerator.as
file
2010-10-28T04:34:31.000000Z
1d585c4b2678e36ed0d44403ed35bbac
2008-11-24T06:53:44.272574Z
4196
mike
ParticleRod.as
file
2010-10-28T04:34:31.000000Z
a95a229bcce5f16576e8aa3bbee4d4b9
2008-11-24T06:53:44.272574Z
4196
mike
ParticleCable.as
file
2010-10-28T04:34:31.000000Z
b289fc57f98c5170eb964493250a309d
2008-11-24T06:53:44.272574Z
4196
mike
ParticleWorld.as
file
2010-10-28T04:34:31.000000Z
29ebff641b6d9cc9def3022dd679889f
2008-11-26T12:12:57.063201Z
4378
mike

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,78 @@
package alternativa.physics.particle {
import alternativa.types.Point3D;
public class Particle {
private static var resultingAcceleration:Point3D = new Point3D();
public var position:Point3D = new Point3D();
public var velocity:Point3D = new Point3D();
public var acceleration:Point3D = new Point3D();
public var accumulatedForce:Point3D = new Point3D();
public var mass:Number;
public var inverseMass:Number;
public var damping:Number = 0.995;
public var next:Particle;
/**
* @param mass масса частицы. Значение меньшее или равное нулю означает бесконечную массу.
*/
public function Particle(mass:Number = 0) {
if (mass <= 0) {
this.mass = 0;
inverseMass = 0;
} else {
this.mass = mass;
inverseMass = 1/mass;
}
}
/**
*
* @param time время в секундах
*/
public function integrate(time:Number):void {
if (time <= 0) {
return;
}
position.x += velocity.x*time;
position.y += velocity.y*time;
position.z += velocity.z*time;
resultingAcceleration.x = acceleration.x + inverseMass*accumulatedForce.x;
resultingAcceleration.y = acceleration.y + inverseMass*accumulatedForce.y;
resultingAcceleration.z = acceleration.z + inverseMass*accumulatedForce.z;
velocity.x += resultingAcceleration.x*time;
velocity.y += resultingAcceleration.y*time;
velocity.z += resultingAcceleration.z*time;
var d:Number = Math.pow(damping, time);
velocity.x *= d;
velocity.y *= d;
velocity.z *= d;
accumulatedForce.x = 0;
accumulatedForce.y = 0;
accumulatedForce.z = 0;
}
public function clearForce():void {
accumulatedForce.x = 0;
accumulatedForce.y = 0;
accumulatedForce.z = 0;
}
public function addForce(force:Point3D):void {
accumulatedForce.x += force.x;
accumulatedForce.y += force.y;
accumulatedForce.z += force.z;
}
public function hasFiniteMass():Boolean {
return inverseMass > 0;
}
}
}

View File

@@ -0,0 +1,46 @@
package alternativa.physics.particle {
import alternativa.types.Point3D;
public class ParticleCable extends ParticleLink {
private static var vector:Point3D = new Point3D();
public var maxLength:Number;
public var restitution:Number;
public function ParticleCable(particle1:Particle, particle2:Particle, maxLength:Number, restitution:Number) {
super(particle1, particle2);
this.maxLength = maxLength;
this.restitution = restitution;
}
override protected function getCurrentLength():Number {
var x:Number = particle1.position.x - particle2.position.x;
var y:Number = particle1.position.y - particle2.position.y;
var z:Number = particle1.position.z - particle2.position.z;
return Math.sqrt(x*x + y*y + z*z);
}
override public function fillContact(contact:ParticleContact):Boolean {
// Для кабеля нормаль контакта направлена в сторону второй частицы
vector.x = particle2.position.x - particle1.position.x;
vector.y = particle2.position.y - particle1.position.y;
vector.z = particle2.position.z - particle1.position.z;
var len:Number = Math.sqrt(vector.x*vector.x + vector.y*vector.y + vector.z*vector.z);
if (len < maxLength) {
return false;
}
contact.particle1 = particle1;
contact.particle2 = particle2;
contact.contactNormal.x = vector.x;
contact.contactNormal.y = vector.y;
contact.contactNormal.z = vector.z;
contact.penetration = len - maxLength;
contact.restitution = restitution;
return true;
}
}
}

View File

@@ -0,0 +1,175 @@
package alternativa.physics.particle {
import alternativa.types.Point3D;
public class ParticleContact {
private static var vector:Point3D = new Point3D();
public var particle1:Particle;
public var particle2:Particle;
public var restitution:Number;
public var penetration:Number;
public var contactNormal:Point3D = new Point3D();
public var isCollision:Boolean;
public var frictionCoeff:Number = 0;
public var next:ParticleContact;
public var index:int;
/**
*
* @param particle1
* @param particle2
* @param restitution
* @param contactNormal
*/
public function ParticleContact(index:int, particle1:Particle = null, particle2:Particle = null, contactNormal:Point3D = null, penetration:Number = 0, restitution:Number = 0) {
this.index = index;
this.particle1 = particle1;
this.particle2 = particle2;
if (contactNormal != null) {
this.contactNormal.x = contactNormal.x;
this.contactNormal.y = contactNormal.y;
this.contactNormal.z = contactNormal.z;
}
this.penetration = penetration;
this.restitution = restitution;
}
/**
*
* @param time
*/
public function resolve(time:Number):void {
resolveVelocity(time);
resolveInterpenetration(time);
}
/**
*
* @return
*/
public function calculateSeparatingVelocity():Number {
vector.x = particle1.velocity.x;
vector.y = particle1.velocity.y;
vector.z = particle1.velocity.z;
if (particle2 != null) {
vector.x -= particle2.velocity.x;
vector.y -= particle2.velocity.y;
vector.z -= particle2.velocity.z;
}
return vector.x*contactNormal.x + vector.y*contactNormal.y + vector.z*contactNormal.z;
}
/**
*
* @param time
*/
private function resolveVelocity(time:Number):void {
var separatingVelocity:Number = calculateSeparatingVelocity();
if (separatingVelocity > 0) {
return;
}
var newSeparatingVelocity:Number = -restitution*separatingVelocity;
var totalInverseMass:Number = particle1.inverseMass;
if (particle2 != null) {
totalInverseMass += particle2.inverseMass;
}
if (totalInverseMass <= 0) {
// Пропускаем, т.к. обе частицы бесконечной массы
return;
}
// Обработка покоящихся контактов
// Проверяем, не была ли относительная скорость приобретена только в течении одного кадра
if (particle2 != null) {
vector.x = particle1.acceleration.x - particle2.acceleration.x;
vector.y = particle1.acceleration.y - particle2.acceleration.y;
vector.z = particle1.acceleration.z - particle2.acceleration.z;
} else {
vector.x = particle1.acceleration.x;
vector.y = particle1.acceleration.y;
vector.z = particle1.acceleration.z;
}
var accCausedSepVelocity:Number = (vector.x*contactNormal.x + vector.y*contactNormal.y + vector.z*contactNormal.z)*time;
if (accCausedSepVelocity < 0) {
// Вычтем скорость сближения, полученную под действием сил во время последнего кадра, из скорости разделения контакта
newSeparatingVelocity += restitution*accCausedSepVelocity;
if (newSeparatingVelocity < 0) {
// Скорость разделения не может быть отрицательной
newSeparatingVelocity = 0;
}
}
var impulse:Number = (newSeparatingVelocity - separatingVelocity)/totalInverseMass;
vector.x = impulse*contactNormal.x;
vector.y = impulse*contactNormal.y;
vector.z = impulse*contactNormal.z;
particle1.velocity.x += vector.x*particle1.inverseMass;
particle1.velocity.y += vector.y*particle1.inverseMass;
particle1.velocity.z += vector.z*particle1.inverseMass;
if (particle2 != null) {
particle2.velocity.x -= vector.x*particle2.inverseMass;
particle2.velocity.y -= vector.y*particle2.inverseMass;
particle2.velocity.z -= vector.z*particle2.inverseMass;
}
// if (isCollision) {
// var tangentSpeed:Number = particle1.velocity.length;
// var deltaSpeed:Number = frictionCoeff*particle1.inverseMass*impulse;
// if (tangentSpeed < deltaSpeed) {
// particle1.velocity.x = 0;
// particle1.velocity.y = 0;
// particle1.velocity.z = 0;
// } else {
// var k:Number = (tangentSpeed - deltaSpeed)/tangentSpeed;
// particle1.velocity.x *= k;
// particle1.velocity.y *= k;
// particle1.velocity.z *= k;
// }
// }
}
/**
*
* @param time
*/
private function resolveInterpenetration(time:Number):void {
if (penetration <= 0) {
return;
}
var totalInverseMass:Number = particle1.inverseMass;
if (particle2 != null) {
totalInverseMass += particle2.inverseMass;
}
if (totalInverseMass <= 0) {
// Пропускаем, т.к. обе частицы бесконечной массы
return;
}
var k:Number = penetration/totalInverseMass;
vector.x = k*contactNormal.x;
vector.y = k*contactNormal.y;
vector.z = k*contactNormal.z;
// trace("\nparticle1.position", particle1.position.z, particle1.velocity.z);
particle1.position.x += vector.x*particle1.inverseMass;
particle1.position.y += vector.y*particle1.inverseMass;
particle1.position.z += vector.z*particle1.inverseMass;
// trace("particle1.position", particle1.position.z, particle1.velocity.z);
if (particle2 != null) {
particle2.position.x -= vector.x*particle2.inverseMass;
particle2.position.y -= vector.y*particle2.inverseMass;
particle2.position.z -= vector.z*particle2.inverseMass;
}
penetration = 0;
}
}
}

View File

@@ -0,0 +1,26 @@
package alternativa.physics.particle {
/**
* Базовй класс для генераторов контактов. Реализует поддержку однонаправленного списока генераторов.
*/
public class ParticleContactGenerator {
/**
* Следующий генератор в списке.
*/
public var next:ParticleContactGenerator;
/**
* Метод создаёт информацию о контактах.
*
* @param contact первый свободный для записи контакт в списке контактов мира
*
* @return следующий свободный для записи контакт после того, как генератор записал свою информацию. Если возвращено значение null,
* то значит список доступных контактов исчерпан.
*/
public function addContacts(contact:ParticleContact):ParticleContact {
return null;
}
}
}

View File

@@ -0,0 +1,87 @@
package alternativa.physics.particle {
public class ParticleContactResolver {
private var iterations:int;
private var iterationsUsed:int;
public function ParticleContactResolver(iterations:int) {
this.iterations = iterations;
}
public function setIterations(value:int):void {
iterations = value;
}
public function resolveContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
resolveAllContacts(contacts, contactsUsed, time);
// resolveCollisionContacts(contacts, contactsUsed, time);
// resolveNonCollisionContacts(contacts, contactsUsed, time);
}
private function resolveAllContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
iterationsUsed = 0;
iterations = 5;
while (iterationsUsed < iterations) {
var maxSepVelocity:Number = 0;
var maxPenetration:Number = 0;
var mostSevereContact:ParticleContact = null;
var contact:ParticleContact = contacts;
while (contact.index < contactsUsed) {
var sepVelocity:Number = contact.calculateSeparatingVelocity();
if (sepVelocity < maxSepVelocity) {
maxSepVelocity = sepVelocity;
mostSevereContact = contact;
}
contact = contact.next;
}
if (mostSevereContact != null) {
mostSevereContact.resolve(time);
}
iterationsUsed++;
}
}
private function resolveNonCollisionContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
iterationsUsed = 0;
while (iterationsUsed < iterations) {
var maxSepVelocity:Number = 0;
var maxPenetration:Number = 0;
var mostSevereContact:ParticleContact = null;
var contact:ParticleContact = contacts;
while (contact.index < contactsUsed) {
if (!contact.isCollision) {
var sepVelocity:Number = contact.calculateSeparatingVelocity();
if (sepVelocity < maxSepVelocity) {
maxSepVelocity = sepVelocity;
mostSevereContact = contact;
}
}
contact = contact.next;
}
if (mostSevereContact != null) {
mostSevereContact.resolve(time);
}
iterationsUsed++;
}
}
private function resolveCollisionContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
var maxPenetration:Number = 0;
var mostSevereContact:ParticleContact = null;
var contact:ParticleContact = contacts;
while (contact.index < contactsUsed) {
if (contact.isCollision && contact.penetration > maxPenetration) {
maxPenetration = contact.penetration;
mostSevereContact = contact;
}
contact = contact.next;
}
if (mostSevereContact != null) {
if (mostSevereContact.calculateSeparatingVelocity() < 0) {
mostSevereContact.resolve(time);
}
}
}
}
}

View File

@@ -0,0 +1,21 @@
package alternativa.physics.particle {
public class ParticleLink {
public var particle1:Particle;
public var particle2:Particle;
public function ParticleLink(particle1:Particle, particle2:Particle) {
this.particle1 = particle1;
this.particle2 = particle2;
}
protected function getCurrentLength():Number {
return 0;
}
public function fillContact(contact:ParticleContact):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,58 @@
package alternativa.physics.particle {
import alternativa.types.Point3D;
public class ParticleRod extends ParticleLink {
private static var vector:Point3D = new Point3D();
public var length:Number;
public function ParticleRod(particle1:Particle, particle2:Particle, length:Number = 0) {
super(particle1, particle2);
if (length <= 0) {
this.length = Point3D.difference(particle1.position, particle2.position).length;
} else {
this.length = length;
}
}
override protected function getCurrentLength():Number {
var x:Number = particle1.position.x - particle2.position.x;
var y:Number = particle1.position.y - particle2.position.y;
var z:Number = particle1.position.z - particle2.position.z;
return Math.sqrt(x*x + y*y + z*z);
}
override public function fillContact(contact:ParticleContact):Boolean {
vector.x = particle1.position.x - particle2.position.x;
vector.y = particle1.position.y - particle2.position.y;
vector.z = particle1.position.z - particle2.position.z;
var len:Number = Math.sqrt(vector.x*vector.x + vector.y*vector.y + vector.z*vector.z);
if (len == length) {
return false;
}
var penetration:Number;
if (len > length) {
vector.x = -vector.x;
vector.y = -vector.y;
vector.z = -vector.z;
penetration = len - length;
} else {
penetration = length - len;
}
contact.particle1 = particle1;
contact.particle2 = particle2;
contact.contactNormal.x = vector.x/len;
contact.contactNormal.y = vector.y/len;
contact.contactNormal.z = vector.z/len;
contact.penetration = penetration;
contact.restitution = 0;
contact.isCollision = false;
return true;
}
}
}

View File

@@ -0,0 +1,113 @@
package alternativa.physics.particle {
import alternativa.physics.particle.Particle;
import alternativa.physics.particle.ParticleContact;
import alternativa.physics.particle.ParticleContactGenerator;
import alternativa.physics.particle.ParticleContactResolver;
import alternativa.physics.registry.ParticleForceRegistry;
public class ParticleWorld {
public var particles:Particle;
public var last:Particle;
public var contacts:ParticleContact;
public var contactGenerators:ParticleContactGenerator;
public var forceRegistry:ParticleForceRegistry;
public var resolver:ParticleContactResolver;
public var maxContacts:int;
public var iterations:int;
public function ParticleWorld(maxContacts:int, iterations:int = 0) {
this.maxContacts = maxContacts;
this.iterations = iterations;
// Создаём контакты
contacts = new ParticleContact(0);
var contact:ParticleContact = contacts;
for (var i:int = 1; i < maxContacts; i++) {
contact = contact.next = new ParticleContact(i);
}
resolver = new ParticleContactResolver(0);
forceRegistry = new ParticleForceRegistry();
}
public function addParticle(particle:Particle):void {
if (particles == null) {
last = particles = particle;
} else {
last.next = particle;
last = particle;
}
}
public function addContactGenerator(generator:ParticleContactGenerator):void {
if (contactGenerators == null) {
contactGenerators = generator;
} else {
var gen:ParticleContactGenerator = contactGenerators;
while (gen.next != null) {
gen = gen.next;
}
gen.next = generator;
}
}
public function startFrame():void {
var particle:Particle = particles;
while (particle != null) {
particle.clearForce();
particle = particle.next;
}
}
/**
* Последоавтельно запускает все зарегистрированные генераторы контактов.
*
* @return количество использованных контактов
*/
public function generateContacts():int {
var contact:ParticleContact = contacts;
var generator:ParticleContactGenerator = contactGenerators;
while (generator != null) {
contact = generator.addContacts(contact);
if (contact == null) {
// Доступные контакты исчерпаны
return maxContacts;
}
generator = generator.next;
}
return contact.index;
}
public function intergrate(time:Number):void {
var particle:Particle = particles;
while (particle != null) {
particle.integrate(time);
particle = particle.next;
}
}
/**
* Запускает физическую симуляцию.
*
* @param time
*/
public function runPhysics(time:Number):void {
// Применение генераторов сил
forceRegistry.updateForces(time);
// Запуск интеграторов для всех частиц
intergrate(time);
// Создание контактов
var contactsUsed:int = generateContacts();
// Обработка полученных контактов
if (iterations == 0) {
resolver.setIterations(3*contactsUsed);
}
resolver.resolveContacts(contacts, contactsUsed, time);
}
}
}

View File

@@ -0,0 +1,78 @@
package alternativa.physics.particle {
import alternativa.types.Point3D;
public class Particle {
private static var resultingAcceleration:Point3D = new Point3D();
public var position:Point3D = new Point3D();
public var velocity:Point3D = new Point3D();
public var acceleration:Point3D = new Point3D();
public var accumulatedForce:Point3D = new Point3D();
public var mass:Number;
public var inverseMass:Number;
public var damping:Number = 0.995;
public var next:Particle;
/**
* @param mass масса частицы. Значение меньшее или равное нулю означает бесконечную массу.
*/
public function Particle(mass:Number = 0) {
if (mass <= 0) {
this.mass = 0;
inverseMass = 0;
} else {
this.mass = mass;
inverseMass = 1/mass;
}
}
/**
*
* @param time время в секундах
*/
public function integrate(time:Number):void {
if (time <= 0) {
return;
}
position.x += velocity.x*time;
position.y += velocity.y*time;
position.z += velocity.z*time;
resultingAcceleration.x = acceleration.x + inverseMass*accumulatedForce.x;
resultingAcceleration.y = acceleration.y + inverseMass*accumulatedForce.y;
resultingAcceleration.z = acceleration.z + inverseMass*accumulatedForce.z;
velocity.x += resultingAcceleration.x*time;
velocity.y += resultingAcceleration.y*time;
velocity.z += resultingAcceleration.z*time;
var d:Number = Math.pow(damping, time);
velocity.x *= d;
velocity.y *= d;
velocity.z *= d;
accumulatedForce.x = 0;
accumulatedForce.y = 0;
accumulatedForce.z = 0;
}
public function clearForce():void {
accumulatedForce.x = 0;
accumulatedForce.y = 0;
accumulatedForce.z = 0;
}
public function addForce(force:Point3D):void {
accumulatedForce.x += force.x;
accumulatedForce.y += force.y;
accumulatedForce.z += force.z;
}
public function hasFiniteMass():Boolean {
return inverseMass > 0;
}
}
}

View File

@@ -0,0 +1,46 @@
package alternativa.physics.particle {
import alternativa.types.Point3D;
public class ParticleCable extends ParticleLink {
private static var vector:Point3D = new Point3D();
public var maxLength:Number;
public var restitution:Number;
public function ParticleCable(particle1:Particle, particle2:Particle, maxLength:Number, restitution:Number) {
super(particle1, particle2);
this.maxLength = maxLength;
this.restitution = restitution;
}
override protected function getCurrentLength():Number {
var x:Number = particle1.position.x - particle2.position.x;
var y:Number = particle1.position.y - particle2.position.y;
var z:Number = particle1.position.z - particle2.position.z;
return Math.sqrt(x*x + y*y + z*z);
}
override public function fillContact(contact:ParticleContact):Boolean {
// Для кабеля нормаль контакта направлена в сторону второй частицы
vector.x = particle2.position.x - particle1.position.x;
vector.y = particle2.position.y - particle1.position.y;
vector.z = particle2.position.z - particle1.position.z;
var len:Number = Math.sqrt(vector.x*vector.x + vector.y*vector.y + vector.z*vector.z);
if (len < maxLength) {
return false;
}
contact.particle1 = particle1;
contact.particle2 = particle2;
contact.contactNormal.x = vector.x;
contact.contactNormal.y = vector.y;
contact.contactNormal.z = vector.z;
contact.penetration = len - maxLength;
contact.restitution = restitution;
return true;
}
}
}

View File

@@ -0,0 +1,175 @@
package alternativa.physics.particle {
import alternativa.types.Point3D;
public class ParticleContact {
private static var vector:Point3D = new Point3D();
public var particle1:Particle;
public var particle2:Particle;
public var restitution:Number;
public var penetration:Number;
public var contactNormal:Point3D = new Point3D();
public var isCollision:Boolean;
public var frictionCoeff:Number = 0;
public var next:ParticleContact;
public var index:int;
/**
*
* @param particle1
* @param particle2
* @param restitution
* @param contactNormal
*/
public function ParticleContact(index:int, particle1:Particle = null, particle2:Particle = null, contactNormal:Point3D = null, penetration:Number = 0, restitution:Number = 0) {
this.index = index;
this.particle1 = particle1;
this.particle2 = particle2;
if (contactNormal != null) {
this.contactNormal.x = contactNormal.x;
this.contactNormal.y = contactNormal.y;
this.contactNormal.z = contactNormal.z;
}
this.penetration = penetration;
this.restitution = restitution;
}
/**
*
* @param time
*/
public function resolve(time:Number):void {
resolveVelocity(time);
resolveInterpenetration(time);
}
/**
*
* @return
*/
public function calculateSeparatingVelocity():Number {
vector.x = particle1.velocity.x;
vector.y = particle1.velocity.y;
vector.z = particle1.velocity.z;
if (particle2 != null) {
vector.x -= particle2.velocity.x;
vector.y -= particle2.velocity.y;
vector.z -= particle2.velocity.z;
}
return vector.x*contactNormal.x + vector.y*contactNormal.y + vector.z*contactNormal.z;
}
/**
*
* @param time
*/
private function resolveVelocity(time:Number):void {
var separatingVelocity:Number = calculateSeparatingVelocity();
if (separatingVelocity > 0) {
return;
}
var newSeparatingVelocity:Number = -restitution*separatingVelocity;
var totalInverseMass:Number = particle1.inverseMass;
if (particle2 != null) {
totalInverseMass += particle2.inverseMass;
}
if (totalInverseMass <= 0) {
// Пропускаем, т.к. обе частицы бесконечной массы
return;
}
// Обработка покоящихся контактов
// Проверяем, не была ли относительная скорость приобретена только в течении одного кадра
if (particle2 != null) {
vector.x = particle1.acceleration.x - particle2.acceleration.x;
vector.y = particle1.acceleration.y - particle2.acceleration.y;
vector.z = particle1.acceleration.z - particle2.acceleration.z;
} else {
vector.x = particle1.acceleration.x;
vector.y = particle1.acceleration.y;
vector.z = particle1.acceleration.z;
}
var accCausedSepVelocity:Number = (vector.x*contactNormal.x + vector.y*contactNormal.y + vector.z*contactNormal.z)*time;
if (accCausedSepVelocity < 0) {
// Вычтем скорость сближения, полученную под действием сил во время последнего кадра, из скорости разделения контакта
newSeparatingVelocity += restitution*accCausedSepVelocity;
if (newSeparatingVelocity < 0) {
// Скорость разделения не может быть отрицательной
newSeparatingVelocity = 0;
}
}
var impulse:Number = (newSeparatingVelocity - separatingVelocity)/totalInverseMass;
vector.x = impulse*contactNormal.x;
vector.y = impulse*contactNormal.y;
vector.z = impulse*contactNormal.z;
particle1.velocity.x += vector.x*particle1.inverseMass;
particle1.velocity.y += vector.y*particle1.inverseMass;
particle1.velocity.z += vector.z*particle1.inverseMass;
if (particle2 != null) {
particle2.velocity.x -= vector.x*particle2.inverseMass;
particle2.velocity.y -= vector.y*particle2.inverseMass;
particle2.velocity.z -= vector.z*particle2.inverseMass;
}
// if (isCollision) {
// var tangentSpeed:Number = particle1.velocity.length;
// var deltaSpeed:Number = frictionCoeff*particle1.inverseMass*impulse;
// if (tangentSpeed < deltaSpeed) {
// particle1.velocity.x = 0;
// particle1.velocity.y = 0;
// particle1.velocity.z = 0;
// } else {
// var k:Number = (tangentSpeed - deltaSpeed)/tangentSpeed;
// particle1.velocity.x *= k;
// particle1.velocity.y *= k;
// particle1.velocity.z *= k;
// }
// }
}
/**
*
* @param time
*/
private function resolveInterpenetration(time:Number):void {
if (penetration <= 0) {
return;
}
var totalInverseMass:Number = particle1.inverseMass;
if (particle2 != null) {
totalInverseMass += particle2.inverseMass;
}
if (totalInverseMass <= 0) {
// Пропускаем, т.к. обе частицы бесконечной массы
return;
}
var k:Number = penetration/totalInverseMass;
vector.x = k*contactNormal.x;
vector.y = k*contactNormal.y;
vector.z = k*contactNormal.z;
// trace("\nparticle1.position", particle1.position.z, particle1.velocity.z);
particle1.position.x += vector.x*particle1.inverseMass;
particle1.position.y += vector.y*particle1.inverseMass;
particle1.position.z += vector.z*particle1.inverseMass;
// trace("particle1.position", particle1.position.z, particle1.velocity.z);
if (particle2 != null) {
particle2.position.x -= vector.x*particle2.inverseMass;
particle2.position.y -= vector.y*particle2.inverseMass;
particle2.position.z -= vector.z*particle2.inverseMass;
}
penetration = 0;
}
}
}

View File

@@ -0,0 +1,26 @@
package alternativa.physics.particle {
/**
* Базовй класс для генераторов контактов. Реализует поддержку однонаправленного списока генераторов.
*/
public class ParticleContactGenerator {
/**
* Следующий генератор в списке.
*/
public var next:ParticleContactGenerator;
/**
* Метод создаёт информацию о контактах.
*
* @param contact первый свободный для записи контакт в списке контактов мира
*
* @return следующий свободный для записи контакт после того, как генератор записал свою информацию. Если возвращено значение null,
* то значит список доступных контактов исчерпан.
*/
public function addContacts(contact:ParticleContact):ParticleContact {
return null;
}
}
}

View File

@@ -0,0 +1,87 @@
package alternativa.physics.particle {
public class ParticleContactResolver {
private var iterations:int;
private var iterationsUsed:int;
public function ParticleContactResolver(iterations:int) {
this.iterations = iterations;
}
public function setIterations(value:int):void {
iterations = value;
}
public function resolveContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
resolveAllContacts(contacts, contactsUsed, time);
// resolveCollisionContacts(contacts, contactsUsed, time);
// resolveNonCollisionContacts(contacts, contactsUsed, time);
}
private function resolveAllContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
iterationsUsed = 0;
iterations = 5;
while (iterationsUsed < iterations) {
var maxSepVelocity:Number = 0;
var maxPenetration:Number = 0;
var mostSevereContact:ParticleContact = null;
var contact:ParticleContact = contacts;
while (contact.index < contactsUsed) {
var sepVelocity:Number = contact.calculateSeparatingVelocity();
if (sepVelocity < maxSepVelocity) {
maxSepVelocity = sepVelocity;
mostSevereContact = contact;
}
contact = contact.next;
}
if (mostSevereContact != null) {
mostSevereContact.resolve(time);
}
iterationsUsed++;
}
}
private function resolveNonCollisionContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
iterationsUsed = 0;
while (iterationsUsed < iterations) {
var maxSepVelocity:Number = 0;
var maxPenetration:Number = 0;
var mostSevereContact:ParticleContact = null;
var contact:ParticleContact = contacts;
while (contact.index < contactsUsed) {
if (!contact.isCollision) {
var sepVelocity:Number = contact.calculateSeparatingVelocity();
if (sepVelocity < maxSepVelocity) {
maxSepVelocity = sepVelocity;
mostSevereContact = contact;
}
}
contact = contact.next;
}
if (mostSevereContact != null) {
mostSevereContact.resolve(time);
}
iterationsUsed++;
}
}
private function resolveCollisionContacts(contacts:ParticleContact, contactsUsed:int, time:Number):void {
var maxPenetration:Number = 0;
var mostSevereContact:ParticleContact = null;
var contact:ParticleContact = contacts;
while (contact.index < contactsUsed) {
if (contact.isCollision && contact.penetration > maxPenetration) {
maxPenetration = contact.penetration;
mostSevereContact = contact;
}
contact = contact.next;
}
if (mostSevereContact != null) {
if (mostSevereContact.calculateSeparatingVelocity() < 0) {
mostSevereContact.resolve(time);
}
}
}
}
}

View File

@@ -0,0 +1,21 @@
package alternativa.physics.particle {
public class ParticleLink {
public var particle1:Particle;
public var particle2:Particle;
public function ParticleLink(particle1:Particle, particle2:Particle) {
this.particle1 = particle1;
this.particle2 = particle2;
}
protected function getCurrentLength():Number {
return 0;
}
public function fillContact(contact:ParticleContact):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,58 @@
package alternativa.physics.particle {
import alternativa.types.Point3D;
public class ParticleRod extends ParticleLink {
private static var vector:Point3D = new Point3D();
public var length:Number;
public function ParticleRod(particle1:Particle, particle2:Particle, length:Number = 0) {
super(particle1, particle2);
if (length <= 0) {
this.length = Point3D.difference(particle1.position, particle2.position).length;
} else {
this.length = length;
}
}
override protected function getCurrentLength():Number {
var x:Number = particle1.position.x - particle2.position.x;
var y:Number = particle1.position.y - particle2.position.y;
var z:Number = particle1.position.z - particle2.position.z;
return Math.sqrt(x*x + y*y + z*z);
}
override public function fillContact(contact:ParticleContact):Boolean {
vector.x = particle1.position.x - particle2.position.x;
vector.y = particle1.position.y - particle2.position.y;
vector.z = particle1.position.z - particle2.position.z;
var len:Number = Math.sqrt(vector.x*vector.x + vector.y*vector.y + vector.z*vector.z);
if (len == length) {
return false;
}
var penetration:Number;
if (len > length) {
vector.x = -vector.x;
vector.y = -vector.y;
vector.z = -vector.z;
penetration = len - length;
} else {
penetration = length - len;
}
contact.particle1 = particle1;
contact.particle2 = particle2;
contact.contactNormal.x = vector.x/len;
contact.contactNormal.y = vector.y/len;
contact.contactNormal.z = vector.z/len;
contact.penetration = penetration;
contact.restitution = 0;
contact.isCollision = false;
return true;
}
}
}

View File

@@ -0,0 +1,113 @@
package alternativa.physics.particle {
import alternativa.physics.particle.Particle;
import alternativa.physics.particle.ParticleContact;
import alternativa.physics.particle.ParticleContactGenerator;
import alternativa.physics.particle.ParticleContactResolver;
import alternativa.physics.registry.ParticleForceRegistry;
public class ParticleWorld {
public var particles:Particle;
public var last:Particle;
public var contacts:ParticleContact;
public var contactGenerators:ParticleContactGenerator;
public var forceRegistry:ParticleForceRegistry;
public var resolver:ParticleContactResolver;
public var maxContacts:int;
public var iterations:int;
public function ParticleWorld(maxContacts:int, iterations:int = 0) {
this.maxContacts = maxContacts;
this.iterations = iterations;
// Создаём контакты
contacts = new ParticleContact(0);
var contact:ParticleContact = contacts;
for (var i:int = 1; i < maxContacts; i++) {
contact = contact.next = new ParticleContact(i);
}
resolver = new ParticleContactResolver(0);
forceRegistry = new ParticleForceRegistry();
}
public function addParticle(particle:Particle):void {
if (particles == null) {
last = particles = particle;
} else {
last.next = particle;
last = particle;
}
}
public function addContactGenerator(generator:ParticleContactGenerator):void {
if (contactGenerators == null) {
contactGenerators = generator;
} else {
var gen:ParticleContactGenerator = contactGenerators;
while (gen.next != null) {
gen = gen.next;
}
gen.next = generator;
}
}
public function startFrame():void {
var particle:Particle = particles;
while (particle != null) {
particle.clearForce();
particle = particle.next;
}
}
/**
* Последоавтельно запускает все зарегистрированные генераторы контактов.
*
* @return количество использованных контактов
*/
public function generateContacts():int {
var contact:ParticleContact = contacts;
var generator:ParticleContactGenerator = contactGenerators;
while (generator != null) {
contact = generator.addContacts(contact);
if (contact == null) {
// Доступные контакты исчерпаны
return maxContacts;
}
generator = generator.next;
}
return contact.index;
}
public function intergrate(time:Number):void {
var particle:Particle = particles;
while (particle != null) {
particle.integrate(time);
particle = particle.next;
}
}
/**
* Запускает физическую симуляцию.
*
* @param time
*/
public function runPhysics(time:Number):void {
// Применение генераторов сил
forceRegistry.updateForces(time);
// Запуск интеграторов для всех частиц
intergrate(time);
// Создание контактов
var contactsUsed:int = generateContacts();
// Обработка полученных контактов
if (iterations == 0) {
resolver.setIterations(3*contactsUsed);
}
resolver.resolveContacts(contacts, contactsUsed, time);
}
}
}

View File

@@ -0,0 +1,29 @@
K 25
svn:wc:ra_dav:version-url
V 103
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry
END
ParticleForceRegistration.as
K 25
svn:wc:ra_dav:version-url
V 132
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry/ParticleForceRegistration.as
END
BodyForceRegistry.as
K 25
svn:wc:ra_dav:version-url
V 124
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry/BodyForceRegistry.as
END
BodyForceRegistration.as
K 25
svn:wc:ra_dav:version-url
V 128
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry/BodyForceRegistration.as
END
ParticleForceRegistry.as
K 25
svn:wc:ra_dav:version-url
V 128
/!svn/ver/4196/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry/ParticleForceRegistry.as
END

View File

@@ -0,0 +1,76 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/registry
http://svndev.alternativaplatform.com
2008-11-24T06:53:44.272574Z
4196
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
ParticleForceRegistration.as
file
2010-10-28T04:34:31.000000Z
eacf3bd4d8f93fcaf786f50cccdc2be2
2008-11-24T06:53:44.272574Z
4196
mike
BodyForceRegistry.as
file
2010-10-28T04:34:31.000000Z
bc7c7f17acaa538a68392942fe5b4ddf
2008-11-24T06:53:44.272574Z
4196
mike
BodyForceRegistration.as
file
2010-10-28T04:34:31.000000Z
8b67908405c7eebf4c91e715ff7e26fd
2008-11-24T06:53:44.272574Z
4196
mike
ParticleForceRegistry.as
file
2010-10-28T04:34:31.000000Z
ed017f73d079580385616a6cf0724645
2008-11-24T06:53:44.272574Z
4196
mike

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,16 @@
package alternativa.physics.registry {
import alternativa.physics.force.IRigidBodyForceGenerator;
import alternativa.physics.rigid.RigidBody;
public class BodyForceRegistration {
public var body:RigidBody;
public var forceGenerator:IRigidBodyForceGenerator;
public var next:BodyForceRegistration;
public function BodyForceRegistration(body:RigidBody, forceGenerator:IRigidBodyForceGenerator) {
this.body = body;
this.forceGenerator = forceGenerator;
}
}
}

View File

@@ -0,0 +1,47 @@
package alternativa.physics.registry {
import alternativa.physics.force.IRigidBodyForceGenerator;
import alternativa.physics.rigid.RigidBody;
public class BodyForceRegistry {
protected var registrations:BodyForceRegistration;
public function BodyForceRegistry() {
}
public function add(body:RigidBody, fg:IRigidBodyForceGenerator):void {
var registration:BodyForceRegistration = new BodyForceRegistration(body, fg);
registration.next = registrations;
registrations = registration;
}
public function remove(body:RigidBody, fg:IRigidBodyForceGenerator):void {
var current:BodyForceRegistration = registrations;
var prev:BodyForceRegistration = null;
while (current != null) {
if (current.body == body && current.forceGenerator == fg) {
if (current == registrations) {
registrations = current.next;
} else {
prev.next = current.next;
}
break;
}
prev = current;
current = current.next;
}
}
public function clear():void {
registrations = null;
}
public function updateForces(time:Number):void {
var current:BodyForceRegistration = registrations;
while (current != null) {
current.forceGenerator.updateForce(current.body, time);
current = current.next;
}
}
}
}

View File

@@ -0,0 +1,16 @@
package alternativa.physics.registry {
import alternativa.physics.force.IParticelForceGenerator;
import alternativa.physics.particle.Particle;
public class ParticleForceRegistration {
public var particle:Particle;
public var forceGenerator:IParticelForceGenerator;
public var next:ParticleForceRegistration;
public function ParticleForceRegistration(particle:Particle, forceGenerator:IParticelForceGenerator) {
this.particle = particle;
this.forceGenerator = forceGenerator;
}
}
}

View File

@@ -0,0 +1,47 @@
package alternativa.physics.registry {
import alternativa.physics.force.IParticelForceGenerator;
import alternativa.physics.particle.Particle;
public class ParticleForceRegistry {
protected var registrations:ParticleForceRegistration;
public function ParticleForceRegistry() {
}
public function add(particle:Particle, fg:IParticelForceGenerator):void {
var registration:ParticleForceRegistration = new ParticleForceRegistration(particle, fg);
registration.next = registrations;
registrations = registration;
}
public function remove(particle:Particle, fg:IParticelForceGenerator):void {
var current:ParticleForceRegistration = registrations;
var prev:ParticleForceRegistration = null;
while (current != null) {
if (current.particle == particle && current.forceGenerator == fg) {
if (current == registrations) {
registrations = current.next;
} else {
prev.next = current.next;
}
break;
}
prev = current;
current = current.next;
}
}
public function clear():void {
registrations = null;
}
public function updateForces(time:Number):void {
var current:ParticleForceRegistration = registrations;
while (current != null) {
current.forceGenerator.updateForce(current.particle, time);
current = current.next;
}
}
}
}

View File

@@ -0,0 +1,16 @@
package alternativa.physics.registry {
import alternativa.physics.force.IRigidBodyForceGenerator;
import alternativa.physics.rigid.RigidBody;
public class BodyForceRegistration {
public var body:RigidBody;
public var forceGenerator:IRigidBodyForceGenerator;
public var next:BodyForceRegistration;
public function BodyForceRegistration(body:RigidBody, forceGenerator:IRigidBodyForceGenerator) {
this.body = body;
this.forceGenerator = forceGenerator;
}
}
}

View File

@@ -0,0 +1,47 @@
package alternativa.physics.registry {
import alternativa.physics.force.IRigidBodyForceGenerator;
import alternativa.physics.rigid.RigidBody;
public class BodyForceRegistry {
protected var registrations:BodyForceRegistration;
public function BodyForceRegistry() {
}
public function add(body:RigidBody, fg:IRigidBodyForceGenerator):void {
var registration:BodyForceRegistration = new BodyForceRegistration(body, fg);
registration.next = registrations;
registrations = registration;
}
public function remove(body:RigidBody, fg:IRigidBodyForceGenerator):void {
var current:BodyForceRegistration = registrations;
var prev:BodyForceRegistration = null;
while (current != null) {
if (current.body == body && current.forceGenerator == fg) {
if (current == registrations) {
registrations = current.next;
} else {
prev.next = current.next;
}
break;
}
prev = current;
current = current.next;
}
}
public function clear():void {
registrations = null;
}
public function updateForces(time:Number):void {
var current:BodyForceRegistration = registrations;
while (current != null) {
current.forceGenerator.updateForce(current.body, time);
current = current.next;
}
}
}
}

View File

@@ -0,0 +1,16 @@
package alternativa.physics.registry {
import alternativa.physics.force.IParticelForceGenerator;
import alternativa.physics.particle.Particle;
public class ParticleForceRegistration {
public var particle:Particle;
public var forceGenerator:IParticelForceGenerator;
public var next:ParticleForceRegistration;
public function ParticleForceRegistration(particle:Particle, forceGenerator:IParticelForceGenerator) {
this.particle = particle;
this.forceGenerator = forceGenerator;
}
}
}

View File

@@ -0,0 +1,47 @@
package alternativa.physics.registry {
import alternativa.physics.force.IParticelForceGenerator;
import alternativa.physics.particle.Particle;
public class ParticleForceRegistry {
protected var registrations:ParticleForceRegistration;
public function ParticleForceRegistry() {
}
public function add(particle:Particle, fg:IParticelForceGenerator):void {
var registration:ParticleForceRegistration = new ParticleForceRegistration(particle, fg);
registration.next = registrations;
registrations = registration;
}
public function remove(particle:Particle, fg:IParticelForceGenerator):void {
var current:ParticleForceRegistration = registrations;
var prev:ParticleForceRegistration = null;
while (current != null) {
if (current.particle == particle && current.forceGenerator == fg) {
if (current == registrations) {
registrations = current.next;
} else {
prev.next = current.next;
}
break;
}
prev = current;
current = current.next;
}
}
public function clear():void {
registrations = null;
}
public function updateForces(time:Number):void {
var current:ParticleForceRegistration = registrations;
while (current != null) {
current.forceGenerator.updateForce(current.particle, time);
current = current.next;
}
}
}
}

View File

@@ -0,0 +1,47 @@
K 25
svn:wc:ra_dav:version-url
V 100
/!svn/ver/5193/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid
END
RigidBodyContactGenerator.as
K 25
svn:wc:ra_dav:version-url
V 129
/!svn/ver/4732/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/RigidBodyContactGenerator.as
END
BodyForceRegistry.as
K 25
svn:wc:ra_dav:version-url
V 121
/!svn/ver/4375/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/BodyForceRegistry.as
END
RigidWorld.as
K 25
svn:wc:ra_dav:version-url
V 114
/!svn/ver/4741/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/RigidWorld.as
END
BodyForceRegistration.as
K 25
svn:wc:ra_dav:version-url
V 125
/!svn/ver/4375/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/BodyForceRegistration.as
END
RigidBodyContact.as
K 25
svn:wc:ra_dav:version-url
V 120
/!svn/ver/4813/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/RigidBodyContact.as
END
RigidBodyContactResolver.as
K 25
svn:wc:ra_dav:version-url
V 128
/!svn/ver/4270/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/RigidBodyContactResolver.as
END
RigidBody.as
K 25
svn:wc:ra_dav:version-url
V 113
/!svn/ver/5193/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/RigidBody.as
END

View File

@@ -0,0 +1,115 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid
http://svndev.alternativaplatform.com
2008-12-17T11:38:42.040147Z
5193
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
RigidBodyContactGenerator.as
file
2010-10-28T04:34:31.000000Z
577dac0a54d7d990734d27170ddf1f14
2008-12-03T05:15:16.843707Z
4732
mike
BodyForceRegistry.as
file
2010-10-28T04:34:31.000000Z
e2125f3fa80a45e6e3a304c33e5cbd47
2008-11-26T12:04:03.108808Z
4375
mike
RigidWorld.as
file
2010-10-28T04:34:31.000000Z
0031086a4c4b3f39f81a0f224ad8560a
2008-12-03T08:31:09.097064Z
4741
mike
generators
dir
BodyForceRegistration.as
file
2010-10-28T04:34:31.000000Z
bed2d6357d27f7311216db6acd156c4e
2008-11-26T12:04:03.108808Z
4375
mike
RigidBodyContact.as
file
2010-10-28T04:34:31.000000Z
473e71d1b8a62f4f9bcd94c2e38d26d6
2008-12-10T13:18:05.069708Z
4813
mike
RigidBodyContactResolver.as
file
2010-10-28T04:34:31.000000Z
ad30ebb588f523d3255ce0a768a39b40
2008-11-25T04:51:53.783696Z
4270
mike
RigidBody.as
file
2010-10-28T04:34:31.000000Z
8f0e9d6652c8f023b9c0b7a78135030d
2008-12-17T11:38:42.040147Z
5193
mike

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,16 @@
package alternativa.physics.rigid {
import alternativa.physics.force.IRigidBodyForceGenerator;
import alternativa.physics.rigid.RigidBody;
public class BodyForceRegistration {
public var body:RigidBody;
public var forceGenerator:IRigidBodyForceGenerator;
public var next:BodyForceRegistration;
public function BodyForceRegistration(body:RigidBody, forceGenerator:IRigidBodyForceGenerator) {
this.body = body;
this.forceGenerator = forceGenerator;
}
}
}

View File

@@ -0,0 +1,47 @@
package alternativa.physics.rigid {
import alternativa.physics.force.IRigidBodyForceGenerator;
import alternativa.physics.rigid.RigidBody;
public class BodyForceRegistry {
protected var registrations:BodyForceRegistration;
public function BodyForceRegistry() {
}
public function add(body:RigidBody, fg:IRigidBodyForceGenerator):void {
var registration:BodyForceRegistration = new BodyForceRegistration(body, fg);
registration.next = registrations;
registrations = registration;
}
public function remove(body:RigidBody, fg:IRigidBodyForceGenerator):void {
var current:BodyForceRegistration = registrations;
var prev:BodyForceRegistration = null;
while (current != null) {
if (current.body == body && current.forceGenerator == fg) {
if (current == registrations) {
registrations = current.next;
} else {
prev.next = current.next;
}
break;
}
prev = current;
current = current.next;
}
}
public function clear():void {
registrations = null;
}
public function updateForces(time:Number):void {
var current:BodyForceRegistration = registrations;
while (current != null) {
current.forceGenerator.updateForce(current.body, time);
current = current.next;
}
}
}
}

View File

@@ -0,0 +1,707 @@
package alternativa.physics.rigid {
import alternativa.physics.altphysics;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
import alternativa.types.Quaternion;
use namespace altphysics;
/**
* Класс представляет твёрдое тело.
*/
public class RigidBody {
private var angularAcceleration:Point3D = new Point3D();
private var motionThreshold:Number = 0.1;
private var motionThreshold2:Number = 2*motionThreshold;
private var baseBias:Number = 0.8;
private var name:String;
public var next:RigidBody;
public var prev:RigidBody;
altphysics var mass:Number;
altphysics var inverseMass:Number;
altphysics var inverseInertiaTensor:Matrix3D = new Matrix3D();
altphysics var linearDamping:Number = 0.95;
altphysics var angularDamping:Number = 0.95;
altphysics var awake:Boolean = true;
altphysics var canSleep:Boolean = true;
altphysics var acceleration:Point3D = new Point3D();
altphysics var angularVelocity:Point3D = new Point3D();
altphysics var position:Point3D = new Point3D();
altphysics var orientation:Quaternion = new Quaternion(1, 0, 0, 0);
altphysics var velocity:Point3D = new Point3D();
altphysics var rotation:Point3D = new Point3D();
altphysics var lastFrameAcceleration:Point3D = new Point3D();
altphysics var transformMatrix:Matrix3D = new Matrix3D();
altphysics var inverseInertiaTensorWorld:Matrix3D = new Matrix3D();
altphysics var forceAccum:Point3D = new Point3D();
altphysics var torqueAccum:Point3D = new Point3D();
altphysics var motion:Number = 10;
/**
* Создаёт новый экзепляр твёрдого тела.
*
* @param inverseMass величина, обратная массе тела. Значение 0 указывает, что тело имеет имеет бесконечную массу и момент инерции.
* @param inverseInertiaTensor обратный тензор момента инерции тела. Значение <code>null</code> указывает, что тело имеет бесконечную массу и момент инерции.
*/
public function RigidBody(inverseMass:Number = 1, inverseInertiaTensor:Matrix3D = null) {
if (inverseMass == 0 || inverseInertiaTensor == null) {
setInfinteMass();
} else {
this.inverseMass = inverseMass;
this.mass = 1/inverseMass;
this.inverseInertiaTensor.copy(inverseInertiaTensor);
}
}
/**
* @return имя тела
*/
public function getName():String {
return name;
}
/**
* Устанавливает имя тела.
*
* @param value имя тела
*/
public function setName(value:String):void {
name = value;
}
/**
* Предельное количество движения, ниже которого тело усыпляеся.
*/
public function getMotionThreshold():Number {
return motionThreshold;
}
/**
* @private
*/
public function setMotionThreshold(value:Number):void {
motionThreshold = value;
motionThreshold2 = value*2;
}
/**
*
* @param value
*/
public function setBaseBias(value:Number):void {
baseBias = value;
}
/**
*
* @param value
*/
public function getBaseBias():Number {
return baseBias;
}
/**
*
*/
public function getMass():Number {
return mass;
}
/**
*
*/
public function setMass(value:Number):void {
if (value <= 0) {
inverseMass = 0;
mass = 0;
} else {
mass = value;
inverseMass = 1/value;
}
}
/**
*
*/
public function getInverseMass():Number {
return inverseMass;
}
/**
* @private
*/
public function setInverseMass(value:Number):void {
if (value > 0) {
inverseMass = value;
mass = 1/value;
} else {
inverseMass = 0;
mass = 0;
}
}
/**
*
* @param m
*/
public function setIntertiaTensor(intertiaTensor:Matrix3D):void {
inverseInertiaTensor.copy(intertiaTensor);
inverseInertiaTensor.invert();
}
/**
*
* @param intertiaTensor
*/
public function getIntertiaTensor(intertiaTensor:Matrix3D):void {
intertiaTensor.copy(inverseInertiaTensor);
intertiaTensor.invert();
}
/**
*
* @param inverseInertiaTensor
*/
public function setInverseIntertiaTensor(inverseInertiaTensor:Matrix3D):void {
this.inverseInertiaTensor.copy(inverseInertiaTensor);
}
/**
*
* @param inverseInertiaTensor
*/
public function getInverseIntertiaTensor(inverseInertiaTensor:Matrix3D):void {
inverseInertiaTensor.copy(this.inverseInertiaTensor);
}
/**
*
*/
public function setInfinteMass():void {
inverseMass = 0;
mass = 0;
inverseInertiaTensor.toIdentity();
inverseInertiaTensor.a = inverseInertiaTensor.f = inverseInertiaTensor.k = 0;
}
/**
*
* @param linearDamping
* @param angularDamping
*/
public function setDamping(linearDamping:Number, angularDamping:Number):void {
this.linearDamping = linearDamping;
this.angularDamping = angularDamping;
}
/**
*
* @param value
*/
public function setLinearDamping(value:Number):void {
linearDamping = value;
}
/**
*
* @return
*/
public function getLinearDamping():Number {
return linearDamping;
}
/**
*
* @param value
*/
public function setAngularDamping(value:Number):void {
angularDamping = value;
}
/**
*
* @return
*/
public function getAngularDamping():Number {
return angularDamping;
}
/**
*
* @param position
*/
public function setPosition(position:Point3D):void {
this.position.x = position.x;
this.position.y = position.y;
this.position.z = position.z;
}
/**
*
* @param x
* @param y
* @param z
*/
public function setPositionComponents(x:Number, y:Number, z:Number):void {
position.x = x;
position.y = y;
position.z = z;
}
/**
*
* @param position
*/
public function getPosition(position:Point3D):void {
position.x = this.position.x;
position.y = this.position.y;
position.z = this.position.z;
}
/**
*
* @param orientation
*/
public function setOrientation(orientation:Quaternion):void {
this.orientation.copy(orientation);
this.orientation.normalize();
}
/**
*
* @param r
* @param i
* @param j
* @param k
*/
public function setOrientationComponents(w:Number, x:Number, y:Number, z:Number):void {
orientation.reset(w, x, y, z);
orientation.normalize();
}
/**
*
* @param orientation
*/
public function getOrientation(orientation:Quaternion):void {
orientation.copy(this.orientation);
}
/**
*
* @param matrix
*/
public function getTransformMatrix(matrix:Matrix3D):void {
matrix.copy(transformMatrix);
}
/**
*
* @param velocity
*/
public function setVelocity(velocity:Point3D):void {
this.velocity.copy(velocity);
}
/**
*
* @param x
* @param y
* @param z
*/
public function setVelocityComponents(x:Number, y:Number, z:Number):void {
velocity.reset(x, y, z);
}
/**
*
* @param velocity
*/
public function getVelocity(velocity:Point3D):void {
velocity.copy(this.velocity);
}
/**
*
* @param acceleration
*
*/
public function setAcceleration(acceleration:Point3D):void {
this.acceleration.copy(acceleration);
}
/**
*
* @param x
* @param y
* @param z
*
*/
public function setAccelerationComponents(x:Number, y:Number, z:Number):void {
acceleration.reset(x, y, z);
}
/**
*
* @param acceleration
*
*/
public function getAcceleration(acceleration:Point3D):void {
acceleration.copy(this.acceleration);
}
/**
*
* @param rotation
*
*/
public function setRotation(rotation:Point3D):void {
this.rotation.copy(rotation);
}
/**
*
* @param x
* @param y
* @param z
*
*/
public function setRotationComponents(x:Number, y:Number, z:Number):void {
rotation.reset(x, y, z);
}
/**
*
* @param rotation
*
*/
public function addRotation(deltaRotation:Point3D):void {
rotation.add(deltaRotation);
}
/**
*
* @param rotation
*
*/
public function getRotation(rotation:Point3D):void {
rotation.copy(this.rotation);
}
/**
*
* @param angularVelocity
*/
public function setAngularVelocity(angularVelocity:Point3D):void {
this.angularVelocity.x = angularVelocity.x;
this.angularVelocity.y = angularVelocity.y;
this.angularVelocity.z = angularVelocity.z;
}
/**
*
* @param x
* @param y
* @param z
*/
public function setAngularVelocityComponents(x:Number, y:Number, z:Number):void {
angularVelocity.x = x;
angularVelocity.y = y;
angularVelocity.z = z;
}
/**
*
* @param awake
*/
public function setAwake(awake:Boolean = true):void {
this.awake = awake;
if (awake) {
motion = motionThreshold2;
} else {
velocity.x = 0;
velocity.y = 0;
velocity.z = 0;
rotation.x = 0;
rotation.y = 0;
rotation.z = 0;
}
}
/**
*
* @return
*/
public function isAwake():Boolean {
return awake;
}
/**
*
* @param canSleep
*/
public function setCanSleep(canSleep:Boolean = true):void {
this.canSleep = canSleep;
if (!canSleep && !awake) {
setAwake();
}
}
/**
*
* @return
*/
public function isCanSleep():Boolean {
return canSleep;
}
/**
* Добавляет глобальную силу, приложенную к центру масс тела.
*
* @param force вектор силы в глобальной системе координат
*/
public function addForce(force:Point3D):void {
forceAccum.x += force.x;
forceAccum.y += force.y;
forceAccum.z += force.z;
setAwake();
}
/**
*
* @param force
*/
public function addForceComponents(x:Number, y:Number, z:Number):void {
forceAccum.x += x;
forceAccum.y += y;
forceAccum.z += z;
setAwake();
}
/**
* Добавляет глобальную силу, приложенную к заданной локальной точке тела.
*
* @param force вектор силы в глобальной системе координат
* @param localPoint локальные координаты точки приложения силы
*/
public function addForceAtLocalPoint(force:Point3D, localPoint:Point3D):void {
forceAccum.x += force.x;
forceAccum.y += force.y;
forceAccum.z += force.z;
// Трансформация локальных координат в глобальные
var x:Number = transformMatrix.a*localPoint.x + transformMatrix.b*localPoint.y + transformMatrix.c*localPoint.z;
var y:Number = transformMatrix.e*localPoint.x + transformMatrix.f*localPoint.y + transformMatrix.g*localPoint.z;
var z:Number = transformMatrix.i*localPoint.x + transformMatrix.j*localPoint.y + transformMatrix.k*localPoint.z;
torqueAccum.x += y*force.z - z*force.y;
torqueAccum.y += z*force.x - x*force.z;
torqueAccum.z += x*force.y - y*force.x;
setAwake();
}
/**
* Добавляет глобальную силу, приложенную к заданной точке тела, выраженной в глобальных координатах.
*
* @param force вектор силы в глобальной системе координат
* @param point глобальные координаты точки приложения силы
*/
public function addForceAtPoint(force:Point3D, point:Point3D):void {
forceAccum.x += force.x;
forceAccum.y += force.y;
forceAccum.z += force.z;
var x:Number = point.x - position.x;
var y:Number = point.y - position.y;
var z:Number = point.z - position.z;
torqueAccum.x += y*force.z - z*force.y;
torqueAccum.y += z*force.x - x*force.z;
torqueAccum.z += x*force.y - y*force.x;
setAwake();
}
/**
* Добавляет локальную силу, приложенную в заданной локальной точке тела.
*
* @param localForce вектор локальной силы
* @param localPoint локальные координаты точки приложения силы
*/
public function addLocalForce(localForce:Point3D, localPoint:Point3D):void {
// Трансформация локального вектора силы в глобальный
var globalForceX:Number = transformMatrix.a*localForce.x + transformMatrix.b*localForce.y + transformMatrix.c*localForce.z;
var globalForceY:Number = transformMatrix.e*localForce.x + transformMatrix.f*localForce.y + transformMatrix.g*localForce.z;
var globalForceZ:Number = transformMatrix.i*localForce.x + transformMatrix.j*localForce.y + transformMatrix.k*localForce.z;
forceAccum.x += globalForceX;
forceAccum.y += globalForceY;
forceAccum.z += globalForceZ;
// Получение глобального вектора плеча силы
var globalPointX:Number = transformMatrix.a*localPoint.x + transformMatrix.b*localPoint.y + transformMatrix.c*localPoint.z;
var globalPointY:Number = transformMatrix.e*localPoint.x + transformMatrix.f*localPoint.y + transformMatrix.g*localPoint.z;
var globalPointZ:Number = transformMatrix.i*localPoint.x + transformMatrix.j*localPoint.y + transformMatrix.k*localPoint.z;
// Приращение момента силы
torqueAccum.x += globalPointY*globalForceZ - globalPointZ*globalForceY;
torqueAccum.y += globalPointZ*globalForceX - globalPointX*globalForceZ;
torqueAccum.z += globalPointX*globalForceY - globalPointY*globalForceX;
setAwake();
}
/**
* Добавляет момент силы.
*
* @param torque вектор момента силы
*/
public function addTorque(torque:Point3D):void {
torqueAccum.x += torque.x;
torqueAccum.y += torque.y;
torqueAccum.z += torque.z;
setAwake();
}
/**
* Добавляет момент силы, заданный покомпонентно.
*/
public function addTorqueComponents(x:Number, y:Number, z:Number):void {
torqueAccum.x += x;
torqueAccum.y += y;
torqueAccum.z += z;
setAwake();
}
/**
* Вычисляет векторные параметры тела в глобальной системе координат.
*/
public function calculateDerivedData():void {
// inlined position.normalize()
var d:Number = orientation.w*orientation.w + orientation.x*orientation.x + orientation.y*orientation.y + orientation.z*orientation.z;
if (d == 0) {
orientation.w = 1;
} else {
d = 1/Math.sqrt(d);
orientation.w *= d;
orientation.x *= d;
orientation.y *= d;
orientation.z *= d;
}
// Вычисление глобальной матрицы трансформации
orientation.toMatrix3D(transformMatrix);
transformMatrix.d = position.x;
transformMatrix.h = position.y;
transformMatrix.l = position.z;
// Расчёт обратного тензора инерции в мировых координатах
var a:Number = transformMatrix.a*inverseInertiaTensor.a + transformMatrix.b*inverseInertiaTensor.e + transformMatrix.c*inverseInertiaTensor.i;
var b:Number = transformMatrix.a*inverseInertiaTensor.b + transformMatrix.b*inverseInertiaTensor.f + transformMatrix.c*inverseInertiaTensor.j;
var c:Number = transformMatrix.a*inverseInertiaTensor.c + transformMatrix.b*inverseInertiaTensor.g + transformMatrix.c*inverseInertiaTensor.k;
var e:Number = transformMatrix.e*inverseInertiaTensor.a + transformMatrix.f*inverseInertiaTensor.e + transformMatrix.g*inverseInertiaTensor.i;
var f:Number = transformMatrix.e*inverseInertiaTensor.b + transformMatrix.f*inverseInertiaTensor.f + transformMatrix.g*inverseInertiaTensor.j;
var g:Number = transformMatrix.e*inverseInertiaTensor.c + transformMatrix.f*inverseInertiaTensor.g + transformMatrix.g*inverseInertiaTensor.k;
var i:Number = transformMatrix.i*inverseInertiaTensor.a + transformMatrix.j*inverseInertiaTensor.e + transformMatrix.k*inverseInertiaTensor.i;
var j:Number = transformMatrix.i*inverseInertiaTensor.b + transformMatrix.j*inverseInertiaTensor.f + transformMatrix.k*inverseInertiaTensor.j;
var k:Number = transformMatrix.i*inverseInertiaTensor.c + transformMatrix.j*inverseInertiaTensor.g + transformMatrix.k*inverseInertiaTensor.k;
// Здесь подразумевается, что матрица трансформации тела не содержит масштабирования
// Произведение [transformMatrix*inverseInertiaTensor]*transpose(transformMatrix)
inverseInertiaTensorWorld.a = a*transformMatrix.a + b*transformMatrix.b + c*transformMatrix.c;
inverseInertiaTensorWorld.b = a*transformMatrix.e + b*transformMatrix.f + c*transformMatrix.g;
inverseInertiaTensorWorld.c = a*transformMatrix.i + b*transformMatrix.j + c*transformMatrix.k;
inverseInertiaTensorWorld.e = e*transformMatrix.a + f*transformMatrix.b + g*transformMatrix.c;
inverseInertiaTensorWorld.f = e*transformMatrix.e + f*transformMatrix.f + g*transformMatrix.g;
inverseInertiaTensorWorld.g = e*transformMatrix.i + f*transformMatrix.j + g*transformMatrix.k;
inverseInertiaTensorWorld.i = i*transformMatrix.a + j*transformMatrix.b + k*transformMatrix.c;
inverseInertiaTensorWorld.j = i*transformMatrix.e + j*transformMatrix.f + k*transformMatrix.g;
inverseInertiaTensorWorld.k = i*transformMatrix.i + j*transformMatrix.j + k*transformMatrix.k;
}
/**
* Интегрирует динамические характеристики и обновляет параметры состояния тела.
*
* @param time временной шаг интегрирования
*/
public function integrate(time:Number):void {
if (!awake) {
return;
}
lastFrameAcceleration.x = acceleration.x + forceAccum.x*inverseMass;
lastFrameAcceleration.y = acceleration.y + forceAccum.y*inverseMass;
lastFrameAcceleration.z = acceleration.z + forceAccum.z*inverseMass;
// angularAcceleration = inverseInertiaTensorWorld*torqueAccum
angularAcceleration.x = inverseInertiaTensorWorld.a*torqueAccum.x + inverseInertiaTensorWorld.b*torqueAccum.y + inverseInertiaTensorWorld.c*torqueAccum.z;
angularAcceleration.y = inverseInertiaTensorWorld.e*torqueAccum.x + inverseInertiaTensorWorld.f*torqueAccum.y + inverseInertiaTensorWorld.g*torqueAccum.z;
angularAcceleration.z = inverseInertiaTensorWorld.i*torqueAccum.x + inverseInertiaTensorWorld.j*torqueAccum.y + inverseInertiaTensorWorld.k*torqueAccum.z;
var d:Number = Math.pow(linearDamping, time);
var dtime:Number = d*time;
velocity.x += lastFrameAcceleration.x*dtime;
velocity.y += lastFrameAcceleration.y*dtime;
velocity.z += lastFrameAcceleration.z*dtime;
rotation.x += angularAcceleration.x*dtime;
rotation.y += angularAcceleration.y*dtime;
rotation.z += angularAcceleration.z*dtime;
rotation.x += angularVelocity.x*time;
rotation.y += angularVelocity.y*time;
rotation.z += angularVelocity.z*time;
position.x += velocity.x*time;
position.y += velocity.y*time;
position.z += velocity.z*time;
orientation.addScaledVector(rotation, time);
// TODO: Нужно ли двойное уменьшение скоростей?
velocity.x *= d;
velocity.y *= d;
velocity.z *= d;
rotation.x *= d;
rotation.y *= d;
rotation.z *= d;
calculateDerivedData();
// inlined clearAccumulators()
forceAccum.x = 0;
forceAccum.y = 0;
forceAccum.z = 0;
torqueAccum.x = 0;
torqueAccum.y = 0;
torqueAccum.z = 0;
// Усыпление тела при необходимости
if (canSleep) {
var currentMotion:Number = velocity.x*velocity.x + velocity.y*velocity.y + velocity.z*velocity.z +
rotation.x*rotation.x + rotation.y*rotation.y + rotation.z*rotation.z;
var bias:Number = Math.pow(baseBias, time);
motion = bias*motion + (1 - bias)*currentMotion;
// trace("[RigidBody::integrate]", name, "motion", motion, "motionEpsilon", motionEpsilon);
if (motion < motionThreshold) {
setAwake(false);
} else {
if (motion > motionThreshold2) {
motion = motionThreshold2;
}
}
}
}
/**
*
*/
public function clearAccumulators():void {
forceAccum.x = 0;
forceAccum.y = 0;
forceAccum.z = 0;
torqueAccum.x = 0;
torqueAccum.y = 0;
torqueAccum.z = 0;
}
}
}

View File

@@ -0,0 +1,550 @@
package alternativa.physics.rigid {
import alternativa.physics.*;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
use namespace altphysics;
/**
* Класс описывает контакт между телами. Контакты могут составлять двунаправленный список.
*/
public class RigidBodyContact {
private var tmpVelocityVector:Point3D = new Point3D();
private var impulse:Point3D = new Point3D();
public static var velocityLimit:Number = 1;
/**
* Ссылка на следующий контакт в списке.
*/
public var next:RigidBodyContact;
/**
* Ссылка на предыдущий контакт в списке.
*/
public var prev:RigidBodyContact;
/**
* Индекс контакта в списке.
*/
public var index:int;
/**
* Первое тело, к которому относится контакт.
*/
public var body1:RigidBody;
/**
* Первое тело, к которому относится контакт (может быть null).
*/
public var body2:RigidBody;
/**
* Точка контакта.
*/
public var contactPoint:Point3D = new Point3D();
/**
* Нормаль контакта. Нормаль направлена от второго тела к первому. В локальной системе координат контакта
* нормаль является базисным вектором, задающим направление оси X.
*/
public var contactNormal:Point3D = new Point3D();
/**
* Коэффициент взаимного трения в точке контакта.
*/
public var friction:Number = 0;
/**
* Коэффициент упругости удара.
*/
public var restitution:Number = 0;
/**
* Глубина взаимного проникновения тел в точке контакта.
*/
public var penetration:Number;
public var timeStamp:int;
/**
* Матрица перехода из локальной системы координат контакта в мировую систему координат без компонентов параллельного переноса.
* Матрица задаёт базис локальной системы координат контакта.
*/
altphysics var contactToWorld:Matrix3D = new Matrix3D();
/**
* Относительная скорость в точке контакта. Вычисляется в методе calculateInternals().
*/
altphysics var contactVelocity:Point3D = new Point3D();
/**
* Приращение скорости в точке контакта по направлению нормали, необходимое для прекращения сближения. Вычисляется в методе calculateInternals().
*/
altphysics var desiredDeltaVelocity:Number;
/**
* Координаты точки контакта относительныо первого тела. Вычисляется в методе calculateInternals().
*/
altphysics var relativeContactPosition1:Point3D = new Point3D();
/**
* Координаты точки контакта относительныо второго тела. Вычисляется в методе calculateInternals().
*/
altphysics var relativeContactPosition2:Point3D = new Point3D();
/**
*
* @param index
*/
public function RigidBodyContact(index:int) {
this.index = index;
}
/**
*
* @param body1
* @param body2
* @param friction
* @param restitution
*/
public function setBodyData(body1:RigidBody, body2:RigidBody, friction:Number, restitution:Number):void {
this.body1 = body1;
this.body2 = body2;
this.friction = friction;
this.restitution = restitution;
}
/**
*
*/
public function matchAwakeState():void {
if (body2 == null) {
return;
}
if (body1.awake != body2.awake) {
if (body1.awake) {
body2.setAwake();
} else {
body1.setAwake();
}
}
}
/**
* Вычисляет и сохраняет базис локальной системы координат контакта.
*/
public function calculateContactBasis():void {
// Нормаль является ортом X локальной системы координат
contactToWorld.a = contactNormal.x;
contactToWorld.e = contactNormal.y;
contactToWorld.i = contactNormal.z;
var nx:Number = contactNormal.x > 0 ? contactNormal.x : -contactNormal.x;
var ny:Number = contactNormal.y > 0 ? contactNormal.y : -contactNormal.y;
var s:Number;
if (nx > ny) {
// Нормаль ближе к глобальной оси X, используем ось Y для вычислений. Локальный орт Y находится как результат векторного произведения globalY x contactNormal.
s = 1/Math.sqrt(nx*nx + contactNormal.z*contactNormal.z);
contactToWorld.b = contactNormal.z*s;
contactToWorld.f = 0;
contactToWorld.j = -contactNormal.x*s;
// Локальный орт Z находится как результат векторного произведения contactNormal x localY.
contactToWorld.c = contactNormal.y*contactToWorld.j;
contactToWorld.g = contactNormal.z*contactToWorld.b - contactNormal.x*contactToWorld.j;
contactToWorld.k = -contactNormal.y*contactToWorld.b;
} else {
// Нормаль ближе к глобальной оси Y, используем ось X для вычислений. Локальный орт Y находится как результат векторного произведения globalX x contactNormal.
s = 1/Math.sqrt(ny*ny + contactNormal.z*contactNormal.z);
contactToWorld.b = 0;
contactToWorld.f = -contactNormal.z*s;
contactToWorld.j = contactNormal.y*s;
// Локальный орт Z находится как результат векторного произведения contactNormal x localY.
contactToWorld.c = contactNormal.y*contactToWorld.j - contactNormal.z*contactToWorld.f;
contactToWorld.g = -contactNormal.x*contactToWorld.j;
contactToWorld.k = contactNormal.x*contactToWorld.f;
}
}
/**
* Вычисляет скорость точки заданного тела в точке контакта в его локальной системе координат.
*
* @param body тело, для которого выполняются вычисления
* @param relativeContactPosition положение точки контакта относительно тела
* @param time временной шаг
* @param result в эту переменную записывается результат
*/
public function calculateLocalVelocity(body:RigidBody, relativeContactPosition:Point3D, time:Number, result:Point3D):void {
// Расчёт прироста скорости за последний кадр от действующих сил
var x:Number = body.lastFrameAcceleration.x*time;
var y:Number = body.lastFrameAcceleration.y*time;
var z:Number = body.lastFrameAcceleration.z*time;
// Прирост скорости в системе координат контакта
// Убираем прирост скорости в направлении контакта
var accVelocityY:Number = contactToWorld.b*x + contactToWorld.f*y + contactToWorld.j*z;
var accVelocityZ:Number = contactToWorld.c*x + contactToWorld.g*y + contactToWorld.k*z;
// Скорость точки контакта в мировой системе координат
// Скорость за счёт вращения тела
var rotation:Point3D = body.rotation;
x = rotation.y*relativeContactPosition.z - rotation.z*relativeContactPosition.y + body.velocity.x;
y = rotation.z*relativeContactPosition.x - rotation.x*relativeContactPosition.z + body.velocity.y;
z = rotation.x*relativeContactPosition.y - rotation.y*relativeContactPosition.x + body.velocity.z;
// Скорость точки контакта в системе координат контакта
// Касательные компоненты прироста скорости добавляются к скорости точки контакта. В дальнейшем они либо уберутся силой трения,
// либо уменьшатся ею.
result.x = contactToWorld.a*x + contactToWorld.e*y + contactToWorld.i*z;
result.y = contactToWorld.b*x + contactToWorld.f*y + contactToWorld.j*z + accVelocityY;
result.z = contactToWorld.c*x + contactToWorld.g*y + contactToWorld.k*z + accVelocityZ;
}
/**
* Вычисляет и устанавливает внутреннее значение для требуемого изменения относительной скорости вдоль нормали контакта.
* С реакциями опоры разбираемся путём вычитания из скорости сближения прироста скорости, полученного во время последнего кадра.
*
* @param time
*/
public function calculateDesiredVelocity(time:Number):void {
// Вычисляем проекцию прироста скорости за текущий кадр на нормаль контакта. Для относительного прироста скорости, вызывающего
// сближение объектов, значение будет отрицательным.
var velocityFromAcc:Number = 0;
if (body1.awake) {
velocityFromAcc = (body1.lastFrameAcceleration.x*contactNormal.x + body1.lastFrameAcceleration.y*contactNormal.y + body1.lastFrameAcceleration.z*contactNormal.z)*time;
}
if (body2 != null && body2.awake) {
velocityFromAcc -= (body2.lastFrameAcceleration.x*contactNormal.x + body2.lastFrameAcceleration.y*contactNormal.y + body2.lastFrameAcceleration.z*contactNormal.z)*time;
}
// Если относительная скорость вдоль нормали контакта слишком мала, игнорируем её
var thisRestitution:Number = restitution;
if (contactVelocity.x > -velocityLimit && contactVelocity.x < velocityLimit) {
thisRestitution = 0;
}
// Вычисляем дельту для скорости отскока, убрав влияние прироста скорости за последний кадр
desiredDeltaVelocity = -contactVelocity.x - thisRestitution*(contactVelocity.x - velocityFromAcc);
}
/**
* Расчитывает внутренние данные о контакта на основе данных о его состоянии. Вызывается до того, как будет запущен
* алгоритм разрешения контактов. Эта функция в нормальных условиях никогда не должна вызываться вручную.
*
* @param time
*/
public function calculateInternals(time:Number):void {
// Расчёт базиса системы координат контакта
// === inlined calculateContactBasis();
contactToWorld.a = contactNormal.x;
contactToWorld.e = contactNormal.y;
contactToWorld.i = contactNormal.z;
var nx:Number = contactNormal.x > 0 ? contactNormal.x : -contactNormal.x;
var ny:Number = contactNormal.y > 0 ? contactNormal.y : -contactNormal.y;
var s:Number;
if (nx > ny) {
// Нормаль ближе к глобальной оси X, используем ось Y для вычислений. Локальный орт Y находится как результат векторного произведения globalY x contactNormal.
s = 1/Math.sqrt(nx*nx + contactNormal.z*contactNormal.z);
contactToWorld.b = contactNormal.z*s;
contactToWorld.f = 0;
contactToWorld.j = -contactNormal.x*s;
// Локальный орт Z находится как результат векторного произведения contactNormal x localY.
contactToWorld.c = contactNormal.y*contactToWorld.j;
contactToWorld.g = contactNormal.z*contactToWorld.b - contactNormal.x*contactToWorld.j;
contactToWorld.k = -contactNormal.y*contactToWorld.b;
} else {
// Нормаль ближе к глобальной оси Y, используем ось X для вычислений. Локальный орт Y находится как результат векторного произведения globalX x contactNormal.
s = 1/Math.sqrt(ny*ny + contactNormal.z*contactNormal.z);
contactToWorld.b = 0;
contactToWorld.f = -contactNormal.z*s;
contactToWorld.j = contactNormal.y*s;
// Локальный орт Z находится как результат векторного произведения contactNormal x localY.
contactToWorld.c = contactNormal.y*contactToWorld.j - contactNormal.z*contactToWorld.f;
contactToWorld.g = -contactNormal.x*contactToWorld.j;
contactToWorld.k = contactNormal.x*contactToWorld.f;
}
// === end inlined
// Расчёт локальной скорости в точке контакта для первого тела
relativeContactPosition1.x = contactPoint.x - body1.position.x;
relativeContactPosition1.y = contactPoint.y - body1.position.y;
relativeContactPosition1.z = contactPoint.z - body1.position.z;
calculateLocalVelocity(body1, relativeContactPosition1, time, contactVelocity);
// Расчёт локальной скорости в точке контакта для второго тела
if (body2 != null) {
relativeContactPosition2.x = contactPoint.x - body2.position.x;
relativeContactPosition2.y = contactPoint.y - body2.position.y;
relativeContactPosition2.z = contactPoint.z - body2.position.z;
calculateLocalVelocity(body2, relativeContactPosition2, time, tmpVelocityVector);
// Расчёт относительной скорости в точке контакта
contactVelocity.x -= tmpVelocityVector.x;
contactVelocity.y -= tmpVelocityVector.y;
contactVelocity.z -= tmpVelocityVector.z;
}
// Расчёт вектора изменения скорости, требуемого для разрешения контакта
calculateDesiredVelocity(time);
}
/**
* Выполняет разделение контакта посредством импульса, пропорционально инерции тел.
*/
public function applyVelocityChange(velocityChange1:Point3D, velocityChange2:Point3D, rotationChange1:Point3D, rotationChange2:Point3D):void {
var inverseInertiaTensor1:Matrix3D = body1.inverseInertiaTensorWorld;
var inverseInertiaTensor2:Matrix3D;
if (body2 != null) {
inverseInertiaTensor2 = body2.inverseInertiaTensorWorld;
}
if (friction == 0) {
calculateFrictionlessImpulse(inverseInertiaTensor1, inverseInertiaTensor2, impulse);
} else {
calculateFrictionImpulse(inverseInertiaTensor1, inverseInertiaTensor2, impulse);
}
// Переведём импульс в мировые координаты
var x:Number = impulse.x;
var y:Number = impulse.y;
var z:Number = impulse.z;
impulse.x = contactToWorld.a*x + contactToWorld.b*y + contactToWorld.c*z;
impulse.y = contactToWorld.e*x + contactToWorld.f*y + contactToWorld.g*z;
impulse.z = contactToWorld.i*x + contactToWorld.j*y + contactToWorld.k*z;
// Разделим импульс на вращательную и линейную часть
x = relativeContactPosition1.y*impulse.z - relativeContactPosition1.z*impulse.y;
y = relativeContactPosition1.z*impulse.x - relativeContactPosition1.x*impulse.z;
z = relativeContactPosition1.x*impulse.y - relativeContactPosition1.y*impulse.x;
rotationChange1.x = inverseInertiaTensor1.a*x + inverseInertiaTensor1.b*y + inverseInertiaTensor1.c*z;
rotationChange1.y = inverseInertiaTensor1.e*x + inverseInertiaTensor1.f*y + inverseInertiaTensor1.g*z;
rotationChange1.z = inverseInertiaTensor1.i*x + inverseInertiaTensor1.j*y + inverseInertiaTensor1.k*z;
velocityChange1.x = impulse.x*body1.inverseMass;
velocityChange1.y = impulse.y*body1.inverseMass;
velocityChange1.z = impulse.z*body1.inverseMass;
body1.velocity.x += velocityChange1.x;
body1.velocity.y += velocityChange1.y;
body1.velocity.z += velocityChange1.z;
body1.rotation.x += rotationChange1.x;
body1.rotation.y += rotationChange1.y;
body1.rotation.z += rotationChange1.z;
if (body2 != null) {
x = relativeContactPosition2.z*impulse.y - relativeContactPosition2.y*impulse.z;
y = relativeContactPosition2.x*impulse.z - relativeContactPosition2.z*impulse.x;
z = relativeContactPosition2.y*impulse.x - relativeContactPosition2.x*impulse.y;
rotationChange2.x = inverseInertiaTensor2.a*x + inverseInertiaTensor2.b*y + inverseInertiaTensor2.c*z;
rotationChange2.y = inverseInertiaTensor2.e*x + inverseInertiaTensor2.f*y + inverseInertiaTensor2.g*z;
rotationChange2.z = inverseInertiaTensor2.i*x + inverseInertiaTensor2.j*y + inverseInertiaTensor2.k*z;
velocityChange2.x = -impulse.x*body2.inverseMass;
velocityChange2.y = -impulse.y*body2.inverseMass;
velocityChange2.z = -impulse.z*body2.inverseMass;
body2.velocity.x += velocityChange2.x;
body2.velocity.y += velocityChange2.y;
body2.velocity.z += velocityChange2.z;
body2.rotation.x += rotationChange2.x;
body2.rotation.y += rotationChange2.y;
body2.rotation.z += rotationChange2.z;
}
}
/**
* Вычисляет импульс, необходимый для коррекции скоростей в точке контакта в отсутствие сил трения.
*/
public function calculateFrictionlessImpulse(inverseInertiaTensor1:Matrix3D, inverseInertiaTensor2:Matrix3D, result:Point3D):void {
tmpVelocityVector.x = relativeContactPosition1.y*contactNormal.z - relativeContactPosition1.z*contactNormal.y;
tmpVelocityVector.y = relativeContactPosition1.z*contactNormal.x - relativeContactPosition1.x*contactNormal.z;
tmpVelocityVector.z = relativeContactPosition1.x*contactNormal.y - relativeContactPosition1.y*contactNormal.x;
var x:Number = inverseInertiaTensor1.a*tmpVelocityVector.x + inverseInertiaTensor1.b*tmpVelocityVector.y + inverseInertiaTensor1.c*tmpVelocityVector.z;
var y:Number = inverseInertiaTensor1.e*tmpVelocityVector.x + inverseInertiaTensor1.f*tmpVelocityVector.y + inverseInertiaTensor1.g*tmpVelocityVector.z;
var z:Number = inverseInertiaTensor1.i*tmpVelocityVector.x + inverseInertiaTensor1.j*tmpVelocityVector.y + inverseInertiaTensor1.k*tmpVelocityVector.z;
tmpVelocityVector.x = y*relativeContactPosition1.z - z*relativeContactPosition1.y;
tmpVelocityVector.y = z*relativeContactPosition1.x - x*relativeContactPosition1.z;
tmpVelocityVector.z = x*relativeContactPosition1.y - y*relativeContactPosition1.x;
var deltaVelocity:Number = tmpVelocityVector.x*contactNormal.x + tmpVelocityVector.y*contactNormal.y + tmpVelocityVector.z*contactNormal.z + body1.inverseMass;
if (body2 != null) {
tmpVelocityVector.x = relativeContactPosition2.y*contactNormal.z - relativeContactPosition2.z*contactNormal.y;
tmpVelocityVector.y = relativeContactPosition2.z*contactNormal.x - relativeContactPosition2.x*contactNormal.z;
tmpVelocityVector.z = relativeContactPosition2.x*contactNormal.y - relativeContactPosition2.y*contactNormal.x;
x = inverseInertiaTensor2.a*tmpVelocityVector.x + inverseInertiaTensor2.b*tmpVelocityVector.y + inverseInertiaTensor2.c*tmpVelocityVector.z;
y = inverseInertiaTensor2.e*tmpVelocityVector.x + inverseInertiaTensor2.f*tmpVelocityVector.y + inverseInertiaTensor2.g*tmpVelocityVector.z;
z = inverseInertiaTensor2.i*tmpVelocityVector.x + inverseInertiaTensor2.j*tmpVelocityVector.y + inverseInertiaTensor2.k*tmpVelocityVector.z;
tmpVelocityVector.x = y*relativeContactPosition2.z - z*relativeContactPosition2.y;
tmpVelocityVector.y = z*relativeContactPosition2.x - x*relativeContactPosition2.z;
tmpVelocityVector.z = x*relativeContactPosition2.y - y*relativeContactPosition2.x;
deltaVelocity += tmpVelocityVector.x*contactNormal.x + tmpVelocityVector.y*contactNormal.y + tmpVelocityVector.z*contactNormal.z + body2.inverseMass;
}
result.x = desiredDeltaVelocity/deltaVelocity;
result.y = 0;
result.z = 0;
}
private var impulseToTorque:Matrix3D = new Matrix3D();
private var deltaVelWorldMatrix:Matrix3D = new Matrix3D();
private var deltaVelWorldMatrix2:Matrix3D = new Matrix3D();
/**
* Вычисляет импульс, необходимый для коррекции скоростей в точке контакта при наличии сил трения.
*/
public function calculateFrictionImpulse(inverseInertiaTensor1:Matrix3D, inverseInertiaTensor2:Matrix3D, result:Point3D):void {
var inverseMass:Number = body1.inverseMass;
relativeContactPosition1.createSkewSymmetricMatrix(impulseToTorque);
deltaVelWorldMatrix.copy(impulseToTorque);
deltaVelWorldMatrix.inverseCombine(inverseInertiaTensor1);
deltaVelWorldMatrix.inverseCombine(impulseToTorque);
deltaVelWorldMatrix.multByScalar(-1);
if (body2 != null) {
relativeContactPosition2.createSkewSymmetricMatrix(impulseToTorque);
deltaVelWorldMatrix2.copy(impulseToTorque);
deltaVelWorldMatrix2.inverseCombine(inverseInertiaTensor2);
deltaVelWorldMatrix2.inverseCombine(impulseToTorque);
deltaVelWorldMatrix2.multByScalar(-1);
deltaVelWorldMatrix.add(deltaVelWorldMatrix2);
inverseMass += body2.inverseMass;
}
deltaVelWorldMatrix2.copy(contactToWorld);
deltaVelWorldMatrix2.transpose();
deltaVelWorldMatrix2.inverseCombine(deltaVelWorldMatrix);
deltaVelWorldMatrix2.inverseCombine(contactToWorld);
deltaVelWorldMatrix2.a += inverseMass;
deltaVelWorldMatrix2.f += inverseMass;
deltaVelWorldMatrix2.k += inverseMass;
deltaVelWorldMatrix.copy(deltaVelWorldMatrix2);
deltaVelWorldMatrix.invert();
result.reset(desiredDeltaVelocity, -contactVelocity.y, -contactVelocity.z);
result.transform(deltaVelWorldMatrix);
var planarImpulse:Number = Math.sqrt(result.y*result.y + result.z*result.z);
if (planarImpulse > result.x*friction) {
result.y /= planarImpulse;
result.z /= planarImpulse;
result.x = desiredDeltaVelocity/(deltaVelWorldMatrix2.a + deltaVelWorldMatrix2.b*friction*result.y + deltaVelWorldMatrix2.c*friction*result.z);
result.y *= friction*result.x;
result.z *= friction*result.x;
}
}
private var angularInertiaWorld:Point3D = new Point3D();
/**
* Выполняет разделение контакта с учётом инертности тел.
*/
public function applyPositionChange(linearChange1:Point3D, linearChange2:Point3D, angularChange1:Point3D, angularChange2:Point3D, penetration:Number):void {
var totalInertia:Number = 0;
var angularInertia1:Number;
var angularInertia2:Number;
var linearInertia1:Number;
var linearInertia2:Number;
// body1
angularInertiaWorld.x = relativeContactPosition1.y*contactNormal.z - relativeContactPosition1.z*contactNormal.y;
angularInertiaWorld.y = relativeContactPosition1.z*contactNormal.x - relativeContactPosition1.x*contactNormal.z;
angularInertiaWorld.z = relativeContactPosition1.x*contactNormal.y - relativeContactPosition1.y*contactNormal.x;
var m:Matrix3D = body1.inverseInertiaTensorWorld;
var x:Number = m.a*angularInertiaWorld.x + m.b*angularInertiaWorld.y + m.c*angularInertiaWorld.z;
var y:Number = m.e*angularInertiaWorld.x + m.f*angularInertiaWorld.y + m.g*angularInertiaWorld.z;
var z:Number = m.i*angularInertiaWorld.x + m.j*angularInertiaWorld.y + m.k*angularInertiaWorld.z;
angularInertiaWorld.x = y*relativeContactPosition1.z - z*relativeContactPosition1.y;
angularInertiaWorld.y = z*relativeContactPosition1.x - x*relativeContactPosition1.z;
angularInertiaWorld.z = x*relativeContactPosition1.y - y*relativeContactPosition1.x;
angularInertia1 = angularInertiaWorld.x*contactNormal.x + angularInertiaWorld.y*contactNormal.y + angularInertiaWorld.z*contactNormal.z;
linearInertia1 = body1.inverseMass;
totalInertia = linearInertia1 + angularInertia1;
if (body2 != null) {
angularInertiaWorld.x = relativeContactPosition2.y*contactNormal.z - relativeContactPosition2.z*contactNormal.y;
angularInertiaWorld.y = relativeContactPosition2.z*contactNormal.x - relativeContactPosition2.x*contactNormal.z;
angularInertiaWorld.z = relativeContactPosition2.x*contactNormal.y - relativeContactPosition2.y*contactNormal.x;
m = body2.inverseInertiaTensorWorld;
x = m.a*angularInertiaWorld.x + m.b*angularInertiaWorld.y + m.c*angularInertiaWorld.z;
y = m.e*angularInertiaWorld.x + m.f*angularInertiaWorld.y + m.g*angularInertiaWorld.z;
z = m.i*angularInertiaWorld.x + m.j*angularInertiaWorld.y + m.k*angularInertiaWorld.z;
angularInertiaWorld.x = y*relativeContactPosition2.z - z*relativeContactPosition2.y;
angularInertiaWorld.y = z*relativeContactPosition2.x - x*relativeContactPosition2.z;
angularInertiaWorld.z = x*relativeContactPosition2.y - y*relativeContactPosition2.x;
angularInertia2 = angularInertiaWorld.x*contactNormal.x + angularInertiaWorld.y*contactNormal.y + angularInertiaWorld.z*contactNormal.z;
linearInertia2 = body2.inverseMass;
totalInertia += linearInertia2 + angularInertia2;
}
// body1
applyPostionChangeToBody(body1, relativeContactPosition1, angularInertia1, linearInertia1, totalInertia, 1, angularChange1, linearChange1);
if (body2 != null) {
applyPostionChangeToBody(body2, relativeContactPosition2, angularInertia2, linearInertia2, totalInertia, -1, angularChange2, linearChange2);
}
}
private var projection:Point3D = new Point3D();
/**
*
* @param body
* @param relativeContactPosition
* @param angularInertia
* @param linearInertia
* @param totalInertia
* @param sign
* @param angularChange
* @param linearChange
*/
private function applyPostionChangeToBody(body:RigidBody, relativeContactPosition:Point3D, angularInertia:Number, linearInertia:Number, totalInertia:Number, sign:Number, angularChange:Point3D, linearChange:Point3D):void {
var angularLimit:Number = 0.2;
var angularMove:Number = sign*penetration*angularInertia/totalInertia;
var linearMove:Number = sign*penetration*linearInertia/totalInertia;
projection.copy(contactNormal);
projection.multiply(-relativeContactPosition.dot(contactNormal));
projection.add(relativeContactPosition);
var maxMagnitude:Number = angularLimit*projection.length;
var totalMove:Number;
if (angularMove < -maxMagnitude) {
totalMove = angularMove + linearMove;
angularMove = -maxMagnitude;
linearMove = totalMove - angularMove;
} else if (angularMove > maxMagnitude) {
totalMove = angularMove + linearMove;
angularMove = maxMagnitude;
linearMove = totalMove - angularMove;
}
if (angularMove == 0) {
angularChange.reset();
} else {
angularChange.copy(relativeContactPosition);
angularChange.cross(contactNormal);
angularChange.transform(body.inverseInertiaTensorWorld);
angularChange.multiply(angularMove/angularInertia);
}
linearChange.copy(contactNormal);
linearChange.multiply(linearMove);
body.position.add(linearChange);
body.orientation.addScaledVector(angularChange, 1);
if (!body.awake) {
body.calculateDerivedData();
}
}
public function toString():String {
return "RigidBodyContact" +
"\n timeStamp: " + timeStamp +
"\n body1: " + body1.getName() +
"\n body2: " + (body2 == null ? "" : body2.getName()) +
"\n contactPoint: " + contactPoint +
"\n contactNormal: " + contactNormal +
"\n restitution: " + restitution +
"\n penetration: " + penetration +
"\n friction: " + friction;
}
}
}

View File

@@ -0,0 +1,22 @@
package alternativa.physics.rigid {
/**
* Генератор контактов для твёрдых тел.
*/
public class RigidBodyContactGenerator {
/**
* Следующий генератор в списке.
*/
public var next:RigidBodyContactGenerator;
/**
* Метод создаёт контакты.
*
* @param contact первый свободный для записи контакт в списке контактов мира
* @return следующий свободный для записи контакт после окончания работы метода. Значение null означает, что доступные контакты исчерпаны.
*/
public function addContacts(contact:RigidBodyContact):RigidBodyContact {
return null;
}
}
}

View File

@@ -0,0 +1,237 @@
package alternativa.physics.rigid {
import alternativa.physics.*;
import alternativa.types.Point3D;
use namespace altphysics;
/**
*
*/
public class RigidBodyContactResolver {
private var velocityIterations:int;
private var positionIterations:int;
private var velocityEpsilon:Number;
private var positionEpsilon:Number;
private var velocityIterationsUsed:int;
private var positionIterationsUsed:int;
private var linearChange1:Point3D = new Point3D();
private var linearChange2:Point3D = new Point3D();
private var angularChange1:Point3D = new Point3D();
private var angularChange2:Point3D = new Point3D();
private var velocityChange1:Point3D = new Point3D();
private var velocityChange2:Point3D = new Point3D();
private var rotationChange1:Point3D = new Point3D();
private var rotationChange2:Point3D = new Point3D();
private var deltaVector:Point3D = new Point3D();
/**
*
* @param velocityIterations
* @param positionIterations
* @param velocityEpsilon
* @param positionEpsilon
*/
public function RigidBodyContactResolver(velocityIterations:int, positionIterations:int, velocityEpsilon:Number = 0.001, positionEpsilon:Number = 0.001) {
this.velocityIterations = velocityIterations;
this.positionIterations = positionIterations;
this.velocityEpsilon = velocityEpsilon;
this.positionEpsilon = positionEpsilon;
}
/**
*
* @param velocityIterations
* @param positionEpsilon
*/
public function setIterations(velocityIterations:int, positionIterations:int):void {
this.velocityIterations = velocityIterations;
this.positionIterations = positionIterations;
}
/**
*
* @return
*/
private function isValid():Boolean {
return (positionIterations > 0) && (velocityIterations > 0) && (positionEpsilon >= 0) && (velocityEpsilon >= 0);
}
/**
*
* @param firstContact
* @param numContacts
* @param time
*/
public function resolveContacts(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
if (numContacts == 0 || !isValid()) {
return;
}
// Подготовка контактов к обработке
prepareContacts(firstContact, numContacts, time);
// Разделение взаимно проникающих контактов
adjustPositions(firstContact, numContacts, time);
// Коррекция скорости для контактов
adjustVelocities(firstContact, numContacts, time);
}
/**
*
* @param firstContact
* @param numContacts
* @param time
*/
public function prepareContacts(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
var contact:RigidBodyContact = firstContact;
while (contact.index < numContacts) {
// Вычисление внутренних данных контакта, необходимых для дальнейшей работы
contact.calculateInternals(time);
contact = contact.next;
}
}
/**
*
* @param firstContact
* @param numContacts
* @param time
*/
public function adjustPositions(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
positionIterationsUsed = 0;
// Итерационное устранение взаимного проникновения тел в точках контактов в порядке наибольшего пересечения тел
while (positionIterationsUsed < positionIterations) {
var max:Number = positionEpsilon;
var mostSevereContact:RigidBodyContact = null;
// Поиск контакта с наибольшим пересечением
var contact:RigidBodyContact = firstContact;
while (contact.index < numContacts) {
if (contact.penetration > max) {
max = contact.penetration;
mostSevereContact = contact;
}
contact = contact.next;
}
if (mostSevereContact == null) {
// Алгоритм прерывается, если не осталось пересечений
return;
}
// Будим спящего
mostSevereContact.matchAwakeState();
// Разделяем контакт
mostSevereContact.applyPositionChange(linearChange1, linearChange2, angularChange1, angularChange2, max);
// Обновляем величину проникновения в каждом контакте, содержащем обработанные объекты
contact = firstContact;
while (contact.index < numContacts) {
if (contact.body1 == mostSevereContact.body1) {
deltaVector.copy(angularChange1);
deltaVector.cross(contact.relativeContactPosition1);
deltaVector.add(linearChange1);
contact.penetration -= deltaVector.dot(contact.contactNormal);
}
if (contact.body1 == mostSevereContact.body2) {
deltaVector.copy(angularChange2);
deltaVector.cross(contact.relativeContactPosition1);
deltaVector.add(linearChange2);
contact.penetration -= deltaVector.dot(contact.contactNormal);
}
if (contact.body2 != null) {
if (contact.body2 == mostSevereContact.body1) {
deltaVector.copy(angularChange1);
deltaVector.cross(contact.relativeContactPosition2);
deltaVector.add(linearChange1);
contact.penetration += deltaVector.dot(contact.contactNormal);
}
if (contact.body2 == mostSevereContact.body2) {
deltaVector.copy(angularChange2);
deltaVector.cross(contact.relativeContactPosition2);
deltaVector.add(linearChange2);
contact.penetration += deltaVector.dot(contact.contactNormal);
}
}
contact = contact.next;
}
positionIterationsUsed++;
}
}
/**
*
* @param firstContact
* @param numContacts
* @param time
*/
public function adjustVelocities(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
velocityIterationsUsed = 0;
while (velocityIterationsUsed < velocityIterations) {
var max:Number = velocityEpsilon;
var mostSevereContact:RigidBodyContact = null;
var contact:RigidBodyContact = firstContact;
while (contact.index < numContacts) {
if (contact.desiredDeltaVelocity > max) {
max = contact.desiredDeltaVelocity;
mostSevereContact = contact;
}
contact = contact.next;
}
if (mostSevereContact == null) {
return;
}
// Будим спящих
mostSevereContact.matchAwakeState();
// Корректируем скорости
mostSevereContact.applyVelocityChange(velocityChange1, velocityChange2, rotationChange1, rotationChange2);
// Обновляем скорости сближения для каждого контакта, содержащего обработанные объекты
contact = firstContact;
var needRecalculation:Boolean = false;
while (contact.index < numContacts) {
if (contact.body1 == mostSevereContact.body1) {
deltaVector.copy(rotationChange1);
deltaVector.cross(contact.relativeContactPosition1);
deltaVector.add(velocityChange1);
deltaVector.transformTranspose(contact.contactToWorld);
contact.contactVelocity.add(deltaVector);
needRecalculation = true;
}
if (contact.body1 == mostSevereContact.body2) {
deltaVector.copy(rotationChange2);
deltaVector.cross(contact.relativeContactPosition1);
deltaVector.add(velocityChange2);
deltaVector.transformTranspose(contact.contactToWorld);
contact.contactVelocity.add(deltaVector);
needRecalculation = true;
}
if (contact.body2 != null) {
if (contact.body2 == mostSevereContact.body1) {
deltaVector.copy(rotationChange1);
deltaVector.cross(contact.relativeContactPosition2);
deltaVector.add(velocityChange1);
deltaVector.transformTranspose(contact.contactToWorld);
contact.contactVelocity.subtract(deltaVector);
needRecalculation = true;
}
if (contact.body2 == mostSevereContact.body2) {
deltaVector.copy(rotationChange2);
deltaVector.cross(contact.relativeContactPosition2);
deltaVector.add(velocityChange2);
deltaVector.transformTranspose(contact.contactToWorld);
contact.contactVelocity.subtract(deltaVector);
needRecalculation = true;
}
}
if (needRecalculation) {
contact.calculateDesiredVelocity(time);
}
contact = contact.next;
}
velocityIterationsUsed++;
}
}
}
}

View File

@@ -0,0 +1,136 @@
package alternativa.physics.rigid {
public class RigidWorld {
private var firstBody:RigidBody;
private var lastBody:RigidBody;
private var contactGenerators:RigidBodyContactGenerator;
private var lastContactGenerator:RigidBodyContactGenerator;
public var contacts:RigidBodyContact;
private var maxContacts:int;
private var resolver:RigidBodyContactResolver;
public var forceRegistry:BodyForceRegistry = new BodyForceRegistry();
public var calculateIterations:Boolean;
public var numContacts:int;
public function RigidWorld(maxContacts:int, iterations:int) {
this.maxContacts = maxContacts;
resolver = new RigidBodyContactResolver(iterations, iterations);
createContactList(maxContacts);
}
private function createContactList(numContacts:int):void {
if (numContacts > 0) {
var contact:RigidBodyContact = contacts = new RigidBodyContact(0);
for (var i:int = 1; i < numContacts; i++) {
contact.next = new RigidBodyContact(i);
contact = contact.next;
}
}
}
public function addBody(body:RigidBody):void {
if (firstBody == null) {
firstBody = body;
} else {
lastBody.next = body;
}
lastBody = body;
while (lastBody.next != null) {
lastBody = lastBody.next;
}
}
public function removeBody(body:RigidBody):void {
if (body == firstBody) {
firstBody = firstBody.next;
body.next = null;
} else {
var current:RigidBody = firstBody;
var prev:RigidBody = null;
while (current != body && current != null) {
prev = current;
current = current.next;
}
if (current != null) {
if (lastBody == body) {
lastBody = prev;
}
prev.next = current.next;
current.next = null;
}
}
}
public function addContactGenerator(gen:RigidBodyContactGenerator):void {
if (contactGenerators == null) {
contactGenerators = gen;
} else {
lastContactGenerator.next = gen;
}
lastContactGenerator = gen;
while (lastContactGenerator.next != null) {
lastContactGenerator = lastContactGenerator.next;
}
}
public function removeContactGenerator(gen:RigidBodyContactGenerator):void {
if (gen == contactGenerators) {
contactGenerators = gen.next;
gen.next = null;
} else {
var prev:RigidBodyContactGenerator = contactGenerators;
var curr:RigidBodyContactGenerator = contactGenerators.next;
while (curr != gen && curr != null) {
prev = curr;
curr = curr.next;
}
if (curr == gen) {
if (lastContactGenerator == gen) {
lastContactGenerator = prev;
}
prev.next = curr.next;
curr.next = null;
}
}
}
public function startFrame():void {
var body:RigidBody = firstBody;
while (body != null) {
body.clearAccumulators();
body.calculateDerivedData();
body = body.next;
}
}
private function generateContacts():int {
var contact:RigidBodyContact = contacts;
var gen:RigidBodyContactGenerator = contactGenerators;
while (gen != null && contact != null) {
contact = gen.addContacts(contact);
gen = gen.next;
}
return contact == null ? maxContacts : contact.index;
}
public function runPhysics(time:Number):void {
forceRegistry.updateForces(time);
var body:RigidBody = firstBody;
while (body != null) {
body.integrate(time);
body = body.next;
}
numContacts = generateContacts();
// trace("[RigidWorld.runPhysics] numContacts", numContacts);
if (calculateIterations) {
resolver.setIterations(4*numContacts, 4*numContacts);
}
resolver.resolveContacts(contacts, numContacts, time);
}
}
}

View File

@@ -0,0 +1,16 @@
package alternativa.physics.rigid {
import alternativa.physics.force.IRigidBodyForceGenerator;
import alternativa.physics.rigid.RigidBody;
public class BodyForceRegistration {
public var body:RigidBody;
public var forceGenerator:IRigidBodyForceGenerator;
public var next:BodyForceRegistration;
public function BodyForceRegistration(body:RigidBody, forceGenerator:IRigidBodyForceGenerator) {
this.body = body;
this.forceGenerator = forceGenerator;
}
}
}

View File

@@ -0,0 +1,47 @@
package alternativa.physics.rigid {
import alternativa.physics.force.IRigidBodyForceGenerator;
import alternativa.physics.rigid.RigidBody;
public class BodyForceRegistry {
protected var registrations:BodyForceRegistration;
public function BodyForceRegistry() {
}
public function add(body:RigidBody, fg:IRigidBodyForceGenerator):void {
var registration:BodyForceRegistration = new BodyForceRegistration(body, fg);
registration.next = registrations;
registrations = registration;
}
public function remove(body:RigidBody, fg:IRigidBodyForceGenerator):void {
var current:BodyForceRegistration = registrations;
var prev:BodyForceRegistration = null;
while (current != null) {
if (current.body == body && current.forceGenerator == fg) {
if (current == registrations) {
registrations = current.next;
} else {
prev.next = current.next;
}
break;
}
prev = current;
current = current.next;
}
}
public function clear():void {
registrations = null;
}
public function updateForces(time:Number):void {
var current:BodyForceRegistration = registrations;
while (current != null) {
current.forceGenerator.updateForce(current.body, time);
current = current.next;
}
}
}
}

View File

@@ -0,0 +1,707 @@
package alternativa.physics.rigid {
import alternativa.physics.altphysics;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
import alternativa.types.Quaternion;
use namespace altphysics;
/**
* Класс представляет твёрдое тело.
*/
public class RigidBody {
private var angularAcceleration:Point3D = new Point3D();
private var motionThreshold:Number = 0.1;
private var motionThreshold2:Number = 2*motionThreshold;
private var baseBias:Number = 0.8;
private var name:String;
public var next:RigidBody;
public var prev:RigidBody;
altphysics var mass:Number;
altphysics var inverseMass:Number;
altphysics var inverseInertiaTensor:Matrix3D = new Matrix3D();
altphysics var linearDamping:Number = 0.95;
altphysics var angularDamping:Number = 0.95;
altphysics var awake:Boolean = true;
altphysics var canSleep:Boolean = true;
altphysics var acceleration:Point3D = new Point3D();
altphysics var angularVelocity:Point3D = new Point3D();
altphysics var position:Point3D = new Point3D();
altphysics var orientation:Quaternion = new Quaternion(1, 0, 0, 0);
altphysics var velocity:Point3D = new Point3D();
altphysics var rotation:Point3D = new Point3D();
altphysics var lastFrameAcceleration:Point3D = new Point3D();
altphysics var transformMatrix:Matrix3D = new Matrix3D();
altphysics var inverseInertiaTensorWorld:Matrix3D = new Matrix3D();
altphysics var forceAccum:Point3D = new Point3D();
altphysics var torqueAccum:Point3D = new Point3D();
altphysics var motion:Number = 10;
/**
* Создаёт новый экзепляр твёрдого тела.
*
* @param inverseMass величина, обратная массе тела. Значение 0 указывает, что тело имеет имеет бесконечную массу и момент инерции.
* @param inverseInertiaTensor обратный тензор момента инерции тела. Значение <code>null</code> указывает, что тело имеет бесконечную массу и момент инерции.
*/
public function RigidBody(inverseMass:Number = 1, inverseInertiaTensor:Matrix3D = null) {
if (inverseMass == 0 || inverseInertiaTensor == null) {
setInfinteMass();
} else {
this.inverseMass = inverseMass;
this.mass = 1/inverseMass;
this.inverseInertiaTensor.copy(inverseInertiaTensor);
}
}
/**
* @return имя тела
*/
public function getName():String {
return name;
}
/**
* Устанавливает имя тела.
*
* @param value имя тела
*/
public function setName(value:String):void {
name = value;
}
/**
* Предельное количество движения, ниже которого тело усыпляеся.
*/
public function getMotionThreshold():Number {
return motionThreshold;
}
/**
* @private
*/
public function setMotionThreshold(value:Number):void {
motionThreshold = value;
motionThreshold2 = value*2;
}
/**
*
* @param value
*/
public function setBaseBias(value:Number):void {
baseBias = value;
}
/**
*
* @param value
*/
public function getBaseBias():Number {
return baseBias;
}
/**
*
*/
public function getMass():Number {
return mass;
}
/**
*
*/
public function setMass(value:Number):void {
if (value <= 0) {
inverseMass = 0;
mass = 0;
} else {
mass = value;
inverseMass = 1/value;
}
}
/**
*
*/
public function getInverseMass():Number {
return inverseMass;
}
/**
* @private
*/
public function setInverseMass(value:Number):void {
if (value > 0) {
inverseMass = value;
mass = 1/value;
} else {
inverseMass = 0;
mass = 0;
}
}
/**
*
* @param m
*/
public function setIntertiaTensor(intertiaTensor:Matrix3D):void {
inverseInertiaTensor.copy(intertiaTensor);
inverseInertiaTensor.invert();
}
/**
*
* @param intertiaTensor
*/
public function getIntertiaTensor(intertiaTensor:Matrix3D):void {
intertiaTensor.copy(inverseInertiaTensor);
intertiaTensor.invert();
}
/**
*
* @param inverseInertiaTensor
*/
public function setInverseIntertiaTensor(inverseInertiaTensor:Matrix3D):void {
this.inverseInertiaTensor.copy(inverseInertiaTensor);
}
/**
*
* @param inverseInertiaTensor
*/
public function getInverseIntertiaTensor(inverseInertiaTensor:Matrix3D):void {
inverseInertiaTensor.copy(this.inverseInertiaTensor);
}
/**
*
*/
public function setInfinteMass():void {
inverseMass = 0;
mass = 0;
inverseInertiaTensor.toIdentity();
inverseInertiaTensor.a = inverseInertiaTensor.f = inverseInertiaTensor.k = 0;
}
/**
*
* @param linearDamping
* @param angularDamping
*/
public function setDamping(linearDamping:Number, angularDamping:Number):void {
this.linearDamping = linearDamping;
this.angularDamping = angularDamping;
}
/**
*
* @param value
*/
public function setLinearDamping(value:Number):void {
linearDamping = value;
}
/**
*
* @return
*/
public function getLinearDamping():Number {
return linearDamping;
}
/**
*
* @param value
*/
public function setAngularDamping(value:Number):void {
angularDamping = value;
}
/**
*
* @return
*/
public function getAngularDamping():Number {
return angularDamping;
}
/**
*
* @param position
*/
public function setPosition(position:Point3D):void {
this.position.x = position.x;
this.position.y = position.y;
this.position.z = position.z;
}
/**
*
* @param x
* @param y
* @param z
*/
public function setPositionComponents(x:Number, y:Number, z:Number):void {
position.x = x;
position.y = y;
position.z = z;
}
/**
*
* @param position
*/
public function getPosition(position:Point3D):void {
position.x = this.position.x;
position.y = this.position.y;
position.z = this.position.z;
}
/**
*
* @param orientation
*/
public function setOrientation(orientation:Quaternion):void {
this.orientation.copy(orientation);
this.orientation.normalize();
}
/**
*
* @param r
* @param i
* @param j
* @param k
*/
public function setOrientationComponents(w:Number, x:Number, y:Number, z:Number):void {
orientation.reset(w, x, y, z);
orientation.normalize();
}
/**
*
* @param orientation
*/
public function getOrientation(orientation:Quaternion):void {
orientation.copy(this.orientation);
}
/**
*
* @param matrix
*/
public function getTransformMatrix(matrix:Matrix3D):void {
matrix.copy(transformMatrix);
}
/**
*
* @param velocity
*/
public function setVelocity(velocity:Point3D):void {
this.velocity.copy(velocity);
}
/**
*
* @param x
* @param y
* @param z
*/
public function setVelocityComponents(x:Number, y:Number, z:Number):void {
velocity.reset(x, y, z);
}
/**
*
* @param velocity
*/
public function getVelocity(velocity:Point3D):void {
velocity.copy(this.velocity);
}
/**
*
* @param acceleration
*
*/
public function setAcceleration(acceleration:Point3D):void {
this.acceleration.copy(acceleration);
}
/**
*
* @param x
* @param y
* @param z
*
*/
public function setAccelerationComponents(x:Number, y:Number, z:Number):void {
acceleration.reset(x, y, z);
}
/**
*
* @param acceleration
*
*/
public function getAcceleration(acceleration:Point3D):void {
acceleration.copy(this.acceleration);
}
/**
*
* @param rotation
*
*/
public function setRotation(rotation:Point3D):void {
this.rotation.copy(rotation);
}
/**
*
* @param x
* @param y
* @param z
*
*/
public function setRotationComponents(x:Number, y:Number, z:Number):void {
rotation.reset(x, y, z);
}
/**
*
* @param rotation
*
*/
public function addRotation(deltaRotation:Point3D):void {
rotation.add(deltaRotation);
}
/**
*
* @param rotation
*
*/
public function getRotation(rotation:Point3D):void {
rotation.copy(this.rotation);
}
/**
*
* @param angularVelocity
*/
public function setAngularVelocity(angularVelocity:Point3D):void {
this.angularVelocity.x = angularVelocity.x;
this.angularVelocity.y = angularVelocity.y;
this.angularVelocity.z = angularVelocity.z;
}
/**
*
* @param x
* @param y
* @param z
*/
public function setAngularVelocityComponents(x:Number, y:Number, z:Number):void {
angularVelocity.x = x;
angularVelocity.y = y;
angularVelocity.z = z;
}
/**
*
* @param awake
*/
public function setAwake(awake:Boolean = true):void {
this.awake = awake;
if (awake) {
motion = motionThreshold2;
} else {
velocity.x = 0;
velocity.y = 0;
velocity.z = 0;
rotation.x = 0;
rotation.y = 0;
rotation.z = 0;
}
}
/**
*
* @return
*/
public function isAwake():Boolean {
return awake;
}
/**
*
* @param canSleep
*/
public function setCanSleep(canSleep:Boolean = true):void {
this.canSleep = canSleep;
if (!canSleep && !awake) {
setAwake();
}
}
/**
*
* @return
*/
public function isCanSleep():Boolean {
return canSleep;
}
/**
* Добавляет глобальную силу, приложенную к центру масс тела.
*
* @param force вектор силы в глобальной системе координат
*/
public function addForce(force:Point3D):void {
forceAccum.x += force.x;
forceAccum.y += force.y;
forceAccum.z += force.z;
setAwake();
}
/**
*
* @param force
*/
public function addForceComponents(x:Number, y:Number, z:Number):void {
forceAccum.x += x;
forceAccum.y += y;
forceAccum.z += z;
setAwake();
}
/**
* Добавляет глобальную силу, приложенную к заданной локальной точке тела.
*
* @param force вектор силы в глобальной системе координат
* @param localPoint локальные координаты точки приложения силы
*/
public function addForceAtLocalPoint(force:Point3D, localPoint:Point3D):void {
forceAccum.x += force.x;
forceAccum.y += force.y;
forceAccum.z += force.z;
// Трансформация локальных координат в глобальные
var x:Number = transformMatrix.a*localPoint.x + transformMatrix.b*localPoint.y + transformMatrix.c*localPoint.z;
var y:Number = transformMatrix.e*localPoint.x + transformMatrix.f*localPoint.y + transformMatrix.g*localPoint.z;
var z:Number = transformMatrix.i*localPoint.x + transformMatrix.j*localPoint.y + transformMatrix.k*localPoint.z;
torqueAccum.x += y*force.z - z*force.y;
torqueAccum.y += z*force.x - x*force.z;
torqueAccum.z += x*force.y - y*force.x;
setAwake();
}
/**
* Добавляет глобальную силу, приложенную к заданной точке тела, выраженной в глобальных координатах.
*
* @param force вектор силы в глобальной системе координат
* @param point глобальные координаты точки приложения силы
*/
public function addForceAtPoint(force:Point3D, point:Point3D):void {
forceAccum.x += force.x;
forceAccum.y += force.y;
forceAccum.z += force.z;
var x:Number = point.x - position.x;
var y:Number = point.y - position.y;
var z:Number = point.z - position.z;
torqueAccum.x += y*force.z - z*force.y;
torqueAccum.y += z*force.x - x*force.z;
torqueAccum.z += x*force.y - y*force.x;
setAwake();
}
/**
* Добавляет локальную силу, приложенную в заданной локальной точке тела.
*
* @param localForce вектор локальной силы
* @param localPoint локальные координаты точки приложения силы
*/
public function addLocalForce(localForce:Point3D, localPoint:Point3D):void {
// Трансформация локального вектора силы в глобальный
var globalForceX:Number = transformMatrix.a*localForce.x + transformMatrix.b*localForce.y + transformMatrix.c*localForce.z;
var globalForceY:Number = transformMatrix.e*localForce.x + transformMatrix.f*localForce.y + transformMatrix.g*localForce.z;
var globalForceZ:Number = transformMatrix.i*localForce.x + transformMatrix.j*localForce.y + transformMatrix.k*localForce.z;
forceAccum.x += globalForceX;
forceAccum.y += globalForceY;
forceAccum.z += globalForceZ;
// Получение глобального вектора плеча силы
var globalPointX:Number = transformMatrix.a*localPoint.x + transformMatrix.b*localPoint.y + transformMatrix.c*localPoint.z;
var globalPointY:Number = transformMatrix.e*localPoint.x + transformMatrix.f*localPoint.y + transformMatrix.g*localPoint.z;
var globalPointZ:Number = transformMatrix.i*localPoint.x + transformMatrix.j*localPoint.y + transformMatrix.k*localPoint.z;
// Приращение момента силы
torqueAccum.x += globalPointY*globalForceZ - globalPointZ*globalForceY;
torqueAccum.y += globalPointZ*globalForceX - globalPointX*globalForceZ;
torqueAccum.z += globalPointX*globalForceY - globalPointY*globalForceX;
setAwake();
}
/**
* Добавляет момент силы.
*
* @param torque вектор момента силы
*/
public function addTorque(torque:Point3D):void {
torqueAccum.x += torque.x;
torqueAccum.y += torque.y;
torqueAccum.z += torque.z;
setAwake();
}
/**
* Добавляет момент силы, заданный покомпонентно.
*/
public function addTorqueComponents(x:Number, y:Number, z:Number):void {
torqueAccum.x += x;
torqueAccum.y += y;
torqueAccum.z += z;
setAwake();
}
/**
* Вычисляет векторные параметры тела в глобальной системе координат.
*/
public function calculateDerivedData():void {
// inlined position.normalize()
var d:Number = orientation.w*orientation.w + orientation.x*orientation.x + orientation.y*orientation.y + orientation.z*orientation.z;
if (d == 0) {
orientation.w = 1;
} else {
d = 1/Math.sqrt(d);
orientation.w *= d;
orientation.x *= d;
orientation.y *= d;
orientation.z *= d;
}
// Вычисление глобальной матрицы трансформации
orientation.toMatrix3D(transformMatrix);
transformMatrix.d = position.x;
transformMatrix.h = position.y;
transformMatrix.l = position.z;
// Расчёт обратного тензора инерции в мировых координатах
var a:Number = transformMatrix.a*inverseInertiaTensor.a + transformMatrix.b*inverseInertiaTensor.e + transformMatrix.c*inverseInertiaTensor.i;
var b:Number = transformMatrix.a*inverseInertiaTensor.b + transformMatrix.b*inverseInertiaTensor.f + transformMatrix.c*inverseInertiaTensor.j;
var c:Number = transformMatrix.a*inverseInertiaTensor.c + transformMatrix.b*inverseInertiaTensor.g + transformMatrix.c*inverseInertiaTensor.k;
var e:Number = transformMatrix.e*inverseInertiaTensor.a + transformMatrix.f*inverseInertiaTensor.e + transformMatrix.g*inverseInertiaTensor.i;
var f:Number = transformMatrix.e*inverseInertiaTensor.b + transformMatrix.f*inverseInertiaTensor.f + transformMatrix.g*inverseInertiaTensor.j;
var g:Number = transformMatrix.e*inverseInertiaTensor.c + transformMatrix.f*inverseInertiaTensor.g + transformMatrix.g*inverseInertiaTensor.k;
var i:Number = transformMatrix.i*inverseInertiaTensor.a + transformMatrix.j*inverseInertiaTensor.e + transformMatrix.k*inverseInertiaTensor.i;
var j:Number = transformMatrix.i*inverseInertiaTensor.b + transformMatrix.j*inverseInertiaTensor.f + transformMatrix.k*inverseInertiaTensor.j;
var k:Number = transformMatrix.i*inverseInertiaTensor.c + transformMatrix.j*inverseInertiaTensor.g + transformMatrix.k*inverseInertiaTensor.k;
// Здесь подразумевается, что матрица трансформации тела не содержит масштабирования
// Произведение [transformMatrix*inverseInertiaTensor]*transpose(transformMatrix)
inverseInertiaTensorWorld.a = a*transformMatrix.a + b*transformMatrix.b + c*transformMatrix.c;
inverseInertiaTensorWorld.b = a*transformMatrix.e + b*transformMatrix.f + c*transformMatrix.g;
inverseInertiaTensorWorld.c = a*transformMatrix.i + b*transformMatrix.j + c*transformMatrix.k;
inverseInertiaTensorWorld.e = e*transformMatrix.a + f*transformMatrix.b + g*transformMatrix.c;
inverseInertiaTensorWorld.f = e*transformMatrix.e + f*transformMatrix.f + g*transformMatrix.g;
inverseInertiaTensorWorld.g = e*transformMatrix.i + f*transformMatrix.j + g*transformMatrix.k;
inverseInertiaTensorWorld.i = i*transformMatrix.a + j*transformMatrix.b + k*transformMatrix.c;
inverseInertiaTensorWorld.j = i*transformMatrix.e + j*transformMatrix.f + k*transformMatrix.g;
inverseInertiaTensorWorld.k = i*transformMatrix.i + j*transformMatrix.j + k*transformMatrix.k;
}
/**
* Интегрирует динамические характеристики и обновляет параметры состояния тела.
*
* @param time временной шаг интегрирования
*/
public function integrate(time:Number):void {
if (!awake) {
return;
}
lastFrameAcceleration.x = acceleration.x + forceAccum.x*inverseMass;
lastFrameAcceleration.y = acceleration.y + forceAccum.y*inverseMass;
lastFrameAcceleration.z = acceleration.z + forceAccum.z*inverseMass;
// angularAcceleration = inverseInertiaTensorWorld*torqueAccum
angularAcceleration.x = inverseInertiaTensorWorld.a*torqueAccum.x + inverseInertiaTensorWorld.b*torqueAccum.y + inverseInertiaTensorWorld.c*torqueAccum.z;
angularAcceleration.y = inverseInertiaTensorWorld.e*torqueAccum.x + inverseInertiaTensorWorld.f*torqueAccum.y + inverseInertiaTensorWorld.g*torqueAccum.z;
angularAcceleration.z = inverseInertiaTensorWorld.i*torqueAccum.x + inverseInertiaTensorWorld.j*torqueAccum.y + inverseInertiaTensorWorld.k*torqueAccum.z;
var d:Number = Math.pow(linearDamping, time);
var dtime:Number = d*time;
velocity.x += lastFrameAcceleration.x*dtime;
velocity.y += lastFrameAcceleration.y*dtime;
velocity.z += lastFrameAcceleration.z*dtime;
rotation.x += angularAcceleration.x*dtime;
rotation.y += angularAcceleration.y*dtime;
rotation.z += angularAcceleration.z*dtime;
rotation.x += angularVelocity.x*time;
rotation.y += angularVelocity.y*time;
rotation.z += angularVelocity.z*time;
position.x += velocity.x*time;
position.y += velocity.y*time;
position.z += velocity.z*time;
orientation.addScaledVector(rotation, time);
// TODO: Нужно ли двойное уменьшение скоростей?
velocity.x *= d;
velocity.y *= d;
velocity.z *= d;
rotation.x *= d;
rotation.y *= d;
rotation.z *= d;
calculateDerivedData();
// inlined clearAccumulators()
forceAccum.x = 0;
forceAccum.y = 0;
forceAccum.z = 0;
torqueAccum.x = 0;
torqueAccum.y = 0;
torqueAccum.z = 0;
// Усыпление тела при необходимости
if (canSleep) {
var currentMotion:Number = velocity.x*velocity.x + velocity.y*velocity.y + velocity.z*velocity.z +
rotation.x*rotation.x + rotation.y*rotation.y + rotation.z*rotation.z;
var bias:Number = Math.pow(baseBias, time);
motion = bias*motion + (1 - bias)*currentMotion;
// trace("[RigidBody::integrate]", name, "motion", motion, "motionEpsilon", motionEpsilon);
if (motion < motionThreshold) {
setAwake(false);
} else {
if (motion > motionThreshold2) {
motion = motionThreshold2;
}
}
}
}
/**
*
*/
public function clearAccumulators():void {
forceAccum.x = 0;
forceAccum.y = 0;
forceAccum.z = 0;
torqueAccum.x = 0;
torqueAccum.y = 0;
torqueAccum.z = 0;
}
}
}

View File

@@ -0,0 +1,550 @@
package alternativa.physics.rigid {
import alternativa.physics.*;
import alternativa.types.Matrix3D;
import alternativa.types.Point3D;
use namespace altphysics;
/**
* Класс описывает контакт между телами. Контакты могут составлять двунаправленный список.
*/
public class RigidBodyContact {
private var tmpVelocityVector:Point3D = new Point3D();
private var impulse:Point3D = new Point3D();
public static var velocityLimit:Number = 1;
/**
* Ссылка на следующий контакт в списке.
*/
public var next:RigidBodyContact;
/**
* Ссылка на предыдущий контакт в списке.
*/
public var prev:RigidBodyContact;
/**
* Индекс контакта в списке.
*/
public var index:int;
/**
* Первое тело, к которому относится контакт.
*/
public var body1:RigidBody;
/**
* Первое тело, к которому относится контакт (может быть null).
*/
public var body2:RigidBody;
/**
* Точка контакта.
*/
public var contactPoint:Point3D = new Point3D();
/**
* Нормаль контакта. Нормаль направлена от второго тела к первому. В локальной системе координат контакта
* нормаль является базисным вектором, задающим направление оси X.
*/
public var contactNormal:Point3D = new Point3D();
/**
* Коэффициент взаимного трения в точке контакта.
*/
public var friction:Number = 0;
/**
* Коэффициент упругости удара.
*/
public var restitution:Number = 0;
/**
* Глубина взаимного проникновения тел в точке контакта.
*/
public var penetration:Number;
public var timeStamp:int;
/**
* Матрица перехода из локальной системы координат контакта в мировую систему координат без компонентов параллельного переноса.
* Матрица задаёт базис локальной системы координат контакта.
*/
altphysics var contactToWorld:Matrix3D = new Matrix3D();
/**
* Относительная скорость в точке контакта. Вычисляется в методе calculateInternals().
*/
altphysics var contactVelocity:Point3D = new Point3D();
/**
* Приращение скорости в точке контакта по направлению нормали, необходимое для прекращения сближения. Вычисляется в методе calculateInternals().
*/
altphysics var desiredDeltaVelocity:Number;
/**
* Координаты точки контакта относительныо первого тела. Вычисляется в методе calculateInternals().
*/
altphysics var relativeContactPosition1:Point3D = new Point3D();
/**
* Координаты точки контакта относительныо второго тела. Вычисляется в методе calculateInternals().
*/
altphysics var relativeContactPosition2:Point3D = new Point3D();
/**
*
* @param index
*/
public function RigidBodyContact(index:int) {
this.index = index;
}
/**
*
* @param body1
* @param body2
* @param friction
* @param restitution
*/
public function setBodyData(body1:RigidBody, body2:RigidBody, friction:Number, restitution:Number):void {
this.body1 = body1;
this.body2 = body2;
this.friction = friction;
this.restitution = restitution;
}
/**
*
*/
public function matchAwakeState():void {
if (body2 == null) {
return;
}
if (body1.awake != body2.awake) {
if (body1.awake) {
body2.setAwake();
} else {
body1.setAwake();
}
}
}
/**
* Вычисляет и сохраняет базис локальной системы координат контакта.
*/
public function calculateContactBasis():void {
// Нормаль является ортом X локальной системы координат
contactToWorld.a = contactNormal.x;
contactToWorld.e = contactNormal.y;
contactToWorld.i = contactNormal.z;
var nx:Number = contactNormal.x > 0 ? contactNormal.x : -contactNormal.x;
var ny:Number = contactNormal.y > 0 ? contactNormal.y : -contactNormal.y;
var s:Number;
if (nx > ny) {
// Нормаль ближе к глобальной оси X, используем ось Y для вычислений. Локальный орт Y находится как результат векторного произведения globalY x contactNormal.
s = 1/Math.sqrt(nx*nx + contactNormal.z*contactNormal.z);
contactToWorld.b = contactNormal.z*s;
contactToWorld.f = 0;
contactToWorld.j = -contactNormal.x*s;
// Локальный орт Z находится как результат векторного произведения contactNormal x localY.
contactToWorld.c = contactNormal.y*contactToWorld.j;
contactToWorld.g = contactNormal.z*contactToWorld.b - contactNormal.x*contactToWorld.j;
contactToWorld.k = -contactNormal.y*contactToWorld.b;
} else {
// Нормаль ближе к глобальной оси Y, используем ось X для вычислений. Локальный орт Y находится как результат векторного произведения globalX x contactNormal.
s = 1/Math.sqrt(ny*ny + contactNormal.z*contactNormal.z);
contactToWorld.b = 0;
contactToWorld.f = -contactNormal.z*s;
contactToWorld.j = contactNormal.y*s;
// Локальный орт Z находится как результат векторного произведения contactNormal x localY.
contactToWorld.c = contactNormal.y*contactToWorld.j - contactNormal.z*contactToWorld.f;
contactToWorld.g = -contactNormal.x*contactToWorld.j;
contactToWorld.k = contactNormal.x*contactToWorld.f;
}
}
/**
* Вычисляет скорость точки заданного тела в точке контакта в его локальной системе координат.
*
* @param body тело, для которого выполняются вычисления
* @param relativeContactPosition положение точки контакта относительно тела
* @param time временной шаг
* @param result в эту переменную записывается результат
*/
public function calculateLocalVelocity(body:RigidBody, relativeContactPosition:Point3D, time:Number, result:Point3D):void {
// Расчёт прироста скорости за последний кадр от действующих сил
var x:Number = body.lastFrameAcceleration.x*time;
var y:Number = body.lastFrameAcceleration.y*time;
var z:Number = body.lastFrameAcceleration.z*time;
// Прирост скорости в системе координат контакта
// Убираем прирост скорости в направлении контакта
var accVelocityY:Number = contactToWorld.b*x + contactToWorld.f*y + contactToWorld.j*z;
var accVelocityZ:Number = contactToWorld.c*x + contactToWorld.g*y + contactToWorld.k*z;
// Скорость точки контакта в мировой системе координат
// Скорость за счёт вращения тела
var rotation:Point3D = body.rotation;
x = rotation.y*relativeContactPosition.z - rotation.z*relativeContactPosition.y + body.velocity.x;
y = rotation.z*relativeContactPosition.x - rotation.x*relativeContactPosition.z + body.velocity.y;
z = rotation.x*relativeContactPosition.y - rotation.y*relativeContactPosition.x + body.velocity.z;
// Скорость точки контакта в системе координат контакта
// Касательные компоненты прироста скорости добавляются к скорости точки контакта. В дальнейшем они либо уберутся силой трения,
// либо уменьшатся ею.
result.x = contactToWorld.a*x + contactToWorld.e*y + contactToWorld.i*z;
result.y = contactToWorld.b*x + contactToWorld.f*y + contactToWorld.j*z + accVelocityY;
result.z = contactToWorld.c*x + contactToWorld.g*y + contactToWorld.k*z + accVelocityZ;
}
/**
* Вычисляет и устанавливает внутреннее значение для требуемого изменения относительной скорости вдоль нормали контакта.
* С реакциями опоры разбираемся путём вычитания из скорости сближения прироста скорости, полученного во время последнего кадра.
*
* @param time
*/
public function calculateDesiredVelocity(time:Number):void {
// Вычисляем проекцию прироста скорости за текущий кадр на нормаль контакта. Для относительного прироста скорости, вызывающего
// сближение объектов, значение будет отрицательным.
var velocityFromAcc:Number = 0;
if (body1.awake) {
velocityFromAcc = (body1.lastFrameAcceleration.x*contactNormal.x + body1.lastFrameAcceleration.y*contactNormal.y + body1.lastFrameAcceleration.z*contactNormal.z)*time;
}
if (body2 != null && body2.awake) {
velocityFromAcc -= (body2.lastFrameAcceleration.x*contactNormal.x + body2.lastFrameAcceleration.y*contactNormal.y + body2.lastFrameAcceleration.z*contactNormal.z)*time;
}
// Если относительная скорость вдоль нормали контакта слишком мала, игнорируем её
var thisRestitution:Number = restitution;
if (contactVelocity.x > -velocityLimit && contactVelocity.x < velocityLimit) {
thisRestitution = 0;
}
// Вычисляем дельту для скорости отскока, убрав влияние прироста скорости за последний кадр
desiredDeltaVelocity = -contactVelocity.x - thisRestitution*(contactVelocity.x - velocityFromAcc);
}
/**
* Расчитывает внутренние данные о контакта на основе данных о его состоянии. Вызывается до того, как будет запущен
* алгоритм разрешения контактов. Эта функция в нормальных условиях никогда не должна вызываться вручную.
*
* @param time
*/
public function calculateInternals(time:Number):void {
// Расчёт базиса системы координат контакта
// === inlined calculateContactBasis();
contactToWorld.a = contactNormal.x;
contactToWorld.e = contactNormal.y;
contactToWorld.i = contactNormal.z;
var nx:Number = contactNormal.x > 0 ? contactNormal.x : -contactNormal.x;
var ny:Number = contactNormal.y > 0 ? contactNormal.y : -contactNormal.y;
var s:Number;
if (nx > ny) {
// Нормаль ближе к глобальной оси X, используем ось Y для вычислений. Локальный орт Y находится как результат векторного произведения globalY x contactNormal.
s = 1/Math.sqrt(nx*nx + contactNormal.z*contactNormal.z);
contactToWorld.b = contactNormal.z*s;
contactToWorld.f = 0;
contactToWorld.j = -contactNormal.x*s;
// Локальный орт Z находится как результат векторного произведения contactNormal x localY.
contactToWorld.c = contactNormal.y*contactToWorld.j;
contactToWorld.g = contactNormal.z*contactToWorld.b - contactNormal.x*contactToWorld.j;
contactToWorld.k = -contactNormal.y*contactToWorld.b;
} else {
// Нормаль ближе к глобальной оси Y, используем ось X для вычислений. Локальный орт Y находится как результат векторного произведения globalX x contactNormal.
s = 1/Math.sqrt(ny*ny + contactNormal.z*contactNormal.z);
contactToWorld.b = 0;
contactToWorld.f = -contactNormal.z*s;
contactToWorld.j = contactNormal.y*s;
// Локальный орт Z находится как результат векторного произведения contactNormal x localY.
contactToWorld.c = contactNormal.y*contactToWorld.j - contactNormal.z*contactToWorld.f;
contactToWorld.g = -contactNormal.x*contactToWorld.j;
contactToWorld.k = contactNormal.x*contactToWorld.f;
}
// === end inlined
// Расчёт локальной скорости в точке контакта для первого тела
relativeContactPosition1.x = contactPoint.x - body1.position.x;
relativeContactPosition1.y = contactPoint.y - body1.position.y;
relativeContactPosition1.z = contactPoint.z - body1.position.z;
calculateLocalVelocity(body1, relativeContactPosition1, time, contactVelocity);
// Расчёт локальной скорости в точке контакта для второго тела
if (body2 != null) {
relativeContactPosition2.x = contactPoint.x - body2.position.x;
relativeContactPosition2.y = contactPoint.y - body2.position.y;
relativeContactPosition2.z = contactPoint.z - body2.position.z;
calculateLocalVelocity(body2, relativeContactPosition2, time, tmpVelocityVector);
// Расчёт относительной скорости в точке контакта
contactVelocity.x -= tmpVelocityVector.x;
contactVelocity.y -= tmpVelocityVector.y;
contactVelocity.z -= tmpVelocityVector.z;
}
// Расчёт вектора изменения скорости, требуемого для разрешения контакта
calculateDesiredVelocity(time);
}
/**
* Выполняет разделение контакта посредством импульса, пропорционально инерции тел.
*/
public function applyVelocityChange(velocityChange1:Point3D, velocityChange2:Point3D, rotationChange1:Point3D, rotationChange2:Point3D):void {
var inverseInertiaTensor1:Matrix3D = body1.inverseInertiaTensorWorld;
var inverseInertiaTensor2:Matrix3D;
if (body2 != null) {
inverseInertiaTensor2 = body2.inverseInertiaTensorWorld;
}
if (friction == 0) {
calculateFrictionlessImpulse(inverseInertiaTensor1, inverseInertiaTensor2, impulse);
} else {
calculateFrictionImpulse(inverseInertiaTensor1, inverseInertiaTensor2, impulse);
}
// Переведём импульс в мировые координаты
var x:Number = impulse.x;
var y:Number = impulse.y;
var z:Number = impulse.z;
impulse.x = contactToWorld.a*x + contactToWorld.b*y + contactToWorld.c*z;
impulse.y = contactToWorld.e*x + contactToWorld.f*y + contactToWorld.g*z;
impulse.z = contactToWorld.i*x + contactToWorld.j*y + contactToWorld.k*z;
// Разделим импульс на вращательную и линейную часть
x = relativeContactPosition1.y*impulse.z - relativeContactPosition1.z*impulse.y;
y = relativeContactPosition1.z*impulse.x - relativeContactPosition1.x*impulse.z;
z = relativeContactPosition1.x*impulse.y - relativeContactPosition1.y*impulse.x;
rotationChange1.x = inverseInertiaTensor1.a*x + inverseInertiaTensor1.b*y + inverseInertiaTensor1.c*z;
rotationChange1.y = inverseInertiaTensor1.e*x + inverseInertiaTensor1.f*y + inverseInertiaTensor1.g*z;
rotationChange1.z = inverseInertiaTensor1.i*x + inverseInertiaTensor1.j*y + inverseInertiaTensor1.k*z;
velocityChange1.x = impulse.x*body1.inverseMass;
velocityChange1.y = impulse.y*body1.inverseMass;
velocityChange1.z = impulse.z*body1.inverseMass;
body1.velocity.x += velocityChange1.x;
body1.velocity.y += velocityChange1.y;
body1.velocity.z += velocityChange1.z;
body1.rotation.x += rotationChange1.x;
body1.rotation.y += rotationChange1.y;
body1.rotation.z += rotationChange1.z;
if (body2 != null) {
x = relativeContactPosition2.z*impulse.y - relativeContactPosition2.y*impulse.z;
y = relativeContactPosition2.x*impulse.z - relativeContactPosition2.z*impulse.x;
z = relativeContactPosition2.y*impulse.x - relativeContactPosition2.x*impulse.y;
rotationChange2.x = inverseInertiaTensor2.a*x + inverseInertiaTensor2.b*y + inverseInertiaTensor2.c*z;
rotationChange2.y = inverseInertiaTensor2.e*x + inverseInertiaTensor2.f*y + inverseInertiaTensor2.g*z;
rotationChange2.z = inverseInertiaTensor2.i*x + inverseInertiaTensor2.j*y + inverseInertiaTensor2.k*z;
velocityChange2.x = -impulse.x*body2.inverseMass;
velocityChange2.y = -impulse.y*body2.inverseMass;
velocityChange2.z = -impulse.z*body2.inverseMass;
body2.velocity.x += velocityChange2.x;
body2.velocity.y += velocityChange2.y;
body2.velocity.z += velocityChange2.z;
body2.rotation.x += rotationChange2.x;
body2.rotation.y += rotationChange2.y;
body2.rotation.z += rotationChange2.z;
}
}
/**
* Вычисляет импульс, необходимый для коррекции скоростей в точке контакта в отсутствие сил трения.
*/
public function calculateFrictionlessImpulse(inverseInertiaTensor1:Matrix3D, inverseInertiaTensor2:Matrix3D, result:Point3D):void {
tmpVelocityVector.x = relativeContactPosition1.y*contactNormal.z - relativeContactPosition1.z*contactNormal.y;
tmpVelocityVector.y = relativeContactPosition1.z*contactNormal.x - relativeContactPosition1.x*contactNormal.z;
tmpVelocityVector.z = relativeContactPosition1.x*contactNormal.y - relativeContactPosition1.y*contactNormal.x;
var x:Number = inverseInertiaTensor1.a*tmpVelocityVector.x + inverseInertiaTensor1.b*tmpVelocityVector.y + inverseInertiaTensor1.c*tmpVelocityVector.z;
var y:Number = inverseInertiaTensor1.e*tmpVelocityVector.x + inverseInertiaTensor1.f*tmpVelocityVector.y + inverseInertiaTensor1.g*tmpVelocityVector.z;
var z:Number = inverseInertiaTensor1.i*tmpVelocityVector.x + inverseInertiaTensor1.j*tmpVelocityVector.y + inverseInertiaTensor1.k*tmpVelocityVector.z;
tmpVelocityVector.x = y*relativeContactPosition1.z - z*relativeContactPosition1.y;
tmpVelocityVector.y = z*relativeContactPosition1.x - x*relativeContactPosition1.z;
tmpVelocityVector.z = x*relativeContactPosition1.y - y*relativeContactPosition1.x;
var deltaVelocity:Number = tmpVelocityVector.x*contactNormal.x + tmpVelocityVector.y*contactNormal.y + tmpVelocityVector.z*contactNormal.z + body1.inverseMass;
if (body2 != null) {
tmpVelocityVector.x = relativeContactPosition2.y*contactNormal.z - relativeContactPosition2.z*contactNormal.y;
tmpVelocityVector.y = relativeContactPosition2.z*contactNormal.x - relativeContactPosition2.x*contactNormal.z;
tmpVelocityVector.z = relativeContactPosition2.x*contactNormal.y - relativeContactPosition2.y*contactNormal.x;
x = inverseInertiaTensor2.a*tmpVelocityVector.x + inverseInertiaTensor2.b*tmpVelocityVector.y + inverseInertiaTensor2.c*tmpVelocityVector.z;
y = inverseInertiaTensor2.e*tmpVelocityVector.x + inverseInertiaTensor2.f*tmpVelocityVector.y + inverseInertiaTensor2.g*tmpVelocityVector.z;
z = inverseInertiaTensor2.i*tmpVelocityVector.x + inverseInertiaTensor2.j*tmpVelocityVector.y + inverseInertiaTensor2.k*tmpVelocityVector.z;
tmpVelocityVector.x = y*relativeContactPosition2.z - z*relativeContactPosition2.y;
tmpVelocityVector.y = z*relativeContactPosition2.x - x*relativeContactPosition2.z;
tmpVelocityVector.z = x*relativeContactPosition2.y - y*relativeContactPosition2.x;
deltaVelocity += tmpVelocityVector.x*contactNormal.x + tmpVelocityVector.y*contactNormal.y + tmpVelocityVector.z*contactNormal.z + body2.inverseMass;
}
result.x = desiredDeltaVelocity/deltaVelocity;
result.y = 0;
result.z = 0;
}
private var impulseToTorque:Matrix3D = new Matrix3D();
private var deltaVelWorldMatrix:Matrix3D = new Matrix3D();
private var deltaVelWorldMatrix2:Matrix3D = new Matrix3D();
/**
* Вычисляет импульс, необходимый для коррекции скоростей в точке контакта при наличии сил трения.
*/
public function calculateFrictionImpulse(inverseInertiaTensor1:Matrix3D, inverseInertiaTensor2:Matrix3D, result:Point3D):void {
var inverseMass:Number = body1.inverseMass;
relativeContactPosition1.createSkewSymmetricMatrix(impulseToTorque);
deltaVelWorldMatrix.copy(impulseToTorque);
deltaVelWorldMatrix.inverseCombine(inverseInertiaTensor1);
deltaVelWorldMatrix.inverseCombine(impulseToTorque);
deltaVelWorldMatrix.multByScalar(-1);
if (body2 != null) {
relativeContactPosition2.createSkewSymmetricMatrix(impulseToTorque);
deltaVelWorldMatrix2.copy(impulseToTorque);
deltaVelWorldMatrix2.inverseCombine(inverseInertiaTensor2);
deltaVelWorldMatrix2.inverseCombine(impulseToTorque);
deltaVelWorldMatrix2.multByScalar(-1);
deltaVelWorldMatrix.add(deltaVelWorldMatrix2);
inverseMass += body2.inverseMass;
}
deltaVelWorldMatrix2.copy(contactToWorld);
deltaVelWorldMatrix2.transpose();
deltaVelWorldMatrix2.inverseCombine(deltaVelWorldMatrix);
deltaVelWorldMatrix2.inverseCombine(contactToWorld);
deltaVelWorldMatrix2.a += inverseMass;
deltaVelWorldMatrix2.f += inverseMass;
deltaVelWorldMatrix2.k += inverseMass;
deltaVelWorldMatrix.copy(deltaVelWorldMatrix2);
deltaVelWorldMatrix.invert();
result.reset(desiredDeltaVelocity, -contactVelocity.y, -contactVelocity.z);
result.transform(deltaVelWorldMatrix);
var planarImpulse:Number = Math.sqrt(result.y*result.y + result.z*result.z);
if (planarImpulse > result.x*friction) {
result.y /= planarImpulse;
result.z /= planarImpulse;
result.x = desiredDeltaVelocity/(deltaVelWorldMatrix2.a + deltaVelWorldMatrix2.b*friction*result.y + deltaVelWorldMatrix2.c*friction*result.z);
result.y *= friction*result.x;
result.z *= friction*result.x;
}
}
private var angularInertiaWorld:Point3D = new Point3D();
/**
* Выполняет разделение контакта с учётом инертности тел.
*/
public function applyPositionChange(linearChange1:Point3D, linearChange2:Point3D, angularChange1:Point3D, angularChange2:Point3D, penetration:Number):void {
var totalInertia:Number = 0;
var angularInertia1:Number;
var angularInertia2:Number;
var linearInertia1:Number;
var linearInertia2:Number;
// body1
angularInertiaWorld.x = relativeContactPosition1.y*contactNormal.z - relativeContactPosition1.z*contactNormal.y;
angularInertiaWorld.y = relativeContactPosition1.z*contactNormal.x - relativeContactPosition1.x*contactNormal.z;
angularInertiaWorld.z = relativeContactPosition1.x*contactNormal.y - relativeContactPosition1.y*contactNormal.x;
var m:Matrix3D = body1.inverseInertiaTensorWorld;
var x:Number = m.a*angularInertiaWorld.x + m.b*angularInertiaWorld.y + m.c*angularInertiaWorld.z;
var y:Number = m.e*angularInertiaWorld.x + m.f*angularInertiaWorld.y + m.g*angularInertiaWorld.z;
var z:Number = m.i*angularInertiaWorld.x + m.j*angularInertiaWorld.y + m.k*angularInertiaWorld.z;
angularInertiaWorld.x = y*relativeContactPosition1.z - z*relativeContactPosition1.y;
angularInertiaWorld.y = z*relativeContactPosition1.x - x*relativeContactPosition1.z;
angularInertiaWorld.z = x*relativeContactPosition1.y - y*relativeContactPosition1.x;
angularInertia1 = angularInertiaWorld.x*contactNormal.x + angularInertiaWorld.y*contactNormal.y + angularInertiaWorld.z*contactNormal.z;
linearInertia1 = body1.inverseMass;
totalInertia = linearInertia1 + angularInertia1;
if (body2 != null) {
angularInertiaWorld.x = relativeContactPosition2.y*contactNormal.z - relativeContactPosition2.z*contactNormal.y;
angularInertiaWorld.y = relativeContactPosition2.z*contactNormal.x - relativeContactPosition2.x*contactNormal.z;
angularInertiaWorld.z = relativeContactPosition2.x*contactNormal.y - relativeContactPosition2.y*contactNormal.x;
m = body2.inverseInertiaTensorWorld;
x = m.a*angularInertiaWorld.x + m.b*angularInertiaWorld.y + m.c*angularInertiaWorld.z;
y = m.e*angularInertiaWorld.x + m.f*angularInertiaWorld.y + m.g*angularInertiaWorld.z;
z = m.i*angularInertiaWorld.x + m.j*angularInertiaWorld.y + m.k*angularInertiaWorld.z;
angularInertiaWorld.x = y*relativeContactPosition2.z - z*relativeContactPosition2.y;
angularInertiaWorld.y = z*relativeContactPosition2.x - x*relativeContactPosition2.z;
angularInertiaWorld.z = x*relativeContactPosition2.y - y*relativeContactPosition2.x;
angularInertia2 = angularInertiaWorld.x*contactNormal.x + angularInertiaWorld.y*contactNormal.y + angularInertiaWorld.z*contactNormal.z;
linearInertia2 = body2.inverseMass;
totalInertia += linearInertia2 + angularInertia2;
}
// body1
applyPostionChangeToBody(body1, relativeContactPosition1, angularInertia1, linearInertia1, totalInertia, 1, angularChange1, linearChange1);
if (body2 != null) {
applyPostionChangeToBody(body2, relativeContactPosition2, angularInertia2, linearInertia2, totalInertia, -1, angularChange2, linearChange2);
}
}
private var projection:Point3D = new Point3D();
/**
*
* @param body
* @param relativeContactPosition
* @param angularInertia
* @param linearInertia
* @param totalInertia
* @param sign
* @param angularChange
* @param linearChange
*/
private function applyPostionChangeToBody(body:RigidBody, relativeContactPosition:Point3D, angularInertia:Number, linearInertia:Number, totalInertia:Number, sign:Number, angularChange:Point3D, linearChange:Point3D):void {
var angularLimit:Number = 0.2;
var angularMove:Number = sign*penetration*angularInertia/totalInertia;
var linearMove:Number = sign*penetration*linearInertia/totalInertia;
projection.copy(contactNormal);
projection.multiply(-relativeContactPosition.dot(contactNormal));
projection.add(relativeContactPosition);
var maxMagnitude:Number = angularLimit*projection.length;
var totalMove:Number;
if (angularMove < -maxMagnitude) {
totalMove = angularMove + linearMove;
angularMove = -maxMagnitude;
linearMove = totalMove - angularMove;
} else if (angularMove > maxMagnitude) {
totalMove = angularMove + linearMove;
angularMove = maxMagnitude;
linearMove = totalMove - angularMove;
}
if (angularMove == 0) {
angularChange.reset();
} else {
angularChange.copy(relativeContactPosition);
angularChange.cross(contactNormal);
angularChange.transform(body.inverseInertiaTensorWorld);
angularChange.multiply(angularMove/angularInertia);
}
linearChange.copy(contactNormal);
linearChange.multiply(linearMove);
body.position.add(linearChange);
body.orientation.addScaledVector(angularChange, 1);
if (!body.awake) {
body.calculateDerivedData();
}
}
public function toString():String {
return "RigidBodyContact" +
"\n timeStamp: " + timeStamp +
"\n body1: " + body1.getName() +
"\n body2: " + (body2 == null ? "" : body2.getName()) +
"\n contactPoint: " + contactPoint +
"\n contactNormal: " + contactNormal +
"\n restitution: " + restitution +
"\n penetration: " + penetration +
"\n friction: " + friction;
}
}
}

View File

@@ -0,0 +1,22 @@
package alternativa.physics.rigid {
/**
* Генератор контактов для твёрдых тел.
*/
public class RigidBodyContactGenerator {
/**
* Следующий генератор в списке.
*/
public var next:RigidBodyContactGenerator;
/**
* Метод создаёт контакты.
*
* @param contact первый свободный для записи контакт в списке контактов мира
* @return следующий свободный для записи контакт после окончания работы метода. Значение null означает, что доступные контакты исчерпаны.
*/
public function addContacts(contact:RigidBodyContact):RigidBodyContact {
return null;
}
}
}

View File

@@ -0,0 +1,237 @@
package alternativa.physics.rigid {
import alternativa.physics.*;
import alternativa.types.Point3D;
use namespace altphysics;
/**
*
*/
public class RigidBodyContactResolver {
private var velocityIterations:int;
private var positionIterations:int;
private var velocityEpsilon:Number;
private var positionEpsilon:Number;
private var velocityIterationsUsed:int;
private var positionIterationsUsed:int;
private var linearChange1:Point3D = new Point3D();
private var linearChange2:Point3D = new Point3D();
private var angularChange1:Point3D = new Point3D();
private var angularChange2:Point3D = new Point3D();
private var velocityChange1:Point3D = new Point3D();
private var velocityChange2:Point3D = new Point3D();
private var rotationChange1:Point3D = new Point3D();
private var rotationChange2:Point3D = new Point3D();
private var deltaVector:Point3D = new Point3D();
/**
*
* @param velocityIterations
* @param positionIterations
* @param velocityEpsilon
* @param positionEpsilon
*/
public function RigidBodyContactResolver(velocityIterations:int, positionIterations:int, velocityEpsilon:Number = 0.001, positionEpsilon:Number = 0.001) {
this.velocityIterations = velocityIterations;
this.positionIterations = positionIterations;
this.velocityEpsilon = velocityEpsilon;
this.positionEpsilon = positionEpsilon;
}
/**
*
* @param velocityIterations
* @param positionEpsilon
*/
public function setIterations(velocityIterations:int, positionIterations:int):void {
this.velocityIterations = velocityIterations;
this.positionIterations = positionIterations;
}
/**
*
* @return
*/
private function isValid():Boolean {
return (positionIterations > 0) && (velocityIterations > 0) && (positionEpsilon >= 0) && (velocityEpsilon >= 0);
}
/**
*
* @param firstContact
* @param numContacts
* @param time
*/
public function resolveContacts(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
if (numContacts == 0 || !isValid()) {
return;
}
// Подготовка контактов к обработке
prepareContacts(firstContact, numContacts, time);
// Разделение взаимно проникающих контактов
adjustPositions(firstContact, numContacts, time);
// Коррекция скорости для контактов
adjustVelocities(firstContact, numContacts, time);
}
/**
*
* @param firstContact
* @param numContacts
* @param time
*/
public function prepareContacts(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
var contact:RigidBodyContact = firstContact;
while (contact.index < numContacts) {
// Вычисление внутренних данных контакта, необходимых для дальнейшей работы
contact.calculateInternals(time);
contact = contact.next;
}
}
/**
*
* @param firstContact
* @param numContacts
* @param time
*/
public function adjustPositions(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
positionIterationsUsed = 0;
// Итерационное устранение взаимного проникновения тел в точках контактов в порядке наибольшего пересечения тел
while (positionIterationsUsed < positionIterations) {
var max:Number = positionEpsilon;
var mostSevereContact:RigidBodyContact = null;
// Поиск контакта с наибольшим пересечением
var contact:RigidBodyContact = firstContact;
while (contact.index < numContacts) {
if (contact.penetration > max) {
max = contact.penetration;
mostSevereContact = contact;
}
contact = contact.next;
}
if (mostSevereContact == null) {
// Алгоритм прерывается, если не осталось пересечений
return;
}
// Будим спящего
mostSevereContact.matchAwakeState();
// Разделяем контакт
mostSevereContact.applyPositionChange(linearChange1, linearChange2, angularChange1, angularChange2, max);
// Обновляем величину проникновения в каждом контакте, содержащем обработанные объекты
contact = firstContact;
while (contact.index < numContacts) {
if (contact.body1 == mostSevereContact.body1) {
deltaVector.copy(angularChange1);
deltaVector.cross(contact.relativeContactPosition1);
deltaVector.add(linearChange1);
contact.penetration -= deltaVector.dot(contact.contactNormal);
}
if (contact.body1 == mostSevereContact.body2) {
deltaVector.copy(angularChange2);
deltaVector.cross(contact.relativeContactPosition1);
deltaVector.add(linearChange2);
contact.penetration -= deltaVector.dot(contact.contactNormal);
}
if (contact.body2 != null) {
if (contact.body2 == mostSevereContact.body1) {
deltaVector.copy(angularChange1);
deltaVector.cross(contact.relativeContactPosition2);
deltaVector.add(linearChange1);
contact.penetration += deltaVector.dot(contact.contactNormal);
}
if (contact.body2 == mostSevereContact.body2) {
deltaVector.copy(angularChange2);
deltaVector.cross(contact.relativeContactPosition2);
deltaVector.add(linearChange2);
contact.penetration += deltaVector.dot(contact.contactNormal);
}
}
contact = contact.next;
}
positionIterationsUsed++;
}
}
/**
*
* @param firstContact
* @param numContacts
* @param time
*/
public function adjustVelocities(firstContact:RigidBodyContact, numContacts:int, time:Number):void {
velocityIterationsUsed = 0;
while (velocityIterationsUsed < velocityIterations) {
var max:Number = velocityEpsilon;
var mostSevereContact:RigidBodyContact = null;
var contact:RigidBodyContact = firstContact;
while (contact.index < numContacts) {
if (contact.desiredDeltaVelocity > max) {
max = contact.desiredDeltaVelocity;
mostSevereContact = contact;
}
contact = contact.next;
}
if (mostSevereContact == null) {
return;
}
// Будим спящих
mostSevereContact.matchAwakeState();
// Корректируем скорости
mostSevereContact.applyVelocityChange(velocityChange1, velocityChange2, rotationChange1, rotationChange2);
// Обновляем скорости сближения для каждого контакта, содержащего обработанные объекты
contact = firstContact;
var needRecalculation:Boolean = false;
while (contact.index < numContacts) {
if (contact.body1 == mostSevereContact.body1) {
deltaVector.copy(rotationChange1);
deltaVector.cross(contact.relativeContactPosition1);
deltaVector.add(velocityChange1);
deltaVector.transformTranspose(contact.contactToWorld);
contact.contactVelocity.add(deltaVector);
needRecalculation = true;
}
if (contact.body1 == mostSevereContact.body2) {
deltaVector.copy(rotationChange2);
deltaVector.cross(contact.relativeContactPosition1);
deltaVector.add(velocityChange2);
deltaVector.transformTranspose(contact.contactToWorld);
contact.contactVelocity.add(deltaVector);
needRecalculation = true;
}
if (contact.body2 != null) {
if (contact.body2 == mostSevereContact.body1) {
deltaVector.copy(rotationChange1);
deltaVector.cross(contact.relativeContactPosition2);
deltaVector.add(velocityChange1);
deltaVector.transformTranspose(contact.contactToWorld);
contact.contactVelocity.subtract(deltaVector);
needRecalculation = true;
}
if (contact.body2 == mostSevereContact.body2) {
deltaVector.copy(rotationChange2);
deltaVector.cross(contact.relativeContactPosition2);
deltaVector.add(velocityChange2);
deltaVector.transformTranspose(contact.contactToWorld);
contact.contactVelocity.subtract(deltaVector);
needRecalculation = true;
}
}
if (needRecalculation) {
contact.calculateDesiredVelocity(time);
}
contact = contact.next;
}
velocityIterationsUsed++;
}
}
}
}

View File

@@ -0,0 +1,136 @@
package alternativa.physics.rigid {
public class RigidWorld {
private var firstBody:RigidBody;
private var lastBody:RigidBody;
private var contactGenerators:RigidBodyContactGenerator;
private var lastContactGenerator:RigidBodyContactGenerator;
public var contacts:RigidBodyContact;
private var maxContacts:int;
private var resolver:RigidBodyContactResolver;
public var forceRegistry:BodyForceRegistry = new BodyForceRegistry();
public var calculateIterations:Boolean;
public var numContacts:int;
public function RigidWorld(maxContacts:int, iterations:int) {
this.maxContacts = maxContacts;
resolver = new RigidBodyContactResolver(iterations, iterations);
createContactList(maxContacts);
}
private function createContactList(numContacts:int):void {
if (numContacts > 0) {
var contact:RigidBodyContact = contacts = new RigidBodyContact(0);
for (var i:int = 1; i < numContacts; i++) {
contact.next = new RigidBodyContact(i);
contact = contact.next;
}
}
}
public function addBody(body:RigidBody):void {
if (firstBody == null) {
firstBody = body;
} else {
lastBody.next = body;
}
lastBody = body;
while (lastBody.next != null) {
lastBody = lastBody.next;
}
}
public function removeBody(body:RigidBody):void {
if (body == firstBody) {
firstBody = firstBody.next;
body.next = null;
} else {
var current:RigidBody = firstBody;
var prev:RigidBody = null;
while (current != body && current != null) {
prev = current;
current = current.next;
}
if (current != null) {
if (lastBody == body) {
lastBody = prev;
}
prev.next = current.next;
current.next = null;
}
}
}
public function addContactGenerator(gen:RigidBodyContactGenerator):void {
if (contactGenerators == null) {
contactGenerators = gen;
} else {
lastContactGenerator.next = gen;
}
lastContactGenerator = gen;
while (lastContactGenerator.next != null) {
lastContactGenerator = lastContactGenerator.next;
}
}
public function removeContactGenerator(gen:RigidBodyContactGenerator):void {
if (gen == contactGenerators) {
contactGenerators = gen.next;
gen.next = null;
} else {
var prev:RigidBodyContactGenerator = contactGenerators;
var curr:RigidBodyContactGenerator = contactGenerators.next;
while (curr != gen && curr != null) {
prev = curr;
curr = curr.next;
}
if (curr == gen) {
if (lastContactGenerator == gen) {
lastContactGenerator = prev;
}
prev.next = curr.next;
curr.next = null;
}
}
}
public function startFrame():void {
var body:RigidBody = firstBody;
while (body != null) {
body.clearAccumulators();
body.calculateDerivedData();
body = body.next;
}
}
private function generateContacts():int {
var contact:RigidBodyContact = contacts;
var gen:RigidBodyContactGenerator = contactGenerators;
while (gen != null && contact != null) {
contact = gen.addContacts(contact);
gen = gen.next;
}
return contact == null ? maxContacts : contact.index;
}
public function runPhysics(time:Number):void {
forceRegistry.updateForces(time);
var body:RigidBody = firstBody;
while (body != null) {
body.integrate(time);
body = body.next;
}
numContacts = generateContacts();
// trace("[RigidWorld.runPhysics] numContacts", numContacts);
if (calculateIterations) {
resolver.setIterations(4*numContacts, 4*numContacts);
}
resolver.resolveContacts(contacts, numContacts, time);
}
}
}

View File

@@ -0,0 +1,35 @@
K 25
svn:wc:ra_dav:version-url
V 111
/!svn/ver/4732/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators
END
AnchoredCableContactGenerator.as
K 25
svn:wc:ra_dav:version-url
V 144
/!svn/ver/4732/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators/AnchoredCableContactGenerator.as
END
RigidBoxCollisionData.as
K 25
svn:wc:ra_dav:version-url
V 136
/!svn/ver/4385/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators/RigidBoxCollisionData.as
END
BoxWithBoxContactGenerator.as
K 25
svn:wc:ra_dav:version-url
V 141
/!svn/ver/4403/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators/BoxWithBoxContactGenerator.as
END
SphereWithPlaneContactGenerator.as
K 25
svn:wc:ra_dav:version-url
V 146
/!svn/ver/4385/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators/SphereWithPlaneContactGenerator.as
END
RigidBox.as
K 25
svn:wc:ra_dav:version-url
V 123
/!svn/ver/4603/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators/RigidBox.as
END

View File

@@ -0,0 +1,89 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp9/libraries/AlternativaPhysics/trunk/src/alternativa/physics/rigid/generators
http://svndev.alternativaplatform.com
2008-12-03T05:15:16.843707Z
4732
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
AnchoredCableContactGenerator.as
file
2010-10-28T04:34:31.000000Z
aa5b3540cfe5796ebad67e11f47a4aa9
2008-12-03T05:15:16.843707Z
4732
mike
RigidBoxCollisionData.as
file
2010-10-28T04:34:31.000000Z
607a1d12afedbe3344ae967c6f599fd4
2008-11-26T13:17:08.913613Z
4385
mike
BoxWithBoxContactGenerator.as
file
2010-10-28T04:34:31.000000Z
47da766c6f7ca03fb1622378c740abb4
2008-11-26T16:00:07.127812Z
4403
mike
SphereWithPlaneContactGenerator.as
file
2010-10-28T04:34:31.000000Z
558aa2a28f3cc4a3932c19e5e2ab230c
2008-11-26T13:17:08.913613Z
4385
mike
has-props
RigidBox.as
file
2010-10-28T04:34:31.000000Z
505d9996c9461cc769d2accf7295c871
2008-11-30T13:03:39.561308Z
4603
mike

Some files were not shown because too many files have changed in this diff Show More