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

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<actionScriptProperties mainApplicationPath="AlternativaPhysics2.as" version="3">
<compiler additionalCompilerArguments="" copyDependentFiles="false" enableModuleDebug="true" generateAccessible="false" htmlExpressInstall="true" htmlGenerate="false" htmlHistoryManagement="false" htmlPlayerVersion="10.0.0" htmlPlayerVersionCheck="true" outputFolderPath="bin" sourceFolderPath="src" strict="true" useApolloConfig="false" verifyDigests="true" warn="true">
<compilerSourcePath/>
<libraryPath defaultLinkType="1">
<libraryPathEntry kind="4" path=""/>
</libraryPath>
<sourceAttachmentPath/>
</compiler>
<applications>
<application path="AlternativaPhysics2.as"/>
</applications>
<modules/>
<buildCSSFiles/>
</actionScriptProperties>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<flexLibProperties version="1">
<includeClasses>
<classEntry path="alternativa.physics.collision.CollisionKdTree2D"/>
<classEntry path="alternativa.physics.collision.primitives.CollisionRect"/>
<classEntry path="alternativa.physics.collision.BoxPlaneCollider"/>
<classEntry path="alternativa.physics.rigid.Body"/>
<classEntry path="alternativa.physics.rigid.primitives.RigidBox"/>
<classEntry path="alternativa.physics.collision.primitives.CollisionSphere"/>
<classEntry path="alternativa.physics.rigid.Contact"/>
<classEntry path="alternativa.physics.collision.CollisionKdNode"/>
<classEntry path="alternativa.physics.collision.BoxSphereCollider"/>
<classEntry path="alternativa.physics.collision.IBodyCollisionPredicate"/>
<classEntry path="alternativa.physics.collision.KdTreeCollisionDetector"/>
<classEntry path="alternativa.physics.collision.primitives.CollisionTriangle"/>
<classEntry path="alternativa.physics.collision.BruteForceCollisionDetector"/>
<classEntry path="alternativa.physics.collision.BoxBoxCollider"/>
<classEntry path="alternativa.physics.collision.BoxRectCollider"/>
<classEntry path="alternativa.physics.rigid.primitives.RigidPlane"/>
<classEntry path="alternativa.physics.collision.ICollisionPredicate"/>
<classEntry path="alternativa.physics.collision.primitives.CollisionBox"/>
<classEntry path="alternativa.physics.collision.BoxTriangleCollider"/>
<classEntry path="alternativa.physics.rigid.primitives.RigidRect"/>
<classEntry path="alternativa.physics.rigid.constraints.Constraint"/>
<classEntry path="alternativa.physics.rigid.constraints.MaxDistanceConstraint"/>
<classEntry path="alternativa.physics.collision.SpherePlaneCollider"/>
<classEntry path="alternativa.physics.rigid.BodyState"/>
<classEntry path="alternativa.physics.types.Vector3"/>
<classEntry path="alternativa.physics.rigid.ContactPoint"/>
<classEntry path="alternativa.physics.collision.primitives.CollisionPrimitive"/>
<classEntry path="alternativa.physics.collision.ICollisionDetector"/>
<classEntry path="alternativa.physics.collision.SphereSphereCollider"/>
<classEntry path="alternativa.physics.collision.CollisionKdTree"/>
<classEntry path="alternativa.physics.rigid.primitives.RigidSphere"/>
<classEntry path="alternativa.physics.rigid.BodyMaterial"/>
<classEntry path="alternativa.physics.collision.types.BoundBox"/>
<classEntry path="alternativa.physics.collision.types.Ray"/>
<classEntry path="alternativa.physics.types.Quaternion"/>
<classEntry path="alternativa.physics.types.Matrix3"/>
<classEntry path="alternativa.physics.collision.IRayCollisionPredicate"/>
<classEntry path="alternativa.physics.collision.types.RayIntersection"/>
<classEntry path="alternativa.physics.types.Matrix4"/>
<classEntry path="alternativa.physics.rigid.PhysicsUtils"/>
<classEntry path="alternativa.physics.altphysics"/>
<classEntry path="alternativa.physics.rigid.primitives.RigidCylinder"/>
<classEntry path="alternativa.physics.collision.ICollider"/>
<classEntry path="alternativa.physics.collision.BoxCollider"/>
<classEntry path="alternativa.physics.rigid.RigidWorld"/>
</includeClasses>
<includeResources/>
<namespaceManifests/>
</flexLibProperties>

19
0.0.5.Optimized/.project Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>AlternativaPhysics</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.adobe.flexbuilder.project.flexbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.maven.ide.eclipse.maven2Nature</nature>
<nature>com.adobe.flexbuilder.project.flexlibnature</nature>
<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,11 @@
K 25
svn:wc:ra_dav:version-url
V 101
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/.settings
END
org.eclipse.core.resources.prefs
K 25
svn:wc:ra_dav:version-url
V 134
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/.settings/org.eclipse.core.resources.prefs
END

View File

@@ -0,0 +1,40 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/.settings
http://svndev.alternativaplatform.com
2009-04-01T12:25:29.638016Z
10301
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
org.eclipse.core.resources.prefs
file
2010-10-28T04:32:38.000000Z
4f70b476f3e5075e399505021df2f89b
2009-04-01T12:25:29.638016Z
10301
mike

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,3 @@
#Fri Mar 20 00:19:33 YEKT 2009
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@@ -0,0 +1,3 @@
#Fri Mar 20 00:19:33 YEKT 2009
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@@ -0,0 +1,29 @@
K 25
svn:wc:ra_dav:version-url
V 91
/!svn/ver/19860/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized
END
.flexLibProperties
K 25
svn:wc:ra_dav:version-url
V 110
/!svn/ver/19850/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/.flexLibProperties
END
.project
K 25
svn:wc:ra_dav:version-url
V 100
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/.project
END
pom.xml
K 25
svn:wc:ra_dav:version-url
V 99
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/pom.xml
END
.actionScriptProperties
K 25
svn:wc:ra_dav:version-url
V 115
/!svn/ver/19809/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/.actionScriptProperties
END

View File

@@ -0,0 +1,5 @@
K 13
svn:mergeinfo
V 85
/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.1.Vector3D:13892-14098
END

View File

@@ -0,0 +1,85 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized
http://svndev.alternativaplatform.com
2009-09-15T05:34:09.526646Z
19860
mike
has-props
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
META-INF
dir
.flexLibProperties
file
2010-10-28T04:32:38.000000Z
6c3b24c32d738d6707eb66c8958d0dde
2009-09-14T12:08:58.450611Z
19850
mike
.project
file
2010-10-28T04:32:38.000000Z
1e90cc68b52b93173b2b5de88eb5c3a3
2009-04-20T17:15:55.260110Z
11541
mike
src
dir
pom.xml
file
2010-10-28T04:32:38.000000Z
3a5f9823247a9630f614b08b93a555eb
2009-09-10T20:48:14.856790Z
19727
mike
.actionScriptProperties
file
2010-10-28T04:32:38.000000Z
b8656f882fff2baedd57a92db86fcf8c
2009-09-13T12:13:24.307076Z
19809
mike
.settings
dir

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<actionScriptProperties mainApplicationPath="AlternativaPhysics2.as" version="3">
<compiler additionalCompilerArguments="" copyDependentFiles="false" enableModuleDebug="true" generateAccessible="false" htmlExpressInstall="true" htmlGenerate="false" htmlHistoryManagement="false" htmlPlayerVersion="10.0.0" htmlPlayerVersionCheck="true" outputFolderPath="bin" sourceFolderPath="src" strict="true" useApolloConfig="false" verifyDigests="true" warn="true">
<compilerSourcePath/>
<libraryPath defaultLinkType="1">
<libraryPathEntry kind="4" path=""/>
</libraryPath>
<sourceAttachmentPath/>
</compiler>
<applications>
<application path="AlternativaPhysics2.as"/>
</applications>
<modules/>
<buildCSSFiles/>
</actionScriptProperties>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<flexLibProperties version="1">
<includeClasses>
<classEntry path="alternativa.physics.collision.CollisionKdTree2D"/>
<classEntry path="alternativa.physics.collision.primitives.CollisionRect"/>
<classEntry path="alternativa.physics.collision.BoxPlaneCollider"/>
<classEntry path="alternativa.physics.rigid.Body"/>
<classEntry path="alternativa.physics.rigid.primitives.RigidBox"/>
<classEntry path="alternativa.physics.collision.primitives.CollisionSphere"/>
<classEntry path="alternativa.physics.rigid.Contact"/>
<classEntry path="alternativa.physics.collision.CollisionKdNode"/>
<classEntry path="alternativa.physics.collision.BoxSphereCollider"/>
<classEntry path="alternativa.physics.collision.IBodyCollisionPredicate"/>
<classEntry path="alternativa.physics.collision.KdTreeCollisionDetector"/>
<classEntry path="alternativa.physics.collision.primitives.CollisionTriangle"/>
<classEntry path="alternativa.physics.collision.BruteForceCollisionDetector"/>
<classEntry path="alternativa.physics.collision.BoxBoxCollider"/>
<classEntry path="alternativa.physics.collision.BoxRectCollider"/>
<classEntry path="alternativa.physics.rigid.primitives.RigidPlane"/>
<classEntry path="alternativa.physics.collision.ICollisionPredicate"/>
<classEntry path="alternativa.physics.collision.primitives.CollisionBox"/>
<classEntry path="alternativa.physics.collision.BoxTriangleCollider"/>
<classEntry path="alternativa.physics.rigid.primitives.RigidRect"/>
<classEntry path="alternativa.physics.rigid.constraints.Constraint"/>
<classEntry path="alternativa.physics.rigid.constraints.MaxDistanceConstraint"/>
<classEntry path="alternativa.physics.collision.SpherePlaneCollider"/>
<classEntry path="alternativa.physics.rigid.BodyState"/>
<classEntry path="alternativa.physics.types.Vector3"/>
<classEntry path="alternativa.physics.rigid.ContactPoint"/>
<classEntry path="alternativa.physics.collision.primitives.CollisionPrimitive"/>
<classEntry path="alternativa.physics.collision.ICollisionDetector"/>
<classEntry path="alternativa.physics.collision.SphereSphereCollider"/>
<classEntry path="alternativa.physics.collision.CollisionKdTree"/>
<classEntry path="alternativa.physics.rigid.primitives.RigidSphere"/>
<classEntry path="alternativa.physics.rigid.BodyMaterial"/>
<classEntry path="alternativa.physics.collision.types.BoundBox"/>
<classEntry path="alternativa.physics.collision.types.Ray"/>
<classEntry path="alternativa.physics.types.Quaternion"/>
<classEntry path="alternativa.physics.types.Matrix3"/>
<classEntry path="alternativa.physics.collision.IRayCollisionPredicate"/>
<classEntry path="alternativa.physics.collision.types.RayIntersection"/>
<classEntry path="alternativa.physics.types.Matrix4"/>
<classEntry path="alternativa.physics.rigid.PhysicsUtils"/>
<classEntry path="alternativa.physics.altphysics"/>
<classEntry path="alternativa.physics.rigid.primitives.RigidCylinder"/>
<classEntry path="alternativa.physics.collision.ICollider"/>
<classEntry path="alternativa.physics.collision.BoxCollider"/>
<classEntry path="alternativa.physics.rigid.RigidWorld"/>
</includeClasses>
<includeResources/>
<namespaceManifests/>
</flexLibProperties>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>AlternativaPhysics</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.adobe.flexbuilder.project.flexbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.maven.ide.eclipse.maven2Nature</nature>
<nature>com.adobe.flexbuilder.project.flexlibnature</nature>
<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,17 @@
<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.fp10.libraries</groupId>
<artifactId>AlternativaPhysics</artifactId>
<packaging>swc</packaging>
<version>0.0.6.0-SNAPSHOT</version>
<parent>
<groupId>platform.tools.maven</groupId>
<artifactId>FlashBasePom</artifactId>
<version>1.0</version>
</parent>
<scm>
<connection>scm:svn:http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/trunk</connection>
</scm>
<dependencies>
</dependencies>
</project>

View File

@@ -0,0 +1,11 @@
K 25
svn:wc:ra_dav:version-url
V 100
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/META-INF
END
MANIFEST.MF
K 25
svn:wc:ra_dav:version-url
V 112
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/META-INF/MANIFEST.MF
END

View File

@@ -0,0 +1,40 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/META-INF
http://svndev.alternativaplatform.com
2009-04-20T17:15:55.260110Z
11541
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
MANIFEST.MF
file
2010-10-28T04:32:37.000000Z
7b64dceb50a6905850ff00a0bfbe77eb
2009-04-20T17:15:55.260110Z
11541
mike

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1 @@
Bundle-Name: platform.clients.fp10.libraries.AlternativaPhysics

View File

@@ -0,0 +1 @@
Bundle-Name: platform.clients.fp10.libraries.AlternativaPhysics

17
0.0.5.Optimized/pom.xml Normal file
View File

@@ -0,0 +1,17 @@
<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.fp10.libraries</groupId>
<artifactId>AlternativaPhysics</artifactId>
<packaging>swc</packaging>
<version>0.0.6.0-SNAPSHOT</version>
<parent>
<groupId>platform.tools.maven</groupId>
<artifactId>FlashBasePom</artifactId>
<version>1.0</version>
</parent>
<scm>
<connection>scm:svn:http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/trunk</connection>
</scm>
<dependencies>
</dependencies>
</project>

View File

@@ -0,0 +1,5 @@
K 25
svn:wc:ra_dav:version-url
V 95
/!svn/ver/19860/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src
END

View File

@@ -0,0 +1,31 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src
http://svndev.alternativaplatform.com
2009-09-15T05:34:09.526646Z
19860
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
alternativa
dir

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,5 @@
K 25
svn:wc:ra_dav:version-url
V 107
/!svn/ver/19860/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa
END

View File

@@ -0,0 +1,31 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa
http://svndev.alternativaplatform.com
2009-09-15T05:34:09.526646Z
19860
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 115
/!svn/ver/19860/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics
END
altphysics.as
K 25
svn:wc:ra_dav:version-url
V 129
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/altphysics.as
END

View File

@@ -0,0 +1,49 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics
http://svndev.alternativaplatform.com
2009-09-15T05:34:09.526646Z
19860
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
types
dir
altphysics.as
file
2010-10-28T04:32:38.000000Z
04fbe40a27502dbbd0dba02a76b2df50
2009-04-20T20:09:14.715403Z
11579
mike
collision
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,113 @@
K 25
svn:wc:ra_dav:version-url
V 125
/!svn/ver/19860/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision
END
BoxSphereCollider.as
K 25
svn:wc:ra_dav:version-url
V 146
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/BoxSphereCollider.as
END
BoxBoxCollider.as
K 25
svn:wc:ra_dav:version-url
V 143
/!svn/ver/19860/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/BoxBoxCollider.as
END
CollisionKdTree2D.as
K 25
svn:wc:ra_dav:version-url
V 146
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/CollisionKdTree2D.as
END
CollisionKdNode.as
K 25
svn:wc:ra_dav:version-url
V 144
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/CollisionKdNode.as
END
KdTreeCollisionDetector.as
K 25
svn:wc:ra_dav:version-url
V 152
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/KdTreeCollisionDetector.as
END
BoxRectCollider.as
K 25
svn:wc:ra_dav:version-url
V 144
/!svn/ver/19860/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/BoxRectCollider.as
END
SpherePlaneCollider.as
K 25
svn:wc:ra_dav:version-url
V 148
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/SpherePlaneCollider.as
END
ICollisionPredicate.as
K 25
svn:wc:ra_dav:version-url
V 148
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/ICollisionPredicate.as
END
BoxPlaneCollider.as
K 25
svn:wc:ra_dav:version-url
V 145
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/BoxPlaneCollider.as
END
CollisionKdTree.as
K 25
svn:wc:ra_dav:version-url
V 144
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/CollisionKdTree.as
END
BoxTriangleCollider.as
K 25
svn:wc:ra_dav:version-url
V 148
/!svn/ver/19860/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/BoxTriangleCollider.as
END
ICollisionDetector.as
K 25
svn:wc:ra_dav:version-url
V 147
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/ICollisionDetector.as
END
IRayCollisionPredicate.as
K 25
svn:wc:ra_dav:version-url
V 151
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/IRayCollisionPredicate.as
END
IBodyCollisionPredicate.as
K 25
svn:wc:ra_dav:version-url
V 152
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/IBodyCollisionPredicate.as
END
BruteForceCollisionDetector.as
K 25
svn:wc:ra_dav:version-url
V 156
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/BruteForceCollisionDetector.as
END
ICollider.as
K 25
svn:wc:ra_dav:version-url
V 138
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/ICollider.as
END
BoxCollider.as
K 25
svn:wc:ra_dav:version-url
V 140
/!svn/ver/19860/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/BoxCollider.as
END
SphereSphereCollider.as
K 25
svn:wc:ra_dav:version-url
V 149
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/SphereSphereCollider.as
END

View File

@@ -0,0 +1,250 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision
http://svndev.alternativaplatform.com
2009-09-15T05:34:09.526646Z
19860
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
BoxSphereCollider.as
file
2010-10-28T04:32:38.000000Z
8bfabb1784a5eb6f6b5a76cbd30d9e88
2009-09-09T15:57:45.246374Z
19648
mike
BoxBoxCollider.as
file
2010-10-28T04:32:38.000000Z
169ad19c54fc6b17524b553cfad80e44
2009-09-15T05:34:09.526646Z
19860
mike
CollisionKdNode.as
file
2010-10-28T04:32:38.000000Z
1d52aba28ce259ca1f9916d70619263d
2009-07-20T03:48:26.517738Z
16522
mike
CollisionKdTree2D.as
file
2010-10-28T04:32:38.000000Z
d8211949ef77817543bc2882480da12f
2009-07-20T03:48:26.517738Z
16522
mike
SpherePlaneCollider.as
file
2010-10-28T04:32:38.000000Z
9416a2154f699ed3330ebf270ac760f9
2009-09-09T15:57:45.246374Z
19648
mike
BoxRectCollider.as
file
2010-10-28T04:32:38.000000Z
709c935cfe3736242305f66c2c53684c
2009-09-15T05:34:09.526646Z
19860
mike
KdTreeCollisionDetector.as
file
2010-10-28T04:32:38.000000Z
ed2340f19df5496ea226eed8a196014e
2009-09-10T19:17:30.639579Z
19708
mike
ICollisionPredicate.as
file
2010-10-28T04:32:38.000000Z
866471ea582e95e46e94eb7ae437c827
2009-04-26T15:39:06.673206Z
11995
mike
BoxPlaneCollider.as
file
2010-10-28T04:32:38.000000Z
827b78750c00050de610aa5b17ef33bd
2009-09-09T15:57:45.246374Z
19648
mike
primitives
dir
CollisionKdTree.as
file
2010-10-28T04:32:38.000000Z
45bb93cc65be7d8a55015c4ac75b1242
2009-07-20T03:48:26.517738Z
16522
mike
BoxTriangleCollider.as
file
2010-10-28T04:32:38.000000Z
43e341cda3486a0a036458cff5202279
2009-09-15T05:34:09.526646Z
19860
mike
ICollisionDetector.as
file
2010-10-28T04:32:38.000000Z
c415c22df9a1b1064c0501d5d525a5cb
2009-07-13T10:34:12.822563Z
16296
mike
IRayCollisionPredicate.as
file
2010-10-28T04:32:38.000000Z
f7443fb728cf4e6157dcfb4f39665889
2009-04-26T15:39:06.673206Z
11995
mike
types
dir
IBodyCollisionPredicate.as
file
2010-10-28T04:32:38.000000Z
fce7e70f95b8ef96709157dcdc8433dd
2009-06-29T14:07:24.833358Z
15390
mike
ICollider.as
file
2010-10-28T04:32:38.000000Z
4eedf93d1111e060a54aadf29b86c63f
2009-09-09T15:57:45.246374Z
19648
mike
BruteForceCollisionDetector.as
file
2010-10-28T04:32:38.000000Z
a313af2ee5d9c84ec2551058b1b4c763
2009-07-13T10:34:12.822563Z
16296
mike
BoxCollider.as
file
2010-10-28T04:32:38.000000Z
eedf135991f73c141baabd079d63332b
2009-09-15T05:34:09.526646Z
19860
mike
SphereSphereCollider.as
file
2010-10-28T04:32:38.000000Z
2e720e302145d54e8391df879f68d6d8
2009-09-09T15:57:45.246374Z
19648
mike

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,568 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.altphysics;
import alternativa.physics.collision.primitives.CollisionBox;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Расчитывает точки контакта двух боксов. Нормаль контакта направляется в сторону бокса с меньшим ID.
*/
public class BoxBoxCollider extends BoxCollider {
private var epsilon:Number = 0.001;
private var toBox1:Vector3 = new Vector3();
private var axis:Vector3 = new Vector3();
private var axis10:Vector3 = new Vector3();
private var axis11:Vector3 = new Vector3();
private var axis12:Vector3 = new Vector3();
private var axis20:Vector3 = new Vector3();
private var axis21:Vector3 = new Vector3();
private var axis22:Vector3 = new Vector3();
private var colAxis:Vector3 = new Vector3();
private var matrix:Matrix4 = new Matrix4();
private var bestAxisIndex:int;
private var minOverlap:Number;
private var points1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var points2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var tmpPoints:Vector.<ContactPoint> = new Vector.<ContactPoint>(8, true);
private var pcount:int;
/**
*
*/
public function BoxBoxCollider() {
for (var i:int = 0; i < 8; i++) {
tmpPoints[i] = new ContactPoint();
points1[i] = new Vector3();
points2[i] = new Vector3();
}
}
/**
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
override public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
if (!haveCollision(prim1, prim2)) return false;
var box1:CollisionBox = CollisionBox(prim1);
var box2:CollisionBox = CollisionBox(prim2);
if (bestAxisIndex < 6) {
// Контакт грань-(грань|ребро|вершина)
findFaceContactPoints(box1, box2, toBox1, bestAxisIndex, contact);
} else {
// Контакт ребро-ребро
bestAxisIndex -= 6;
findEdgesIntersection(box1, box2, toBox1, int(bestAxisIndex/3), bestAxisIndex%3, contact);
}
contact.body1 = box1.body;
contact.body2 = box2.body;
return true;
}
/**
* Выполняет быстрый тест на наличие пересечения двух примитивов.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @return true, если пересечение существует, иначе false
*/
override public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
var box1:CollisionBox = CollisionBox(prim1);
var box2:CollisionBox = CollisionBox(prim2);
var transform1:Matrix4 = box1.transform;
var transform2:Matrix4 = box2.transform;
minOverlap = 1e10;
// Вектор из центра второго бокса в центр первого
toBox1.x = transform1.d - transform2.d;
toBox1.y = transform1.h - transform2.h;
toBox1.z = transform1.l - transform2.l;
// Проверка пересечения по основным осям
axis10.x = transform1.a;
axis10.y = transform1.e;
axis10.z = transform1.i;
if (!testMainAxis(box1, box2, axis10, 0, toBox1)) return false;
axis11.x = transform1.b;
axis11.y = transform1.f;
axis11.z = transform1.j;
if (!testMainAxis(box1, box2, axis11, 1, toBox1)) return false;
axis12.x = transform1.c;
axis12.y = transform1.g;
axis12.z = transform1.k;
if (!testMainAxis(box1, box2, axis12, 2, toBox1)) return false;
axis20.x = transform2.a;
axis20.y = transform2.e;
axis20.z = transform2.i;
if (!testMainAxis(box1, box2, axis20, 3, toBox1)) return false;
axis21.x = transform2.b;
axis21.y = transform2.f;
axis21.z = transform2.j;
if (!testMainAxis(box1, box2, axis21, 4, toBox1)) return false;
axis22.x = transform2.c;
axis22.y = transform2.g;
axis22.z = transform2.k;
if (!testMainAxis(box1, box2, axis22, 5, toBox1)) return false;
// Проверка производных осей
if (!testDerivedAxis(box1, box2, axis10, axis20, 6, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis10, axis21, 7, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis10, axis22, 8, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis11, axis20, 9, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis11, axis21, 10, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis11, axis22, 11, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis12, axis20, 12, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis12, axis21, 13, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis12, axis22, 14, toBox1)) return false;
return true;
}
/**
* Выполняет поиск точек контакта грани одного бокса с гранью/ребром/вершиной другого.
*
* @param box1 первый бокс
* @param box2 второй бокс
* @param toBox1 вектор, направленный из центра второго бокса в центр первого
* @param faceAxisIdx индекс оси первого бокса, перпендикулярной грани, с которой произошло столкновение
* @param contactInfo структура, в которую записывается информация о точках контакта
*/
private function findFaceContactPoints(box1:CollisionBox, box2:CollisionBox, toBox1:Vector3, faceAxisIdx:int, contactInfo:Contact):void {
var swapNormal:Boolean = false;
if (faceAxisIdx > 2) {
// Столкновение с гранью второго бокса. Для дальнейших расчётов боксы меняются местами,
// но нормаль контакта всё равно должна быть направлена в сторону первоначального box1
var tmpBox:CollisionBox = box1;
box1 = box2;
box2 = tmpBox;
toBox1.x = -toBox1.x;
toBox1.y = -toBox1.y;
toBox1.z = -toBox1.z;
faceAxisIdx -= 3;
swapNormal = true;
}
var transform1:Matrix4 = box1.transform;
var transform2:Matrix4 = box2.transform;
transform1.getAxis(faceAxisIdx, colAxis);
var negativeFace:Boolean = colAxis.x*toBox1.x + colAxis.y*toBox1.y + colAxis.z*toBox1.z > 0;
if (!negativeFace) {
colAxis.x = -colAxis.x;
colAxis.y = -colAxis.y;
colAxis.z = -colAxis.z;
}
// Ищем ось второго бокса, определяющую наиболее антипараллельную грань
var incidentAxisIdx:int = 0;
var maxDot:Number = 0;
var incidentAxisDot:Number = 0;
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
transform2.getAxis(axisIdx, axis);
var dot:Number = axis.x*colAxis.x + axis.y*colAxis.y + axis.z*colAxis.z;
var absDot:Number = dot < 0 ? -dot : dot;
if (absDot > maxDot) {
maxDot = absDot;
incidentAxisDot = dot;
incidentAxisIdx = axisIdx;
}
}
// Получаем список вершин грани второго бокса, переводим их в систему координат первого бокса и выполняем обрезку
// по грани первого бокса. Таким образом получается список потенциальных точек контакта.
transform2.getAxis(incidentAxisIdx, axis);
getFaceVertsByAxis(box2.hs, incidentAxisIdx, incidentAxisDot < 0, points1);
// TODO: Рассчитать результирующую матрицу и конвертировать точки один раз
transform2.transformVectorsN(points1, points2, 4);
transform1.transformVectorsInverseN(points2, points1, 4);
var pnum:int = clip(box1.hs, faceAxisIdx);
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
var pen:Number;
pcount = 0;
for (var i:int = 0; i < pnum; i++) {
var v:Vector3 = points1[i];
if ((pen = getPointBoxPenetration(box1.hs, v, faceAxisIdx, negativeFace)) > -epsilon) {
var cp:ContactPoint = tmpPoints[pcount++];
var cpPos:Vector3 = cp.pos;
cpPos.x = transform1.a*v.x + transform1.b*v.y + transform1.c*v.z + transform1.d;
cpPos.y = transform1.e*v.x + transform1.f*v.y + transform1.g*v.z + transform1.h;
cpPos.z = transform1.i*v.x + transform1.j*v.y + transform1.k*v.z + transform1.l;
cp.r1.x = cpPos.x - transform1.d;
cp.r1.y = cpPos.y - transform1.h;
cp.r1.z = cpPos.z - transform1.l;
cp.r2.x = cpPos.x - transform2.d;
cp.r2.y = cpPos.y - transform2.h;
cp.r2.z = cpPos.z - transform2.l;
cp.penetration = pen;
}
}
if (swapNormal) {
contactInfo.normal.x = -colAxis.x;
contactInfo.normal.y = -colAxis.y;
contactInfo.normal.x = -colAxis.x;
} else {
contactInfo.normal.x = colAxis.x;
contactInfo.normal.y = colAxis.y;
contactInfo.normal.x = colAxis.x;
}
if (pcount > 4) {
reducePoints();
}
for (i = 0; i < pcount; i++) {
ContactPoint(contactInfo.points[i]).copyFrom(tmpPoints[i]);
}
contactInfo.pcount = pcount;
}
/**
*
* @param contactInfo
*/
private function reducePoints():void {
var i:int;
var minIdx:int;
var cp1:ContactPoint;
var cp2:ContactPoint;
while (pcount > 4) {
var minLen:Number = 1e10;
var p1:Vector3 = ContactPoint(tmpPoints[int(pcount - 1)]).pos;
var p2:Vector3;
for (i = 0; i < pcount; i++) {
p2 = ContactPoint(tmpPoints[i]).pos;
var dx:Number = p2.x - p1.x;
var dy:Number = p2.y - p1.y;
var dz:Number = p2.z - p1.z;
var len:Number = dx*dx + dy*dy + dz*dz;
if (len < minLen) {
minLen = len;
minIdx = i;
}
p1 = p2;
}
cp1 = tmpPoints[minIdx == 0 ? int(pcount - 1) : int(minIdx - 1)];
cp2 = tmpPoints[minIdx];
p1 = cp1.pos;
p2 = cp2.pos;
p2.x = 0.5*(p1.x + p2.x);
p2.y = 0.5*(p1.y + p2.y);
p2.z = 0.5*(p1.z + p2.z);
cp2.penetration = 0.5*(cp1.penetration + cp2.penetration);
if (minIdx > 0) {
for (i = minIdx; i < pcount; i++) {
tmpPoints[int(i - 1)] = tmpPoints[i];
}
tmpPoints[int(pcount - 1)] = cp1;
}
pcount--;
}
}
/**
*
* @param hs
* @param p
* @param faceAxisIdx
* @param negativeFace
* @return
*/
private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, negativeFace:Boolean):Number {
switch (faceAxisIdx) {
case 0:
if (negativeFace) return p.x + hs.x;
else return hs.x - p.x;
case 1:
if (negativeFace) return p.y + hs.y;
else return hs.y - p.y;
case 2:
if (negativeFace) return p.z + hs.z;
else return hs.z - p.z;
}
return 0;
}
/**
* Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
*
* @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
* @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
* @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
*/
private function clip(hs:Vector3, faceAxisIdx:int):int {
var pnum:int = 4;
switch (faceAxisIdx) {
case 0:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
case 1:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighX(hs.x, pnum, points2, points1, epsilon);
case 2:
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
}
return 0;
}
/**
* Вычисляет точку столкновения рёбер двух боксов.
*
* @param box1 первый бокс
* @param box2 второй бокс
* @param toBox1 вектор, направленный из центра второго бокса в центр первого
* @param axisIdx1 индекс направляющей оси ребра первого бокса
* @param axisIdx2 индекс направляющей оси ребра второго бокса
* @param contactInfo структура, в которую записывается информация о столкновении
*/
private function findEdgesIntersection(box1:CollisionBox, box2:CollisionBox, toBox1:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
var transform1:Matrix4 = box1.transform;
var transform2:Matrix4 = box2.transform;
transform1.getAxis(axisIdx1, axis10);
transform2.getAxis(axisIdx2, axis20);
var normalX:Number = axis10.y*axis20.z - axis10.z*axis20.y;
var normalY:Number = axis10.z*axis20.x - axis10.x*axis20.z;
var normalZ:Number = axis10.x*axis20.y - axis10.y*axis20.x;
var k:Number = 1/Math.sqrt(normalX*normalX + normalY*normalY + normalZ*normalZ);
normalX *= k;
normalY *= k;
normalZ *= k;
// Разворот нормали в сторону первого бокса
if (normalX*toBox1.x + normalY*toBox1.y + normalZ*toBox1.z < 0) {
normalX = -normalX;
normalY = -normalY;
normalZ = -normalZ;
}
// Находим среднюю точку на каждом из пересекающихся рёбер
var halfLen1:Number;
var halfLen2:Number;
var x1:Number = box1.hs.x;
var y1:Number = box1.hs.y;
var z1:Number = box1.hs.z;
var x2:Number = box2.hs.x;
var y2:Number = box2.hs.y;
var z2:Number = box2.hs.z;
// x
if (axisIdx1 == 0) {
x1 = 0;
halfLen1 = box1.hs.x;
} else {
if (normalX*transform1.a + normalY*transform1.e + normalZ*transform1.i > 0) {
x1 = -x1;
}
}
if (axisIdx2 == 0) {
x2 = 0;
halfLen2 = box2.hs.x;
} else {
if (normalX*transform2.a + normalY*transform2.e + normalZ*transform2.i < 0) {
x2 = -x2;
}
}
// y
if (axisIdx1 == 1) {
y1 = 0;
halfLen1 = box1.hs.y;
} else {
if (normalX*transform1.b + normalY*transform1.f + normalZ*transform1.j > 0) {
y1 = -y1;
}
}
if (axisIdx2 == 1) {
y2 = 0;
halfLen2 = box2.hs.y;
} else {
if (normalX*transform2.b + normalY*transform2.f + normalZ*transform2.j < 0) {
y2 = -y2;
}
}
// z
if (axisIdx1 == 2) {
z1 = 0;
halfLen1 = box1.hs.z;
} else {
if (normalX*transform1.c + normalY*transform1.g + normalZ*transform1.k > 0) {
z1 = -z1;
}
}
if (axisIdx2 == 2) {
z2 = 0;
halfLen2 = box2.hs.z;
} else {
if (normalX*transform2.c + normalY*transform2.g + normalZ*transform2.k < 0) {
z2 = -z2;
}
}
// Получаем глобальные координаты средних точек рёбер
var xx:Number = x1;
var yy:Number = y1;
var zz:Number = z1;
x1 = transform1.a*xx + transform1.b*yy + transform1.c*zz + transform1.d;
y1 = transform1.e*xx + transform1.f*yy + transform1.g*zz + transform1.h;
z1 = transform1.i*xx + transform1.j*yy + transform1.k*zz + transform1.l;
xx = x2;
yy = y2;
zz = z2;
x2 = transform2.a*xx + transform2.b*yy + transform2.c*zz + transform2.d;
y2 = transform2.e*xx + transform2.f*yy + transform2.g*zz + transform2.h;
z2 = transform2.i*xx + transform2.j*yy + transform2.k*zz + transform2.l;
// Находим точку пересечения рёбер, решая систему уравнений
k = axis10.x*axis20.x + axis10.y*axis20.y + axis10.z*axis20.z;
var det:Number = k*k - 1;
xx = x2 - x1;
yy = y2 - y1;
zz = z2 - z1;
var c1:Number = axis10.x*xx + axis10.y*yy + axis10.z*zz;
var c2:Number = axis20.x*xx + axis20.y*yy + axis20.z*zz;
var t1:Number = (c2*k - c1)/det;
var t2:Number = (c2 - c1*k)/det;
// Запись данных о столкновении
contact.normal.x = normalX;
contact.normal.y = normalY;
contact.normal.z = normalZ;
contact.pcount = 1;
var cp:ContactPoint = contact.points[0];
var cpPos:Vector3 = cp.pos;
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
cpPos.x = 0.5*(x1 + axis10.x*t1 + x2 + axis20.x*t2);
cpPos.y = 0.5*(y1 + axis10.y*t1 + y2 + axis20.y*t2);
cpPos.z = 0.5*(z1 + axis10.z*t1 + z2 + axis20.z*t2);
cp.r1.x = cpPos.x - transform1.d;
cp.r1.y = cpPos.y - transform1.h;
cp.r1.z = cpPos.z - transform1.l;
cp.r2.x = cpPos.x - transform2.d;
cp.r2.y = cpPos.y - transform2.h;
cp.r2.z = cpPos.z - transform2.l;
cp.penetration = minOverlap;
}
/**
* Проверяет пересечение боксов вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
*
* @param box1
* @param box2
* @param axis
* @param axisIndex
* @param toBox1
* @param bestAxis
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
*/
private function testMainAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, axisIndex:int, toBox1:Vector3):Boolean {
var overlap:Number = overlapOnAxis(box1, box2, axis, toBox1);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
*
* @param box1
* @param box2
* @param axis1
* @param axis2
* @param axisIndex
* @param toBox1
* @return
*/
private function testDerivedAxis(box1:CollisionBox, box2:CollisionBox, axis1:Vector3, axis2:Vector3, axisIndex:int, toBox1:Vector3):Boolean {
// axis = axis1 cross axis2
axis.x = axis1.y*axis2.z - axis1.z*axis2.y;
axis.y = axis1.z*axis2.x - axis1.x*axis2.z;
axis.z = axis1.x*axis2.y - axis1.y*axis2.x;
var lenSqr:Number = axis.x*axis.x + axis.y*axis.y + axis.z*axis.z;
if (lenSqr < 0.0001) return true;
var k:Number = 1/Math.sqrt(lenSqr);
axis.x *= k;
axis.y *= k;
axis.z *= k;
var overlap:Number = overlapOnAxis(box1, box2, axis, toBox1);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
* Вычисляет глубину перекрытия двух боксов вдоль заданной оси.
*
* @param box1 первый бокс
* @param box2 второй бокс
* @param axis ось
* @param toBox1 вектор, соединяющий центр второго бокса с центром первого
* @return величина перекрытия боксов вдоль оси
*/
public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, toBox1:Vector3):Number {
var m:Matrix4 = box1.transform;
var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box1.hs.x;
if (d < 0) d = -d;
var projection:Number = d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box1.hs.y;
if (d < 0) d = -d;
projection += d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box1.hs.z;
if (d < 0) d = -d;
projection += d;
m = box2.transform;
d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box2.hs.x;
if (d < 0) d = -d;
projection += d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box2.hs.y;
if (d < 0) d = -d;
projection += d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box2.hs.z;
if (d < 0) d = -d;
projection += d;
d = toBox1.x*axis.x + toBox1.y*axis.y + toBox1.z*axis.z;
if (d < 0) d = -d;
return projection - d;
}
}
}
import alternativa.physics.types.Vector3;
class CollisionPointTmp {
public var pos:Vector3 = new Vector3();
public var penetration:Number;
}

View File

@@ -0,0 +1,387 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.rigid.Contact;
import alternativa.physics.types.Vector3;
public class BoxCollider implements ICollider {
public function BoxCollider() {
}
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
return false;
}
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
/**
* Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
*
* @param box бокс, в котором ишутся вершины
* @param axisIdx индекс нормальной оси
* @param reverse если указано значение true, возвращаются вершины противоположной грани
* @param result список, в который помещаются вершины
*/
protected function getFaceVertsByAxis(hs:Vector3, axisIdx:int, negativeFace:Boolean, result:Vector.<Vector3>):void {
var v:Vector3;
switch (axisIdx) {
case 0:
if (negativeFace) {
v = result[0]; v.x = -hs.x; v.y = hs.y; v.z = -hs.z;
v = result[1]; v.x = -hs.x; v.y = -hs.y; v.z = -hs.z;
v = result[2]; v.x = -hs.x; v.y = -hs.y; v.z = hs.z;
v = result[3]; v.x = -hs.x; v.y = hs.y; v.z = hs.z;
} else {
v = result[0]; v.x = hs.x; v.y = -hs.y; v.z = -hs.z;
v = result[1]; v.x = hs.x; v.y = hs.y; v.z = -hs.z;
v = result[2]; v.x = hs.x; v.y = hs.y; v.z = hs.z;
v = result[3]; v.x = hs.x; v.y = -hs.y; v.z = hs.z;
}
break;
case 1:
if (negativeFace) {
v = result[0]; v.x = -hs.x; v.y = -hs.y; v.z = -hs.z;
v = result[1]; v.x = hs.x; v.y = -hs.y; v.z = -hs.z;
v = result[2]; v.x = hs.x; v.y = -hs.y; v.z = hs.z;
v = result[3]; v.x = -hs.x; v.y = -hs.y; v.z = hs.z;
} else {
v = result[0]; v.x = hs.x; v.y = hs.y; v.z = -hs.z;
v = result[1]; v.x = -hs.x; v.y = hs.y; v.z = -hs.z;
v = result[2]; v.x = -hs.x; v.y = hs.y; v.z = hs.z;
v = result[3]; v.x = hs.x; v.y = hs.y; v.z = hs.z;
}
break;
case 2:
if (negativeFace) {
v = result[0]; v.x = -hs.x; v.y = hs.y; v.z = -hs.z;
v = result[1]; v.x = hs.x; v.y = hs.y; v.z = -hs.z;
v = result[2]; v.x = hs.x; v.y = -hs.y; v.z = -hs.z;
v = result[3]; v.x = -hs.x; v.y = -hs.y; v.z = -hs.z;
} else {
v = result[0]; v.x = -hs.x; v.y = -hs.y; v.z = hs.z;
v = result[1]; v.x = hs.x; v.y = -hs.y; v.z = hs.z;
v = result[2]; v.x = hs.x; v.y = hs.y; v.z = hs.z;
v = result[3]; v.x = -hs.x; v.y = hs.y; v.z = hs.z;
}
break;
}
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipLowX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var x1:Number = x - epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.x > x1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.x < x1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (x - p1.x)/dx;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.x > x1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (x - p1.x)/dx;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipHighX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var x1:Number = x + epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.x < x1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.x > x1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (x - p1.x)/dx;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.x < x1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (x - p1.x)/dx;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipLowY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var y1:Number = y - epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.y > y1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.y < y1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (y - p1.y)/dy;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.y > y1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (y - p1.y)/dy;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipHighY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var y1:Number = y + epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.y < y1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.y > y1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (y - p1.y)/dy;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.y < y1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (y - p1.y)/dy;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipLowZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var z1:Number = z - epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.z > z1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.z < z1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (z - p1.z)/dz;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.z > z1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (z - p1.z)/dz;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipHighZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var z1:Number = z + epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.z < z1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.z > z1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (z - p1.z)/dz;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.z < z1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (z - p1.z)/dz;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
}
}

View File

@@ -0,0 +1,87 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.rigid.Contact;
import alternativa.physics.types.Vector3;
public class BoxPlaneCollider implements ICollider {
private var verts1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var verts2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var normal:Vector3 = new Vector3();
public function BoxPlaneCollider() {
for (var i:int = 0; i < 8; i++) {
verts1[i] = new Vector3();
verts2[i] = new Vector3();
}
}
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
// var box:RigidBox = body1 as RigidBox;
// var plane:RigidPlane;
// if (box == null) {
// box = body2 as RigidBox;
// plane = body1 as RigidPlane;
// } else {
// plane = body2 as RigidPlane;
// }
//
// // Вычисляем глобальные координаты вершин бокса
// var sx:Number = box.halfSize.x;
// var sy:Number = box.halfSize.y;
// var sz:Number = box.halfSize.z;
// (verts1[0] as Vector3).reset(-sx, -sy, -sz);
// (verts1[1] as Vector3).reset(sx, -sy, -sz);
// (verts1[2] as Vector3).reset(sx, sy, -sz);
// (verts1[3] as Vector3).reset(-sx, sy, -sz);
// (verts1[4] as Vector3).reset(-sx, -sy, sz);
// (verts1[5] as Vector3).reset(sx, -sy, sz);
// (verts1[6] as Vector3).reset(sx, sy, sz);
// (verts1[7] as Vector3).reset(-sx, sy, sz);
//
// box.transform.transformVectors(verts1, verts2);
// // Вычисляем глобальные нормаль и смещение плоскости
// plane.baseMatrix.transformVector(plane.normal, normal);
// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
// // Проверяем наличие столкновений с каждой вершиной
// collisionInfo.pcount = 0;
// for (var i:int = 0; i < 8; i++) {
// // Вершина добавляется в список точек столкновения, если лежит под плоскостью
// var dist:Number = (verts2[i] as Vector3).dot(normal);
// if (dist < offset) {
// var cp:ContactPoint;
// if (collisionInfo.pcount == collisionInfo.points.length) {
// cp = new ContactPoint();
// collisionInfo.points[collisionInfo.pcount] = cp;
// } else {
// cp = collisionInfo.points[collisionInfo.pcount];
// }
// cp.pos.copy(verts2[i]);
// cp.r1.diff(cp.pos, box.state.pos);
// cp.r2.diff(cp.pos, plane.state.pos);
// cp.penetration = offset - dist;
// collisionInfo.pcount++;
// }
// }
// if (collisionInfo.pcount > 0) {
// collisionInfo.body1 = box;
// collisionInfo.body2 = plane;
// collisionInfo.normal.copy(normal);
// return true;
// }
return false;
}
/**
* @param prim1
* @param prim2
* @return
*/
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,522 @@
package alternativa.physics.collision {
import alternativa.physics.altphysics;
import alternativa.physics.collision.primitives.CollisionBox;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.primitives.CollisionRect;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
*
*/
public class BoxRectCollider extends BoxCollider {
private var epsilon:Number = 0.001;
private var vectorToBox:Vector3 = new Vector3();
private var axis:Vector3 = new Vector3();
private var axis10:Vector3 = new Vector3();
private var axis11:Vector3 = new Vector3();
private var axis12:Vector3 = new Vector3();
private var axis20:Vector3 = new Vector3();
private var axis21:Vector3 = new Vector3();
private var axis22:Vector3 = new Vector3();
private var bestAxisIndex:int;
private var minOverlap:Number;
private var points1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var points2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
/**
*
*/
public function BoxRectCollider() {
for (var i:int = 0; i < 8; i++) {
points1[i] = new Vector3();
points2[i] = new Vector3();
}
}
/**
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
override public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
if (!haveCollision(prim1, prim2)) return false;
var box:CollisionBox = prim1 as CollisionBox;
var rect:CollisionRect;
if (box == null) {
box = prim2 as CollisionBox;
rect = prim1 as CollisionRect;
} else {
rect = prim2 as CollisionRect;
}
if (bestAxisIndex < 4) {
// Контакт вдоль одной из основных осей
if (!findFaceContactPoints(box, rect, vectorToBox, bestAxisIndex, contact)) return false;
} else {
// Контакт ребро-ребро
bestAxisIndex -= 4;
findEdgesIntersection(box, rect, vectorToBox, int(bestAxisIndex/2), bestAxisIndex%2, contact);
}
contact.body1 = box.body;
contact.body2 = rect.body;
return true;
}
/**
* Выполняет быстрый тест на наличие пересечения двух примитивов.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @return true, если пересечение существует, иначе false
*/
override public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
minOverlap = 1e10;
var box:CollisionBox = prim1 as CollisionBox;
var rect:CollisionRect;
if (box == null) {
box = prim2 as CollisionBox;
rect = prim1 as CollisionRect;
} else {
rect = prim2 as CollisionRect;
}
var boxTransform:Matrix4 = box.transform;
var rectTransform:Matrix4 = rect.transform;
// Вектор из центра прямоугольника в центр бокса
vectorToBox.x = boxTransform.d - rectTransform.d;
vectorToBox.y = boxTransform.h - rectTransform.h;
vectorToBox.z = boxTransform.l - rectTransform.l;
// Проверка пересечения по нормали прямоугольника
rectTransform.getAxis(2, axis22);
if (!testMainAxis(box, rect, axis22, 0, vectorToBox)) return false;
// Проверка пересечения по основным осям бокса
boxTransform.getAxis(0, axis10);
if (!testMainAxis(box, rect, axis10, 1, vectorToBox)) return false;
boxTransform.getAxis(1, axis11);
if (!testMainAxis(box, rect, axis11, 2, vectorToBox)) return false;
boxTransform.getAxis(2, axis12);
if (!testMainAxis(box, rect, axis12, 3, vectorToBox)) return false;
// Получаем направляющие рёбер прямоугольника
rectTransform.getAxis(0, axis20);
rectTransform.getAxis(1, axis21);
// Проверка производных осей
if (!testDerivedAxis(box, rect, axis10, axis20, 4, vectorToBox)) return false;
if (!testDerivedAxis(box, rect, axis10, axis21, 5, vectorToBox)) return false;
if (!testDerivedAxis(box, rect, axis11, axis20, 6, vectorToBox)) return false;
if (!testDerivedAxis(box, rect, axis11, axis21, 7, vectorToBox)) return false;
if (!testDerivedAxis(box, rect, axis12, axis20, 8, vectorToBox)) return false;
if (!testDerivedAxis(box, rect, axis12, axis21, 9, vectorToBox)) return false;
return true;
}
/**
* Выполняет поиск точек контакта бокса с прямоугольником.
*
* @param box бокс
* @param rect прямоугольник
* @param vectorToBox вектор, направленный из центра прямоугольника в центр бокса
* @param faceAxisIdx индекс оси, идентифицирующей полскость столкновения (грань бокса или полскость прямоугольника)
* @param colInfo структура, в которую записывается информация о точках контакта
*/
private function findFaceContactPoints(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, faceAxisIdx:int, contact:Contact):Boolean {
var pnum:int;
var i:int;
var v:Vector3;
var cp:ContactPoint;
var boxTransform:Matrix4 = box.transform;
var rectTransform:Matrix4 = rect.transform;
var colAxis:Vector3 = contact.normal;
if (faceAxisIdx == 0) {
// Столкновение с плоскостью прямоугольника
// Проверим положение бокса относительно плоскости прямоугольника
colAxis.x = rectTransform.c;
colAxis.y = rectTransform.g;
colAxis.z = rectTransform.k;
// var offset:Number = colAxis.x*rectTransform.d + colAxis.y*rectTransform.h + colAxis.z*rectTransform.l;
// if (bbPos.vDot(colAxis) < offset) return false;
// Ищем ось бокса, определяющую наиболее антипараллельную грань
var incidentAxisIdx:int = 0;
var incidentAxisDot:Number;
var maxDot:Number = 0;
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
boxTransform.getAxis(axisIdx, axis);
var dot:Number = axis.x*colAxis.x + axis.y*colAxis.y + axis.z*colAxis.z;
var absDot:Number = dot < 0 ? -dot : dot;
if (absDot > maxDot) {
maxDot = absDot;
incidentAxisIdx = axisIdx;
incidentAxisDot = dot;
}
}
// Получаем список вершин грани бокса, переводим их в систему координат прямоугольника и выполняем обрезку
// по прямоугольнику. Таким образом получается список потенциальных точек контакта.
boxTransform.getAxis(incidentAxisIdx, axis);
getFaceVertsByAxis(box.hs, incidentAxisIdx, incidentAxisDot > 0, points1);
boxTransform.transformVectorsN(points1, points2, 4);
rectTransform.transformVectorsInverseN(points2, points1, 4);
pnum = clipByRect(rect.hs);
// Проверяем каждую потенциальную точку на принадлежность нижней полуплоскости прямоугольника и добавляем такие точки в список контактов
contact.pcount = 0;
for (i = 0; i < pnum; i++) {
v = points1[i];
if (v.z < epsilon) {
cp = contact.points[contact.pcount++];
cp.penetration = -v.z;
var cpPos:Vector3 = cp.pos;
cpPos.x = rectTransform.a*v.x + rectTransform.b*v.y + rectTransform.c*v.z + rectTransform.d;
cpPos.y = rectTransform.e*v.x + rectTransform.f*v.y + rectTransform.g*v.z + rectTransform.h;
cpPos.z = rectTransform.i*v.x + rectTransform.j*v.y + rectTransform.k*v.z + rectTransform.l;
v = cp.r1;
v.x = cpPos.x - boxTransform.d;
v.y = cpPos.y - boxTransform.h;
v.z = cpPos.z - boxTransform.l;
v = cp.r2;
v.x = cpPos.x - rectTransform.d;
v.y = cpPos.y - rectTransform.h;
v.z = cpPos.z - rectTransform.l;
}
}
} else {
// Столкновение с гранью бокса
faceAxisIdx--;
boxTransform.getAxis(faceAxisIdx, colAxis);
var negativeFace:Boolean = colAxis.x*vectorToBox.x + colAxis.y*vectorToBox.y + colAxis.z*vectorToBox.z > 0;
if (!negativeFace) {
colAxis.x = -colAxis.x;
colAxis.y = -colAxis.y;
colAxis.z = -colAxis.z;
}
if (rectTransform.c*colAxis.x + rectTransform.g*colAxis.y + rectTransform.k*colAxis.z < 0) return false;
getFaceVertsByAxis(rect.hs, 2, false, points1);
rectTransform.transformVectorsN(points1, points2, 4);
boxTransform.transformVectorsInverseN(points2, points1, 4);
pnum = clipByBox(box.hs, faceAxisIdx);
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
var pen:Number;
contact.pcount = 0;
for (i = 0; i < pnum; i++) {
v = points1[i];
if ((pen = getPointBoxPenetration(box.hs, v, faceAxisIdx, negativeFace)) > -epsilon) {
cp = contact.points[contact.pcount++];
cp.penetration = pen;
cpPos = cp.pos;
cpPos.x = boxTransform.a*v.x + boxTransform.b*v.y + boxTransform.c*v.z + boxTransform.d;
cpPos.y = boxTransform.e*v.x + boxTransform.f*v.y + boxTransform.g*v.z + boxTransform.h;
cpPos.z = boxTransform.i*v.x + boxTransform.j*v.y + boxTransform.k*v.z + boxTransform.l;
v = cp.r1;
v.x = cpPos.x - boxTransform.d;
v.y = cpPos.y - boxTransform.h;
v.z = cpPos.z - boxTransform.l;
v = cp.r2;
v.x = cpPos.x - rectTransform.d;
v.y = cpPos.y - rectTransform.h;
v.z = cpPos.z - rectTransform.l;
}
}
}
return true;
}
/**
*
* @param hs
* @param p
* @param axisIndex
* @param reverse
* @return
*/
private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
switch (faceAxisIdx) {
case 0:
if (reverse) return p.x + hs.x;
else return hs.x - p.x;
case 1:
if (reverse) return p.y + hs.y;
else return hs.y - p.y;
case 2:
if (reverse) return p.z + hs.z;
else return hs.z - p.z;
}
return 0;
}
/**
* Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
*
* @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
* @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
* @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
*/
private function clipByBox(hs:Vector3, faceAxisIdx:int):int {
var pnum:int = 4;
switch (faceAxisIdx) {
case 0:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
case 1:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighX(hs.x, pnum, points2, points1, epsilon);
case 2:
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
}
return 0;
}
/**
*
* @param hs
* @return
*/
private function clipByRect(hs:Vector3):int {
var pnum:int = 4;
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
}
/**
* Вычисляет точку столкновения рёбер двух боксов.
*
* @param box первый бокс
* @param rect второй бокс
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
* @param axisIdx1 индекс направляющей оси ребра первого бокса
* @param axisIdx2 индекс направляющей оси ребра второго бокса
* @param colInfo структура, в которую записывается информация о столкновении
*/
private function findEdgesIntersection(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
var boxTransform:Matrix4 = box.transform;
var rectTransform:Matrix4 = rect.transform;
boxTransform.getAxis(axisIdx1, axis10);
rectTransform.getAxis(axisIdx2, axis20);
var colAxis:Vector3 = contact.normal;
colAxis.x = axis10.y*axis20.z - axis10.z*axis20.y;
colAxis.y = axis10.z*axis20.x - axis10.x*axis20.z;
colAxis.z = axis10.x*axis20.y - axis10.y*axis20.x;
var k:Number = 1/Math.sqrt(colAxis.x*colAxis.x + colAxis.y*colAxis.y + colAxis.z*colAxis.z);
colAxis.x *= k;
colAxis.y *= k;
colAxis.z *= k;
// Разворот оси в сторону бокса
if (colAxis.x*vectorToBox.x + colAxis.y*vectorToBox.y + colAxis.z*vectorToBox.z < 0) {
colAxis.x = -colAxis.x;
colAxis.y = -colAxis.y;
colAxis.z = -colAxis.z;
}
// Находим среднюю точку на каждом из пересекающихся рёбер
var halfLen1:Number;
var halfLen2:Number;
var vx:Number = box.hs.x;
var vy:Number = box.hs.y;
var vz:Number = box.hs.z;
var x2:Number = rect.hs.x;
var y2:Number = rect.hs.y;
var z2:Number = rect.hs.z;
// x
if (axisIdx1 == 0) {
vx = 0;
halfLen1 = box.hs.x;
} else {
if (boxTransform.a*colAxis.x + boxTransform.e*colAxis.y + boxTransform.i*colAxis.z > 0) {
vx = -vx;
}
}
if (axisIdx2 == 0) {
x2 = 0;
halfLen2 = rect.hs.x;
} else {
if (rectTransform.a*colAxis.x + rectTransform.e*colAxis.y + rectTransform.i*colAxis.z < 0) {
x2 = -x2;
}
}
// y
if (axisIdx1 == 1) {
vy = 0;
halfLen1 = box.hs.y;
} else {
if (boxTransform.b*colAxis.x + boxTransform.f*colAxis.y + boxTransform.j*colAxis.z > 0) {
vy = -vy;
}
}
if (axisIdx2 == 1) {
y2 = 0;
halfLen2 = rect.hs.y;
} else {
if (rectTransform.b*colAxis.x + rectTransform.f*colAxis.y + rectTransform.j*colAxis.z < 0) {
y2 = -y2;
}
}
// z
if (axisIdx1 == 2) {
vz = 0;
halfLen1 = box.hs.z;
} else {
if (boxTransform.c*colAxis.x + boxTransform.g*colAxis.y + boxTransform.k*colAxis.z > 0) {
vz = -vz;
}
}
// Получаем глобальные координаты средних точек рёбер
var x1:Number = boxTransform.a*vx + boxTransform.b*vy + boxTransform.c*vz + boxTransform.d;
var y1:Number = boxTransform.e*vx + boxTransform.f*vy + boxTransform.g*vz + boxTransform.h;
var z1:Number = boxTransform.i*vx + boxTransform.j*vy + boxTransform.k*vz + boxTransform.l;
vx = x2;
vy = y2;
vz = z2;
x2 = rectTransform.a*vx + rectTransform.b*vy + rectTransform.c*vz + rectTransform.d;
y2 = rectTransform.e*vx + rectTransform.f*vy + rectTransform.g*vz + rectTransform.h;
z2 = rectTransform.i*vx + rectTransform.j*vy + rectTransform.k*vz + rectTransform.l;
// Находим точку пересечения рёбер, решая систему уравнений
k = axis10.x*axis20.x + axis10.y*axis20.y + axis10.z*axis20.z;
var det:Number = k*k - 1;
vx = x2 - x1;
vy = y2 - y1;
vz = z2 - z1;
var c1:Number = axis10.x*vx + axis10.y*vy + axis10.z*vz;
var c2:Number = axis20.x*vx + axis20.y*vy + axis20.z*vz;
var t1:Number = (c2*k - c1)/det;
var t2:Number = (c2 - c1*k)/det;
// Запись данных о столкновении
contact.pcount = 1;
var cp:ContactPoint = contact.points[0];
cp.penetration = minOverlap;
var cpPos:Vector3 = cp.pos;
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
cpPos.x = 0.5*(x1 + axis10.x*t1 + x2 + axis20.x*t2);
cpPos.y = 0.5*(y1 + axis10.y*t1 + y2 + axis20.y*t2);
cpPos.z = 0.5*(z1 + axis10.z*t1 + z2 + axis20.z*t2);
var v:Vector3 = cp.r1;
v.x = cpPos.x - boxTransform.d;
v.y = cpPos.y - boxTransform.h;
v.z = cpPos.z - boxTransform.l;
v = cp.r2;
v.x = cpPos.x - rectTransform.d;
v.y = cpPos.y - rectTransform.h;
v.z = cpPos.z - rectTransform.l;
}
/**
* Проверяет пересечение вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
*
* @param box
* @param rect
* @param axis
* @param axisIndex
* @param vectorToBox
* @param bestAxis
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
*/
private function testMainAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
*
* @param box
* @param rect
* @param axis1
* @param axis2
* @param axisIndex
* @param vectorToBox
* @return
*
*/
private function testDerivedAxis(box:CollisionBox, rect:CollisionRect, axis1:Vector3, axis2:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
// axis = axis1 cross axis2
axis.x = axis1.y*axis2.z - axis1.z*axis2.y;
axis.y = axis1.z*axis2.x - axis1.x*axis2.z;
axis.z = axis1.x*axis2.y - axis1.y*axis2.x;
var lenSqr:Number = axis.x*axis.x + axis.y*axis.y + axis.z*axis.z;
if (lenSqr < 0.0001) return true;
var k:Number = 1/Math.sqrt(lenSqr);
axis.x *= k;
axis.y *= k;
axis.z *= k;
var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
* Вычисляет глубину перекрытия вдоль заданной оси.
*
* @param box бокс
* @param rect прямоугольник
* @param axis ось
* @param vectorToBox вектор, соединяющий центр прямоугольника с центром бокса
* @return величина перекрытия вдоль оси
*/
public function overlapOnAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, vectorToBox:Vector3):Number {
var m:Matrix4 = box.transform;
var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box.hs.x;
if (d < 0) d = -d;
var projection:Number = d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box.hs.y;
if (d < 0) d = -d;
projection += d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box.hs.z;
if (d < 0) d = -d;
projection += d;
m = rect.transform;
d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*rect.hs.x;
if (d < 0) d = -d;
projection += d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*rect.hs.y;
if (d < 0) d = -d;
projection += d;
d = vectorToBox.x*axis.x + vectorToBox.y*axis.y + vectorToBox.z*axis.z;
if (d < 0) d = -d;
return projection - d;
}
}
}

View File

@@ -0,0 +1,113 @@
package alternativa.physics.collision {
import alternativa.physics.altphysics;
import alternativa.physics.collision.primitives.CollisionBox;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.primitives.CollisionSphere;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
*
*/
public class BoxSphereCollider implements ICollider {
private var center:Vector3 = new Vector3();
private var closestPt:Vector3 = new Vector3();
private var bPos:Vector3 = new Vector3();
private var sPos:Vector3 = new Vector3();
/**
*
*/
public function BoxSphereCollider() {
}
/**
*
* @param body1
* @param body2
* @param collisionInfo
* @return
*/
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
var sphere:CollisionSphere = prim1 as CollisionSphere;
var box:CollisionBox;
if (sphere == null) {
sphere = prim2 as CollisionSphere;
box = prim1 as CollisionBox;
} else {
box = prim2 as CollisionBox;
}
// Трансформируем центр сферы в систему координат бокса
sphere.transform.getAxis(3, sPos);
box.transform.getAxis(3, bPos);
box.transform.transformVectorInverse(sPos, center);
// Выполняем поиск разделяющей оси
var hs:Vector3 = box.hs;
var sx:Number = hs.x + sphere.r;
var sy:Number = hs.y + sphere.r;
var sz:Number = hs.z + sphere.r;
if (center.x > sx || center.x < -sx
|| center.y > sy || center.y < -sy
|| center.z > sz || center.z < -sz) {
return false;
}
// Находим ближайшую к сфере точку на боксе
if (center.x > hs.x) {
closestPt.x = hs.x;
} else if (center.x < -hs.x) {
closestPt.x = -hs.x;
} else {
closestPt.x = center.x;
}
if (center.y > hs.y) {
closestPt.y = hs.y;
} else if (center.y < -hs.y) {
closestPt.y = -hs.y;
} else {
closestPt.y = center.y;
}
if (center.z > hs.z) {
closestPt.z = hs.z;
} else if (center.z < -hs.z) {
closestPt.z = -hs.z;
} else {
closestPt.z = center.z;
}
// TODO: Предусмотреть обработку случая, когда центр сферы внутри бокса
var distSqr:Number = center.vSubtract(closestPt).vLengthSqr();
if (distSqr > sphere.r*sphere.r) {
return false;
}
// Зафиксированно столкновение
contact.body1 = sphere.body;
contact.body2 = box.body;
contact.normal.vCopy(closestPt).vTransformBy4(box.transform).vSubtract(sPos).vNormalize().vReverse();
contact.pcount = 1;
var cp:ContactPoint = contact.points[0];
cp.penetration = sphere.r - Math.sqrt(distSqr);
cp.pos.vCopy(contact.normal).vScale(-sphere.r).vAdd(sPos);
cp.r1.vDiff(cp.pos, sPos);
cp.r2.vDiff(cp.pos, bPos);
return true;
}
/**
* @param prim1
* @param prim2
* @return
*/
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,682 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.primitives.CollisionBox;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.primitives.CollisionTriangle;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
/**
*
*/
public class BoxTriangleCollider extends BoxCollider {
public var epsilon:Number = 0.001;
private var bestAxisIndex:int;
private var minOverlap:Number;
private var toBox:Vector3 = new Vector3();
private var axis:Vector3 = new Vector3();
private var colNormal:Vector3 = new Vector3();
private var axis10:Vector3 = new Vector3();
private var axis11:Vector3 = new Vector3();
private var axis12:Vector3 = new Vector3();
private var axis20:Vector3 = new Vector3();
private var axis21:Vector3 = new Vector3();
private var axis22:Vector3 = new Vector3();
private var point1:Vector3 = new Vector3();
private var point2:Vector3 = new Vector3();
private var vector:Vector3 = new Vector3();
private var points1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var points2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
/**
*
*/
public function BoxTriangleCollider() {
for (var i:int = 0; i < 8; i++) {
points1[i] = new Vector3();
points2[i] = new Vector3();
}
}
/**
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
override public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
if (!haveCollision(prim1, prim2)) return false;
var tri:CollisionTriangle = prim1 as CollisionTriangle;
var box:CollisionBox;
if (tri == null) {
box = CollisionBox(prim1);
tri = CollisionTriangle(prim2);
} else {
box = CollisionBox(prim2);
}
if (bestAxisIndex < 4) {
// Контакт вдоль одной из основных осей
if (!findFaceContactPoints(box, tri, toBox, bestAxisIndex, contact)) return false;
} else {
// Контакт ребро-ребро
bestAxisIndex -= 4;
findEdgesIntersection(box, tri, toBox, bestAxisIndex%3, int(bestAxisIndex/3), contact);
}
contact.body1 = box.body;
contact.body2 = tri.body;
return true;
}
/**
* Выполняет быстрый тест на наличие пересечения двух примитивов.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @return true, если пересечение существует, иначе false
*/
override public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
var tri:CollisionTriangle = prim1 as CollisionTriangle;
var box:CollisionBox;
if (tri == null) {
box = CollisionBox(prim1);
tri = CollisionTriangle(prim2);
} else {
box = CollisionBox(prim2);
}
var boxTransform:Matrix4 = box.transform;
var triTransform:Matrix4 = tri.transform;
toBox.x = boxTransform.d - triTransform.d;
toBox.y = boxTransform.h - triTransform.h;
toBox.z = boxTransform.l - triTransform.l;
minOverlap = 1e308;
// Сначала проверяется нормаль треугольника
axis.x = triTransform.c;
axis.y = triTransform.g;
axis.z = triTransform.k;
if (!testMainAxis(box, tri, axis, 0, toBox)) return false;
// Проверка основных осей бокса
axis10.x = boxTransform.a;
axis10.y = boxTransform.e;
axis10.z = boxTransform.i;
if (!testMainAxis(box, tri, axis10, 1, toBox)) return false;
axis11.x = boxTransform.b;
axis11.y = boxTransform.f;
axis11.z = boxTransform.j;
if (!testMainAxis(box, tri, axis11, 2, toBox)) return false;
axis12.x = boxTransform.c;
axis12.y = boxTransform.g;
axis12.z = boxTransform.k;
if (!testMainAxis(box, tri, axis12, 3, toBox)) return false;
// Проверка производных осей
// TODO: заменить вычисления векторных произведений инлайнами
var v:Vector3 = tri.e0;
axis20.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z;
axis20.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z;
axis20.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z;
if (!testDerivedAxis(box, tri, axis10, axis20, 4, toBox)) return false;
if (!testDerivedAxis(box, tri, axis11, axis20, 5, toBox)) return false;
if (!testDerivedAxis(box, tri, axis12, axis20, 6, toBox)) return false;
v = tri.e1;
axis21.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z;
axis21.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z;
axis21.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z;
if (!testDerivedAxis(box, tri, axis10, axis21, 7, toBox)) return false;
if (!testDerivedAxis(box, tri, axis11, axis21, 8, toBox)) return false;
if (!testDerivedAxis(box, tri, axis12, axis21, 9, toBox)) return false;
v = tri.e2;
axis22.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z;
axis22.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z;
axis22.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z;
if (!testDerivedAxis(box, tri, axis10, axis22, 10, toBox)) return false;
if (!testDerivedAxis(box, tri, axis11, axis22, 11, toBox)) return false;
if (!testDerivedAxis(box, tri, axis12, axis22, 12, toBox)) return false;
return true;
}
/**
* Тестирует пересечение примитивов вдоль заданной оси.
*
* @param box бокс
* @param tri треугольник
* @param axis ось
* @param axisIndex индекс оси
* @param toBox вектор, соединяющий центр треугольника с центром бокса
* @return true, если примитивы имеют перекрытие проекций вдоль указанной оси, иначе false
*/
private function testMainAxis(box:CollisionBox, tri:CollisionTriangle, axis:Vector3, axisIndex:int, toBox:Vector3):Boolean {
var overlap:Number = overlapOnAxis(box, tri, axis, toBox);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
*
* @param box
* @param tri
* @param axis1
* @param axis2
* @param axisIndex
* @param toBox
* @return
*
*/
private function testDerivedAxis(box:CollisionBox, tri:CollisionTriangle, axis1:Vector3, axis2:Vector3, axisIndex:int, toBox:Vector3):Boolean {
// axis = axis1 cross axis2
axis.x = axis1.y*axis2.z - axis1.z*axis2.y;
axis.y = axis1.z*axis2.x - axis1.x*axis2.z;
axis.z = axis1.x*axis2.y - axis1.y*axis2.x;
var lenSqr:Number = axis.x*axis.x + axis.y*axis.y + axis.z*axis.z;
if (lenSqr < 0.0001) return true;
var k:Number = 1/Math.sqrt(lenSqr);
axis.x *= k;
axis.y *= k;
axis.z *= k;
var overlap:Number = overlapOnAxis(box, tri, axis, toBox);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
* Рассчитывает величину перекрытия проекций бокса и треугольника на заданную ось.
*
* @param box бокс
* @param tri треугольник
* @param axis единичный направляющий вектор оси
* @param toBox вектор, соединяющий центр треугольника с центром бокса
* @return величина перекрытия. Положительное значение указывает на наличие перекрытия, нулевое или отрицательное значение указывает на отсутствие перекрытия.
*/
private function overlapOnAxis(box:CollisionBox, tri:CollisionTriangle, axis:Vector3, toBox:Vector3):Number {
var t:Matrix4 = box.transform;
var projection:Number = (t.a*axis.x + t.e*axis.y + t.i*axis.z)*box.hs.x;
if (projection < 0) projection = -projection;
var d:Number = (t.b*axis.x + t.f*axis.y + t.j*axis.z)*box.hs.y;
projection += d < 0 ? -d : d;
d = (t.c*axis.x + t.g*axis.y + t.k*axis.z)*box.hs.z;
projection += d < 0 ? -d : d;
var vectorProjection:Number = toBox.x*axis.x + toBox.y*axis.y + toBox.z*axis.z;
// Для оптимизации ось преобразуется в систему треугольника, а не его вершины в мировую систему
t = tri.transform;
var ax:Number = t.a*axis.x + t.e*axis.y + t.i*axis.z;
var ay:Number = t.b*axis.x + t.f*axis.y + t.j*axis.z;
var az:Number = t.c*axis.x + t.g*axis.y + t.k*axis.z;
var max:Number = 0;
if (vectorProjection < 0) {
vectorProjection = -vectorProjection;
d = tri.v0.x*ax + tri.v0.y*ay + tri.v0.z*az;
if (d < max) max = d;
d = tri.v1.x*ax + tri.v1.y*ay + tri.v1.z*az;
if (d < max) max = d;
d = tri.v2.x*ax + tri.v2.y*ay + tri.v2.z*az;
if (d < max) max = d;
max = -max;
} else {
d = tri.v0.x*ax + tri.v0.y*ay + tri.v0.z*az;
if (d > max) max = d;
d = tri.v1.x*ax + tri.v1.y*ay + tri.v1.z*az;
if (d > max) max = d;
d = tri.v2.x*ax + tri.v2.y*ay + tri.v2.z*az;
if (d > max) max = d;
}
return projection + max - vectorProjection;
}
/**
* Определяет точки контакта бокса и треугольника.
*
* @param box бокс
* @param tri треугольник
* @param toBox вектор, соединяющий центр треугольника с центром бокса
* @param faceAxisIndex индекс оси, вдоль которой перекрытие минимально
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
private function findFaceContactPoints(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, faceAxisIndex:int, contact:Contact):Boolean {
if (faceAxisIndex == 0) {
// Столкновение с плоскостью треугольника
return getBoxToTriContact(box, tri, toBox, contact);
} else {
// Столкновение с гранью бокса
return getTriToBoxContact(box, tri, toBox, faceAxisIndex, contact);
}
}
/**
* Определяет точки контакта бокса с плоскостью треугольника.
*
* @param box бокс
* @param tri треугольник
* @param toBox вектор, соединяющий центр треугольника с центром бокса
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
private function getBoxToTriContact(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, contact:Contact):Boolean {
var boxTransform:Matrix4 = box.transform;
var triTransform:Matrix4 = tri.transform;
colNormal.x = triTransform.c;
colNormal.y = triTransform.g;
colNormal.z = triTransform.k;
var over:Boolean = toBox.x*colNormal.x + toBox.y*colNormal.y + toBox.z*colNormal.z > 0;
if (!over) {
colNormal.x = -colNormal.x;
colNormal.y = -colNormal.y;
colNormal.z = -colNormal.z;
}
// Ищем ось бокса, определяющую наиболее антипараллельную грань
var incFaceAxisIdx:int = 0;
var incAxisDot:Number = 0;
var maxDot:Number = 0;
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
boxTransform.getAxis(axisIdx, axis);
var dot:Number = axis.x*colNormal.x + axis.y*colNormal.y + axis.z*colNormal.z;
var absDot:Number = dot < 0 ? -dot : dot;
if (absDot > maxDot) {
maxDot = absDot;
incAxisDot = dot;
incFaceAxisIdx = axisIdx;
}
}
// Обрезка грани
var negativeFace:Boolean = incAxisDot > 0;
getFaceVertsByAxis(box.hs, incFaceAxisIdx, negativeFace, points1);
boxTransform.transformVectorsN(points1, points2, 4);
triTransform.transformVectorsInverseN(points2, points1, 4);
var pnum:int = clipByTriangle(tri);
// Среди конечного списка точек определяются лежащие под плоскостью треугольника
var cp:ContactPoint;
contact.pcount = 0;
for (var i:int = 0; i < pnum; i++) {
var v:Vector3 = points2[i];
if ((over && v.z < 0) || (!over && v.z > 0)) {
cp = contact.points[contact.pcount++];
var cpPos:Vector3 = cp.pos;
cpPos.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z + triTransform.d;
cpPos.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z + triTransform.h;
cpPos.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z + triTransform.l;
var r:Vector3 = cp.r1;
r.x = cpPos.x - boxTransform.d;
r.y = cpPos.y - boxTransform.h;
r.z = cpPos.z - boxTransform.l;
r = cp.r2;
r.x = cpPos.x - triTransform.d;
r.y = cpPos.y - triTransform.h;
r.z = cpPos.z - triTransform.l;
cp.penetration = over ? -v.z : v.z;
}
}
contact.normal.x = colNormal.x;
contact.normal.y = colNormal.y;
contact.normal.z = colNormal.z;
return true;
}
/**
* Оперделяет точки контакта треугольника с гранью бокса.
*
* @param box бокс
* @param tri треугольник
* @param toBox вектор, соединяющий центр треугольника с центром бокса
* @param faceAxisIndex индекс оси, вдоль которой перекрытие минимально
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
private function getTriToBoxContact(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, faceAxisIdx:int, contact:Contact):Boolean {
faceAxisIdx--;
var boxTransform:Matrix4 = box.transform;
var triTransform:Matrix4 = tri.transform;
boxTransform.getAxis(faceAxisIdx, colNormal);
var negativeFace:Boolean = toBox.x*colNormal.x + toBox.y*colNormal.y + toBox.z*colNormal.z > 0;
if (!negativeFace) {
colNormal.x = -colNormal.x;
colNormal.y = -colNormal.y;
colNormal.z = -colNormal.z;
}
var v:Vector3 = points1[0];
v.x = tri.v0.x;
v.y = tri.v0.y;
v.z = tri.v0.z;
v = points1[1];
v.x = tri.v1.x;
v.y = tri.v1.y;
v.z = tri.v1.z;
v = points1[2];
v.x = tri.v2.x;
v.y = tri.v2.y;
v.z = tri.v2.z;
triTransform.transformVectorsN(points1, points2, 3);
boxTransform.transformVectorsInverseN(points2, points1, 3);
var pnum:int = clipByBox(box.hs, faceAxisIdx);
// Проверяем каждую потенциальную точку на принадлежность боксу и добавляем такие точки в список контактов
var penetration:Number;
contact.pcount = 0;
for (var i:int = 0; i < pnum; i++) {
v = points1[i];
penetration = getPointBoxPenetration(box.hs, v, faceAxisIdx, negativeFace);
if (penetration > -epsilon) {
var cp:ContactPoint = contact.points[contact.pcount++];
var cpPos:Vector3 = cp.pos;
cpPos.x = boxTransform.a*v.x + boxTransform.b*v.y + boxTransform.c*v.z + boxTransform.d;
cpPos.y = boxTransform.e*v.x + boxTransform.f*v.y + boxTransform.g*v.z + boxTransform.h;
cpPos.z = boxTransform.i*v.x + boxTransform.j*v.y + boxTransform.k*v.z + boxTransform.l;
var r:Vector3 = cp.r1;
r.x = cpPos.x - boxTransform.d;
r.y = cpPos.y - boxTransform.h;
r.z = cpPos.z - boxTransform.l;
r = cp.r2;
r.x = cpPos.x - triTransform.d;
r.y = cpPos.y - triTransform.h;
r.z = cpPos.z - triTransform.l;
cp.penetration = penetration;
}
}
contact.normal.x = colNormal.x;
contact.normal.y = colNormal.y;
contact.normal.z = colNormal.z;
return true;
}
/**
* Вычисляет величину проникновения точки в бокс.
*
* @param hs вектор половинных размеров бокса
* @param p точка в системе координат бокса
* @param axisIndex индекс оси
* @param negativeFace если true, проверяется нижняя грань
* @return величина проникновения точки в бокс
*/
private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, negativeFace:Boolean):Number {
switch (faceAxisIdx) {
case 0:
if (negativeFace) return p.x + hs.x;
else return hs.x - p.x;
case 1:
if (negativeFace) return p.y + hs.y;
else return hs.y - p.y;
case 2:
if (negativeFace) return p.z + hs.z;
else return hs.z - p.z;
}
return 0;
}
/**
* Выполняет обрезку грани, вершины которой заданы в списке points1. Результат сохраняется в этом же списке.
*
* @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
* @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
* @return количество вершин, получившихся в результате обрезки грани
*/
private function clipByBox(hs:Vector3, faceAxisIdx:int):int {
var pnum:int = 3;
switch (faceAxisIdx) {
case 0:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
case 1:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighX(hs.x, pnum, points2, points1, epsilon);
case 2:
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
}
return 0;
}
/**
* Обрезает грань прямоугольника сторонами треугольника. Обрезка выполняется в системе координат и в проекции на
* плоскость треугольника. Входные вершины грани в количестве четырёх штук должны находиться в списке points1.
* Конечные вершины сохраняются в списке points2.
*
* @param tri треугольник
* @return количество врешин в конечном списке
*/
private function clipByTriangle(tri:CollisionTriangle):int {
var vnum:int = 4;
vnum = clipByLine(tri.v0, tri.e0, points1, vnum, points2);
if (vnum == 0) return 0;
vnum = clipByLine(tri.v1, tri.e1, points2, vnum, points1);
if (vnum == 0) return 0;
return clipByLine(tri.v2, tri.e2, points1, vnum, points2);;
}
/**
* Обрезает полигон указанной прямой.
*
* @param linePoint точка на прямой
* @param lineDir единичный направляющий вектор прямой
* @param verticesIn список вершин исходного полигона
* @param vnum количество вершин исходного полигона
* @param verticesOut список, куда будут записаны вершины конечного полигона
* @return количество вершин конечного полигона
*/
private function clipByLine(linePoint:Vector3, lineDir:Vector3, verticesIn:Vector.<Vector3>, vnum:int, verticesOut:Vector.<Vector3>):int {
var nx:Number = -lineDir.y;
var ny:Number = lineDir.x;
var offset:Number = linePoint.x*nx + linePoint.y*ny;
var v1:Vector3 = verticesIn[int(vnum - 1)];
var offset1:Number = v1.x*nx + v1.y*ny;
var t:Number;
var v:Vector3;
var num:int = 0;
for (var i:int = 0; i < vnum; i++) {
var v2:Vector3 = verticesIn[i];
var offset2:Number = v2.x*nx + v2.y*ny;
if (offset1 < offset) {
// Первая точка ребра во внешней полуплоскости
if (offset2 > offset) {
// Вторая точка ребра во внутренней полуплоскости, в конечный список добавляется точка на пересечении
t = (offset - offset1)/(offset2 - offset1);
v = verticesOut[num];
v.x = v1.x + t*(v2.x - v1.x);
v.y = v1.y + t*(v2.y - v1.y);
v.z = v1.z + t*(v2.z - v1.z);
num++;
}
} else {
// Первая точка ребра во внутренней полуплоскости. Добавляем её в конечный список.
v = verticesOut[num];
v.x = v1.x;
v.y = v1.y;
v.z = v1.z;
num++;
if (offset2 < offset) {
// Вторая точка ребра во внешней полуплоскости, в конечный список добавляется точка на пересечении
t = (offset - offset1)/(offset2 - offset1);
v = verticesOut[num];
v.x = v1.x + t*(v2.x - v1.x);
v.y = v1.y + t*(v2.y - v1.y);
v.z = v1.z + t*(v2.z - v1.z);
num++;
}
}
v1 = v2;
offset1 = offset2;
}
return num;
}
/**
*
* @param box
* @param tri
* @param toBox
* @param boxAxisIdx
* @param triAxisIdx
* @param contact
*/
private function findEdgesIntersection(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, boxAxisIdx:int, triAxisIdx:int, contact:Contact):void {
// Определение точки и направляющего вектора ребра треугольника
var tmpx1:Number;
var tmpy1:Number;
var tmpz1:Number;
var tmpx2:Number;
var tmpy2:Number;
var tmpz2:Number;
switch (triAxisIdx) {
case 0:
tmpx1 = tri.e0.x;
tmpy1 = tri.e0.y;
tmpz1 = tri.e0.z;
tmpx2 = tri.v0.x;
tmpy2 = tri.v0.y;
tmpz2 = tri.v0.z;
break;
case 1:
tmpx1 = tri.e1.x;
tmpy1 = tri.e1.y;
tmpz1 = tri.e1.z;
tmpx2 = tri.v1.x;
tmpy2 = tri.v1.y;
tmpz2 = tri.v1.z;
break;
case 2:
tmpx1 = tri.e2.x;
tmpy1 = tri.e2.y;
tmpz1 = tri.e2.z;
tmpx2 = tri.v2.x;
tmpy2 = tri.v2.y;
tmpz2 = tri.v2.z;
break;
}
var triTransform:Matrix4 = tri.transform;
axis20.x = triTransform.a*tmpx1 + triTransform.b*tmpy1 + triTransform.c*tmpz1;
axis20.y = triTransform.e*tmpx1 + triTransform.f*tmpy1 + triTransform.g*tmpz1;
axis20.z = triTransform.i*tmpx1 + triTransform.j*tmpy1 + triTransform.k*tmpz1;
var x2:Number = triTransform.a*tmpx2 + triTransform.b*tmpy2 + triTransform.c*tmpz2 + triTransform.d;
var y2:Number = triTransform.e*tmpx2 + triTransform.f*tmpy2 + triTransform.g*tmpz2 + triTransform.h;
var z2:Number = triTransform.i*tmpx2 + triTransform.j*tmpy2 + triTransform.k*tmpz2 + triTransform.l;
// Определение нормали контакта, точки и направляющего вектора ребра бокса
var boxTransform:Matrix4 = box.transform;
boxTransform.getAxis(boxAxisIdx, axis10);
// Нормаль контакта
var v:Vector3 = contact.normal;
v.x = axis10.y*axis20.z - axis10.z*axis20.y;
v.y = axis10.z*axis20.x - axis10.x*axis20.z;
v.z = axis10.x*axis20.y - axis10.y*axis20.x;
k = 1/Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
v.x *= k;
v.y *= k;
v.z *= k;
// Разворот нормали в сторону бокса
if (v.x*toBox.x + v.y*toBox.y + v.z*toBox.z < 0) {
v.x = -v.x;
v.y = -v.y;
v.z = -v.z;
}
var boxHalfLen:Number;
tmpx1 = box.hs.x;
tmpy1 = box.hs.y;
tmpz1 = box.hs.z;
// X
if (boxAxisIdx == 0) {
tmpx1 = 0;
boxHalfLen = box.hs.x;
} else {
if (boxTransform.a*v.x + boxTransform.e*v.y + boxTransform.i*v.z > 0) {
tmpx1 = -tmpx1;
}
}
// Y
if (boxAxisIdx == 1) {
tmpy1 = 0;
boxHalfLen = box.hs.y;
} else {
if (boxTransform.b*v.x + boxTransform.f*v.y + boxTransform.j*v.z > 0) {
tmpy1 = -tmpy1;
}
}
// Z
if (boxAxisIdx == 2) {
tmpz1 = 0;
boxHalfLen = box.hs.z;
} else {
if (boxTransform.c*v.x + boxTransform.g*v.y + boxTransform.k*v.z > 0) {
tmpz1 = -tmpz1;
}
}
var x1:Number = boxTransform.a*tmpx1 + boxTransform.b*tmpy1 + boxTransform.c*tmpz1 + boxTransform.d;
var y1:Number = boxTransform.e*tmpx1 + boxTransform.f*tmpy1 + boxTransform.g*tmpz1 + boxTransform.h;
var z1:Number = boxTransform.i*tmpx1 + boxTransform.j*tmpy1 + boxTransform.k*tmpz1 + boxTransform.l;
// Находим точку пересечения рёбер, решая систему уравнений
// axis10 - направляющий вектор ребра бокса
// x1, y1, z1 - средняя точка ребра бокса
// axis20 - направляющий вектор ребра треугольника
// x2, y2, z2 - начальная точка ребра треугольника
var k:Number = axis10.x*axis20.x + axis10.y*axis20.y + axis10.z*axis20.z;
var det:Number = k*k - 1;
var vx:Number = x2 - x1;
var vy:Number = y2 - y1;
var vz:Number = z2 - z1;
var c1:Number = axis10.x*vx + axis10.y*vy + axis10.z*vz;
var c2:Number = axis20.x*vx + axis20.y*vy + axis20.z*vz;
var t1:Number = (c2*k - c1)/det;
var t2:Number = (c2 - c1*k)/det;
// Запись данных о контакте
contact.pcount = 1;
var cp:ContactPoint = contact.points[0];
cp.penetration = minOverlap;
v = cp.pos;
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
v.x = 0.5*(x1 + axis10.x*t1 + x2 + axis20.x*t2);
v.y = 0.5*(y1 + axis10.y*t1 + y2 + axis20.y*t2);
v.z = 0.5*(z1 + axis10.z*t1 + z2 + axis20.z*t2);
var r:Vector3 = cp.r1;
r.x = v.x - boxTransform.d;
r.y = v.y - boxTransform.h;
r.z = v.z - boxTransform.l;
r = cp.r2;
r.x = v.x - triTransform.d;
r.y = v.y - triTransform.h;
r.z = v.z - triTransform.l;
}
}
}

View File

@@ -0,0 +1,63 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.types.RayIntersection;
import alternativa.physics.rigid.Body;
import alternativa.physics.rigid.Contact;
import alternativa.physics.types.Vector3;
public class BruteForceCollisionDetector implements ICollisionDetector {
public function BruteForceCollisionDetector() {
}
public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean=true):Boolean {
return false;
}
public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean=true):Boolean {
return false;
}
public function init():void {
}
public function getAllCollisions(contacts:Vector.<Contact>):int {
return 0;
}
public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
return false;
}
public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
return false;
}
public function testBodyPrimitiveCollision(body:Body, primitive:CollisionPrimitive):Boolean {
return false;
}
/**
*
* @param prim1
* @param prim2
* @param contact
* @return
*/
public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
return false;
}
/**
*
* @param prim1
* @param prim2
* @return
*/
public function testCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,18 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.types.BoundBox;
public class CollisionKdNode {
public var indices:Vector.<int>;
public var splitIndices:Vector.<int>;
public var boundBox:BoundBox;
public var parent:CollisionKdNode;
public var splitTree:CollisionKdTree2D;
public var axis:int = -1; // 0 - x, 1 - y, 2 - z
public var coord:Number;
public var positiveNode:CollisionKdNode;
public var negativeNode:CollisionKdNode;
}
}

View File

@@ -0,0 +1,280 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.types.BoundBox;
/**
* @author mike
*/
public class CollisionKdTree {
public var threshold:Number = 0.1;
public var minPrimitivesPerNode:int = 1;
public var rootNode:CollisionKdNode;
public var staticChildren:Vector.<CollisionPrimitive> = new Vector.<CollisionPrimitive>();
public var numStaticChildren:int;
public var staticBoundBoxes:Vector.<BoundBox> = new Vector.<BoundBox>();
private var splitAxis:int;
private var splitCoord:Number;
private var splitCost:Number;
private static const nodeBoundBoxThreshold:BoundBox = new BoundBox();
private static const splitCoordsX:Vector.<Number> = new Vector.<Number>();
private static const splitCoordsY:Vector.<Number> = new Vector.<Number>();
private static const splitCoordsZ:Vector.<Number> = new Vector.<Number>();
private static const _nodeBB:Vector.<Number> = new Vector.<Number>(6);
private static const _bb:Vector.<Number> = new Vector.<Number>(6);
/**
* @param primitive
*/
public function addStaticPrimitive(primitive:CollisionPrimitive):void {
staticChildren[numStaticChildren++] = primitive;
}
/**
* @param primitive
* @return
*/
public function removeStaticPrimitive(primitive:CollisionPrimitive):Boolean {
var idx:int = staticChildren.indexOf(primitive);
if (idx < 0) return false;
staticChildren.splice(idx, 1);
numStaticChildren--;
return true;
}
/**
* @param boundBox
*/
public function createTree(boundBox:BoundBox = null):void {
if (numStaticChildren == 0) return;
// Создаём корневую ноду
rootNode = new CollisionKdNode();
rootNode.indices = new Vector.<int>();
// Расчитываем баунды объектов и рутовой ноды
var rootNodeBoundBox:BoundBox = rootNode.boundBox = (boundBox != null) ? boundBox : new BoundBox();
for (var i:int = 0; i < numStaticChildren; ++i) {
var child:CollisionPrimitive = staticChildren[i];
var childBoundBox:BoundBox = staticBoundBoxes[i] = child.calculateAABB();
rootNodeBoundBox.addBoundBox(childBoundBox);
rootNode.indices[i] = i;
}
staticBoundBoxes.length = numStaticChildren;
// Разделяем рутовую ноду
splitNode(rootNode);
splitCoordsX.length = splitCoordsY.length = splitCoordsZ.length = 0;
}
/**
* @param node
*/
private function splitNode(node:CollisionKdNode):void {
var indices:Vector.<int> = node.indices;
var numPrimitives:int = indices.length;
if (numPrimitives <= minPrimitivesPerNode) return;
// Подготовка баунда с погрешностями
var nodeBoundBox:BoundBox = node.boundBox;
nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
var doubleThreshold:Number = threshold*2;
// Собираем опорные координаты
var i:int;
var j:int;
var numSplitCoordsX:int = 0
var numSplitCoordsY:int = 0;
var numSplitCoordsZ:int = 0;
for (i = 0; i < numPrimitives; ++i) {
var boundBox:BoundBox = staticBoundBoxes[indices[i]];
if (boundBox.maxX - boundBox.minX <= doubleThreshold) {
if (boundBox.minX <= nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = nodeBoundBox.minX;
else if (boundBox.maxX >= nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = nodeBoundBox.maxX;
else splitCoordsX[numSplitCoordsX++] = (boundBox.minX + boundBox.maxX)*0.5;
} else {
if (boundBox.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = boundBox.minX;
if (boundBox.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = boundBox.maxX;
}
if (boundBox.maxY - boundBox.minY <= doubleThreshold) {
if (boundBox.minY <= nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = nodeBoundBox.minY;
else if (boundBox.maxY >= nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = nodeBoundBox.maxY;
else splitCoordsY[numSplitCoordsY++] = (boundBox.minY + boundBox.maxY)*0.5;
} else {
if (boundBox.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = boundBox.minY;
if (boundBox.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = boundBox.maxY;
}
if (boundBox.maxZ - boundBox.minZ <= doubleThreshold) {
if (boundBox.minZ <= nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = nodeBoundBox.minZ;
else if (boundBox.maxZ >= nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = nodeBoundBox.maxZ;
else splitCoordsZ[numSplitCoordsZ++] = (boundBox.minZ + boundBox.maxZ)*0.5;
} else {
if (boundBox.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.minZ;
if (boundBox.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.maxZ;
}
}
// Поиск наилучшего сплита
splitAxis = -1;
splitCost = 1e308;
_nodeBB[0] = nodeBoundBox.minX;
_nodeBB[1] = nodeBoundBox.minY;
_nodeBB[2] = nodeBoundBox.minZ;
_nodeBB[3] = nodeBoundBox.maxX;
_nodeBB[4] = nodeBoundBox.maxY;
_nodeBB[5] = nodeBoundBox.maxZ;
checkNodeAxis(node, 0, numSplitCoordsX, splitCoordsX, _nodeBB);
checkNodeAxis(node, 1, numSplitCoordsY, splitCoordsY, _nodeBB);
checkNodeAxis(node, 2, numSplitCoordsZ, splitCoordsZ, _nodeBB);
// Если сплит не найден, выходим
if (splitAxis < 0) return;
// Сплиттер найден. Разделение узла.
var axisX:Boolean = splitAxis == 0
var axisY:Boolean = splitAxis == 1;
node.axis = splitAxis;
node.coord = splitCoord;
// Создаём дочерние ноды
node.negativeNode = new CollisionKdNode();
node.negativeNode.parent = node;
node.negativeNode.boundBox = nodeBoundBox.clone();
node.positiveNode = new CollisionKdNode();
node.positiveNode.parent = node;
node.positiveNode.boundBox = nodeBoundBox.clone();
if (axisX) node.negativeNode.boundBox.maxX = node.positiveNode.boundBox.minX = splitCoord;
else if (axisY) node.negativeNode.boundBox.maxY = node.positiveNode.boundBox.minY = splitCoord;
else node.negativeNode.boundBox.maxZ = node.positiveNode.boundBox.minZ = splitCoord;
// Распределяем объекты по дочерним нодам
var coordMin:Number = splitCoord - threshold;
var coordMax:Number = splitCoord + threshold;
for (i = 0; i < numPrimitives; ++i) {
boundBox = staticBoundBoxes[indices[i]];
var min:Number = axisX ? boundBox.minX : (axisY ? boundBox.minY : boundBox.minZ);
var max:Number = axisX ? boundBox.maxX : (axisY ? boundBox.maxY : boundBox.maxZ);
if (max <= coordMax) {
if (min < coordMin) {
// Объект в негативной стороне
if (node.negativeNode.indices == null) node.negativeNode.indices = new Vector.<int>();
node.negativeNode.indices.push(indices[i]);
indices[i] = -1;
} else {
if (node.splitIndices == null) node.splitIndices = new Vector.<int>();
node.splitIndices.push(indices[i]);
indices[i] = -1;
}
} else {
if (min >= coordMin) {
// Объект в положительной стороне
if (node.positiveNode.indices == null) node.positiveNode.indices = new Vector.<int>();
node.positiveNode.indices.push(indices[i]);
indices[i] = -1;
}
}
}
// Очистка списка объектов
for (i = 0, j = 0; i < numPrimitives; ++i) {
if (indices[i] >= 0) indices[j++] = indices[i];
}
if (j > 0) indices.length = j;
else node.indices = null;
if (node.splitIndices != null) {
node.splitTree = new CollisionKdTree2D(this, node);
node.splitTree.createTree();
}
// Разделение дочерних нод
if (node.negativeNode.indices != null) splitNode(node.negativeNode);
if (node.positiveNode.indices != null) splitNode(node.positiveNode);
}
/**
*
* @param node
* @param axis
* @param numSplitCoords
* @param splitCoords
* @param bb
*/
private function checkNodeAxis(node:CollisionKdNode, axis:int, numSplitCoords:int, splitCoords:Vector.<Number>, bb:Vector.<Number>):void {
var axis1:int = (axis + 1)%3;
var axis2:int = (axis + 2)%3;
var area:Number = (bb[axis1 + 3] - bb[axis1])*(bb[axis2 + 3] - bb[axis2]);
for (var i:int = 0; i < numSplitCoords; ++i) {
var currSplitCoord:Number = splitCoords[i];
if (isNaN(currSplitCoord)) continue;
var minCoord:Number = currSplitCoord - threshold;
var maxCoord:Number = currSplitCoord + threshold;
var areaNegative:Number = area*(currSplitCoord - bb[axis]);
var areaPositive:Number = area*(bb[int(axis + 3)] - currSplitCoord);
var numNegative:int = 0;
var numPositive:int = 0;
var conflict:Boolean = false;
// Проверяем объекты
var numObjects:int = node.indices.length;
for (var j:int = 0; j < numObjects; j++) {
var boundBox:BoundBox = staticBoundBoxes[node.indices[j]];
_bb[0] = boundBox.minX;
_bb[1] = boundBox.minY;
_bb[2] = boundBox.minZ;
_bb[3] = boundBox.maxX;
_bb[4] = boundBox.maxY;
_bb[5] = boundBox.maxZ;
if (_bb[axis + 3] <= maxCoord) {
if (_bb[axis] < minCoord) numNegative++;
} else {
if (_bb[axis] >= minCoord) {
numPositive++;
} else {
conflict = true;
break;
}
}
}
// Если хороший сплит, сохраняем
var cost:Number = areaNegative*numNegative + areaPositive*numPositive;
if (!conflict && cost < splitCost) {
splitAxis = axis;
splitCost = cost;
splitCoord = currSplitCoord;
}
for (j = i + 1; j < numSplitCoords; ++j) {
if (splitCoords[j] >= currSplitCoord - threshold && splitCoords[j] <= currSplitCoord + threshold) splitCoords[j] = NaN;
}
}
}
/**
*
*/
public function traceTree():void {
traceNode("", rootNode);
}
/**
* @param str
* @param node
*/
private function traceNode(str:String, node:CollisionKdNode):void {
if (node == null) return;
trace(str, node.axis == -1 ? "end" : ((node.axis == 0) ? "X" : ((node.axis == 1) ? "Y" : "Z")), "splitCoord=" + splitCoord, "bound", node.boundBox, "objs:", node.indices);
traceNode(str + "-", node.negativeNode);
traceNode(str + "+", node.positiveNode);
}
}
}

View File

@@ -0,0 +1,226 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.types.BoundBox;
/**
*
*/
public class CollisionKdTree2D {
public var threshold:Number = 0.1;
public var minPrimitivesPerNode:int = 1;
public var parentTree:CollisionKdTree;
public var parentNode:CollisionKdNode;
public var rootNode:CollisionKdNode;
private var splitAxis:int;
private var splitCost:Number;
private var splitCoord:Number;
private static const nodeBoundBoxThreshold:BoundBox = new BoundBox();
private static const splitCoordsX:Vector.<Number> = new Vector.<Number>();
private static const splitCoordsY:Vector.<Number> = new Vector.<Number>();
private static const splitCoordsZ:Vector.<Number> = new Vector.<Number>();
private static const _nodeBB:Vector.<Number> = new Vector.<Number>(6);
private static const _bb:Vector.<Number> = new Vector.<Number>(6);
/**
*
* @param parentTree
* @param parentNode
*/
public function CollisionKdTree2D(parentTree:CollisionKdTree, parentNode:CollisionKdNode) {
this.parentTree = parentTree;
this.parentNode = parentNode;
}
/**
*
*/
public function createTree():void {
rootNode = new CollisionKdNode();
rootNode.boundBox = parentNode.boundBox.clone();
rootNode.indices = new Vector.<int>();
var numObjects:int = parentNode.splitIndices.length;
for (var i:int = 0; i < numObjects; ++i) rootNode.indices[i] = parentNode.splitIndices[i];
splitNode(rootNode);
splitCoordsX.length = splitCoordsY.length = splitCoordsZ.length = 0;
}
/**
* @param node
*/
private function splitNode(node:CollisionKdNode):void {
if (node.indices.length <= minPrimitivesPerNode) return;
var objects:Vector.<int> = node.indices;
var i:int;
var j:int;
var nodeBoundBox:BoundBox = node.boundBox;
// Подготовка баунда с погрешностями
nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
var doubleThreshold:Number = threshold*2;
var staticBoundBoxes:Vector.<BoundBox> = parentTree.staticBoundBoxes;
// Собираем опорные координаты
var numSplitCoordsX:int;
var numSplitCoordsY:int;
var numSplitCoordsZ:int;
var numObjects:int = objects.length;
for (i = 0; i < numObjects; ++i) {
var bb:BoundBox = staticBoundBoxes[objects[i]];
if (parentNode.axis != 0) {
if (bb.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = bb.minX;
if (bb.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = bb.maxX;
}
if (parentNode.axis != 1) {
if (bb.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = bb.minY;
if (bb.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = bb.maxY;
}
if (parentNode.axis != 2) {
if (bb.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = bb.minZ;
if (bb.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = bb.maxZ;
}
}
// Поиск наилучшего сплита
splitAxis = -1;
splitCost = 1e308;
_nodeBB[0] = nodeBoundBox.minX;
_nodeBB[1] = nodeBoundBox.minY;
_nodeBB[2] = nodeBoundBox.minZ;
_nodeBB[3] = nodeBoundBox.maxX;
_nodeBB[4] = nodeBoundBox.maxY;
_nodeBB[5] = nodeBoundBox.maxZ;
if (parentNode.axis != 0) checkNodeAxis(node, 0, numSplitCoordsX, splitCoordsX, _nodeBB);
if (parentNode.axis != 1) checkNodeAxis(node, 1, numSplitCoordsY, splitCoordsY, _nodeBB);
if (parentNode.axis != 2) checkNodeAxis(node, 2, numSplitCoordsZ, splitCoordsZ, _nodeBB);
// Если сплит не найден, выходим
if (splitAxis < 0) return;
// Сплиттер найден. Разделение узла.
var axisX:Boolean = splitAxis == 0
var axisY:Boolean = splitAxis == 1;
node.axis = splitAxis;
node.coord = splitCoord;
// Создаём дочерние ноды
node.negativeNode = new CollisionKdNode();
node.negativeNode.parent = node;
node.negativeNode.boundBox = nodeBoundBox.clone();
node.positiveNode = new CollisionKdNode();
node.positiveNode.parent = node;
node.positiveNode.boundBox = nodeBoundBox.clone();
if (axisX) node.negativeNode.boundBox.maxX = node.positiveNode.boundBox.minX = splitCoord;
else if (axisY) node.negativeNode.boundBox.maxY = node.positiveNode.boundBox.minY = splitCoord;
else node.negativeNode.boundBox.maxZ = node.positiveNode.boundBox.minZ = splitCoord;
// Распределяем объекты по дочерним нодам
var coordMin:Number = splitCoord - threshold;
var coordMax:Number = splitCoord + threshold;
for (i = 0; i < numObjects; ++i) {
bb = staticBoundBoxes[objects[i]];
var min:Number = axisX ? bb.minX : (axisY ? bb.minY : bb.minZ);
var max:Number = axisX ? bb.maxX : (axisY ? bb.maxY : bb.maxZ);
if (max <= coordMax) {
if (min < coordMin) {
// Объект в негативной стороне
if (node.negativeNode.indices == null) node.negativeNode.indices = new Vector.<int>();
node.negativeNode.indices.push(objects[i]);
objects[i] = -1;
}
} else {
if (min >= coordMin) {
if (max > coordMax) {
// Объект в положительной стороне
if (node.positiveNode.indices == null) node.positiveNode.indices = new Vector.<int>();
node.positiveNode.indices.push(objects[i]);
objects[i] = -1;
}
}
}
}
// Очистка списка объектов
for (i = 0, j = 0; i < numObjects; ++i) {
if (objects[i] >= 0) objects[j++] = objects[i];
}
if (j > 0) objects.length = j;
else node.indices = null;
// Разделение дочерних нод
if (node.negativeNode.indices != null) splitNode(node.negativeNode);
if (node.positiveNode.indices != null) splitNode(node.positiveNode);
}
/**
*
* @param node
* @param axis
* @param numSplitCoords
* @param splitCoords
* @param bb
*/
private function checkNodeAxis(node:CollisionKdNode, axis:int, numSplitCoords:int, splitCoords:Vector.<Number>, bb:Vector.<Number>):void {
var axis1:int = (axis + 1)%3;
var axis2:int = (axis + 2)%3;
var area:Number = (bb[axis1 + 3] - bb[axis1])*(bb[axis2 + 3] - bb[axis2]);
var staticBoundBoxes:Vector.<BoundBox> = parentTree.staticBoundBoxes;
for (var i:int = 0; i < numSplitCoords; ++i) {
var currSplitCoord:Number = splitCoords[i];
if (isNaN(currSplitCoord)) continue;
var minCoord:Number = currSplitCoord - threshold;
var maxCoord:Number = currSplitCoord + threshold;
var areaNegative:Number = area*(currSplitCoord - bb[axis]);
var areaPositive:Number = area*(bb[int(axis + 3)] - currSplitCoord);
var numNegative:int = 0;
var numPositive:int = 0;
var conflict:Boolean = false;
// Проверяем объекты
var numObjects:int = node.indices.length;
for (var j:int = 0; j < numObjects; j++) {
var boundBox:BoundBox = staticBoundBoxes[node.indices[j]];
_bb[0] = boundBox.minX;
_bb[1] = boundBox.minY;
_bb[2] = boundBox.minZ;
_bb[3] = boundBox.maxX;
_bb[4] = boundBox.maxY;
_bb[5] = boundBox.maxZ;
if (_bb[axis + 3] <= maxCoord) {
if (_bb[axis] < minCoord) numNegative++;
} else {
if (_bb[axis] >= minCoord) {
numPositive++;
} else {
conflict = true;
break;
}
}
}
// Если хороший сплит, сохраняем
var cost:Number = areaNegative*numNegative + areaPositive*numPositive;
if (!conflict && cost < splitCost) {
splitAxis = axis;
splitCost = cost;
splitCoord = currSplitCoord;
}
for (j = i + 1; j < numSplitCoords; ++j) {
if (splitCoords[j] >= currSplitCoord - threshold && splitCoords[j] <= currSplitCoord + threshold) splitCoords[j] = NaN;
}
}
}
}
}

View File

@@ -0,0 +1,7 @@
package alternativa.physics.collision {
import alternativa.physics.rigid.Body;
public interface IBodyCollisionPredicate {
function considerBodies(body1:Body, body2:Body):Boolean;
}
}

View File

@@ -0,0 +1,30 @@
package alternativa.physics.collision {
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.rigid.Contact;
/**
* Интерфейс определителя столкновений между двумя примитивами.
*/
public interface ICollider {
/**
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
/**
* Выполняет быстрый тест на наличие пересечения двух примитивов.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @return true, если пересечение существует, иначе false
*/
function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean;
}
}

View File

@@ -0,0 +1,92 @@
package alternativa.physics.collision {
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.types.RayIntersection;
import alternativa.physics.rigid.Body;
import alternativa.physics.rigid.Contact;
import alternativa.physics.types.Vector3;
/**
* Интерфейс детектора столкновений.
*/
public interface ICollisionDetector {
/**
* Добавляет физический примитив в коллайдер.
*
* @param primitive добавляемый примитив
* @param isStatic указывает тип примитива: статический или динамический
* @return true если примитив был успешно добавлен, иначе false
*/
function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean;
/**
* Удаляет физический примитив из коллайдера.
*
* @param primitive удаляемый примитив
* @param isStatic указывает тип примитива: статический или динамический
* @return true если примитив был успшено удалён, иначе false
*/
function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean;
/**
* Выполняет инициализацию детектора после обновления списка примитивов.
*/
function init():void;
/**
* Получает все столкновения в текущей конфигурации физической геометрии.
*
* @param contacts список контактов, в кторые будет записана информация о столкновении
* @return количество найденных столкновений
*/
function getAllCollisions(contacts:Vector.<Contact>):int;
/**
* Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
* В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
*
* @param origin начальная точка луча в мировых координатах
* @param dir направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
* @param collisionGroup идентификатор группы
* @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
* @param predicate предикат, применяемый к столкновениям
* @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
* переданной структуре не гарантируется.
* @return true в случае наличия пересечения, иначе false
*/
function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
/**
* Тестирует луч на пересечение со статической физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
* В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
*
* @param origin начальная точка луча в мировых координатах
* @param dir направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
* @param collisionGroup идентификатор группы
* @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
* @param predicate предикат, применяемый к столкновениям
* @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
* переданной структуре не гарантируется.
* @return true в случае наличия пересечения, иначе false
*/
function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
/**
*
* @param prim1
* @param prim2
* @param contact
* @return
*/
function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
/**
*
* @param prim1
* @param prim2
* @return
*/
function testCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean;
}
}

View File

@@ -0,0 +1,9 @@
package alternativa.physics.collision {
import alternativa.physics.collision.primitives.CollisionPrimitive;
public interface ICollisionPredicate {
function considerCollision(primitive:CollisionPrimitive):Boolean;
}
}

View File

@@ -0,0 +1,7 @@
package alternativa.physics.collision {
import alternativa.physics.rigid.Body;
public interface IRayCollisionPredicate {
function considerBody(body:Body):Boolean;
}
}

View File

@@ -0,0 +1,484 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.altphysics;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.collision.types.RayIntersection;
import alternativa.physics.rigid.Body;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Детектор, хранящий статическую геометрию в kD-дереве и использующий дерево для ускорения тестов на пересечения.
*/
public class KdTreeCollisionDetector implements ICollisionDetector {
altphysics var tree:CollisionKdTree;
altphysics var dynamicPrimitives:Vector.<CollisionPrimitive>;
altphysics var dynamicPrimitivesNum:int;
altphysics var threshold:Number = 0.0001;
private var colliders:Object = {};
private var _time:MinMax = new MinMax();
private var _n:Vector3 = new Vector3();
private var _o:Vector3 = new Vector3();
private var _dynamicIntersection:RayIntersection = new RayIntersection();
/**
*
*/
public function KdTreeCollisionDetector() {
tree = new CollisionKdTree();
dynamicPrimitives = new Vector.<CollisionPrimitive>();
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.BOX, new BoxBoxCollider());
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.SPHERE, new BoxSphereCollider());
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.RECT, new BoxRectCollider());
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.TRIANGLE, new BoxTriangleCollider());
// addCollider(CollisionPrimitive.BOX, CollisionPrimitive.PLANE, new BoxPlaneCollider());
// addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.PLANE, new SpherePlaneCollider());
addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.SPHERE, new SphereSphereCollider());
}
/**
* @param primitive
* @param isStatic
* @return
*/
public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
if (isStatic) tree.addStaticPrimitive(primitive);
else dynamicPrimitives[dynamicPrimitivesNum++] = primitive;
return true;
}
/**
*
* @param primitive
* @param isStatic
* @return
*
*/
public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
if (isStatic) return tree.removeStaticPrimitive(primitive);
var idx:int = dynamicPrimitives.indexOf(primitive);
if (idx < 0) return false;
dynamicPrimitives.splice(idx, 1);
dynamicPrimitivesNum--;
return true;
}
/**
*
*/
public function init():void {
tree.createTree();
// tree.traceTree();
}
/**
*
* @param contacts
* @return
*/
public function getAllCollisions(contacts:Vector.<Contact>):int {
var colNum:int = 0;
for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
var primitive:CollisionPrimitive = dynamicPrimitives[i];
primitive.calculateAABB();
if (primitive.body != null && primitive.body.frozen) continue;
colNum += getPrimitiveNodeCollisions(tree.rootNode, primitive, contacts, colNum);
// Столкновения динамических примитивов между собой
// TODO: Попробовать различные оптимизации (сортировка по баундам, встраивание в дерево)
for (var j:int = i + 1; j < dynamicPrimitivesNum; j++) {
if (collide(primitive, dynamicPrimitives[j], contacts[colNum])) colNum++;
}
}
return colNum;
}
/**
* @param prim1
* @param prim2
* @param contact
* @return
*/
public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
if ((prim1.collisionGroup & prim2.collisionGroup) == 0) return false;
if (prim1.body != null && prim1.body == prim2.body) return false;
if (!prim1.aabb.intersects(prim2.aabb, 0.01)) return false;
var collider:ICollider = colliders[prim1.type <= prim2.type ? (prim1.type << 16) | prim2.type : (prim2.type << 16) | prim1.type] as ICollider;
if (collider != null && collider.getContact(prim1, prim2, contact)) {
if (prim1.postCollisionPredicate != null && !prim1.postCollisionPredicate.considerCollision(prim2)) return false;
if (prim2.postCollisionPredicate != null && !prim2.postCollisionPredicate.considerCollision(prim1)) return false;
// Сохраняем ссылку на контакт для каждого тела
if (prim1.body != null) prim1.body.contacts[prim1.body.contactsNum++] = contact;
if (prim2.body != null) prim2.body.contacts[prim2.body.contactsNum++] = contact;
// Вычисляем максимальную глубину пересечения для контакта
contact.maxPenetration = (contact.points[0] as ContactPoint).penetration;
var pen:Number;
for (var i:int = contact.pcount - 1; i >= 1; i--) {
if ((pen = (contact.points[i] as ContactPoint).penetration) > contact.maxPenetration) contact.maxPenetration = pen;
}
return true;
}
return false;
}
/**
* @param prim1
* @param prim2
* @param contact
* @return
*/
public function testCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
if ((prim1.collisionGroup & prim2.collisionGroup) == 0) return false;
if (prim1.body != null && prim1.body == prim2.body) return false;
if (!prim1.aabb.intersects(prim2.aabb, 0.01)) return false;
var collider:ICollider = colliders[prim1.type <= prim2.type ? (prim1.type << 16) | prim2.type : (prim2.type << 16) | prim1.type] as ICollider;
if (collider != null && collider.haveCollision(prim1, prim2)) {
if (prim1.postCollisionPredicate != null && !prim1.postCollisionPredicate.considerCollision(prim2)) return false;
if (prim2.postCollisionPredicate != null && !prim2.postCollisionPredicate.considerCollision(prim1)) return false;
return true;
}
return false;
}
/**
* Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
* В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча.
*
* @param origin
* @param dir
* @param collisionGroup идентификатор группы
* @param maxTime параметр, задающий длину проверяемого сегмента
* @param predicate
* @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
* переданной структуре не гарантируется.
* @return true в случае наличия пересечения, иначе false
*/
public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
var hasStaticIntersection:Boolean = intersectRayWithStatic(origin, dir, collisionGroup, maxTime, predicate, result);
var hasDynamicIntersection:Boolean = intersectRayWithDynamic(origin, dir, collisionGroup, maxTime, predicate, _dynamicIntersection);
if (!(hasDynamicIntersection || hasStaticIntersection)) return false;
if (hasDynamicIntersection && hasStaticIntersection) {
if (result.t > _dynamicIntersection.t) result.copy(_dynamicIntersection);
return true;
}
if (hasStaticIntersection) return true;
result.copy(_dynamicIntersection);
return true;
}
/**
*
* @param origin
* @param dir
* @param collisionGroup
* @param maxTime
* @param predicate
* @param result
* @return
*
*/
public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
// Вычисление точки пересечения с корневм узлом
if (!getRayBoundBoxIntersection(origin, dir, tree.rootNode.boundBox, _time)) return false;
if (_time.max < 0 || _time.min > maxTime) return false;
if (_time.min <= 0) {
_time.min = 0;
_o.x = origin.x;
_o.y = origin.y;
_o.z = origin.z;
} else {
_o.x = origin.x + _time.min*dir.x;
_o.y = origin.y + _time.min*dir.y;
_o.z = origin.z + _time.min*dir.z;
}
if (_time.max > maxTime) _time.max = maxTime;
var hasIntersection:Boolean = testRayAgainstNode(tree.rootNode, origin, _o, dir, collisionGroup, _time.min, _time.max, predicate, result);
return hasIntersection ? result.t <= maxTime : false;
}
/**
*
* @param body
* @param primitive
* @return
*
*/
public function testBodyPrimitiveCollision(body:Body, primitive:CollisionPrimitive):Boolean {
return false;
}
/**
*
* @param type1
* @param type2
* @param collider
*/
private function addCollider(type1:int, type2:int, collider:ICollider):void {
colliders[type1 <= type2 ? (type1 << 16) | type2 : (type2 << 16) | type1] = collider;
}
/**
* Выполняет поиск столкновений динамического примитива с примитивами узла дерева.
*
* @param node
* @param primitive
* @param contacts
* @param startIndex
* @return
*/
private function getPrimitiveNodeCollisions(node:CollisionKdNode, primitive:CollisionPrimitive, contacts:Vector.<Contact>, startIndex:int):int {
var colNum:int = 0;
if (node.indices != null) {
// Поиск столкновений со статическими примитивами узла
var primitives:Vector.<CollisionPrimitive> = tree.staticChildren;
var indices:Vector.<int> = node.indices;
for (var i:int = indices.length - 1; i >= 0; i--)
if (collide(primitive, primitives[indices[i]], contacts[startIndex + colNum])) colNum++;
}
if (node.axis == -1) return colNum;
var min:Number;
var max:Number;
switch (node.axis) {
case 0:
min = primitive.aabb.minX;
max = primitive.aabb.maxX;
break;
case 1:
min = primitive.aabb.minY;
max = primitive.aabb.maxY;
break;
case 2:
min = primitive.aabb.minZ;
max = primitive.aabb.maxZ;
break;
}
if (min < node.coord) colNum += getPrimitiveNodeCollisions(node.negativeNode, primitive, contacts, startIndex + colNum);
if (max > node.coord) colNum += getPrimitiveNodeCollisions(node.positiveNode, primitive, contacts, startIndex + colNum);
return colNum;
}
private static var _rayAABB:BoundBox = new BoundBox();
/**
* Тест пересечения луча с динамическими примитивами.
*
* @param origin
* @param dir
* @param collisionGroup
* @param maxTime
* @param predicate
* @param result
* @return
*/
private function intersectRayWithDynamic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
var xx:Number = origin.x + dir.x*maxTime;
var yy:Number = origin.y + dir.y*maxTime;
var zz:Number = origin.z + dir.z*maxTime;
if (xx < origin.x) {
_rayAABB.minX = xx;
_rayAABB.maxX = origin.x;
} else {
_rayAABB.minX = origin.x;
_rayAABB.maxX = xx;
}
if (yy < origin.y) {
_rayAABB.minY = yy;
_rayAABB.maxY = origin.y;
} else {
_rayAABB.minY = origin.y;
_rayAABB.maxY = yy;
}
if (zz < origin.z) {
_rayAABB.minZ = zz;
_rayAABB.maxZ = origin.z;
} else {
_rayAABB.minZ = origin.z;
_rayAABB.maxZ = zz;
}
var minTime:Number = maxTime + 1;
for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
var primitive:CollisionPrimitive = dynamicPrimitives[i];
if ((primitive.collisionGroup & collisionGroup) == 0) continue;
var paabb:BoundBox = primitive.aabb;
if (_rayAABB.maxX < paabb.minX || _rayAABB.minX > paabb.maxX || _rayAABB.maxY < paabb.minY || _rayAABB.minY > paabb.maxY || _rayAABB.maxZ < paabb.minZ || _rayAABB.minZ > paabb.maxZ) continue;
if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
var t:Number = primitive.getRayIntersection(origin, dir, threshold, _n);
if (t > 0 && t < minTime) {
minTime = t;
result.primitive = primitive;
result.normal.x = _n.x;
result.normal.y = _n.y;
result.normal.z = _n.z;
}
}
if (minTime > maxTime) return false;
result.pos.x = origin.x + dir.x*minTime;
result.pos.y = origin.y + dir.y*minTime;
result.pos.z = origin.z + dir.z*minTime;
result.t = minTime;
return true;
}
/**
* Вычисляет точки пересечения луча с AABB.
*
* @param origin точка начала луча
* @param dir направляющий вектор луча. Вектор может иметь любую отличную от нуля длину.
* @param bb AABB, с которым пересекается луч
* @param time возвращаемое значение. В эту переменную записывается минимальное и максимальное время пересечения
* @return true в случае наличия хотя бы одного пересечения, иначе false
*/
private function getRayBoundBoxIntersection(origin:Vector3, dir:Vector3, bb:BoundBox, time:MinMax):Boolean {
time.min = -1;
time.max = 1e308;
var t1:Number;
var t2:Number;
// Цикл по осям бокса
for (var i:int = 0; i < 3; i++) {
switch (i) {
case 0:
if (dir.x < threshold && dir.x > -threshold) {
if (origin.x < bb.minX || origin.x > bb.maxX) return false;
else continue;
}
t1 = (bb.minX - origin.x)/dir.x;
t2 = (bb.maxX - origin.x)/dir.x;
break;
case 1:
if (dir.y < threshold && dir.y > -threshold) {
if (origin.y < bb.minY || origin.y > bb.maxY) return false;
else continue;
}
t1 = (bb.minY - origin.y)/dir.y;
t2 = (bb.maxY - origin.y)/dir.y;
break;
case 2:
if (dir.z < threshold && dir.z > -threshold) {
if (origin.z < bb.minZ || origin.z > bb.maxZ) return false;
else continue;
}
t1 = (bb.minZ - origin.z)/dir.z;
t2 = (bb.maxZ - origin.z)/dir.z;
break;
}
if (t1 < t2) {
if (t1 > time.min) time.min = t1;
if (t2 < time.max) time.max = t2;
} else {
if (t2 > time.min) time.min = t2;
if (t1 < time.max) time.max = t1;
}
if (time.max < time.min) return false;
}
return true;
}
/**
*
* @param node
* @param origin
* @param dir
* @param collisionGroup
* @param t1 время входа луча в узел
* @param t2 время выхода луча из узла
* @param intersection
*/
private function testRayAgainstNode(node:CollisionKdNode, origin:Vector3, localOrigin:Vector3, dir:Vector3, collisionGroup:int, t1:Number, t2:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
// При наличии в узле объектов, проверяем пересечение с ними
if (node.indices != null && getRayNodeIntersection(origin, dir, collisionGroup, tree.staticChildren, node.indices, predicate, result)) return true;
// Выход из функции если это конечный узел
if (node.axis == -1) return false;
// Определение времени пересечения луча и плоскости разделения узла
var splitTime:Number;
var currChildNode:CollisionKdNode;
switch (node.axis) {
case 0:
if (dir.x > -threshold && dir.x < threshold) splitTime = t2 + 1;
else splitTime = (node.coord - origin.x)/dir.x;
currChildNode = localOrigin.x < node.coord ? node.negativeNode : node.positiveNode;
break;
case 1:
if (dir.y > -threshold && dir.y < threshold) splitTime = t2 + 1;
else splitTime = (node.coord - origin.y)/dir.y;
currChildNode = localOrigin.y < node.coord ? node.negativeNode : node.positiveNode;
break;
case 2:
if (dir.z > -threshold && dir.z < threshold) splitTime = t2 + 1;
else splitTime = (node.coord - origin.z)/dir.z;
currChildNode = localOrigin.z < node.coord ? node.negativeNode : node.positiveNode;
break;
}
// Определение порядка проверки
if (splitTime < t1 || splitTime > t2) {
// Луч не переходит в соседний дочерний узел
return testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, t2, predicate, result);
} else {
// Луч переходит из одного дочернего узла в другой
var intersects:Boolean = testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, splitTime, predicate, result);
if (intersects) return true;
_o.x = origin.x + splitTime*dir.x;
_o.y = origin.y + splitTime*dir.y;
_o.z = origin.z + splitTime*dir.z;
return testRayAgainstNode(currChildNode == node.negativeNode ? node.positiveNode : node.negativeNode, origin, _o, dir, collisionGroup, splitTime, t2, predicate, result);
}
}
/**
*
* @param origin
* @param dir
* @param collisionGroup
* @param primitives
* @param indices
* @param intersection
* @return
*
*/
private function getRayNodeIntersection(origin:Vector3, dir:Vector3, collisionGroup:int, primitives:Vector.<CollisionPrimitive>, indices:Vector.<int>, predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
var pnum:int = indices.length;
var minTime:Number = 1e308;
for (var i:int = 0; i < pnum; i++) {
var primitive:CollisionPrimitive = primitives[indices[i]];
if ((primitive.collisionGroup & collisionGroup) == 0) continue;
if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
var t:Number = primitive.getRayIntersection(origin, dir, threshold, _n);
if (t > 0 && t < minTime) {
minTime = t;
intersection.primitive = primitive;
intersection.normal.x = _n.x;
intersection.normal.y = _n.y;
intersection.normal.z = _n.z;
}
}
if (minTime == 1e308) return false;
intersection.pos.x = origin.x + dir.x*minTime;
intersection.pos.y = origin.y + dir.y*minTime;
intersection.pos.z = origin.z + dir.z*minTime;
intersection.t = minTime;
return true;
}
}
}
class MinMax {
public var min:Number = 0;
public var max:Number = 0;
}

View File

@@ -0,0 +1,67 @@
package alternativa.physics.collision {
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.rigid.Contact;
import alternativa.physics.types.Vector3;
/**
*
*/
public class SpherePlaneCollider implements ICollider {
private var normal:Vector3 = new Vector3();
/**
*
*/
public function SpherePlaneCollider() {
}
/**
*
* @param body1
* @param body2
* @param collisionInfo
* @return
*/
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
// var sphere:RigidSphere = body1 as RigidSphere;
// var plane:RigidPlane;
// if (sphere == null) {
// sphere = body2 as RigidSphere;
// plane = body1 as RigidPlane;
// } else {
// plane = body2 as RigidPlane;
// }
//
// // Вычисляем глобальные нормаль и смещение плоскости
// plane.baseMatrix.transformVector(plane.normal, normal);
// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
//
// var dist:Number = sphere.state.pos.dot(normal) - offset;
// if (dist > sphere.r) return false;
//
// collisionInfo.body1 = sphere;
// collisionInfo.body2 = plane;
// collisionInfo.normal.copy(normal);
// collisionInfo.pcount = 1;
//
// var cp:ContactPoint = collisionInfo.points[0];
// cp.penetration = sphere.r - dist;
// cp.pos.copy(normal).reverse().scale(sphere.r).add(sphere.state.pos);
// cp.r1.diff(cp.pos, sphere.state.pos);
// cp.r2.diff(cp.pos, plane.state.pos);
return true;
}
/**
* @param prim1
* @param prim2
* @return
*/
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,69 @@
package alternativa.physics.collision {
import alternativa.physics.altphysics;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.primitives.CollisionSphere;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Vector3;
use namespace altphysics;
public class SphereSphereCollider implements ICollider {
private var p1:Vector3 = new Vector3();
private var p2:Vector3 = new Vector3();
public function SphereSphereCollider() {
}
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
var s1:CollisionSphere;
var s2:CollisionSphere;
if (prim1.body != null) {
s1 = prim1 as CollisionSphere;
s2 = prim2 as CollisionSphere;
} else {
s1 = prim2 as CollisionSphere;
s2 = prim1 as CollisionSphere;
}
s1.transform.getAxis(3, p1);
s2.transform.getAxis(3, p2);
var dx:Number = p1.x - p2.x;
var dy:Number = p1.y - p2.y;
var dz:Number = p1.z - p2.z;
var len:Number = dx*dx + dy*dy + dz*dz;
var sum:Number = s1.r + s2.r;
if (len > sum*sum) return false;
len = Math.sqrt(len);
dx /= len;
dy /= len;
dz /= len;
contact.body1 = s1.body;
contact.body2 = s2.body;
contact.normal.x = dx;
contact.normal.y = dy;
contact.normal.z = dz;
contact.pcount = 1;
var cp:ContactPoint = contact.points[0];
cp.penetration = sum - len;
cp.pos.x = p1.x - dx*s1.r;
cp.pos.y = p1.y - dy*s1.r;
cp.pos.z = p1.z - dz*s1.r;
cp.r1.vDiff(cp.pos, p1);
cp.r2.vDiff(cp.pos, p2);
return true;
}
/**
* @param prim1
* @param prim2
* @return
*/
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,568 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.altphysics;
import alternativa.physics.collision.primitives.CollisionBox;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Расчитывает точки контакта двух боксов. Нормаль контакта направляется в сторону бокса с меньшим ID.
*/
public class BoxBoxCollider extends BoxCollider {
private var epsilon:Number = 0.001;
private var toBox1:Vector3 = new Vector3();
private var axis:Vector3 = new Vector3();
private var axis10:Vector3 = new Vector3();
private var axis11:Vector3 = new Vector3();
private var axis12:Vector3 = new Vector3();
private var axis20:Vector3 = new Vector3();
private var axis21:Vector3 = new Vector3();
private var axis22:Vector3 = new Vector3();
private var colAxis:Vector3 = new Vector3();
private var matrix:Matrix4 = new Matrix4();
private var bestAxisIndex:int;
private var minOverlap:Number;
private var points1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var points2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var tmpPoints:Vector.<ContactPoint> = new Vector.<ContactPoint>(8, true);
private var pcount:int;
/**
*
*/
public function BoxBoxCollider() {
for (var i:int = 0; i < 8; i++) {
tmpPoints[i] = new ContactPoint();
points1[i] = new Vector3();
points2[i] = new Vector3();
}
}
/**
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
override public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
if (!haveCollision(prim1, prim2)) return false;
var box1:CollisionBox = CollisionBox(prim1);
var box2:CollisionBox = CollisionBox(prim2);
if (bestAxisIndex < 6) {
// Контакт грань-(грань|ребро|вершина)
findFaceContactPoints(box1, box2, toBox1, bestAxisIndex, contact);
} else {
// Контакт ребро-ребро
bestAxisIndex -= 6;
findEdgesIntersection(box1, box2, toBox1, int(bestAxisIndex/3), bestAxisIndex%3, contact);
}
contact.body1 = box1.body;
contact.body2 = box2.body;
return true;
}
/**
* Выполняет быстрый тест на наличие пересечения двух примитивов.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @return true, если пересечение существует, иначе false
*/
override public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
var box1:CollisionBox = CollisionBox(prim1);
var box2:CollisionBox = CollisionBox(prim2);
var transform1:Matrix4 = box1.transform;
var transform2:Matrix4 = box2.transform;
minOverlap = 1e10;
// Вектор из центра второго бокса в центр первого
toBox1.x = transform1.d - transform2.d;
toBox1.y = transform1.h - transform2.h;
toBox1.z = transform1.l - transform2.l;
// Проверка пересечения по основным осям
axis10.x = transform1.a;
axis10.y = transform1.e;
axis10.z = transform1.i;
if (!testMainAxis(box1, box2, axis10, 0, toBox1)) return false;
axis11.x = transform1.b;
axis11.y = transform1.f;
axis11.z = transform1.j;
if (!testMainAxis(box1, box2, axis11, 1, toBox1)) return false;
axis12.x = transform1.c;
axis12.y = transform1.g;
axis12.z = transform1.k;
if (!testMainAxis(box1, box2, axis12, 2, toBox1)) return false;
axis20.x = transform2.a;
axis20.y = transform2.e;
axis20.z = transform2.i;
if (!testMainAxis(box1, box2, axis20, 3, toBox1)) return false;
axis21.x = transform2.b;
axis21.y = transform2.f;
axis21.z = transform2.j;
if (!testMainAxis(box1, box2, axis21, 4, toBox1)) return false;
axis22.x = transform2.c;
axis22.y = transform2.g;
axis22.z = transform2.k;
if (!testMainAxis(box1, box2, axis22, 5, toBox1)) return false;
// Проверка производных осей
if (!testDerivedAxis(box1, box2, axis10, axis20, 6, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis10, axis21, 7, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis10, axis22, 8, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis11, axis20, 9, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis11, axis21, 10, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis11, axis22, 11, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis12, axis20, 12, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis12, axis21, 13, toBox1)) return false;
if (!testDerivedAxis(box1, box2, axis12, axis22, 14, toBox1)) return false;
return true;
}
/**
* Выполняет поиск точек контакта грани одного бокса с гранью/ребром/вершиной другого.
*
* @param box1 первый бокс
* @param box2 второй бокс
* @param toBox1 вектор, направленный из центра второго бокса в центр первого
* @param faceAxisIdx индекс оси первого бокса, перпендикулярной грани, с которой произошло столкновение
* @param contactInfo структура, в которую записывается информация о точках контакта
*/
private function findFaceContactPoints(box1:CollisionBox, box2:CollisionBox, toBox1:Vector3, faceAxisIdx:int, contactInfo:Contact):void {
var swapNormal:Boolean = false;
if (faceAxisIdx > 2) {
// Столкновение с гранью второго бокса. Для дальнейших расчётов боксы меняются местами,
// но нормаль контакта всё равно должна быть направлена в сторону первоначального box1
var tmpBox:CollisionBox = box1;
box1 = box2;
box2 = tmpBox;
toBox1.x = -toBox1.x;
toBox1.y = -toBox1.y;
toBox1.z = -toBox1.z;
faceAxisIdx -= 3;
swapNormal = true;
}
var transform1:Matrix4 = box1.transform;
var transform2:Matrix4 = box2.transform;
transform1.getAxis(faceAxisIdx, colAxis);
var negativeFace:Boolean = colAxis.x*toBox1.x + colAxis.y*toBox1.y + colAxis.z*toBox1.z > 0;
if (!negativeFace) {
colAxis.x = -colAxis.x;
colAxis.y = -colAxis.y;
colAxis.z = -colAxis.z;
}
// Ищем ось второго бокса, определяющую наиболее антипараллельную грань
var incidentAxisIdx:int = 0;
var maxDot:Number = 0;
var incidentAxisDot:Number = 0;
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
transform2.getAxis(axisIdx, axis);
var dot:Number = axis.x*colAxis.x + axis.y*colAxis.y + axis.z*colAxis.z;
var absDot:Number = dot < 0 ? -dot : dot;
if (absDot > maxDot) {
maxDot = absDot;
incidentAxisDot = dot;
incidentAxisIdx = axisIdx;
}
}
// Получаем список вершин грани второго бокса, переводим их в систему координат первого бокса и выполняем обрезку
// по грани первого бокса. Таким образом получается список потенциальных точек контакта.
transform2.getAxis(incidentAxisIdx, axis);
getFaceVertsByAxis(box2.hs, incidentAxisIdx, incidentAxisDot < 0, points1);
// TODO: Рассчитать результирующую матрицу и конвертировать точки один раз
transform2.transformVectorsN(points1, points2, 4);
transform1.transformVectorsInverseN(points2, points1, 4);
var pnum:int = clip(box1.hs, faceAxisIdx);
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
var pen:Number;
pcount = 0;
for (var i:int = 0; i < pnum; i++) {
var v:Vector3 = points1[i];
if ((pen = getPointBoxPenetration(box1.hs, v, faceAxisIdx, negativeFace)) > -epsilon) {
var cp:ContactPoint = tmpPoints[pcount++];
var cpPos:Vector3 = cp.pos;
cpPos.x = transform1.a*v.x + transform1.b*v.y + transform1.c*v.z + transform1.d;
cpPos.y = transform1.e*v.x + transform1.f*v.y + transform1.g*v.z + transform1.h;
cpPos.z = transform1.i*v.x + transform1.j*v.y + transform1.k*v.z + transform1.l;
cp.r1.x = cpPos.x - transform1.d;
cp.r1.y = cpPos.y - transform1.h;
cp.r1.z = cpPos.z - transform1.l;
cp.r2.x = cpPos.x - transform2.d;
cp.r2.y = cpPos.y - transform2.h;
cp.r2.z = cpPos.z - transform2.l;
cp.penetration = pen;
}
}
if (swapNormal) {
contactInfo.normal.x = -colAxis.x;
contactInfo.normal.y = -colAxis.y;
contactInfo.normal.x = -colAxis.x;
} else {
contactInfo.normal.x = colAxis.x;
contactInfo.normal.y = colAxis.y;
contactInfo.normal.x = colAxis.x;
}
if (pcount > 4) {
reducePoints();
}
for (i = 0; i < pcount; i++) {
ContactPoint(contactInfo.points[i]).copyFrom(tmpPoints[i]);
}
contactInfo.pcount = pcount;
}
/**
*
* @param contactInfo
*/
private function reducePoints():void {
var i:int;
var minIdx:int;
var cp1:ContactPoint;
var cp2:ContactPoint;
while (pcount > 4) {
var minLen:Number = 1e10;
var p1:Vector3 = ContactPoint(tmpPoints[int(pcount - 1)]).pos;
var p2:Vector3;
for (i = 0; i < pcount; i++) {
p2 = ContactPoint(tmpPoints[i]).pos;
var dx:Number = p2.x - p1.x;
var dy:Number = p2.y - p1.y;
var dz:Number = p2.z - p1.z;
var len:Number = dx*dx + dy*dy + dz*dz;
if (len < minLen) {
minLen = len;
minIdx = i;
}
p1 = p2;
}
cp1 = tmpPoints[minIdx == 0 ? int(pcount - 1) : int(minIdx - 1)];
cp2 = tmpPoints[minIdx];
p1 = cp1.pos;
p2 = cp2.pos;
p2.x = 0.5*(p1.x + p2.x);
p2.y = 0.5*(p1.y + p2.y);
p2.z = 0.5*(p1.z + p2.z);
cp2.penetration = 0.5*(cp1.penetration + cp2.penetration);
if (minIdx > 0) {
for (i = minIdx; i < pcount; i++) {
tmpPoints[int(i - 1)] = tmpPoints[i];
}
tmpPoints[int(pcount - 1)] = cp1;
}
pcount--;
}
}
/**
*
* @param hs
* @param p
* @param faceAxisIdx
* @param negativeFace
* @return
*/
private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, negativeFace:Boolean):Number {
switch (faceAxisIdx) {
case 0:
if (negativeFace) return p.x + hs.x;
else return hs.x - p.x;
case 1:
if (negativeFace) return p.y + hs.y;
else return hs.y - p.y;
case 2:
if (negativeFace) return p.z + hs.z;
else return hs.z - p.z;
}
return 0;
}
/**
* Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
*
* @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
* @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
* @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
*/
private function clip(hs:Vector3, faceAxisIdx:int):int {
var pnum:int = 4;
switch (faceAxisIdx) {
case 0:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
case 1:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighX(hs.x, pnum, points2, points1, epsilon);
case 2:
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
}
return 0;
}
/**
* Вычисляет точку столкновения рёбер двух боксов.
*
* @param box1 первый бокс
* @param box2 второй бокс
* @param toBox1 вектор, направленный из центра второго бокса в центр первого
* @param axisIdx1 индекс направляющей оси ребра первого бокса
* @param axisIdx2 индекс направляющей оси ребра второго бокса
* @param contactInfo структура, в которую записывается информация о столкновении
*/
private function findEdgesIntersection(box1:CollisionBox, box2:CollisionBox, toBox1:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
var transform1:Matrix4 = box1.transform;
var transform2:Matrix4 = box2.transform;
transform1.getAxis(axisIdx1, axis10);
transform2.getAxis(axisIdx2, axis20);
var normalX:Number = axis10.y*axis20.z - axis10.z*axis20.y;
var normalY:Number = axis10.z*axis20.x - axis10.x*axis20.z;
var normalZ:Number = axis10.x*axis20.y - axis10.y*axis20.x;
var k:Number = 1/Math.sqrt(normalX*normalX + normalY*normalY + normalZ*normalZ);
normalX *= k;
normalY *= k;
normalZ *= k;
// Разворот нормали в сторону первого бокса
if (normalX*toBox1.x + normalY*toBox1.y + normalZ*toBox1.z < 0) {
normalX = -normalX;
normalY = -normalY;
normalZ = -normalZ;
}
// Находим среднюю точку на каждом из пересекающихся рёбер
var halfLen1:Number;
var halfLen2:Number;
var x1:Number = box1.hs.x;
var y1:Number = box1.hs.y;
var z1:Number = box1.hs.z;
var x2:Number = box2.hs.x;
var y2:Number = box2.hs.y;
var z2:Number = box2.hs.z;
// x
if (axisIdx1 == 0) {
x1 = 0;
halfLen1 = box1.hs.x;
} else {
if (normalX*transform1.a + normalY*transform1.e + normalZ*transform1.i > 0) {
x1 = -x1;
}
}
if (axisIdx2 == 0) {
x2 = 0;
halfLen2 = box2.hs.x;
} else {
if (normalX*transform2.a + normalY*transform2.e + normalZ*transform2.i < 0) {
x2 = -x2;
}
}
// y
if (axisIdx1 == 1) {
y1 = 0;
halfLen1 = box1.hs.y;
} else {
if (normalX*transform1.b + normalY*transform1.f + normalZ*transform1.j > 0) {
y1 = -y1;
}
}
if (axisIdx2 == 1) {
y2 = 0;
halfLen2 = box2.hs.y;
} else {
if (normalX*transform2.b + normalY*transform2.f + normalZ*transform2.j < 0) {
y2 = -y2;
}
}
// z
if (axisIdx1 == 2) {
z1 = 0;
halfLen1 = box1.hs.z;
} else {
if (normalX*transform1.c + normalY*transform1.g + normalZ*transform1.k > 0) {
z1 = -z1;
}
}
if (axisIdx2 == 2) {
z2 = 0;
halfLen2 = box2.hs.z;
} else {
if (normalX*transform2.c + normalY*transform2.g + normalZ*transform2.k < 0) {
z2 = -z2;
}
}
// Получаем глобальные координаты средних точек рёбер
var xx:Number = x1;
var yy:Number = y1;
var zz:Number = z1;
x1 = transform1.a*xx + transform1.b*yy + transform1.c*zz + transform1.d;
y1 = transform1.e*xx + transform1.f*yy + transform1.g*zz + transform1.h;
z1 = transform1.i*xx + transform1.j*yy + transform1.k*zz + transform1.l;
xx = x2;
yy = y2;
zz = z2;
x2 = transform2.a*xx + transform2.b*yy + transform2.c*zz + transform2.d;
y2 = transform2.e*xx + transform2.f*yy + transform2.g*zz + transform2.h;
z2 = transform2.i*xx + transform2.j*yy + transform2.k*zz + transform2.l;
// Находим точку пересечения рёбер, решая систему уравнений
k = axis10.x*axis20.x + axis10.y*axis20.y + axis10.z*axis20.z;
var det:Number = k*k - 1;
xx = x2 - x1;
yy = y2 - y1;
zz = z2 - z1;
var c1:Number = axis10.x*xx + axis10.y*yy + axis10.z*zz;
var c2:Number = axis20.x*xx + axis20.y*yy + axis20.z*zz;
var t1:Number = (c2*k - c1)/det;
var t2:Number = (c2 - c1*k)/det;
// Запись данных о столкновении
contact.normal.x = normalX;
contact.normal.y = normalY;
contact.normal.z = normalZ;
contact.pcount = 1;
var cp:ContactPoint = contact.points[0];
var cpPos:Vector3 = cp.pos;
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
cpPos.x = 0.5*(x1 + axis10.x*t1 + x2 + axis20.x*t2);
cpPos.y = 0.5*(y1 + axis10.y*t1 + y2 + axis20.y*t2);
cpPos.z = 0.5*(z1 + axis10.z*t1 + z2 + axis20.z*t2);
cp.r1.x = cpPos.x - transform1.d;
cp.r1.y = cpPos.y - transform1.h;
cp.r1.z = cpPos.z - transform1.l;
cp.r2.x = cpPos.x - transform2.d;
cp.r2.y = cpPos.y - transform2.h;
cp.r2.z = cpPos.z - transform2.l;
cp.penetration = minOverlap;
}
/**
* Проверяет пересечение боксов вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
*
* @param box1
* @param box2
* @param axis
* @param axisIndex
* @param toBox1
* @param bestAxis
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
*/
private function testMainAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, axisIndex:int, toBox1:Vector3):Boolean {
var overlap:Number = overlapOnAxis(box1, box2, axis, toBox1);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
*
* @param box1
* @param box2
* @param axis1
* @param axis2
* @param axisIndex
* @param toBox1
* @return
*/
private function testDerivedAxis(box1:CollisionBox, box2:CollisionBox, axis1:Vector3, axis2:Vector3, axisIndex:int, toBox1:Vector3):Boolean {
// axis = axis1 cross axis2
axis.x = axis1.y*axis2.z - axis1.z*axis2.y;
axis.y = axis1.z*axis2.x - axis1.x*axis2.z;
axis.z = axis1.x*axis2.y - axis1.y*axis2.x;
var lenSqr:Number = axis.x*axis.x + axis.y*axis.y + axis.z*axis.z;
if (lenSqr < 0.0001) return true;
var k:Number = 1/Math.sqrt(lenSqr);
axis.x *= k;
axis.y *= k;
axis.z *= k;
var overlap:Number = overlapOnAxis(box1, box2, axis, toBox1);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
* Вычисляет глубину перекрытия двух боксов вдоль заданной оси.
*
* @param box1 первый бокс
* @param box2 второй бокс
* @param axis ось
* @param toBox1 вектор, соединяющий центр второго бокса с центром первого
* @return величина перекрытия боксов вдоль оси
*/
public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, toBox1:Vector3):Number {
var m:Matrix4 = box1.transform;
var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box1.hs.x;
if (d < 0) d = -d;
var projection:Number = d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box1.hs.y;
if (d < 0) d = -d;
projection += d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box1.hs.z;
if (d < 0) d = -d;
projection += d;
m = box2.transform;
d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box2.hs.x;
if (d < 0) d = -d;
projection += d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box2.hs.y;
if (d < 0) d = -d;
projection += d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box2.hs.z;
if (d < 0) d = -d;
projection += d;
d = toBox1.x*axis.x + toBox1.y*axis.y + toBox1.z*axis.z;
if (d < 0) d = -d;
return projection - d;
}
}
}
import alternativa.physics.types.Vector3;
class CollisionPointTmp {
public var pos:Vector3 = new Vector3();
public var penetration:Number;
}

View File

@@ -0,0 +1,387 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.rigid.Contact;
import alternativa.physics.types.Vector3;
public class BoxCollider implements ICollider {
public function BoxCollider() {
}
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
return false;
}
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
/**
* Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
*
* @param box бокс, в котором ишутся вершины
* @param axisIdx индекс нормальной оси
* @param reverse если указано значение true, возвращаются вершины противоположной грани
* @param result список, в который помещаются вершины
*/
protected function getFaceVertsByAxis(hs:Vector3, axisIdx:int, negativeFace:Boolean, result:Vector.<Vector3>):void {
var v:Vector3;
switch (axisIdx) {
case 0:
if (negativeFace) {
v = result[0]; v.x = -hs.x; v.y = hs.y; v.z = -hs.z;
v = result[1]; v.x = -hs.x; v.y = -hs.y; v.z = -hs.z;
v = result[2]; v.x = -hs.x; v.y = -hs.y; v.z = hs.z;
v = result[3]; v.x = -hs.x; v.y = hs.y; v.z = hs.z;
} else {
v = result[0]; v.x = hs.x; v.y = -hs.y; v.z = -hs.z;
v = result[1]; v.x = hs.x; v.y = hs.y; v.z = -hs.z;
v = result[2]; v.x = hs.x; v.y = hs.y; v.z = hs.z;
v = result[3]; v.x = hs.x; v.y = -hs.y; v.z = hs.z;
}
break;
case 1:
if (negativeFace) {
v = result[0]; v.x = -hs.x; v.y = -hs.y; v.z = -hs.z;
v = result[1]; v.x = hs.x; v.y = -hs.y; v.z = -hs.z;
v = result[2]; v.x = hs.x; v.y = -hs.y; v.z = hs.z;
v = result[3]; v.x = -hs.x; v.y = -hs.y; v.z = hs.z;
} else {
v = result[0]; v.x = hs.x; v.y = hs.y; v.z = -hs.z;
v = result[1]; v.x = -hs.x; v.y = hs.y; v.z = -hs.z;
v = result[2]; v.x = -hs.x; v.y = hs.y; v.z = hs.z;
v = result[3]; v.x = hs.x; v.y = hs.y; v.z = hs.z;
}
break;
case 2:
if (negativeFace) {
v = result[0]; v.x = -hs.x; v.y = hs.y; v.z = -hs.z;
v = result[1]; v.x = hs.x; v.y = hs.y; v.z = -hs.z;
v = result[2]; v.x = hs.x; v.y = -hs.y; v.z = -hs.z;
v = result[3]; v.x = -hs.x; v.y = -hs.y; v.z = -hs.z;
} else {
v = result[0]; v.x = -hs.x; v.y = -hs.y; v.z = hs.z;
v = result[1]; v.x = hs.x; v.y = -hs.y; v.z = hs.z;
v = result[2]; v.x = hs.x; v.y = hs.y; v.z = hs.z;
v = result[3]; v.x = -hs.x; v.y = hs.y; v.z = hs.z;
}
break;
}
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipLowX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var x1:Number = x - epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.x > x1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.x < x1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (x - p1.x)/dx;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.x > x1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (x - p1.x)/dx;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipHighX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var x1:Number = x + epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.x < x1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.x > x1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (x - p1.x)/dx;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.x < x1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (x - p1.x)/dx;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipLowY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var y1:Number = y - epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.y > y1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.y < y1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (y - p1.y)/dy;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.y > y1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (y - p1.y)/dy;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipHighY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var y1:Number = y + epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.y < y1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.y > y1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (y - p1.y)/dy;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.y < y1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (y - p1.y)/dy;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipLowZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var z1:Number = z - epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.z > z1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.z < z1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (z - p1.z)/dz;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.z > z1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (z - p1.z)/dz;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
/**
*
* @param x
* @param pnum
* @param points
* @param result
* @return
*/
protected function clipHighZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
var z1:Number = z + epsilon;
var num:int = 0;
var p1:Vector3 = points[int(pnum - 1)];
var p2:Vector3;
var dx:Number;
var dy:Number;
var dz:Number;
var t:Number;
var v:Vector3;
for (var i:int = 0; i < pnum; i++) {
p2 = points[i];
if (p1.z < z1) {
v = result[num++];
v.x = p1.x;
v.y = p1.y;
v.z = p1.z;
if (p2.z > z1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (z - p1.z)/dz;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
} else if (p2.z < z1) {
dx = p2.x - p1.x;
dy = p2.y - p1.y;
dz = p2.z - p1.z;
t = (z - p1.z)/dz;
v = result[num++];
v.x = p1.x + t*dx;
v.y = p1.y + t*dy;
v.z = p1.z + t*dz;
}
p1 = p2;
}
return num;
}
}
}

View File

@@ -0,0 +1,87 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.rigid.Contact;
import alternativa.physics.types.Vector3;
public class BoxPlaneCollider implements ICollider {
private var verts1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var verts2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var normal:Vector3 = new Vector3();
public function BoxPlaneCollider() {
for (var i:int = 0; i < 8; i++) {
verts1[i] = new Vector3();
verts2[i] = new Vector3();
}
}
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
// var box:RigidBox = body1 as RigidBox;
// var plane:RigidPlane;
// if (box == null) {
// box = body2 as RigidBox;
// plane = body1 as RigidPlane;
// } else {
// plane = body2 as RigidPlane;
// }
//
// // Вычисляем глобальные координаты вершин бокса
// var sx:Number = box.halfSize.x;
// var sy:Number = box.halfSize.y;
// var sz:Number = box.halfSize.z;
// (verts1[0] as Vector3).reset(-sx, -sy, -sz);
// (verts1[1] as Vector3).reset(sx, -sy, -sz);
// (verts1[2] as Vector3).reset(sx, sy, -sz);
// (verts1[3] as Vector3).reset(-sx, sy, -sz);
// (verts1[4] as Vector3).reset(-sx, -sy, sz);
// (verts1[5] as Vector3).reset(sx, -sy, sz);
// (verts1[6] as Vector3).reset(sx, sy, sz);
// (verts1[7] as Vector3).reset(-sx, sy, sz);
//
// box.transform.transformVectors(verts1, verts2);
// // Вычисляем глобальные нормаль и смещение плоскости
// plane.baseMatrix.transformVector(plane.normal, normal);
// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
// // Проверяем наличие столкновений с каждой вершиной
// collisionInfo.pcount = 0;
// for (var i:int = 0; i < 8; i++) {
// // Вершина добавляется в список точек столкновения, если лежит под плоскостью
// var dist:Number = (verts2[i] as Vector3).dot(normal);
// if (dist < offset) {
// var cp:ContactPoint;
// if (collisionInfo.pcount == collisionInfo.points.length) {
// cp = new ContactPoint();
// collisionInfo.points[collisionInfo.pcount] = cp;
// } else {
// cp = collisionInfo.points[collisionInfo.pcount];
// }
// cp.pos.copy(verts2[i]);
// cp.r1.diff(cp.pos, box.state.pos);
// cp.r2.diff(cp.pos, plane.state.pos);
// cp.penetration = offset - dist;
// collisionInfo.pcount++;
// }
// }
// if (collisionInfo.pcount > 0) {
// collisionInfo.body1 = box;
// collisionInfo.body2 = plane;
// collisionInfo.normal.copy(normal);
// return true;
// }
return false;
}
/**
* @param prim1
* @param prim2
* @return
*/
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,522 @@
package alternativa.physics.collision {
import alternativa.physics.altphysics;
import alternativa.physics.collision.primitives.CollisionBox;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.primitives.CollisionRect;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
*
*/
public class BoxRectCollider extends BoxCollider {
private var epsilon:Number = 0.001;
private var vectorToBox:Vector3 = new Vector3();
private var axis:Vector3 = new Vector3();
private var axis10:Vector3 = new Vector3();
private var axis11:Vector3 = new Vector3();
private var axis12:Vector3 = new Vector3();
private var axis20:Vector3 = new Vector3();
private var axis21:Vector3 = new Vector3();
private var axis22:Vector3 = new Vector3();
private var bestAxisIndex:int;
private var minOverlap:Number;
private var points1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var points2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
/**
*
*/
public function BoxRectCollider() {
for (var i:int = 0; i < 8; i++) {
points1[i] = new Vector3();
points2[i] = new Vector3();
}
}
/**
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
override public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
if (!haveCollision(prim1, prim2)) return false;
var box:CollisionBox = prim1 as CollisionBox;
var rect:CollisionRect;
if (box == null) {
box = prim2 as CollisionBox;
rect = prim1 as CollisionRect;
} else {
rect = prim2 as CollisionRect;
}
if (bestAxisIndex < 4) {
// Контакт вдоль одной из основных осей
if (!findFaceContactPoints(box, rect, vectorToBox, bestAxisIndex, contact)) return false;
} else {
// Контакт ребро-ребро
bestAxisIndex -= 4;
findEdgesIntersection(box, rect, vectorToBox, int(bestAxisIndex/2), bestAxisIndex%2, contact);
}
contact.body1 = box.body;
contact.body2 = rect.body;
return true;
}
/**
* Выполняет быстрый тест на наличие пересечения двух примитивов.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @return true, если пересечение существует, иначе false
*/
override public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
minOverlap = 1e10;
var box:CollisionBox = prim1 as CollisionBox;
var rect:CollisionRect;
if (box == null) {
box = prim2 as CollisionBox;
rect = prim1 as CollisionRect;
} else {
rect = prim2 as CollisionRect;
}
var boxTransform:Matrix4 = box.transform;
var rectTransform:Matrix4 = rect.transform;
// Вектор из центра прямоугольника в центр бокса
vectorToBox.x = boxTransform.d - rectTransform.d;
vectorToBox.y = boxTransform.h - rectTransform.h;
vectorToBox.z = boxTransform.l - rectTransform.l;
// Проверка пересечения по нормали прямоугольника
rectTransform.getAxis(2, axis22);
if (!testMainAxis(box, rect, axis22, 0, vectorToBox)) return false;
// Проверка пересечения по основным осям бокса
boxTransform.getAxis(0, axis10);
if (!testMainAxis(box, rect, axis10, 1, vectorToBox)) return false;
boxTransform.getAxis(1, axis11);
if (!testMainAxis(box, rect, axis11, 2, vectorToBox)) return false;
boxTransform.getAxis(2, axis12);
if (!testMainAxis(box, rect, axis12, 3, vectorToBox)) return false;
// Получаем направляющие рёбер прямоугольника
rectTransform.getAxis(0, axis20);
rectTransform.getAxis(1, axis21);
// Проверка производных осей
if (!testDerivedAxis(box, rect, axis10, axis20, 4, vectorToBox)) return false;
if (!testDerivedAxis(box, rect, axis10, axis21, 5, vectorToBox)) return false;
if (!testDerivedAxis(box, rect, axis11, axis20, 6, vectorToBox)) return false;
if (!testDerivedAxis(box, rect, axis11, axis21, 7, vectorToBox)) return false;
if (!testDerivedAxis(box, rect, axis12, axis20, 8, vectorToBox)) return false;
if (!testDerivedAxis(box, rect, axis12, axis21, 9, vectorToBox)) return false;
return true;
}
/**
* Выполняет поиск точек контакта бокса с прямоугольником.
*
* @param box бокс
* @param rect прямоугольник
* @param vectorToBox вектор, направленный из центра прямоугольника в центр бокса
* @param faceAxisIdx индекс оси, идентифицирующей полскость столкновения (грань бокса или полскость прямоугольника)
* @param colInfo структура, в которую записывается информация о точках контакта
*/
private function findFaceContactPoints(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, faceAxisIdx:int, contact:Contact):Boolean {
var pnum:int;
var i:int;
var v:Vector3;
var cp:ContactPoint;
var boxTransform:Matrix4 = box.transform;
var rectTransform:Matrix4 = rect.transform;
var colAxis:Vector3 = contact.normal;
if (faceAxisIdx == 0) {
// Столкновение с плоскостью прямоугольника
// Проверим положение бокса относительно плоскости прямоугольника
colAxis.x = rectTransform.c;
colAxis.y = rectTransform.g;
colAxis.z = rectTransform.k;
// var offset:Number = colAxis.x*rectTransform.d + colAxis.y*rectTransform.h + colAxis.z*rectTransform.l;
// if (bbPos.vDot(colAxis) < offset) return false;
// Ищем ось бокса, определяющую наиболее антипараллельную грань
var incidentAxisIdx:int = 0;
var incidentAxisDot:Number;
var maxDot:Number = 0;
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
boxTransform.getAxis(axisIdx, axis);
var dot:Number = axis.x*colAxis.x + axis.y*colAxis.y + axis.z*colAxis.z;
var absDot:Number = dot < 0 ? -dot : dot;
if (absDot > maxDot) {
maxDot = absDot;
incidentAxisIdx = axisIdx;
incidentAxisDot = dot;
}
}
// Получаем список вершин грани бокса, переводим их в систему координат прямоугольника и выполняем обрезку
// по прямоугольнику. Таким образом получается список потенциальных точек контакта.
boxTransform.getAxis(incidentAxisIdx, axis);
getFaceVertsByAxis(box.hs, incidentAxisIdx, incidentAxisDot > 0, points1);
boxTransform.transformVectorsN(points1, points2, 4);
rectTransform.transformVectorsInverseN(points2, points1, 4);
pnum = clipByRect(rect.hs);
// Проверяем каждую потенциальную точку на принадлежность нижней полуплоскости прямоугольника и добавляем такие точки в список контактов
contact.pcount = 0;
for (i = 0; i < pnum; i++) {
v = points1[i];
if (v.z < epsilon) {
cp = contact.points[contact.pcount++];
cp.penetration = -v.z;
var cpPos:Vector3 = cp.pos;
cpPos.x = rectTransform.a*v.x + rectTransform.b*v.y + rectTransform.c*v.z + rectTransform.d;
cpPos.y = rectTransform.e*v.x + rectTransform.f*v.y + rectTransform.g*v.z + rectTransform.h;
cpPos.z = rectTransform.i*v.x + rectTransform.j*v.y + rectTransform.k*v.z + rectTransform.l;
v = cp.r1;
v.x = cpPos.x - boxTransform.d;
v.y = cpPos.y - boxTransform.h;
v.z = cpPos.z - boxTransform.l;
v = cp.r2;
v.x = cpPos.x - rectTransform.d;
v.y = cpPos.y - rectTransform.h;
v.z = cpPos.z - rectTransform.l;
}
}
} else {
// Столкновение с гранью бокса
faceAxisIdx--;
boxTransform.getAxis(faceAxisIdx, colAxis);
var negativeFace:Boolean = colAxis.x*vectorToBox.x + colAxis.y*vectorToBox.y + colAxis.z*vectorToBox.z > 0;
if (!negativeFace) {
colAxis.x = -colAxis.x;
colAxis.y = -colAxis.y;
colAxis.z = -colAxis.z;
}
if (rectTransform.c*colAxis.x + rectTransform.g*colAxis.y + rectTransform.k*colAxis.z < 0) return false;
getFaceVertsByAxis(rect.hs, 2, false, points1);
rectTransform.transformVectorsN(points1, points2, 4);
boxTransform.transformVectorsInverseN(points2, points1, 4);
pnum = clipByBox(box.hs, faceAxisIdx);
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
var pen:Number;
contact.pcount = 0;
for (i = 0; i < pnum; i++) {
v = points1[i];
if ((pen = getPointBoxPenetration(box.hs, v, faceAxisIdx, negativeFace)) > -epsilon) {
cp = contact.points[contact.pcount++];
cp.penetration = pen;
cpPos = cp.pos;
cpPos.x = boxTransform.a*v.x + boxTransform.b*v.y + boxTransform.c*v.z + boxTransform.d;
cpPos.y = boxTransform.e*v.x + boxTransform.f*v.y + boxTransform.g*v.z + boxTransform.h;
cpPos.z = boxTransform.i*v.x + boxTransform.j*v.y + boxTransform.k*v.z + boxTransform.l;
v = cp.r1;
v.x = cpPos.x - boxTransform.d;
v.y = cpPos.y - boxTransform.h;
v.z = cpPos.z - boxTransform.l;
v = cp.r2;
v.x = cpPos.x - rectTransform.d;
v.y = cpPos.y - rectTransform.h;
v.z = cpPos.z - rectTransform.l;
}
}
}
return true;
}
/**
*
* @param hs
* @param p
* @param axisIndex
* @param reverse
* @return
*/
private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
switch (faceAxisIdx) {
case 0:
if (reverse) return p.x + hs.x;
else return hs.x - p.x;
case 1:
if (reverse) return p.y + hs.y;
else return hs.y - p.y;
case 2:
if (reverse) return p.z + hs.z;
else return hs.z - p.z;
}
return 0;
}
/**
* Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
*
* @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
* @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
* @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
*/
private function clipByBox(hs:Vector3, faceAxisIdx:int):int {
var pnum:int = 4;
switch (faceAxisIdx) {
case 0:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
case 1:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighX(hs.x, pnum, points2, points1, epsilon);
case 2:
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
}
return 0;
}
/**
*
* @param hs
* @return
*/
private function clipByRect(hs:Vector3):int {
var pnum:int = 4;
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
}
/**
* Вычисляет точку столкновения рёбер двух боксов.
*
* @param box первый бокс
* @param rect второй бокс
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
* @param axisIdx1 индекс направляющей оси ребра первого бокса
* @param axisIdx2 индекс направляющей оси ребра второго бокса
* @param colInfo структура, в которую записывается информация о столкновении
*/
private function findEdgesIntersection(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
var boxTransform:Matrix4 = box.transform;
var rectTransform:Matrix4 = rect.transform;
boxTransform.getAxis(axisIdx1, axis10);
rectTransform.getAxis(axisIdx2, axis20);
var colAxis:Vector3 = contact.normal;
colAxis.x = axis10.y*axis20.z - axis10.z*axis20.y;
colAxis.y = axis10.z*axis20.x - axis10.x*axis20.z;
colAxis.z = axis10.x*axis20.y - axis10.y*axis20.x;
var k:Number = 1/Math.sqrt(colAxis.x*colAxis.x + colAxis.y*colAxis.y + colAxis.z*colAxis.z);
colAxis.x *= k;
colAxis.y *= k;
colAxis.z *= k;
// Разворот оси в сторону бокса
if (colAxis.x*vectorToBox.x + colAxis.y*vectorToBox.y + colAxis.z*vectorToBox.z < 0) {
colAxis.x = -colAxis.x;
colAxis.y = -colAxis.y;
colAxis.z = -colAxis.z;
}
// Находим среднюю точку на каждом из пересекающихся рёбер
var halfLen1:Number;
var halfLen2:Number;
var vx:Number = box.hs.x;
var vy:Number = box.hs.y;
var vz:Number = box.hs.z;
var x2:Number = rect.hs.x;
var y2:Number = rect.hs.y;
var z2:Number = rect.hs.z;
// x
if (axisIdx1 == 0) {
vx = 0;
halfLen1 = box.hs.x;
} else {
if (boxTransform.a*colAxis.x + boxTransform.e*colAxis.y + boxTransform.i*colAxis.z > 0) {
vx = -vx;
}
}
if (axisIdx2 == 0) {
x2 = 0;
halfLen2 = rect.hs.x;
} else {
if (rectTransform.a*colAxis.x + rectTransform.e*colAxis.y + rectTransform.i*colAxis.z < 0) {
x2 = -x2;
}
}
// y
if (axisIdx1 == 1) {
vy = 0;
halfLen1 = box.hs.y;
} else {
if (boxTransform.b*colAxis.x + boxTransform.f*colAxis.y + boxTransform.j*colAxis.z > 0) {
vy = -vy;
}
}
if (axisIdx2 == 1) {
y2 = 0;
halfLen2 = rect.hs.y;
} else {
if (rectTransform.b*colAxis.x + rectTransform.f*colAxis.y + rectTransform.j*colAxis.z < 0) {
y2 = -y2;
}
}
// z
if (axisIdx1 == 2) {
vz = 0;
halfLen1 = box.hs.z;
} else {
if (boxTransform.c*colAxis.x + boxTransform.g*colAxis.y + boxTransform.k*colAxis.z > 0) {
vz = -vz;
}
}
// Получаем глобальные координаты средних точек рёбер
var x1:Number = boxTransform.a*vx + boxTransform.b*vy + boxTransform.c*vz + boxTransform.d;
var y1:Number = boxTransform.e*vx + boxTransform.f*vy + boxTransform.g*vz + boxTransform.h;
var z1:Number = boxTransform.i*vx + boxTransform.j*vy + boxTransform.k*vz + boxTransform.l;
vx = x2;
vy = y2;
vz = z2;
x2 = rectTransform.a*vx + rectTransform.b*vy + rectTransform.c*vz + rectTransform.d;
y2 = rectTransform.e*vx + rectTransform.f*vy + rectTransform.g*vz + rectTransform.h;
z2 = rectTransform.i*vx + rectTransform.j*vy + rectTransform.k*vz + rectTransform.l;
// Находим точку пересечения рёбер, решая систему уравнений
k = axis10.x*axis20.x + axis10.y*axis20.y + axis10.z*axis20.z;
var det:Number = k*k - 1;
vx = x2 - x1;
vy = y2 - y1;
vz = z2 - z1;
var c1:Number = axis10.x*vx + axis10.y*vy + axis10.z*vz;
var c2:Number = axis20.x*vx + axis20.y*vy + axis20.z*vz;
var t1:Number = (c2*k - c1)/det;
var t2:Number = (c2 - c1*k)/det;
// Запись данных о столкновении
contact.pcount = 1;
var cp:ContactPoint = contact.points[0];
cp.penetration = minOverlap;
var cpPos:Vector3 = cp.pos;
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
cpPos.x = 0.5*(x1 + axis10.x*t1 + x2 + axis20.x*t2);
cpPos.y = 0.5*(y1 + axis10.y*t1 + y2 + axis20.y*t2);
cpPos.z = 0.5*(z1 + axis10.z*t1 + z2 + axis20.z*t2);
var v:Vector3 = cp.r1;
v.x = cpPos.x - boxTransform.d;
v.y = cpPos.y - boxTransform.h;
v.z = cpPos.z - boxTransform.l;
v = cp.r2;
v.x = cpPos.x - rectTransform.d;
v.y = cpPos.y - rectTransform.h;
v.z = cpPos.z - rectTransform.l;
}
/**
* Проверяет пересечение вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
*
* @param box
* @param rect
* @param axis
* @param axisIndex
* @param vectorToBox
* @param bestAxis
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
*/
private function testMainAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
*
* @param box
* @param rect
* @param axis1
* @param axis2
* @param axisIndex
* @param vectorToBox
* @return
*
*/
private function testDerivedAxis(box:CollisionBox, rect:CollisionRect, axis1:Vector3, axis2:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
// axis = axis1 cross axis2
axis.x = axis1.y*axis2.z - axis1.z*axis2.y;
axis.y = axis1.z*axis2.x - axis1.x*axis2.z;
axis.z = axis1.x*axis2.y - axis1.y*axis2.x;
var lenSqr:Number = axis.x*axis.x + axis.y*axis.y + axis.z*axis.z;
if (lenSqr < 0.0001) return true;
var k:Number = 1/Math.sqrt(lenSqr);
axis.x *= k;
axis.y *= k;
axis.z *= k;
var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
* Вычисляет глубину перекрытия вдоль заданной оси.
*
* @param box бокс
* @param rect прямоугольник
* @param axis ось
* @param vectorToBox вектор, соединяющий центр прямоугольника с центром бокса
* @return величина перекрытия вдоль оси
*/
public function overlapOnAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, vectorToBox:Vector3):Number {
var m:Matrix4 = box.transform;
var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box.hs.x;
if (d < 0) d = -d;
var projection:Number = d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box.hs.y;
if (d < 0) d = -d;
projection += d;
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box.hs.z;
if (d < 0) d = -d;
projection += d;
m = rect.transform;
d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*rect.hs.x;
if (d < 0) d = -d;
projection += d;
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*rect.hs.y;
if (d < 0) d = -d;
projection += d;
d = vectorToBox.x*axis.x + vectorToBox.y*axis.y + vectorToBox.z*axis.z;
if (d < 0) d = -d;
return projection - d;
}
}
}

View File

@@ -0,0 +1,113 @@
package alternativa.physics.collision {
import alternativa.physics.altphysics;
import alternativa.physics.collision.primitives.CollisionBox;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.primitives.CollisionSphere;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
*
*/
public class BoxSphereCollider implements ICollider {
private var center:Vector3 = new Vector3();
private var closestPt:Vector3 = new Vector3();
private var bPos:Vector3 = new Vector3();
private var sPos:Vector3 = new Vector3();
/**
*
*/
public function BoxSphereCollider() {
}
/**
*
* @param body1
* @param body2
* @param collisionInfo
* @return
*/
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
var sphere:CollisionSphere = prim1 as CollisionSphere;
var box:CollisionBox;
if (sphere == null) {
sphere = prim2 as CollisionSphere;
box = prim1 as CollisionBox;
} else {
box = prim2 as CollisionBox;
}
// Трансформируем центр сферы в систему координат бокса
sphere.transform.getAxis(3, sPos);
box.transform.getAxis(3, bPos);
box.transform.transformVectorInverse(sPos, center);
// Выполняем поиск разделяющей оси
var hs:Vector3 = box.hs;
var sx:Number = hs.x + sphere.r;
var sy:Number = hs.y + sphere.r;
var sz:Number = hs.z + sphere.r;
if (center.x > sx || center.x < -sx
|| center.y > sy || center.y < -sy
|| center.z > sz || center.z < -sz) {
return false;
}
// Находим ближайшую к сфере точку на боксе
if (center.x > hs.x) {
closestPt.x = hs.x;
} else if (center.x < -hs.x) {
closestPt.x = -hs.x;
} else {
closestPt.x = center.x;
}
if (center.y > hs.y) {
closestPt.y = hs.y;
} else if (center.y < -hs.y) {
closestPt.y = -hs.y;
} else {
closestPt.y = center.y;
}
if (center.z > hs.z) {
closestPt.z = hs.z;
} else if (center.z < -hs.z) {
closestPt.z = -hs.z;
} else {
closestPt.z = center.z;
}
// TODO: Предусмотреть обработку случая, когда центр сферы внутри бокса
var distSqr:Number = center.vSubtract(closestPt).vLengthSqr();
if (distSqr > sphere.r*sphere.r) {
return false;
}
// Зафиксированно столкновение
contact.body1 = sphere.body;
contact.body2 = box.body;
contact.normal.vCopy(closestPt).vTransformBy4(box.transform).vSubtract(sPos).vNormalize().vReverse();
contact.pcount = 1;
var cp:ContactPoint = contact.points[0];
cp.penetration = sphere.r - Math.sqrt(distSqr);
cp.pos.vCopy(contact.normal).vScale(-sphere.r).vAdd(sPos);
cp.r1.vDiff(cp.pos, sPos);
cp.r2.vDiff(cp.pos, bPos);
return true;
}
/**
* @param prim1
* @param prim2
* @return
*/
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,682 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.primitives.CollisionBox;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.primitives.CollisionTriangle;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
/**
*
*/
public class BoxTriangleCollider extends BoxCollider {
public var epsilon:Number = 0.001;
private var bestAxisIndex:int;
private var minOverlap:Number;
private var toBox:Vector3 = new Vector3();
private var axis:Vector3 = new Vector3();
private var colNormal:Vector3 = new Vector3();
private var axis10:Vector3 = new Vector3();
private var axis11:Vector3 = new Vector3();
private var axis12:Vector3 = new Vector3();
private var axis20:Vector3 = new Vector3();
private var axis21:Vector3 = new Vector3();
private var axis22:Vector3 = new Vector3();
private var point1:Vector3 = new Vector3();
private var point2:Vector3 = new Vector3();
private var vector:Vector3 = new Vector3();
private var points1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
private var points2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
/**
*
*/
public function BoxTriangleCollider() {
for (var i:int = 0; i < 8; i++) {
points1[i] = new Vector3();
points2[i] = new Vector3();
}
}
/**
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
override public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
if (!haveCollision(prim1, prim2)) return false;
var tri:CollisionTriangle = prim1 as CollisionTriangle;
var box:CollisionBox;
if (tri == null) {
box = CollisionBox(prim1);
tri = CollisionTriangle(prim2);
} else {
box = CollisionBox(prim2);
}
if (bestAxisIndex < 4) {
// Контакт вдоль одной из основных осей
if (!findFaceContactPoints(box, tri, toBox, bestAxisIndex, contact)) return false;
} else {
// Контакт ребро-ребро
bestAxisIndex -= 4;
findEdgesIntersection(box, tri, toBox, bestAxisIndex%3, int(bestAxisIndex/3), contact);
}
contact.body1 = box.body;
contact.body2 = tri.body;
return true;
}
/**
* Выполняет быстрый тест на наличие пересечения двух примитивов.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @return true, если пересечение существует, иначе false
*/
override public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
var tri:CollisionTriangle = prim1 as CollisionTriangle;
var box:CollisionBox;
if (tri == null) {
box = CollisionBox(prim1);
tri = CollisionTriangle(prim2);
} else {
box = CollisionBox(prim2);
}
var boxTransform:Matrix4 = box.transform;
var triTransform:Matrix4 = tri.transform;
toBox.x = boxTransform.d - triTransform.d;
toBox.y = boxTransform.h - triTransform.h;
toBox.z = boxTransform.l - triTransform.l;
minOverlap = 1e308;
// Сначала проверяется нормаль треугольника
axis.x = triTransform.c;
axis.y = triTransform.g;
axis.z = triTransform.k;
if (!testMainAxis(box, tri, axis, 0, toBox)) return false;
// Проверка основных осей бокса
axis10.x = boxTransform.a;
axis10.y = boxTransform.e;
axis10.z = boxTransform.i;
if (!testMainAxis(box, tri, axis10, 1, toBox)) return false;
axis11.x = boxTransform.b;
axis11.y = boxTransform.f;
axis11.z = boxTransform.j;
if (!testMainAxis(box, tri, axis11, 2, toBox)) return false;
axis12.x = boxTransform.c;
axis12.y = boxTransform.g;
axis12.z = boxTransform.k;
if (!testMainAxis(box, tri, axis12, 3, toBox)) return false;
// Проверка производных осей
// TODO: заменить вычисления векторных произведений инлайнами
var v:Vector3 = tri.e0;
axis20.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z;
axis20.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z;
axis20.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z;
if (!testDerivedAxis(box, tri, axis10, axis20, 4, toBox)) return false;
if (!testDerivedAxis(box, tri, axis11, axis20, 5, toBox)) return false;
if (!testDerivedAxis(box, tri, axis12, axis20, 6, toBox)) return false;
v = tri.e1;
axis21.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z;
axis21.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z;
axis21.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z;
if (!testDerivedAxis(box, tri, axis10, axis21, 7, toBox)) return false;
if (!testDerivedAxis(box, tri, axis11, axis21, 8, toBox)) return false;
if (!testDerivedAxis(box, tri, axis12, axis21, 9, toBox)) return false;
v = tri.e2;
axis22.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z;
axis22.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z;
axis22.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z;
if (!testDerivedAxis(box, tri, axis10, axis22, 10, toBox)) return false;
if (!testDerivedAxis(box, tri, axis11, axis22, 11, toBox)) return false;
if (!testDerivedAxis(box, tri, axis12, axis22, 12, toBox)) return false;
return true;
}
/**
* Тестирует пересечение примитивов вдоль заданной оси.
*
* @param box бокс
* @param tri треугольник
* @param axis ось
* @param axisIndex индекс оси
* @param toBox вектор, соединяющий центр треугольника с центром бокса
* @return true, если примитивы имеют перекрытие проекций вдоль указанной оси, иначе false
*/
private function testMainAxis(box:CollisionBox, tri:CollisionTriangle, axis:Vector3, axisIndex:int, toBox:Vector3):Boolean {
var overlap:Number = overlapOnAxis(box, tri, axis, toBox);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
*
* @param box
* @param tri
* @param axis1
* @param axis2
* @param axisIndex
* @param toBox
* @return
*
*/
private function testDerivedAxis(box:CollisionBox, tri:CollisionTriangle, axis1:Vector3, axis2:Vector3, axisIndex:int, toBox:Vector3):Boolean {
// axis = axis1 cross axis2
axis.x = axis1.y*axis2.z - axis1.z*axis2.y;
axis.y = axis1.z*axis2.x - axis1.x*axis2.z;
axis.z = axis1.x*axis2.y - axis1.y*axis2.x;
var lenSqr:Number = axis.x*axis.x + axis.y*axis.y + axis.z*axis.z;
if (lenSqr < 0.0001) return true;
var k:Number = 1/Math.sqrt(lenSqr);
axis.x *= k;
axis.y *= k;
axis.z *= k;
var overlap:Number = overlapOnAxis(box, tri, axis, toBox);
if (overlap < -epsilon) return false;
if (overlap + epsilon < minOverlap) {
minOverlap = overlap;
bestAxisIndex = axisIndex;
}
return true;
}
/**
* Рассчитывает величину перекрытия проекций бокса и треугольника на заданную ось.
*
* @param box бокс
* @param tri треугольник
* @param axis единичный направляющий вектор оси
* @param toBox вектор, соединяющий центр треугольника с центром бокса
* @return величина перекрытия. Положительное значение указывает на наличие перекрытия, нулевое или отрицательное значение указывает на отсутствие перекрытия.
*/
private function overlapOnAxis(box:CollisionBox, tri:CollisionTriangle, axis:Vector3, toBox:Vector3):Number {
var t:Matrix4 = box.transform;
var projection:Number = (t.a*axis.x + t.e*axis.y + t.i*axis.z)*box.hs.x;
if (projection < 0) projection = -projection;
var d:Number = (t.b*axis.x + t.f*axis.y + t.j*axis.z)*box.hs.y;
projection += d < 0 ? -d : d;
d = (t.c*axis.x + t.g*axis.y + t.k*axis.z)*box.hs.z;
projection += d < 0 ? -d : d;
var vectorProjection:Number = toBox.x*axis.x + toBox.y*axis.y + toBox.z*axis.z;
// Для оптимизации ось преобразуется в систему треугольника, а не его вершины в мировую систему
t = tri.transform;
var ax:Number = t.a*axis.x + t.e*axis.y + t.i*axis.z;
var ay:Number = t.b*axis.x + t.f*axis.y + t.j*axis.z;
var az:Number = t.c*axis.x + t.g*axis.y + t.k*axis.z;
var max:Number = 0;
if (vectorProjection < 0) {
vectorProjection = -vectorProjection;
d = tri.v0.x*ax + tri.v0.y*ay + tri.v0.z*az;
if (d < max) max = d;
d = tri.v1.x*ax + tri.v1.y*ay + tri.v1.z*az;
if (d < max) max = d;
d = tri.v2.x*ax + tri.v2.y*ay + tri.v2.z*az;
if (d < max) max = d;
max = -max;
} else {
d = tri.v0.x*ax + tri.v0.y*ay + tri.v0.z*az;
if (d > max) max = d;
d = tri.v1.x*ax + tri.v1.y*ay + tri.v1.z*az;
if (d > max) max = d;
d = tri.v2.x*ax + tri.v2.y*ay + tri.v2.z*az;
if (d > max) max = d;
}
return projection + max - vectorProjection;
}
/**
* Определяет точки контакта бокса и треугольника.
*
* @param box бокс
* @param tri треугольник
* @param toBox вектор, соединяющий центр треугольника с центром бокса
* @param faceAxisIndex индекс оси, вдоль которой перекрытие минимально
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
private function findFaceContactPoints(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, faceAxisIndex:int, contact:Contact):Boolean {
if (faceAxisIndex == 0) {
// Столкновение с плоскостью треугольника
return getBoxToTriContact(box, tri, toBox, contact);
} else {
// Столкновение с гранью бокса
return getTriToBoxContact(box, tri, toBox, faceAxisIndex, contact);
}
}
/**
* Определяет точки контакта бокса с плоскостью треугольника.
*
* @param box бокс
* @param tri треугольник
* @param toBox вектор, соединяющий центр треугольника с центром бокса
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
private function getBoxToTriContact(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, contact:Contact):Boolean {
var boxTransform:Matrix4 = box.transform;
var triTransform:Matrix4 = tri.transform;
colNormal.x = triTransform.c;
colNormal.y = triTransform.g;
colNormal.z = triTransform.k;
var over:Boolean = toBox.x*colNormal.x + toBox.y*colNormal.y + toBox.z*colNormal.z > 0;
if (!over) {
colNormal.x = -colNormal.x;
colNormal.y = -colNormal.y;
colNormal.z = -colNormal.z;
}
// Ищем ось бокса, определяющую наиболее антипараллельную грань
var incFaceAxisIdx:int = 0;
var incAxisDot:Number = 0;
var maxDot:Number = 0;
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
boxTransform.getAxis(axisIdx, axis);
var dot:Number = axis.x*colNormal.x + axis.y*colNormal.y + axis.z*colNormal.z;
var absDot:Number = dot < 0 ? -dot : dot;
if (absDot > maxDot) {
maxDot = absDot;
incAxisDot = dot;
incFaceAxisIdx = axisIdx;
}
}
// Обрезка грани
var negativeFace:Boolean = incAxisDot > 0;
getFaceVertsByAxis(box.hs, incFaceAxisIdx, negativeFace, points1);
boxTransform.transformVectorsN(points1, points2, 4);
triTransform.transformVectorsInverseN(points2, points1, 4);
var pnum:int = clipByTriangle(tri);
// Среди конечного списка точек определяются лежащие под плоскостью треугольника
var cp:ContactPoint;
contact.pcount = 0;
for (var i:int = 0; i < pnum; i++) {
var v:Vector3 = points2[i];
if ((over && v.z < 0) || (!over && v.z > 0)) {
cp = contact.points[contact.pcount++];
var cpPos:Vector3 = cp.pos;
cpPos.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z + triTransform.d;
cpPos.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z + triTransform.h;
cpPos.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z + triTransform.l;
var r:Vector3 = cp.r1;
r.x = cpPos.x - boxTransform.d;
r.y = cpPos.y - boxTransform.h;
r.z = cpPos.z - boxTransform.l;
r = cp.r2;
r.x = cpPos.x - triTransform.d;
r.y = cpPos.y - triTransform.h;
r.z = cpPos.z - triTransform.l;
cp.penetration = over ? -v.z : v.z;
}
}
contact.normal.x = colNormal.x;
contact.normal.y = colNormal.y;
contact.normal.z = colNormal.z;
return true;
}
/**
* Оперделяет точки контакта треугольника с гранью бокса.
*
* @param box бокс
* @param tri треугольник
* @param toBox вектор, соединяющий центр треугольника с центром бокса
* @param faceAxisIndex индекс оси, вдоль которой перекрытие минимально
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
private function getTriToBoxContact(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, faceAxisIdx:int, contact:Contact):Boolean {
faceAxisIdx--;
var boxTransform:Matrix4 = box.transform;
var triTransform:Matrix4 = tri.transform;
boxTransform.getAxis(faceAxisIdx, colNormal);
var negativeFace:Boolean = toBox.x*colNormal.x + toBox.y*colNormal.y + toBox.z*colNormal.z > 0;
if (!negativeFace) {
colNormal.x = -colNormal.x;
colNormal.y = -colNormal.y;
colNormal.z = -colNormal.z;
}
var v:Vector3 = points1[0];
v.x = tri.v0.x;
v.y = tri.v0.y;
v.z = tri.v0.z;
v = points1[1];
v.x = tri.v1.x;
v.y = tri.v1.y;
v.z = tri.v1.z;
v = points1[2];
v.x = tri.v2.x;
v.y = tri.v2.y;
v.z = tri.v2.z;
triTransform.transformVectorsN(points1, points2, 3);
boxTransform.transformVectorsInverseN(points2, points1, 3);
var pnum:int = clipByBox(box.hs, faceAxisIdx);
// Проверяем каждую потенциальную точку на принадлежность боксу и добавляем такие точки в список контактов
var penetration:Number;
contact.pcount = 0;
for (var i:int = 0; i < pnum; i++) {
v = points1[i];
penetration = getPointBoxPenetration(box.hs, v, faceAxisIdx, negativeFace);
if (penetration > -epsilon) {
var cp:ContactPoint = contact.points[contact.pcount++];
var cpPos:Vector3 = cp.pos;
cpPos.x = boxTransform.a*v.x + boxTransform.b*v.y + boxTransform.c*v.z + boxTransform.d;
cpPos.y = boxTransform.e*v.x + boxTransform.f*v.y + boxTransform.g*v.z + boxTransform.h;
cpPos.z = boxTransform.i*v.x + boxTransform.j*v.y + boxTransform.k*v.z + boxTransform.l;
var r:Vector3 = cp.r1;
r.x = cpPos.x - boxTransform.d;
r.y = cpPos.y - boxTransform.h;
r.z = cpPos.z - boxTransform.l;
r = cp.r2;
r.x = cpPos.x - triTransform.d;
r.y = cpPos.y - triTransform.h;
r.z = cpPos.z - triTransform.l;
cp.penetration = penetration;
}
}
contact.normal.x = colNormal.x;
contact.normal.y = colNormal.y;
contact.normal.z = colNormal.z;
return true;
}
/**
* Вычисляет величину проникновения точки в бокс.
*
* @param hs вектор половинных размеров бокса
* @param p точка в системе координат бокса
* @param axisIndex индекс оси
* @param negativeFace если true, проверяется нижняя грань
* @return величина проникновения точки в бокс
*/
private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, negativeFace:Boolean):Number {
switch (faceAxisIdx) {
case 0:
if (negativeFace) return p.x + hs.x;
else return hs.x - p.x;
case 1:
if (negativeFace) return p.y + hs.y;
else return hs.y - p.y;
case 2:
if (negativeFace) return p.z + hs.z;
else return hs.z - p.z;
}
return 0;
}
/**
* Выполняет обрезку грани, вершины которой заданы в списке points1. Результат сохраняется в этом же списке.
*
* @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
* @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
* @return количество вершин, получившихся в результате обрезки грани
*/
private function clipByBox(hs:Vector3, faceAxisIdx:int):int {
var pnum:int = 3;
switch (faceAxisIdx) {
case 0:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
case 1:
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighX(hs.x, pnum, points2, points1, epsilon);
case 2:
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
return clipHighY(hs.y, pnum, points2, points1, epsilon);
}
return 0;
}
/**
* Обрезает грань прямоугольника сторонами треугольника. Обрезка выполняется в системе координат и в проекции на
* плоскость треугольника. Входные вершины грани в количестве четырёх штук должны находиться в списке points1.
* Конечные вершины сохраняются в списке points2.
*
* @param tri треугольник
* @return количество врешин в конечном списке
*/
private function clipByTriangle(tri:CollisionTriangle):int {
var vnum:int = 4;
vnum = clipByLine(tri.v0, tri.e0, points1, vnum, points2);
if (vnum == 0) return 0;
vnum = clipByLine(tri.v1, tri.e1, points2, vnum, points1);
if (vnum == 0) return 0;
return clipByLine(tri.v2, tri.e2, points1, vnum, points2);;
}
/**
* Обрезает полигон указанной прямой.
*
* @param linePoint точка на прямой
* @param lineDir единичный направляющий вектор прямой
* @param verticesIn список вершин исходного полигона
* @param vnum количество вершин исходного полигона
* @param verticesOut список, куда будут записаны вершины конечного полигона
* @return количество вершин конечного полигона
*/
private function clipByLine(linePoint:Vector3, lineDir:Vector3, verticesIn:Vector.<Vector3>, vnum:int, verticesOut:Vector.<Vector3>):int {
var nx:Number = -lineDir.y;
var ny:Number = lineDir.x;
var offset:Number = linePoint.x*nx + linePoint.y*ny;
var v1:Vector3 = verticesIn[int(vnum - 1)];
var offset1:Number = v1.x*nx + v1.y*ny;
var t:Number;
var v:Vector3;
var num:int = 0;
for (var i:int = 0; i < vnum; i++) {
var v2:Vector3 = verticesIn[i];
var offset2:Number = v2.x*nx + v2.y*ny;
if (offset1 < offset) {
// Первая точка ребра во внешней полуплоскости
if (offset2 > offset) {
// Вторая точка ребра во внутренней полуплоскости, в конечный список добавляется точка на пересечении
t = (offset - offset1)/(offset2 - offset1);
v = verticesOut[num];
v.x = v1.x + t*(v2.x - v1.x);
v.y = v1.y + t*(v2.y - v1.y);
v.z = v1.z + t*(v2.z - v1.z);
num++;
}
} else {
// Первая точка ребра во внутренней полуплоскости. Добавляем её в конечный список.
v = verticesOut[num];
v.x = v1.x;
v.y = v1.y;
v.z = v1.z;
num++;
if (offset2 < offset) {
// Вторая точка ребра во внешней полуплоскости, в конечный список добавляется точка на пересечении
t = (offset - offset1)/(offset2 - offset1);
v = verticesOut[num];
v.x = v1.x + t*(v2.x - v1.x);
v.y = v1.y + t*(v2.y - v1.y);
v.z = v1.z + t*(v2.z - v1.z);
num++;
}
}
v1 = v2;
offset1 = offset2;
}
return num;
}
/**
*
* @param box
* @param tri
* @param toBox
* @param boxAxisIdx
* @param triAxisIdx
* @param contact
*/
private function findEdgesIntersection(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, boxAxisIdx:int, triAxisIdx:int, contact:Contact):void {
// Определение точки и направляющего вектора ребра треугольника
var tmpx1:Number;
var tmpy1:Number;
var tmpz1:Number;
var tmpx2:Number;
var tmpy2:Number;
var tmpz2:Number;
switch (triAxisIdx) {
case 0:
tmpx1 = tri.e0.x;
tmpy1 = tri.e0.y;
tmpz1 = tri.e0.z;
tmpx2 = tri.v0.x;
tmpy2 = tri.v0.y;
tmpz2 = tri.v0.z;
break;
case 1:
tmpx1 = tri.e1.x;
tmpy1 = tri.e1.y;
tmpz1 = tri.e1.z;
tmpx2 = tri.v1.x;
tmpy2 = tri.v1.y;
tmpz2 = tri.v1.z;
break;
case 2:
tmpx1 = tri.e2.x;
tmpy1 = tri.e2.y;
tmpz1 = tri.e2.z;
tmpx2 = tri.v2.x;
tmpy2 = tri.v2.y;
tmpz2 = tri.v2.z;
break;
}
var triTransform:Matrix4 = tri.transform;
axis20.x = triTransform.a*tmpx1 + triTransform.b*tmpy1 + triTransform.c*tmpz1;
axis20.y = triTransform.e*tmpx1 + triTransform.f*tmpy1 + triTransform.g*tmpz1;
axis20.z = triTransform.i*tmpx1 + triTransform.j*tmpy1 + triTransform.k*tmpz1;
var x2:Number = triTransform.a*tmpx2 + triTransform.b*tmpy2 + triTransform.c*tmpz2 + triTransform.d;
var y2:Number = triTransform.e*tmpx2 + triTransform.f*tmpy2 + triTransform.g*tmpz2 + triTransform.h;
var z2:Number = triTransform.i*tmpx2 + triTransform.j*tmpy2 + triTransform.k*tmpz2 + triTransform.l;
// Определение нормали контакта, точки и направляющего вектора ребра бокса
var boxTransform:Matrix4 = box.transform;
boxTransform.getAxis(boxAxisIdx, axis10);
// Нормаль контакта
var v:Vector3 = contact.normal;
v.x = axis10.y*axis20.z - axis10.z*axis20.y;
v.y = axis10.z*axis20.x - axis10.x*axis20.z;
v.z = axis10.x*axis20.y - axis10.y*axis20.x;
k = 1/Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
v.x *= k;
v.y *= k;
v.z *= k;
// Разворот нормали в сторону бокса
if (v.x*toBox.x + v.y*toBox.y + v.z*toBox.z < 0) {
v.x = -v.x;
v.y = -v.y;
v.z = -v.z;
}
var boxHalfLen:Number;
tmpx1 = box.hs.x;
tmpy1 = box.hs.y;
tmpz1 = box.hs.z;
// X
if (boxAxisIdx == 0) {
tmpx1 = 0;
boxHalfLen = box.hs.x;
} else {
if (boxTransform.a*v.x + boxTransform.e*v.y + boxTransform.i*v.z > 0) {
tmpx1 = -tmpx1;
}
}
// Y
if (boxAxisIdx == 1) {
tmpy1 = 0;
boxHalfLen = box.hs.y;
} else {
if (boxTransform.b*v.x + boxTransform.f*v.y + boxTransform.j*v.z > 0) {
tmpy1 = -tmpy1;
}
}
// Z
if (boxAxisIdx == 2) {
tmpz1 = 0;
boxHalfLen = box.hs.z;
} else {
if (boxTransform.c*v.x + boxTransform.g*v.y + boxTransform.k*v.z > 0) {
tmpz1 = -tmpz1;
}
}
var x1:Number = boxTransform.a*tmpx1 + boxTransform.b*tmpy1 + boxTransform.c*tmpz1 + boxTransform.d;
var y1:Number = boxTransform.e*tmpx1 + boxTransform.f*tmpy1 + boxTransform.g*tmpz1 + boxTransform.h;
var z1:Number = boxTransform.i*tmpx1 + boxTransform.j*tmpy1 + boxTransform.k*tmpz1 + boxTransform.l;
// Находим точку пересечения рёбер, решая систему уравнений
// axis10 - направляющий вектор ребра бокса
// x1, y1, z1 - средняя точка ребра бокса
// axis20 - направляющий вектор ребра треугольника
// x2, y2, z2 - начальная точка ребра треугольника
var k:Number = axis10.x*axis20.x + axis10.y*axis20.y + axis10.z*axis20.z;
var det:Number = k*k - 1;
var vx:Number = x2 - x1;
var vy:Number = y2 - y1;
var vz:Number = z2 - z1;
var c1:Number = axis10.x*vx + axis10.y*vy + axis10.z*vz;
var c2:Number = axis20.x*vx + axis20.y*vy + axis20.z*vz;
var t1:Number = (c2*k - c1)/det;
var t2:Number = (c2 - c1*k)/det;
// Запись данных о контакте
contact.pcount = 1;
var cp:ContactPoint = contact.points[0];
cp.penetration = minOverlap;
v = cp.pos;
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
v.x = 0.5*(x1 + axis10.x*t1 + x2 + axis20.x*t2);
v.y = 0.5*(y1 + axis10.y*t1 + y2 + axis20.y*t2);
v.z = 0.5*(z1 + axis10.z*t1 + z2 + axis20.z*t2);
var r:Vector3 = cp.r1;
r.x = v.x - boxTransform.d;
r.y = v.y - boxTransform.h;
r.z = v.z - boxTransform.l;
r = cp.r2;
r.x = v.x - triTransform.d;
r.y = v.y - triTransform.h;
r.z = v.z - triTransform.l;
}
}
}

View File

@@ -0,0 +1,63 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.types.RayIntersection;
import alternativa.physics.rigid.Body;
import alternativa.physics.rigid.Contact;
import alternativa.physics.types.Vector3;
public class BruteForceCollisionDetector implements ICollisionDetector {
public function BruteForceCollisionDetector() {
}
public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean=true):Boolean {
return false;
}
public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean=true):Boolean {
return false;
}
public function init():void {
}
public function getAllCollisions(contacts:Vector.<Contact>):int {
return 0;
}
public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
return false;
}
public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
return false;
}
public function testBodyPrimitiveCollision(body:Body, primitive:CollisionPrimitive):Boolean {
return false;
}
/**
*
* @param prim1
* @param prim2
* @param contact
* @return
*/
public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
return false;
}
/**
*
* @param prim1
* @param prim2
* @return
*/
public function testCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,18 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.types.BoundBox;
public class CollisionKdNode {
public var indices:Vector.<int>;
public var splitIndices:Vector.<int>;
public var boundBox:BoundBox;
public var parent:CollisionKdNode;
public var splitTree:CollisionKdTree2D;
public var axis:int = -1; // 0 - x, 1 - y, 2 - z
public var coord:Number;
public var positiveNode:CollisionKdNode;
public var negativeNode:CollisionKdNode;
}
}

View File

@@ -0,0 +1,280 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.types.BoundBox;
/**
* @author mike
*/
public class CollisionKdTree {
public var threshold:Number = 0.1;
public var minPrimitivesPerNode:int = 1;
public var rootNode:CollisionKdNode;
public var staticChildren:Vector.<CollisionPrimitive> = new Vector.<CollisionPrimitive>();
public var numStaticChildren:int;
public var staticBoundBoxes:Vector.<BoundBox> = new Vector.<BoundBox>();
private var splitAxis:int;
private var splitCoord:Number;
private var splitCost:Number;
private static const nodeBoundBoxThreshold:BoundBox = new BoundBox();
private static const splitCoordsX:Vector.<Number> = new Vector.<Number>();
private static const splitCoordsY:Vector.<Number> = new Vector.<Number>();
private static const splitCoordsZ:Vector.<Number> = new Vector.<Number>();
private static const _nodeBB:Vector.<Number> = new Vector.<Number>(6);
private static const _bb:Vector.<Number> = new Vector.<Number>(6);
/**
* @param primitive
*/
public function addStaticPrimitive(primitive:CollisionPrimitive):void {
staticChildren[numStaticChildren++] = primitive;
}
/**
* @param primitive
* @return
*/
public function removeStaticPrimitive(primitive:CollisionPrimitive):Boolean {
var idx:int = staticChildren.indexOf(primitive);
if (idx < 0) return false;
staticChildren.splice(idx, 1);
numStaticChildren--;
return true;
}
/**
* @param boundBox
*/
public function createTree(boundBox:BoundBox = null):void {
if (numStaticChildren == 0) return;
// Создаём корневую ноду
rootNode = new CollisionKdNode();
rootNode.indices = new Vector.<int>();
// Расчитываем баунды объектов и рутовой ноды
var rootNodeBoundBox:BoundBox = rootNode.boundBox = (boundBox != null) ? boundBox : new BoundBox();
for (var i:int = 0; i < numStaticChildren; ++i) {
var child:CollisionPrimitive = staticChildren[i];
var childBoundBox:BoundBox = staticBoundBoxes[i] = child.calculateAABB();
rootNodeBoundBox.addBoundBox(childBoundBox);
rootNode.indices[i] = i;
}
staticBoundBoxes.length = numStaticChildren;
// Разделяем рутовую ноду
splitNode(rootNode);
splitCoordsX.length = splitCoordsY.length = splitCoordsZ.length = 0;
}
/**
* @param node
*/
private function splitNode(node:CollisionKdNode):void {
var indices:Vector.<int> = node.indices;
var numPrimitives:int = indices.length;
if (numPrimitives <= minPrimitivesPerNode) return;
// Подготовка баунда с погрешностями
var nodeBoundBox:BoundBox = node.boundBox;
nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
var doubleThreshold:Number = threshold*2;
// Собираем опорные координаты
var i:int;
var j:int;
var numSplitCoordsX:int = 0
var numSplitCoordsY:int = 0;
var numSplitCoordsZ:int = 0;
for (i = 0; i < numPrimitives; ++i) {
var boundBox:BoundBox = staticBoundBoxes[indices[i]];
if (boundBox.maxX - boundBox.minX <= doubleThreshold) {
if (boundBox.minX <= nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = nodeBoundBox.minX;
else if (boundBox.maxX >= nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = nodeBoundBox.maxX;
else splitCoordsX[numSplitCoordsX++] = (boundBox.minX + boundBox.maxX)*0.5;
} else {
if (boundBox.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = boundBox.minX;
if (boundBox.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = boundBox.maxX;
}
if (boundBox.maxY - boundBox.minY <= doubleThreshold) {
if (boundBox.minY <= nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = nodeBoundBox.minY;
else if (boundBox.maxY >= nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = nodeBoundBox.maxY;
else splitCoordsY[numSplitCoordsY++] = (boundBox.minY + boundBox.maxY)*0.5;
} else {
if (boundBox.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = boundBox.minY;
if (boundBox.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = boundBox.maxY;
}
if (boundBox.maxZ - boundBox.minZ <= doubleThreshold) {
if (boundBox.minZ <= nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = nodeBoundBox.minZ;
else if (boundBox.maxZ >= nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = nodeBoundBox.maxZ;
else splitCoordsZ[numSplitCoordsZ++] = (boundBox.minZ + boundBox.maxZ)*0.5;
} else {
if (boundBox.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.minZ;
if (boundBox.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.maxZ;
}
}
// Поиск наилучшего сплита
splitAxis = -1;
splitCost = 1e308;
_nodeBB[0] = nodeBoundBox.minX;
_nodeBB[1] = nodeBoundBox.minY;
_nodeBB[2] = nodeBoundBox.minZ;
_nodeBB[3] = nodeBoundBox.maxX;
_nodeBB[4] = nodeBoundBox.maxY;
_nodeBB[5] = nodeBoundBox.maxZ;
checkNodeAxis(node, 0, numSplitCoordsX, splitCoordsX, _nodeBB);
checkNodeAxis(node, 1, numSplitCoordsY, splitCoordsY, _nodeBB);
checkNodeAxis(node, 2, numSplitCoordsZ, splitCoordsZ, _nodeBB);
// Если сплит не найден, выходим
if (splitAxis < 0) return;
// Сплиттер найден. Разделение узла.
var axisX:Boolean = splitAxis == 0
var axisY:Boolean = splitAxis == 1;
node.axis = splitAxis;
node.coord = splitCoord;
// Создаём дочерние ноды
node.negativeNode = new CollisionKdNode();
node.negativeNode.parent = node;
node.negativeNode.boundBox = nodeBoundBox.clone();
node.positiveNode = new CollisionKdNode();
node.positiveNode.parent = node;
node.positiveNode.boundBox = nodeBoundBox.clone();
if (axisX) node.negativeNode.boundBox.maxX = node.positiveNode.boundBox.minX = splitCoord;
else if (axisY) node.negativeNode.boundBox.maxY = node.positiveNode.boundBox.minY = splitCoord;
else node.negativeNode.boundBox.maxZ = node.positiveNode.boundBox.minZ = splitCoord;
// Распределяем объекты по дочерним нодам
var coordMin:Number = splitCoord - threshold;
var coordMax:Number = splitCoord + threshold;
for (i = 0; i < numPrimitives; ++i) {
boundBox = staticBoundBoxes[indices[i]];
var min:Number = axisX ? boundBox.minX : (axisY ? boundBox.minY : boundBox.minZ);
var max:Number = axisX ? boundBox.maxX : (axisY ? boundBox.maxY : boundBox.maxZ);
if (max <= coordMax) {
if (min < coordMin) {
// Объект в негативной стороне
if (node.negativeNode.indices == null) node.negativeNode.indices = new Vector.<int>();
node.negativeNode.indices.push(indices[i]);
indices[i] = -1;
} else {
if (node.splitIndices == null) node.splitIndices = new Vector.<int>();
node.splitIndices.push(indices[i]);
indices[i] = -1;
}
} else {
if (min >= coordMin) {
// Объект в положительной стороне
if (node.positiveNode.indices == null) node.positiveNode.indices = new Vector.<int>();
node.positiveNode.indices.push(indices[i]);
indices[i] = -1;
}
}
}
// Очистка списка объектов
for (i = 0, j = 0; i < numPrimitives; ++i) {
if (indices[i] >= 0) indices[j++] = indices[i];
}
if (j > 0) indices.length = j;
else node.indices = null;
if (node.splitIndices != null) {
node.splitTree = new CollisionKdTree2D(this, node);
node.splitTree.createTree();
}
// Разделение дочерних нод
if (node.negativeNode.indices != null) splitNode(node.negativeNode);
if (node.positiveNode.indices != null) splitNode(node.positiveNode);
}
/**
*
* @param node
* @param axis
* @param numSplitCoords
* @param splitCoords
* @param bb
*/
private function checkNodeAxis(node:CollisionKdNode, axis:int, numSplitCoords:int, splitCoords:Vector.<Number>, bb:Vector.<Number>):void {
var axis1:int = (axis + 1)%3;
var axis2:int = (axis + 2)%3;
var area:Number = (bb[axis1 + 3] - bb[axis1])*(bb[axis2 + 3] - bb[axis2]);
for (var i:int = 0; i < numSplitCoords; ++i) {
var currSplitCoord:Number = splitCoords[i];
if (isNaN(currSplitCoord)) continue;
var minCoord:Number = currSplitCoord - threshold;
var maxCoord:Number = currSplitCoord + threshold;
var areaNegative:Number = area*(currSplitCoord - bb[axis]);
var areaPositive:Number = area*(bb[int(axis + 3)] - currSplitCoord);
var numNegative:int = 0;
var numPositive:int = 0;
var conflict:Boolean = false;
// Проверяем объекты
var numObjects:int = node.indices.length;
for (var j:int = 0; j < numObjects; j++) {
var boundBox:BoundBox = staticBoundBoxes[node.indices[j]];
_bb[0] = boundBox.minX;
_bb[1] = boundBox.minY;
_bb[2] = boundBox.minZ;
_bb[3] = boundBox.maxX;
_bb[4] = boundBox.maxY;
_bb[5] = boundBox.maxZ;
if (_bb[axis + 3] <= maxCoord) {
if (_bb[axis] < minCoord) numNegative++;
} else {
if (_bb[axis] >= minCoord) {
numPositive++;
} else {
conflict = true;
break;
}
}
}
// Если хороший сплит, сохраняем
var cost:Number = areaNegative*numNegative + areaPositive*numPositive;
if (!conflict && cost < splitCost) {
splitAxis = axis;
splitCost = cost;
splitCoord = currSplitCoord;
}
for (j = i + 1; j < numSplitCoords; ++j) {
if (splitCoords[j] >= currSplitCoord - threshold && splitCoords[j] <= currSplitCoord + threshold) splitCoords[j] = NaN;
}
}
}
/**
*
*/
public function traceTree():void {
traceNode("", rootNode);
}
/**
* @param str
* @param node
*/
private function traceNode(str:String, node:CollisionKdNode):void {
if (node == null) return;
trace(str, node.axis == -1 ? "end" : ((node.axis == 0) ? "X" : ((node.axis == 1) ? "Y" : "Z")), "splitCoord=" + splitCoord, "bound", node.boundBox, "objs:", node.indices);
traceNode(str + "-", node.negativeNode);
traceNode(str + "+", node.positiveNode);
}
}
}

View File

@@ -0,0 +1,226 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.collision.types.BoundBox;
/**
*
*/
public class CollisionKdTree2D {
public var threshold:Number = 0.1;
public var minPrimitivesPerNode:int = 1;
public var parentTree:CollisionKdTree;
public var parentNode:CollisionKdNode;
public var rootNode:CollisionKdNode;
private var splitAxis:int;
private var splitCost:Number;
private var splitCoord:Number;
private static const nodeBoundBoxThreshold:BoundBox = new BoundBox();
private static const splitCoordsX:Vector.<Number> = new Vector.<Number>();
private static const splitCoordsY:Vector.<Number> = new Vector.<Number>();
private static const splitCoordsZ:Vector.<Number> = new Vector.<Number>();
private static const _nodeBB:Vector.<Number> = new Vector.<Number>(6);
private static const _bb:Vector.<Number> = new Vector.<Number>(6);
/**
*
* @param parentTree
* @param parentNode
*/
public function CollisionKdTree2D(parentTree:CollisionKdTree, parentNode:CollisionKdNode) {
this.parentTree = parentTree;
this.parentNode = parentNode;
}
/**
*
*/
public function createTree():void {
rootNode = new CollisionKdNode();
rootNode.boundBox = parentNode.boundBox.clone();
rootNode.indices = new Vector.<int>();
var numObjects:int = parentNode.splitIndices.length;
for (var i:int = 0; i < numObjects; ++i) rootNode.indices[i] = parentNode.splitIndices[i];
splitNode(rootNode);
splitCoordsX.length = splitCoordsY.length = splitCoordsZ.length = 0;
}
/**
* @param node
*/
private function splitNode(node:CollisionKdNode):void {
if (node.indices.length <= minPrimitivesPerNode) return;
var objects:Vector.<int> = node.indices;
var i:int;
var j:int;
var nodeBoundBox:BoundBox = node.boundBox;
// Подготовка баунда с погрешностями
nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
var doubleThreshold:Number = threshold*2;
var staticBoundBoxes:Vector.<BoundBox> = parentTree.staticBoundBoxes;
// Собираем опорные координаты
var numSplitCoordsX:int;
var numSplitCoordsY:int;
var numSplitCoordsZ:int;
var numObjects:int = objects.length;
for (i = 0; i < numObjects; ++i) {
var bb:BoundBox = staticBoundBoxes[objects[i]];
if (parentNode.axis != 0) {
if (bb.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = bb.minX;
if (bb.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = bb.maxX;
}
if (parentNode.axis != 1) {
if (bb.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = bb.minY;
if (bb.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = bb.maxY;
}
if (parentNode.axis != 2) {
if (bb.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = bb.minZ;
if (bb.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = bb.maxZ;
}
}
// Поиск наилучшего сплита
splitAxis = -1;
splitCost = 1e308;
_nodeBB[0] = nodeBoundBox.minX;
_nodeBB[1] = nodeBoundBox.minY;
_nodeBB[2] = nodeBoundBox.minZ;
_nodeBB[3] = nodeBoundBox.maxX;
_nodeBB[4] = nodeBoundBox.maxY;
_nodeBB[5] = nodeBoundBox.maxZ;
if (parentNode.axis != 0) checkNodeAxis(node, 0, numSplitCoordsX, splitCoordsX, _nodeBB);
if (parentNode.axis != 1) checkNodeAxis(node, 1, numSplitCoordsY, splitCoordsY, _nodeBB);
if (parentNode.axis != 2) checkNodeAxis(node, 2, numSplitCoordsZ, splitCoordsZ, _nodeBB);
// Если сплит не найден, выходим
if (splitAxis < 0) return;
// Сплиттер найден. Разделение узла.
var axisX:Boolean = splitAxis == 0
var axisY:Boolean = splitAxis == 1;
node.axis = splitAxis;
node.coord = splitCoord;
// Создаём дочерние ноды
node.negativeNode = new CollisionKdNode();
node.negativeNode.parent = node;
node.negativeNode.boundBox = nodeBoundBox.clone();
node.positiveNode = new CollisionKdNode();
node.positiveNode.parent = node;
node.positiveNode.boundBox = nodeBoundBox.clone();
if (axisX) node.negativeNode.boundBox.maxX = node.positiveNode.boundBox.minX = splitCoord;
else if (axisY) node.negativeNode.boundBox.maxY = node.positiveNode.boundBox.minY = splitCoord;
else node.negativeNode.boundBox.maxZ = node.positiveNode.boundBox.minZ = splitCoord;
// Распределяем объекты по дочерним нодам
var coordMin:Number = splitCoord - threshold;
var coordMax:Number = splitCoord + threshold;
for (i = 0; i < numObjects; ++i) {
bb = staticBoundBoxes[objects[i]];
var min:Number = axisX ? bb.minX : (axisY ? bb.minY : bb.minZ);
var max:Number = axisX ? bb.maxX : (axisY ? bb.maxY : bb.maxZ);
if (max <= coordMax) {
if (min < coordMin) {
// Объект в негативной стороне
if (node.negativeNode.indices == null) node.negativeNode.indices = new Vector.<int>();
node.negativeNode.indices.push(objects[i]);
objects[i] = -1;
}
} else {
if (min >= coordMin) {
if (max > coordMax) {
// Объект в положительной стороне
if (node.positiveNode.indices == null) node.positiveNode.indices = new Vector.<int>();
node.positiveNode.indices.push(objects[i]);
objects[i] = -1;
}
}
}
}
// Очистка списка объектов
for (i = 0, j = 0; i < numObjects; ++i) {
if (objects[i] >= 0) objects[j++] = objects[i];
}
if (j > 0) objects.length = j;
else node.indices = null;
// Разделение дочерних нод
if (node.negativeNode.indices != null) splitNode(node.negativeNode);
if (node.positiveNode.indices != null) splitNode(node.positiveNode);
}
/**
*
* @param node
* @param axis
* @param numSplitCoords
* @param splitCoords
* @param bb
*/
private function checkNodeAxis(node:CollisionKdNode, axis:int, numSplitCoords:int, splitCoords:Vector.<Number>, bb:Vector.<Number>):void {
var axis1:int = (axis + 1)%3;
var axis2:int = (axis + 2)%3;
var area:Number = (bb[axis1 + 3] - bb[axis1])*(bb[axis2 + 3] - bb[axis2]);
var staticBoundBoxes:Vector.<BoundBox> = parentTree.staticBoundBoxes;
for (var i:int = 0; i < numSplitCoords; ++i) {
var currSplitCoord:Number = splitCoords[i];
if (isNaN(currSplitCoord)) continue;
var minCoord:Number = currSplitCoord - threshold;
var maxCoord:Number = currSplitCoord + threshold;
var areaNegative:Number = area*(currSplitCoord - bb[axis]);
var areaPositive:Number = area*(bb[int(axis + 3)] - currSplitCoord);
var numNegative:int = 0;
var numPositive:int = 0;
var conflict:Boolean = false;
// Проверяем объекты
var numObjects:int = node.indices.length;
for (var j:int = 0; j < numObjects; j++) {
var boundBox:BoundBox = staticBoundBoxes[node.indices[j]];
_bb[0] = boundBox.minX;
_bb[1] = boundBox.minY;
_bb[2] = boundBox.minZ;
_bb[3] = boundBox.maxX;
_bb[4] = boundBox.maxY;
_bb[5] = boundBox.maxZ;
if (_bb[axis + 3] <= maxCoord) {
if (_bb[axis] < minCoord) numNegative++;
} else {
if (_bb[axis] >= minCoord) {
numPositive++;
} else {
conflict = true;
break;
}
}
}
// Если хороший сплит, сохраняем
var cost:Number = areaNegative*numNegative + areaPositive*numPositive;
if (!conflict && cost < splitCost) {
splitAxis = axis;
splitCost = cost;
splitCoord = currSplitCoord;
}
for (j = i + 1; j < numSplitCoords; ++j) {
if (splitCoords[j] >= currSplitCoord - threshold && splitCoords[j] <= currSplitCoord + threshold) splitCoords[j] = NaN;
}
}
}
}
}

View File

@@ -0,0 +1,7 @@
package alternativa.physics.collision {
import alternativa.physics.rigid.Body;
public interface IBodyCollisionPredicate {
function considerBodies(body1:Body, body2:Body):Boolean;
}
}

View File

@@ -0,0 +1,30 @@
package alternativa.physics.collision {
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.rigid.Contact;
/**
* Интерфейс определителя столкновений между двумя примитивами.
*/
public interface ICollider {
/**
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
* @return true, если пересечение существует, иначе false
*/
function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
/**
* Выполняет быстрый тест на наличие пересечения двух примитивов.
*
* @param prim1 первый примитив
* @param prim2 второй примитив
* @return true, если пересечение существует, иначе false
*/
function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean;
}
}

View File

@@ -0,0 +1,92 @@
package alternativa.physics.collision {
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.types.RayIntersection;
import alternativa.physics.rigid.Body;
import alternativa.physics.rigid.Contact;
import alternativa.physics.types.Vector3;
/**
* Интерфейс детектора столкновений.
*/
public interface ICollisionDetector {
/**
* Добавляет физический примитив в коллайдер.
*
* @param primitive добавляемый примитив
* @param isStatic указывает тип примитива: статический или динамический
* @return true если примитив был успешно добавлен, иначе false
*/
function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean;
/**
* Удаляет физический примитив из коллайдера.
*
* @param primitive удаляемый примитив
* @param isStatic указывает тип примитива: статический или динамический
* @return true если примитив был успшено удалён, иначе false
*/
function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean;
/**
* Выполняет инициализацию детектора после обновления списка примитивов.
*/
function init():void;
/**
* Получает все столкновения в текущей конфигурации физической геометрии.
*
* @param contacts список контактов, в кторые будет записана информация о столкновении
* @return количество найденных столкновений
*/
function getAllCollisions(contacts:Vector.<Contact>):int;
/**
* Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
* В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
*
* @param origin начальная точка луча в мировых координатах
* @param dir направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
* @param collisionGroup идентификатор группы
* @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
* @param predicate предикат, применяемый к столкновениям
* @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
* переданной структуре не гарантируется.
* @return true в случае наличия пересечения, иначе false
*/
function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
/**
* Тестирует луч на пересечение со статической физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
* В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
*
* @param origin начальная точка луча в мировых координатах
* @param dir направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
* @param collisionGroup идентификатор группы
* @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
* @param predicate предикат, применяемый к столкновениям
* @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
* переданной структуре не гарантируется.
* @return true в случае наличия пересечения, иначе false
*/
function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
/**
*
* @param prim1
* @param prim2
* @param contact
* @return
*/
function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
/**
*
* @param prim1
* @param prim2
* @return
*/
function testCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean;
}
}

View File

@@ -0,0 +1,9 @@
package alternativa.physics.collision {
import alternativa.physics.collision.primitives.CollisionPrimitive;
public interface ICollisionPredicate {
function considerCollision(primitive:CollisionPrimitive):Boolean;
}
}

View File

@@ -0,0 +1,7 @@
package alternativa.physics.collision {
import alternativa.physics.rigid.Body;
public interface IRayCollisionPredicate {
function considerBody(body:Body):Boolean;
}
}

View File

@@ -0,0 +1,484 @@
package alternativa.physics.collision {
import __AS3__.vec.Vector;
import alternativa.physics.altphysics;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.collision.types.RayIntersection;
import alternativa.physics.rigid.Body;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Детектор, хранящий статическую геометрию в kD-дереве и использующий дерево для ускорения тестов на пересечения.
*/
public class KdTreeCollisionDetector implements ICollisionDetector {
altphysics var tree:CollisionKdTree;
altphysics var dynamicPrimitives:Vector.<CollisionPrimitive>;
altphysics var dynamicPrimitivesNum:int;
altphysics var threshold:Number = 0.0001;
private var colliders:Object = {};
private var _time:MinMax = new MinMax();
private var _n:Vector3 = new Vector3();
private var _o:Vector3 = new Vector3();
private var _dynamicIntersection:RayIntersection = new RayIntersection();
/**
*
*/
public function KdTreeCollisionDetector() {
tree = new CollisionKdTree();
dynamicPrimitives = new Vector.<CollisionPrimitive>();
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.BOX, new BoxBoxCollider());
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.SPHERE, new BoxSphereCollider());
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.RECT, new BoxRectCollider());
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.TRIANGLE, new BoxTriangleCollider());
// addCollider(CollisionPrimitive.BOX, CollisionPrimitive.PLANE, new BoxPlaneCollider());
// addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.PLANE, new SpherePlaneCollider());
addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.SPHERE, new SphereSphereCollider());
}
/**
* @param primitive
* @param isStatic
* @return
*/
public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
if (isStatic) tree.addStaticPrimitive(primitive);
else dynamicPrimitives[dynamicPrimitivesNum++] = primitive;
return true;
}
/**
*
* @param primitive
* @param isStatic
* @return
*
*/
public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
if (isStatic) return tree.removeStaticPrimitive(primitive);
var idx:int = dynamicPrimitives.indexOf(primitive);
if (idx < 0) return false;
dynamicPrimitives.splice(idx, 1);
dynamicPrimitivesNum--;
return true;
}
/**
*
*/
public function init():void {
tree.createTree();
// tree.traceTree();
}
/**
*
* @param contacts
* @return
*/
public function getAllCollisions(contacts:Vector.<Contact>):int {
var colNum:int = 0;
for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
var primitive:CollisionPrimitive = dynamicPrimitives[i];
primitive.calculateAABB();
if (primitive.body != null && primitive.body.frozen) continue;
colNum += getPrimitiveNodeCollisions(tree.rootNode, primitive, contacts, colNum);
// Столкновения динамических примитивов между собой
// TODO: Попробовать различные оптимизации (сортировка по баундам, встраивание в дерево)
for (var j:int = i + 1; j < dynamicPrimitivesNum; j++) {
if (collide(primitive, dynamicPrimitives[j], contacts[colNum])) colNum++;
}
}
return colNum;
}
/**
* @param prim1
* @param prim2
* @param contact
* @return
*/
public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
if ((prim1.collisionGroup & prim2.collisionGroup) == 0) return false;
if (prim1.body != null && prim1.body == prim2.body) return false;
if (!prim1.aabb.intersects(prim2.aabb, 0.01)) return false;
var collider:ICollider = colliders[prim1.type <= prim2.type ? (prim1.type << 16) | prim2.type : (prim2.type << 16) | prim1.type] as ICollider;
if (collider != null && collider.getContact(prim1, prim2, contact)) {
if (prim1.postCollisionPredicate != null && !prim1.postCollisionPredicate.considerCollision(prim2)) return false;
if (prim2.postCollisionPredicate != null && !prim2.postCollisionPredicate.considerCollision(prim1)) return false;
// Сохраняем ссылку на контакт для каждого тела
if (prim1.body != null) prim1.body.contacts[prim1.body.contactsNum++] = contact;
if (prim2.body != null) prim2.body.contacts[prim2.body.contactsNum++] = contact;
// Вычисляем максимальную глубину пересечения для контакта
contact.maxPenetration = (contact.points[0] as ContactPoint).penetration;
var pen:Number;
for (var i:int = contact.pcount - 1; i >= 1; i--) {
if ((pen = (contact.points[i] as ContactPoint).penetration) > contact.maxPenetration) contact.maxPenetration = pen;
}
return true;
}
return false;
}
/**
* @param prim1
* @param prim2
* @param contact
* @return
*/
public function testCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
if ((prim1.collisionGroup & prim2.collisionGroup) == 0) return false;
if (prim1.body != null && prim1.body == prim2.body) return false;
if (!prim1.aabb.intersects(prim2.aabb, 0.01)) return false;
var collider:ICollider = colliders[prim1.type <= prim2.type ? (prim1.type << 16) | prim2.type : (prim2.type << 16) | prim1.type] as ICollider;
if (collider != null && collider.haveCollision(prim1, prim2)) {
if (prim1.postCollisionPredicate != null && !prim1.postCollisionPredicate.considerCollision(prim2)) return false;
if (prim2.postCollisionPredicate != null && !prim2.postCollisionPredicate.considerCollision(prim1)) return false;
return true;
}
return false;
}
/**
* Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
* В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча.
*
* @param origin
* @param dir
* @param collisionGroup идентификатор группы
* @param maxTime параметр, задающий длину проверяемого сегмента
* @param predicate
* @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
* переданной структуре не гарантируется.
* @return true в случае наличия пересечения, иначе false
*/
public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
var hasStaticIntersection:Boolean = intersectRayWithStatic(origin, dir, collisionGroup, maxTime, predicate, result);
var hasDynamicIntersection:Boolean = intersectRayWithDynamic(origin, dir, collisionGroup, maxTime, predicate, _dynamicIntersection);
if (!(hasDynamicIntersection || hasStaticIntersection)) return false;
if (hasDynamicIntersection && hasStaticIntersection) {
if (result.t > _dynamicIntersection.t) result.copy(_dynamicIntersection);
return true;
}
if (hasStaticIntersection) return true;
result.copy(_dynamicIntersection);
return true;
}
/**
*
* @param origin
* @param dir
* @param collisionGroup
* @param maxTime
* @param predicate
* @param result
* @return
*
*/
public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
// Вычисление точки пересечения с корневм узлом
if (!getRayBoundBoxIntersection(origin, dir, tree.rootNode.boundBox, _time)) return false;
if (_time.max < 0 || _time.min > maxTime) return false;
if (_time.min <= 0) {
_time.min = 0;
_o.x = origin.x;
_o.y = origin.y;
_o.z = origin.z;
} else {
_o.x = origin.x + _time.min*dir.x;
_o.y = origin.y + _time.min*dir.y;
_o.z = origin.z + _time.min*dir.z;
}
if (_time.max > maxTime) _time.max = maxTime;
var hasIntersection:Boolean = testRayAgainstNode(tree.rootNode, origin, _o, dir, collisionGroup, _time.min, _time.max, predicate, result);
return hasIntersection ? result.t <= maxTime : false;
}
/**
*
* @param body
* @param primitive
* @return
*
*/
public function testBodyPrimitiveCollision(body:Body, primitive:CollisionPrimitive):Boolean {
return false;
}
/**
*
* @param type1
* @param type2
* @param collider
*/
private function addCollider(type1:int, type2:int, collider:ICollider):void {
colliders[type1 <= type2 ? (type1 << 16) | type2 : (type2 << 16) | type1] = collider;
}
/**
* Выполняет поиск столкновений динамического примитива с примитивами узла дерева.
*
* @param node
* @param primitive
* @param contacts
* @param startIndex
* @return
*/
private function getPrimitiveNodeCollisions(node:CollisionKdNode, primitive:CollisionPrimitive, contacts:Vector.<Contact>, startIndex:int):int {
var colNum:int = 0;
if (node.indices != null) {
// Поиск столкновений со статическими примитивами узла
var primitives:Vector.<CollisionPrimitive> = tree.staticChildren;
var indices:Vector.<int> = node.indices;
for (var i:int = indices.length - 1; i >= 0; i--)
if (collide(primitive, primitives[indices[i]], contacts[startIndex + colNum])) colNum++;
}
if (node.axis == -1) return colNum;
var min:Number;
var max:Number;
switch (node.axis) {
case 0:
min = primitive.aabb.minX;
max = primitive.aabb.maxX;
break;
case 1:
min = primitive.aabb.minY;
max = primitive.aabb.maxY;
break;
case 2:
min = primitive.aabb.minZ;
max = primitive.aabb.maxZ;
break;
}
if (min < node.coord) colNum += getPrimitiveNodeCollisions(node.negativeNode, primitive, contacts, startIndex + colNum);
if (max > node.coord) colNum += getPrimitiveNodeCollisions(node.positiveNode, primitive, contacts, startIndex + colNum);
return colNum;
}
private static var _rayAABB:BoundBox = new BoundBox();
/**
* Тест пересечения луча с динамическими примитивами.
*
* @param origin
* @param dir
* @param collisionGroup
* @param maxTime
* @param predicate
* @param result
* @return
*/
private function intersectRayWithDynamic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
var xx:Number = origin.x + dir.x*maxTime;
var yy:Number = origin.y + dir.y*maxTime;
var zz:Number = origin.z + dir.z*maxTime;
if (xx < origin.x) {
_rayAABB.minX = xx;
_rayAABB.maxX = origin.x;
} else {
_rayAABB.minX = origin.x;
_rayAABB.maxX = xx;
}
if (yy < origin.y) {
_rayAABB.minY = yy;
_rayAABB.maxY = origin.y;
} else {
_rayAABB.minY = origin.y;
_rayAABB.maxY = yy;
}
if (zz < origin.z) {
_rayAABB.minZ = zz;
_rayAABB.maxZ = origin.z;
} else {
_rayAABB.minZ = origin.z;
_rayAABB.maxZ = zz;
}
var minTime:Number = maxTime + 1;
for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
var primitive:CollisionPrimitive = dynamicPrimitives[i];
if ((primitive.collisionGroup & collisionGroup) == 0) continue;
var paabb:BoundBox = primitive.aabb;
if (_rayAABB.maxX < paabb.minX || _rayAABB.minX > paabb.maxX || _rayAABB.maxY < paabb.minY || _rayAABB.minY > paabb.maxY || _rayAABB.maxZ < paabb.minZ || _rayAABB.minZ > paabb.maxZ) continue;
if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
var t:Number = primitive.getRayIntersection(origin, dir, threshold, _n);
if (t > 0 && t < minTime) {
minTime = t;
result.primitive = primitive;
result.normal.x = _n.x;
result.normal.y = _n.y;
result.normal.z = _n.z;
}
}
if (minTime > maxTime) return false;
result.pos.x = origin.x + dir.x*minTime;
result.pos.y = origin.y + dir.y*minTime;
result.pos.z = origin.z + dir.z*minTime;
result.t = minTime;
return true;
}
/**
* Вычисляет точки пересечения луча с AABB.
*
* @param origin точка начала луча
* @param dir направляющий вектор луча. Вектор может иметь любую отличную от нуля длину.
* @param bb AABB, с которым пересекается луч
* @param time возвращаемое значение. В эту переменную записывается минимальное и максимальное время пересечения
* @return true в случае наличия хотя бы одного пересечения, иначе false
*/
private function getRayBoundBoxIntersection(origin:Vector3, dir:Vector3, bb:BoundBox, time:MinMax):Boolean {
time.min = -1;
time.max = 1e308;
var t1:Number;
var t2:Number;
// Цикл по осям бокса
for (var i:int = 0; i < 3; i++) {
switch (i) {
case 0:
if (dir.x < threshold && dir.x > -threshold) {
if (origin.x < bb.minX || origin.x > bb.maxX) return false;
else continue;
}
t1 = (bb.minX - origin.x)/dir.x;
t2 = (bb.maxX - origin.x)/dir.x;
break;
case 1:
if (dir.y < threshold && dir.y > -threshold) {
if (origin.y < bb.minY || origin.y > bb.maxY) return false;
else continue;
}
t1 = (bb.minY - origin.y)/dir.y;
t2 = (bb.maxY - origin.y)/dir.y;
break;
case 2:
if (dir.z < threshold && dir.z > -threshold) {
if (origin.z < bb.minZ || origin.z > bb.maxZ) return false;
else continue;
}
t1 = (bb.minZ - origin.z)/dir.z;
t2 = (bb.maxZ - origin.z)/dir.z;
break;
}
if (t1 < t2) {
if (t1 > time.min) time.min = t1;
if (t2 < time.max) time.max = t2;
} else {
if (t2 > time.min) time.min = t2;
if (t1 < time.max) time.max = t1;
}
if (time.max < time.min) return false;
}
return true;
}
/**
*
* @param node
* @param origin
* @param dir
* @param collisionGroup
* @param t1 время входа луча в узел
* @param t2 время выхода луча из узла
* @param intersection
*/
private function testRayAgainstNode(node:CollisionKdNode, origin:Vector3, localOrigin:Vector3, dir:Vector3, collisionGroup:int, t1:Number, t2:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
// При наличии в узле объектов, проверяем пересечение с ними
if (node.indices != null && getRayNodeIntersection(origin, dir, collisionGroup, tree.staticChildren, node.indices, predicate, result)) return true;
// Выход из функции если это конечный узел
if (node.axis == -1) return false;
// Определение времени пересечения луча и плоскости разделения узла
var splitTime:Number;
var currChildNode:CollisionKdNode;
switch (node.axis) {
case 0:
if (dir.x > -threshold && dir.x < threshold) splitTime = t2 + 1;
else splitTime = (node.coord - origin.x)/dir.x;
currChildNode = localOrigin.x < node.coord ? node.negativeNode : node.positiveNode;
break;
case 1:
if (dir.y > -threshold && dir.y < threshold) splitTime = t2 + 1;
else splitTime = (node.coord - origin.y)/dir.y;
currChildNode = localOrigin.y < node.coord ? node.negativeNode : node.positiveNode;
break;
case 2:
if (dir.z > -threshold && dir.z < threshold) splitTime = t2 + 1;
else splitTime = (node.coord - origin.z)/dir.z;
currChildNode = localOrigin.z < node.coord ? node.negativeNode : node.positiveNode;
break;
}
// Определение порядка проверки
if (splitTime < t1 || splitTime > t2) {
// Луч не переходит в соседний дочерний узел
return testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, t2, predicate, result);
} else {
// Луч переходит из одного дочернего узла в другой
var intersects:Boolean = testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, splitTime, predicate, result);
if (intersects) return true;
_o.x = origin.x + splitTime*dir.x;
_o.y = origin.y + splitTime*dir.y;
_o.z = origin.z + splitTime*dir.z;
return testRayAgainstNode(currChildNode == node.negativeNode ? node.positiveNode : node.negativeNode, origin, _o, dir, collisionGroup, splitTime, t2, predicate, result);
}
}
/**
*
* @param origin
* @param dir
* @param collisionGroup
* @param primitives
* @param indices
* @param intersection
* @return
*
*/
private function getRayNodeIntersection(origin:Vector3, dir:Vector3, collisionGroup:int, primitives:Vector.<CollisionPrimitive>, indices:Vector.<int>, predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
var pnum:int = indices.length;
var minTime:Number = 1e308;
for (var i:int = 0; i < pnum; i++) {
var primitive:CollisionPrimitive = primitives[indices[i]];
if ((primitive.collisionGroup & collisionGroup) == 0) continue;
if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
var t:Number = primitive.getRayIntersection(origin, dir, threshold, _n);
if (t > 0 && t < minTime) {
minTime = t;
intersection.primitive = primitive;
intersection.normal.x = _n.x;
intersection.normal.y = _n.y;
intersection.normal.z = _n.z;
}
}
if (minTime == 1e308) return false;
intersection.pos.x = origin.x + dir.x*minTime;
intersection.pos.y = origin.y + dir.y*minTime;
intersection.pos.z = origin.z + dir.z*minTime;
intersection.t = minTime;
return true;
}
}
}
class MinMax {
public var min:Number = 0;
public var max:Number = 0;
}

View File

@@ -0,0 +1,67 @@
package alternativa.physics.collision {
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.rigid.Contact;
import alternativa.physics.types.Vector3;
/**
*
*/
public class SpherePlaneCollider implements ICollider {
private var normal:Vector3 = new Vector3();
/**
*
*/
public function SpherePlaneCollider() {
}
/**
*
* @param body1
* @param body2
* @param collisionInfo
* @return
*/
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
// var sphere:RigidSphere = body1 as RigidSphere;
// var plane:RigidPlane;
// if (sphere == null) {
// sphere = body2 as RigidSphere;
// plane = body1 as RigidPlane;
// } else {
// plane = body2 as RigidPlane;
// }
//
// // Вычисляем глобальные нормаль и смещение плоскости
// plane.baseMatrix.transformVector(plane.normal, normal);
// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
//
// var dist:Number = sphere.state.pos.dot(normal) - offset;
// if (dist > sphere.r) return false;
//
// collisionInfo.body1 = sphere;
// collisionInfo.body2 = plane;
// collisionInfo.normal.copy(normal);
// collisionInfo.pcount = 1;
//
// var cp:ContactPoint = collisionInfo.points[0];
// cp.penetration = sphere.r - dist;
// cp.pos.copy(normal).reverse().scale(sphere.r).add(sphere.state.pos);
// cp.r1.diff(cp.pos, sphere.state.pos);
// cp.r2.diff(cp.pos, plane.state.pos);
return true;
}
/**
* @param prim1
* @param prim2
* @return
*/
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,69 @@
package alternativa.physics.collision {
import alternativa.physics.altphysics;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.primitives.CollisionSphere;
import alternativa.physics.rigid.Contact;
import alternativa.physics.rigid.ContactPoint;
import alternativa.physics.types.Vector3;
use namespace altphysics;
public class SphereSphereCollider implements ICollider {
private var p1:Vector3 = new Vector3();
private var p2:Vector3 = new Vector3();
public function SphereSphereCollider() {
}
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
var s1:CollisionSphere;
var s2:CollisionSphere;
if (prim1.body != null) {
s1 = prim1 as CollisionSphere;
s2 = prim2 as CollisionSphere;
} else {
s1 = prim2 as CollisionSphere;
s2 = prim1 as CollisionSphere;
}
s1.transform.getAxis(3, p1);
s2.transform.getAxis(3, p2);
var dx:Number = p1.x - p2.x;
var dy:Number = p1.y - p2.y;
var dz:Number = p1.z - p2.z;
var len:Number = dx*dx + dy*dy + dz*dz;
var sum:Number = s1.r + s2.r;
if (len > sum*sum) return false;
len = Math.sqrt(len);
dx /= len;
dy /= len;
dz /= len;
contact.body1 = s1.body;
contact.body2 = s2.body;
contact.normal.x = dx;
contact.normal.y = dy;
contact.normal.z = dz;
contact.pcount = 1;
var cp:ContactPoint = contact.points[0];
cp.penetration = sum - len;
cp.pos.x = p1.x - dx*s1.r;
cp.pos.y = p1.y - dy*s1.r;
cp.pos.z = p1.z - dz*s1.r;
cp.r1.vDiff(cp.pos, p1);
cp.r2.vDiff(cp.pos, p2);
return true;
}
/**
* @param prim1
* @param prim2
* @return
*/
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
return false;
}
}
}

View File

@@ -0,0 +1,35 @@
K 25
svn:wc:ra_dav:version-url
V 136
/!svn/ver/19853/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/primitives
END
CollisionTriangle.as
K 25
svn:wc:ra_dav:version-url
V 157
/!svn/ver/19853/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/primitives/CollisionTriangle.as
END
CollisionSphere.as
K 25
svn:wc:ra_dav:version-url
V 155
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/primitives/CollisionSphere.as
END
CollisionBox.as
K 25
svn:wc:ra_dav:version-url
V 152
/!svn/ver/19853/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/primitives/CollisionBox.as
END
CollisionPrimitive.as
K 25
svn:wc:ra_dav:version-url
V 158
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/primitives/CollisionPrimitive.as
END
CollisionRect.as
K 25
svn:wc:ra_dav:version-url
V 153
/!svn/ver/19853/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/primitives/CollisionRect.as
END

View File

@@ -0,0 +1,88 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/primitives
http://svndev.alternativaplatform.com
2009-09-14T13:27:15.443685Z
19853
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
CollisionTriangle.as
file
2010-10-28T04:32:38.000000Z
9567a440e5f4552480eec32af2b1541a
2009-09-14T13:27:15.443685Z
19853
mike
CollisionSphere.as
file
2010-10-28T04:32:38.000000Z
d0d8b961b5e74673c357cd98e0963f4a
2009-09-09T12:16:47.747177Z
19632
mike
CollisionBox.as
file
2010-10-28T04:32:38.000000Z
6949185b7324dde6fc0a48547d56fb92
2009-09-14T13:27:15.443685Z
19853
mike
CollisionPrimitive.as
file
2010-10-28T04:32:38.000000Z
709fc587a0f9d3cb6a67dce3f3e4c92e
2009-09-11T09:22:20.813670Z
19776
mike
CollisionRect.as
file
2010-10-28T04:32:38.000000Z
ace6d006988f2caaf2577db09794910c
2009-09-14T13:27:15.443685Z
19853
mike

View File

@@ -0,0 +1,178 @@
package alternativa.physics.collision.primitives {
import alternativa.physics.altphysics;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Ориентированный бокс.
*/
public class CollisionBox extends CollisionPrimitive {
// Половинные размеры вдоль каждой из осей
public var hs:Vector3 = new Vector3();
/**
* @param hs
* @param collisionGroup
*/
public function CollisionBox(hs:Vector3, collisionGroup:int) {
super(BOX, collisionGroup);
this.hs.vCopy(hs);
}
/**
* @return
*/
override public function calculateAABB():BoundBox {
var t:Matrix4 = transform;
aabb.maxX = hs.x*(t.a < 0 ? -t.a : t.a) + hs.y*(t.b < 0 ? -t.b : t.b) + hs.z*(t.c < 0 ? -t.c : t.c);
aabb.minX = -aabb.maxX;
aabb.maxY = hs.x*(t.e < 0 ? -t.e : t.e) + hs.y*(t.f < 0 ? -t.f : t.f) + hs.z*(t.g < 0 ? -t.g : t.g);
aabb.minY = -aabb.maxY;
aabb.maxZ = hs.x*(t.i < 0 ? -t.i : t.i) + hs.y*(t.j < 0 ? -t.j : t.j) + hs.z*(t.k < 0 ? -t.k : t.k);
aabb.minZ = -aabb.maxZ;
aabb.minX += t.d;
aabb.maxX += t.d;
aabb.minY += t.h;
aabb.maxY += t.h;
aabb.minZ += t.l;
aabb.maxZ += t.l;
return aabb;
}
/**
* @param source
* @return
*/
override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
var box:CollisionBox = source as CollisionBox;
if (box == null) return this;
super.copyFrom(box);
hs.vCopy(box.hs);
return this;
}
/**
* @return
*/
override protected function createPrimitive():CollisionPrimitive {
return new CollisionBox(hs, collisionGroup);
}
/**
* Вычисляет пересечение луча с примитивом.
*
* @param origin начальная точка луча в мировых координатах
* @param vector направляющий вектор луча в мировых координатах. Вектор может быть любой отличной от нуля длины.
* @param epsilon погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную погрешность, считается равной нулю.
* @param normal если пересечение существует, в этот параметр записывается нормаль к примитиву в точке пересечения
* @return если пересечение существует, возвращается неотрицательное время точки пересечения, в противном случае возвращается -1.
*/
override public function getRayIntersection(origin:Vector3, vector:Vector3, epsilon:Number, normal:Vector3):Number {
var tMin:Number = -1;
var tMax:Number = 1e308;
var t1:Number;
var t2:Number;
// Перевод параметров сегмента в систему координат примитива
// Inlined transform.transformVectorInverse(origin, _o);
var vx:Number = origin.x - transform.d;
var vy:Number = origin.y - transform.h;
var vz:Number = origin.z - transform.l;
var ox:Number = transform.a*vx + transform.e*vy + transform.i*vz;
var oy:Number = transform.b*vx + transform.f*vy + transform.j*vz;
var oz:Number = transform.c*vx + transform.g*vy + transform.k*vz;
// Inlined transform.deltaTransformVectorInverse(vector, _v);
vx = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
vy = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
vz = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
// X
if (vx < epsilon && vx > -epsilon) {
if (ox < -hs.x || ox > hs.x) return -1;
} else {
t1 = (-hs.x - ox)/vx;
t2 = (hs.x - ox)/vx;
if (t1 < t2) {
if (t1 > tMin) {
tMin = t1;
normal.x = -1;
normal.y = normal.z = 0;
}
if (t2 < tMax) tMax = t2;
} else {
if (t2 > tMin) {
tMin = t2;
normal.x = 1;
normal.y = normal.z = 0;
}
if (t1 < tMax) tMax = t1;
}
if (tMax < tMin) return -1;
}
// Y
if (vy < epsilon && vy > -epsilon) {
if (oy < -hs.y || oy > hs.y) return -1;
} else {
t1 = (-hs.y - oy)/vy;
t2 = (hs.y - oy)/vy;
if (t1 < t2) {
if (t1 > tMin) {
tMin = t1;
normal.y = -1;
normal.x = normal.z = 0;
}
if (t2 < tMax) tMax = t2;
} else {
if (t2 > tMin) {
tMin = t2;
normal.y = 1;
normal.x = normal.z = 0;
}
if (t1 < tMax) tMax = t1;
}
if (tMax < tMin) return -1;
}
// Z
if (vz < epsilon && vz > -epsilon) {
if (oz < -hs.z || oz > hs.z) return -1;
} else {
t1 = (-hs.z - oz)/vz;
t2 = (hs.z - oz)/vz;
if (t1 < t2) {
if (t1 > tMin) {
tMin = t1;
normal.z = -1;
normal.x = normal.y = 0;
}
if (t2 < tMax) tMax = t2;
} else {
if (t2 > tMin) {
tMin = t2;
normal.z = 1;
normal.x = normal.y = 0;
}
if (t1 < tMax) tMax = t1;
}
if (tMax < tMin) return -1;
}
// Перевод нормали в мировую систему координат
// Inlined normal.vDeltaTransformBy4(transform);
vx = normal.x;
vy = normal.y;
vz = normal.z;
normal.x = transform.a*vx + transform.b*vy + transform.c*vz;
normal.y = transform.e*vx + transform.f*vy + transform.g*vz;
normal.z = transform.i*vx + transform.j*vy + transform.k*vz;
return tMin;
}
}
}

View File

@@ -0,0 +1,143 @@
package alternativa.physics.collision.primitives {
import alternativa.physics.collision.ICollisionPredicate;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.rigid.Body;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
/**
* Базовый класс для примитивов, использующихся детектором столкновений.
*/
public class CollisionPrimitive {
// Константы типов примитива
public static const BOX:int = 1;
public static const PLANE:int = 2;
public static const SPHERE:int = 3;
public static const RECT:int = 4;
public static const TRIANGLE:int = 5;
// Тип примитива
public var type:int;
// Группы примитива. Каждая группа определяется установленным битом. Столкновения проверяются только для примитивов,
// имеющих хотя бы одну общую группу.
public var collisionGroup:int;
// Предикат примитива, вызывающийся детектором при нахождении столкновения. В зависимости от возвращённого
// предикатом результата столкновение либо регистрируется, либо игнорируется.
// Nullable
public var postCollisionPredicate:ICollisionPredicate;
// Тело, владеющее примитивом. Поле сделано открытым для быстрого доступа на чтение. Установка значения должна выполняться вызовом метода setBody().
// Nullable
public var body:Body;
// Трансформация примитива в системе координат тела, если оно указано. Не допускается масштабирование матрицы.
public var localTransform:Matrix4;
// Полная трансформация примитива. Не допускается масштабирование матрицы.
public var transform:Matrix4 = new Matrix4();
// AABB в мировой системе координат. Расчитывается системой вызовом функции calculateBoundBox().
public var aabb:BoundBox = new BoundBox();
/**
* Создаёт новый экземпляр примитива.
*
* @param type тип примитива
* @param collisionGroup группа примитива
*/
public function CollisionPrimitive(type:int, collisionGroup:int) {
this.type = type;
this.collisionGroup = collisionGroup;
}
/**
* Устанавливает тело, владеющее примитивом.
*
* @param body тело, которое владеет примитивом. Если указано значение null, матрица локальной трансформации удаляется.
* @param localTransform трансформация примитива в системе координат тела. Указание значения null равносильно
* заданию единичной матрицы, однако в первом случае не будет дополнительного умножения матриц при вычислении полной трансформации примитива.
*/
public function setBody(body:Body, localTransform:Matrix4 = null):void {
if (this.body == body) return;
this.body = body;
if (body == null) {
this.localTransform = null;
} else {
if (localTransform != null) {
if (this.localTransform == null) {
this.localTransform = new Matrix4();
}
this.localTransform.copy(localTransform);
} else {
this.localTransform = null;
}
}
}
/**
* Рассчитывает AABB примитива. Наследники должны переопределять этот метод, реализуя в нём корректный рассчёт.
*
* @return ссылка на свой AABB
*/
public function calculateAABB():BoundBox {
return aabb;
}
/**
* Вычисляет пересечение луча с примитивом.
*
* @param origin начальная точка луча в мировых координатах
* @param vector направляющий вектор луча в мировых координатах. Вектор может быть любой отличной от нуля длины.
* @param epsilon погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную погрешность, считается равной нулю.
* @param normal если пересечение существует, в этот параметр записывается нормаль к примитиву в точке пересечения
* @return если пересечение существует, возвращается неотрицательное время точки пересечения, в противном случае возвращается -1.
*/
public function getRayIntersection(origin:Vector3, vector:Vector3, epsilon:Number, normal:Vector3):Number {
return -1;
}
/**
* Клонирует примитив. Переопределять не рекомендуется. Вместо этого переопределяются методы createPrimitive() и copyFrom().
*
* @return клон примитива
*/
public function clone():CollisionPrimitive {
var p:CollisionPrimitive = createPrimitive();
return p.copyFrom(this);
}
/**
* Копирует параметры указанного примитива. Объекты копируются по значению.
*
* @param source примитив, чьи параметры копируются
* @return this
*/
public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
if (source == null) {
throw new ArgumentError("Parameter source cannot be null");
}
type = source.type;
transform.copy(source.transform);
collisionGroup = source.collisionGroup;
setBody(source.body, source.localTransform);
aabb.copyFrom(source.aabb);
return this;
}
/**
* Создаёт строковое представление объекта.
*
* @return строковое представление объекта
*/
public function toString():String {
return "[CollisionPrimitive type=" + type + "]";
}
/**
* Создаёт новый экземпляр примитива соответствующего типа.
*
* @return новый экземпляр примитива
*/
protected function createPrimitive():CollisionPrimitive {
return new CollisionPrimitive(type, collisionGroup);
}
}
}

View File

@@ -0,0 +1,123 @@
package alternativa.physics.collision.primitives {
import alternativa.physics.altphysics;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Ориентированный прямоугольник. Задаётся половинами размеров вдоль осей X (ширина) и Y (длина) локальной системы
* координат примитива. Таким образом, прямоугольник лежит в плоскости XY, его стороны параллельны осям этой
* плоскости, а нормаль направлена вдоль локальной оси Z.
*
* Прямоугольник может быть одно- или двусторонним. В случае одностороннего прямоугольника,
* столкновения с внутренней стороны не регистрируются.
*/
public class CollisionRect extends CollisionPrimitive {
// Половинные размеры прямоугольника вдоль осей X и Y. Нормаль направлена вдоль оси Z.
public var hs:Vector3 = new Vector3();
// Флаг указывает, является примитив одно- или двусторонним
public var twoSided:Boolean = true;
// Малое значение. Используется для указания фиктивной высоты примитива и в функции определения пересечения с сегментом.
private static const EPSILON:Number = 0.005;
/**
* Создаёт новый экземпляр примитива.
*
* @param hs половинные размерв прямоугольника вдоль осей X и Y. Значение z игнорируется.
* @param collisionGroup группа примитива
*/
public function CollisionRect(hs:Vector3, collisionGroup:int) {
super(RECT, collisionGroup);
this.hs.vCopy(hs);
}
/**
* Расчитывает ограничивающий бокс прямоугольника. Для избежания проблем высота примитива принимается равной
* не нулю, а малому значению.
*
* @return
*/
override public function calculateAABB():BoundBox {
// Баунд бокс прямоугольника имеет минимальную высоту, отличную от нуля во избежание проблем с построением kd-дерева
var t:Matrix4 = transform;
aabb.maxX = hs.x*(t.a < 0 ? -t.a : t.a) + hs.y*(t.b < 0 ? -t.b : t.b) + EPSILON*(t.c < 0 ? -t.c : t.c);
aabb.minX = -aabb.maxX;
aabb.maxY = hs.x*(t.e < 0 ? -t.e : t.e) + hs.y*(t.f < 0 ? -t.f : t.f) + EPSILON*(t.g < 0 ? -t.g : t.g);
aabb.minY = -aabb.maxY;
aabb.maxZ = hs.x*(t.i < 0 ? -t.i : t.i) + hs.y*(t.j < 0 ? -t.j : t.j) + EPSILON*(t.k < 0 ? -t.k : t.k);
aabb.minZ = -aabb.maxZ;
aabb.minX += t.d;
aabb.maxX += t.d;
aabb.minY += t.h;
aabb.maxY += t.h;
aabb.minZ += t.l;
aabb.maxZ += t.l;
return aabb;
}
/**
* @param source
* @return
*/
override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
var rect:CollisionRect = source as CollisionRect;
if (rect == null) return this;
super.copyFrom(rect);
hs.vCopy(rect.hs);
return this;
}
/**
* @return
*/
override protected function createPrimitive():CollisionPrimitive {
return new CollisionRect(hs, collisionGroup);
}
/**
* @param origin
* @param vector
* @param collisionGroup
* @param threshold
* @param normal
* @return
*/
override public function getRayIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
// Перевод параметров сегмента в систему координат примитива
// Inlined transform.transformVectorInverse(origin, _o);
var vx:Number = origin.x - transform.d;
var vy:Number = origin.y - transform.h;
var vz:Number = origin.z - transform.l;
var ox:Number = transform.a*vx + transform.e*vy + transform.i*vz;
var oy:Number = transform.b*vx + transform.f*vy + transform.j*vz;
var oz:Number = transform.c*vx + transform.g*vy + transform.k*vz;
// Inlined transform.deltaTransformVectorInverse(vector, _v);
vx = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
vy = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
vz = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
// Проверка параллельности сегмента и плоскости примитива
if (vz > -threshold && vz < threshold) return -1;
var t:Number = -oz/vz;
if (t < 0) return -1;
// Проверка вхождения точки пересечения в прямоугольник
ox += vx*t;
oy += vy*t;
oz = 0;
if (ox < (-hs.x - threshold) || ox > (hs.x + threshold) || oy < (-hs.y - threshold) || oy > (hs.y + threshold)) return -1;
normal.x = transform.c;
normal.y = transform.g;
normal.z = transform.k;
return t;
}
}
}

View File

@@ -0,0 +1,80 @@
package alternativa.physics.collision.primitives {
import alternativa.physics.altphysics;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Сфера.
*/
public class CollisionSphere extends CollisionPrimitive {
// Радиус сферы
public var r:Number = 0;
/**
*
* @param r
* @param collisionGroup
*/
public function CollisionSphere(r:Number, collisionGroup:int) {
super(SPHERE, collisionGroup);
this.r = r;
}
/**
* @return
*/
override public function calculateAABB():BoundBox {
aabb.maxX = transform.d + r;
aabb.minX = transform.d - r;
aabb.maxY = transform.h + r;
aabb.minY = transform.h - r;
aabb.maxZ = transform.l + r;
aabb.minZ = transform.l - r;
return aabb;
}
/**
* @param origin
* @param vector
* @param threshold
* @param normal
* @return
*/
override public function getRayIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
var px:Number = origin.x - transform.d;
var py:Number = origin.y - transform.h;
var pz:Number = origin.z - transform.l;
var k:Number = vector.x*px + vector.y*py + vector.z*pz;
if (k > 0) return -1;
var a:Number = vector.x*vector.x + vector.y*vector.y + vector.z*vector.z;
var D:Number = k*k - a*(px*px + py*py + pz*pz - r*r);
if (D < 0) return -1;
return -(k + Math.sqrt(D))/a;
}
/**
* @param source
* @return
*/
override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
var sphere:CollisionSphere = source as CollisionSphere;
if (sphere == null) return this;
super.copyFrom(sphere);
r = sphere.r;
return this;
}
/**
* @return
*/
override protected function createPrimitive():CollisionPrimitive {
return new CollisionSphere(r, collisionGroup);
}
}
}

View File

@@ -0,0 +1,243 @@
package alternativa.physics.collision.primitives {
import alternativa.physics.altphysics;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Примитив треугольник.
*/
public class CollisionTriangle extends CollisionPrimitive {
public var v0:Vector3 = new Vector3();
public var v1:Vector3 = new Vector3();
public var v2:Vector3 = new Vector3();
public var e0:Vector3 = new Vector3();
public var e1:Vector3 = new Vector3();
public var e2:Vector3 = new Vector3();
// public var len0:Number;
// public var len1:Number;
// public var len2:Number;
//
/**
*
* @param v0
* @param v1
* @param v2
* @param collisionGroup
*/
public function CollisionTriangle(v0:Vector3, v1:Vector3, v2:Vector3, collisionGroup:int) {
super(TRIANGLE, collisionGroup);
initVertices(v0, v1, v2);
}
/**
* Рассчитывает AABB примитива.
*
* @return ссылка на свой AABB
*/
override public function calculateAABB():BoundBox {
var epsilon:Number = 0.005;
var a:Number;
var b:Number;
var eps_c:Number = epsilon*transform.c;
var eps_g:Number = epsilon*transform.g;
var eps_k:Number = epsilon*transform.k;
// Вершина 0
// Ось X
a = v0.x*transform.a + v0.y*transform.b;
aabb.minX = aabb.maxX = a + eps_c;
b = a - eps_c;
if (b > aabb.maxX) aabb.maxX = b;
else if (b < aabb.minX) aabb.minX = b;
// Ось Y
a = v0.x*transform.e + v0.y*transform.f;
aabb.minY = aabb.maxY = a + eps_g;
b = a - eps_g;
if (b > aabb.maxY) aabb.maxY = b;
else if (b < aabb.minY) aabb.minY = b;
// Ось Z
a = v0.x*transform.i + v0.y*transform.j;
aabb.minZ = aabb.maxZ = a + eps_k;
b = a - eps_k;
if (b > aabb.maxZ) aabb.maxZ = b;
else if (b < aabb.minZ) aabb.minZ = b;
// Вершина 1
// Ось X
a = v1.x*transform.a + v1.y*transform.b;
b = a + eps_c;
if (b > aabb.maxX) aabb.maxX = b;
else if (b < aabb.minX) aabb.minX = b;
b = a - eps_c;
if (b > aabb.maxX) aabb.maxX = b;
else if (b < aabb.minX) aabb.minX = b;
// Ось Y
a = v1.x*transform.e + v1.y*transform.f;
b = a + eps_g;
if (b > aabb.maxY) aabb.maxY = b;
else if (b < aabb.minY) aabb.minY = b;
b = a - eps_g;
if (b > aabb.maxY) aabb.maxY = b;
else if (b < aabb.minY) aabb.minY = b;
// Ось Z
a = v1.x*transform.i + v1.y*transform.j;
b = a + eps_k;
if (b > aabb.maxZ) aabb.maxZ = b;
else if (b < aabb.minZ) aabb.minZ = b;
b = a - eps_k;
if (b > aabb.maxZ) aabb.maxZ = b;
else if (b < aabb.minZ) aabb.minZ = b;
// Вершина 2
// Ось X
a = v2.x*transform.a + v2.y*transform.b;
b = a + eps_c;
if (b > aabb.maxX) aabb.maxX = b;
else if (b < aabb.minX) aabb.minX = b;
b = a - eps_c;
if (b > aabb.maxX) aabb.maxX = b;
else if (b < aabb.minX) aabb.minX = b;
// Ось Y
a = v2.x*transform.e + v2.y*transform.f;
b = a + eps_g;
if (b > aabb.maxY) aabb.maxY = b;
else if (b < aabb.minY) aabb.minY = b;
b = a - eps_g;
if (b > aabb.maxY) aabb.maxY = b;
else if (b < aabb.minY) aabb.minY = b;
// Ось Z
a = v2.x*transform.i + v2.y*transform.j;
b = a + eps_k;
if (b > aabb.maxZ) aabb.maxZ = b;
else if (b < aabb.minZ) aabb.minZ = b;
b = a - eps_k;
if (b > aabb.maxZ) aabb.maxZ = b;
else if (b < aabb.minZ) aabb.minZ = b;
aabb.minX += transform.d;
aabb.maxX += transform.d;
aabb.minY += transform.h;
aabb.maxY += transform.h;
aabb.minZ += transform.l;
aabb.maxZ += transform.l;
return aabb;
}
/**
* Вычисляет пересечение луча с примитивом.
*
* @param origin начальная точка луча в мировых координатах
* @param vector направляющий вектор луча в мировых координатах. Вектор может быть любой отличной от нуля длины.
* @param epsilon погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную погрешность, считается равной нулю.
* @param normal если пересечение существует, в этот параметр записывается нормаль к примитиву в точке пересечения
* @return если пересечение существует, возвращается неотрицательное время точки пересечения, в противном случае возвращается -1.
*/
override public function getRayIntersection(origin:Vector3, vector:Vector3, epsilon:Number, normal:Vector3):Number {
// Луч трансформируется в систему координат примитива, затем проверяется пересечение
var vz:Number = vector.x*transform.c + vector.y*transform.g + vector.z*transform.k;
// Если луч параллелен плоскости птимитива, то пересечения нет
if (vz < epsilon && vz > -epsilon) return -1;
var tx:Number = origin.x - transform.d;
var ty:Number = origin.y - transform.h;
var tz:Number = origin.z - transform.l;
var oz:Number = tx*transform.c + ty*transform.g + tz*transform.k;
var t:Number = -oz/vz;
if (t < 0) return -1;
var ox:Number = tx*transform.a + ty*transform.e + tz*transform.i;
var oy:Number = tx*transform.b + ty*transform.f + tz*transform.j;
tx = ox + t*(vector.x*transform.a + vector.y*transform.e + vector.z*transform.i);
ty = oy + t*(vector.x*transform.b + vector.y*transform.f + vector.z*transform.j);
tz = oz + t*vz;
// Проверка вхождения точки в треугольник
if ((e0.x*(ty - v0.y) - e0.y*(tx - v0.x) < 0) || (e1.x*(ty - v1.y) - e1.y*(tx - v1.x) < 0) || (e2.x*(ty - v2.y) - e2.y*(tx - v2.x) < 0)) return -1;
// Запись нормали
normal.x = transform.c;
normal.y = transform.g;
normal.z = transform.k;
return t;
}
/**
* Копирует параметры указанного примитива. Объекты копируются по значению.
*
* @param source примитив, чьи параметры копируются
* @return this
*/
override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
super.copyFrom(source);
var tri:CollisionTriangle = source as CollisionTriangle;
if (tri != null) {
v0.vCopy(tri.v0);
v1.vCopy(tri.v1);
v2.vCopy(tri.v2);
e0.vCopy(tri.e0);
e1.vCopy(tri.e1);
e2.vCopy(tri.e2);
// len0 = tri.len0;
// len1 = tri.len1;
// len2 = tri.len2;
}
return this;
}
/**
* Создаёт строковое представление объекта.
*
* @return строковое представление объекта
*/
override public function toString():String {
return "[CollisionTriangle v0=" + v0 + ", v1=" + v1 + ", v2=" + v2 + "]";
}
/**
* Создаёт новый экземпляр примитива соответствующего типа.
*
* @return новый экземпляр примитива
*/
override protected function createPrimitive():CollisionPrimitive {
return new CollisionTriangle(v0, v1, v2, collisionGroup);
}
/**
*
* @param v0
* @param v1
* @param v2
*/
private function initVertices(v0:Vector3, v1:Vector3, v2:Vector3):void {
this.v0.vCopy(v0);
this.v1.vCopy(v1);
this.v2.vCopy(v2);
e0.vDiff(v1, v0);
// len0 = e0.vLength();
e0.vNormalize();
e1.vDiff(v2, v1);
// len1 = e1.vLength();
e1.vNormalize();
e2.vDiff(v0, v2);
// len2 = e2.vLength();
e2.vNormalize();
}
}
}

View File

@@ -0,0 +1,178 @@
package alternativa.physics.collision.primitives {
import alternativa.physics.altphysics;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Ориентированный бокс.
*/
public class CollisionBox extends CollisionPrimitive {
// Половинные размеры вдоль каждой из осей
public var hs:Vector3 = new Vector3();
/**
* @param hs
* @param collisionGroup
*/
public function CollisionBox(hs:Vector3, collisionGroup:int) {
super(BOX, collisionGroup);
this.hs.vCopy(hs);
}
/**
* @return
*/
override public function calculateAABB():BoundBox {
var t:Matrix4 = transform;
aabb.maxX = hs.x*(t.a < 0 ? -t.a : t.a) + hs.y*(t.b < 0 ? -t.b : t.b) + hs.z*(t.c < 0 ? -t.c : t.c);
aabb.minX = -aabb.maxX;
aabb.maxY = hs.x*(t.e < 0 ? -t.e : t.e) + hs.y*(t.f < 0 ? -t.f : t.f) + hs.z*(t.g < 0 ? -t.g : t.g);
aabb.minY = -aabb.maxY;
aabb.maxZ = hs.x*(t.i < 0 ? -t.i : t.i) + hs.y*(t.j < 0 ? -t.j : t.j) + hs.z*(t.k < 0 ? -t.k : t.k);
aabb.minZ = -aabb.maxZ;
aabb.minX += t.d;
aabb.maxX += t.d;
aabb.minY += t.h;
aabb.maxY += t.h;
aabb.minZ += t.l;
aabb.maxZ += t.l;
return aabb;
}
/**
* @param source
* @return
*/
override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
var box:CollisionBox = source as CollisionBox;
if (box == null) return this;
super.copyFrom(box);
hs.vCopy(box.hs);
return this;
}
/**
* @return
*/
override protected function createPrimitive():CollisionPrimitive {
return new CollisionBox(hs, collisionGroup);
}
/**
* Вычисляет пересечение луча с примитивом.
*
* @param origin начальная точка луча в мировых координатах
* @param vector направляющий вектор луча в мировых координатах. Вектор может быть любой отличной от нуля длины.
* @param epsilon погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную погрешность, считается равной нулю.
* @param normal если пересечение существует, в этот параметр записывается нормаль к примитиву в точке пересечения
* @return если пересечение существует, возвращается неотрицательное время точки пересечения, в противном случае возвращается -1.
*/
override public function getRayIntersection(origin:Vector3, vector:Vector3, epsilon:Number, normal:Vector3):Number {
var tMin:Number = -1;
var tMax:Number = 1e308;
var t1:Number;
var t2:Number;
// Перевод параметров сегмента в систему координат примитива
// Inlined transform.transformVectorInverse(origin, _o);
var vx:Number = origin.x - transform.d;
var vy:Number = origin.y - transform.h;
var vz:Number = origin.z - transform.l;
var ox:Number = transform.a*vx + transform.e*vy + transform.i*vz;
var oy:Number = transform.b*vx + transform.f*vy + transform.j*vz;
var oz:Number = transform.c*vx + transform.g*vy + transform.k*vz;
// Inlined transform.deltaTransformVectorInverse(vector, _v);
vx = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
vy = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
vz = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
// X
if (vx < epsilon && vx > -epsilon) {
if (ox < -hs.x || ox > hs.x) return -1;
} else {
t1 = (-hs.x - ox)/vx;
t2 = (hs.x - ox)/vx;
if (t1 < t2) {
if (t1 > tMin) {
tMin = t1;
normal.x = -1;
normal.y = normal.z = 0;
}
if (t2 < tMax) tMax = t2;
} else {
if (t2 > tMin) {
tMin = t2;
normal.x = 1;
normal.y = normal.z = 0;
}
if (t1 < tMax) tMax = t1;
}
if (tMax < tMin) return -1;
}
// Y
if (vy < epsilon && vy > -epsilon) {
if (oy < -hs.y || oy > hs.y) return -1;
} else {
t1 = (-hs.y - oy)/vy;
t2 = (hs.y - oy)/vy;
if (t1 < t2) {
if (t1 > tMin) {
tMin = t1;
normal.y = -1;
normal.x = normal.z = 0;
}
if (t2 < tMax) tMax = t2;
} else {
if (t2 > tMin) {
tMin = t2;
normal.y = 1;
normal.x = normal.z = 0;
}
if (t1 < tMax) tMax = t1;
}
if (tMax < tMin) return -1;
}
// Z
if (vz < epsilon && vz > -epsilon) {
if (oz < -hs.z || oz > hs.z) return -1;
} else {
t1 = (-hs.z - oz)/vz;
t2 = (hs.z - oz)/vz;
if (t1 < t2) {
if (t1 > tMin) {
tMin = t1;
normal.z = -1;
normal.x = normal.y = 0;
}
if (t2 < tMax) tMax = t2;
} else {
if (t2 > tMin) {
tMin = t2;
normal.z = 1;
normal.x = normal.y = 0;
}
if (t1 < tMax) tMax = t1;
}
if (tMax < tMin) return -1;
}
// Перевод нормали в мировую систему координат
// Inlined normal.vDeltaTransformBy4(transform);
vx = normal.x;
vy = normal.y;
vz = normal.z;
normal.x = transform.a*vx + transform.b*vy + transform.c*vz;
normal.y = transform.e*vx + transform.f*vy + transform.g*vz;
normal.z = transform.i*vx + transform.j*vy + transform.k*vz;
return tMin;
}
}
}

View File

@@ -0,0 +1,143 @@
package alternativa.physics.collision.primitives {
import alternativa.physics.collision.ICollisionPredicate;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.rigid.Body;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
/**
* Базовый класс для примитивов, использующихся детектором столкновений.
*/
public class CollisionPrimitive {
// Константы типов примитива
public static const BOX:int = 1;
public static const PLANE:int = 2;
public static const SPHERE:int = 3;
public static const RECT:int = 4;
public static const TRIANGLE:int = 5;
// Тип примитива
public var type:int;
// Группы примитива. Каждая группа определяется установленным битом. Столкновения проверяются только для примитивов,
// имеющих хотя бы одну общую группу.
public var collisionGroup:int;
// Предикат примитива, вызывающийся детектором при нахождении столкновения. В зависимости от возвращённого
// предикатом результата столкновение либо регистрируется, либо игнорируется.
// Nullable
public var postCollisionPredicate:ICollisionPredicate;
// Тело, владеющее примитивом. Поле сделано открытым для быстрого доступа на чтение. Установка значения должна выполняться вызовом метода setBody().
// Nullable
public var body:Body;
// Трансформация примитива в системе координат тела, если оно указано. Не допускается масштабирование матрицы.
public var localTransform:Matrix4;
// Полная трансформация примитива. Не допускается масштабирование матрицы.
public var transform:Matrix4 = new Matrix4();
// AABB в мировой системе координат. Расчитывается системой вызовом функции calculateBoundBox().
public var aabb:BoundBox = new BoundBox();
/**
* Создаёт новый экземпляр примитива.
*
* @param type тип примитива
* @param collisionGroup группа примитива
*/
public function CollisionPrimitive(type:int, collisionGroup:int) {
this.type = type;
this.collisionGroup = collisionGroup;
}
/**
* Устанавливает тело, владеющее примитивом.
*
* @param body тело, которое владеет примитивом. Если указано значение null, матрица локальной трансформации удаляется.
* @param localTransform трансформация примитива в системе координат тела. Указание значения null равносильно
* заданию единичной матрицы, однако в первом случае не будет дополнительного умножения матриц при вычислении полной трансформации примитива.
*/
public function setBody(body:Body, localTransform:Matrix4 = null):void {
if (this.body == body) return;
this.body = body;
if (body == null) {
this.localTransform = null;
} else {
if (localTransform != null) {
if (this.localTransform == null) {
this.localTransform = new Matrix4();
}
this.localTransform.copy(localTransform);
} else {
this.localTransform = null;
}
}
}
/**
* Рассчитывает AABB примитива. Наследники должны переопределять этот метод, реализуя в нём корректный рассчёт.
*
* @return ссылка на свой AABB
*/
public function calculateAABB():BoundBox {
return aabb;
}
/**
* Вычисляет пересечение луча с примитивом.
*
* @param origin начальная точка луча в мировых координатах
* @param vector направляющий вектор луча в мировых координатах. Вектор может быть любой отличной от нуля длины.
* @param epsilon погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную погрешность, считается равной нулю.
* @param normal если пересечение существует, в этот параметр записывается нормаль к примитиву в точке пересечения
* @return если пересечение существует, возвращается неотрицательное время точки пересечения, в противном случае возвращается -1.
*/
public function getRayIntersection(origin:Vector3, vector:Vector3, epsilon:Number, normal:Vector3):Number {
return -1;
}
/**
* Клонирует примитив. Переопределять не рекомендуется. Вместо этого переопределяются методы createPrimitive() и copyFrom().
*
* @return клон примитива
*/
public function clone():CollisionPrimitive {
var p:CollisionPrimitive = createPrimitive();
return p.copyFrom(this);
}
/**
* Копирует параметры указанного примитива. Объекты копируются по значению.
*
* @param source примитив, чьи параметры копируются
* @return this
*/
public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
if (source == null) {
throw new ArgumentError("Parameter source cannot be null");
}
type = source.type;
transform.copy(source.transform);
collisionGroup = source.collisionGroup;
setBody(source.body, source.localTransform);
aabb.copyFrom(source.aabb);
return this;
}
/**
* Создаёт строковое представление объекта.
*
* @return строковое представление объекта
*/
public function toString():String {
return "[CollisionPrimitive type=" + type + "]";
}
/**
* Создаёт новый экземпляр примитива соответствующего типа.
*
* @return новый экземпляр примитива
*/
protected function createPrimitive():CollisionPrimitive {
return new CollisionPrimitive(type, collisionGroup);
}
}
}

View File

@@ -0,0 +1,123 @@
package alternativa.physics.collision.primitives {
import alternativa.physics.altphysics;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Ориентированный прямоугольник. Задаётся половинами размеров вдоль осей X (ширина) и Y (длина) локальной системы
* координат примитива. Таким образом, прямоугольник лежит в плоскости XY, его стороны параллельны осям этой
* плоскости, а нормаль направлена вдоль локальной оси Z.
*
* Прямоугольник может быть одно- или двусторонним. В случае одностороннего прямоугольника,
* столкновения с внутренней стороны не регистрируются.
*/
public class CollisionRect extends CollisionPrimitive {
// Половинные размеры прямоугольника вдоль осей X и Y. Нормаль направлена вдоль оси Z.
public var hs:Vector3 = new Vector3();
// Флаг указывает, является примитив одно- или двусторонним
public var twoSided:Boolean = true;
// Малое значение. Используется для указания фиктивной высоты примитива и в функции определения пересечения с сегментом.
private static const EPSILON:Number = 0.005;
/**
* Создаёт новый экземпляр примитива.
*
* @param hs половинные размерв прямоугольника вдоль осей X и Y. Значение z игнорируется.
* @param collisionGroup группа примитива
*/
public function CollisionRect(hs:Vector3, collisionGroup:int) {
super(RECT, collisionGroup);
this.hs.vCopy(hs);
}
/**
* Расчитывает ограничивающий бокс прямоугольника. Для избежания проблем высота примитива принимается равной
* не нулю, а малому значению.
*
* @return
*/
override public function calculateAABB():BoundBox {
// Баунд бокс прямоугольника имеет минимальную высоту, отличную от нуля во избежание проблем с построением kd-дерева
var t:Matrix4 = transform;
aabb.maxX = hs.x*(t.a < 0 ? -t.a : t.a) + hs.y*(t.b < 0 ? -t.b : t.b) + EPSILON*(t.c < 0 ? -t.c : t.c);
aabb.minX = -aabb.maxX;
aabb.maxY = hs.x*(t.e < 0 ? -t.e : t.e) + hs.y*(t.f < 0 ? -t.f : t.f) + EPSILON*(t.g < 0 ? -t.g : t.g);
aabb.minY = -aabb.maxY;
aabb.maxZ = hs.x*(t.i < 0 ? -t.i : t.i) + hs.y*(t.j < 0 ? -t.j : t.j) + EPSILON*(t.k < 0 ? -t.k : t.k);
aabb.minZ = -aabb.maxZ;
aabb.minX += t.d;
aabb.maxX += t.d;
aabb.minY += t.h;
aabb.maxY += t.h;
aabb.minZ += t.l;
aabb.maxZ += t.l;
return aabb;
}
/**
* @param source
* @return
*/
override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
var rect:CollisionRect = source as CollisionRect;
if (rect == null) return this;
super.copyFrom(rect);
hs.vCopy(rect.hs);
return this;
}
/**
* @return
*/
override protected function createPrimitive():CollisionPrimitive {
return new CollisionRect(hs, collisionGroup);
}
/**
* @param origin
* @param vector
* @param collisionGroup
* @param threshold
* @param normal
* @return
*/
override public function getRayIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
// Перевод параметров сегмента в систему координат примитива
// Inlined transform.transformVectorInverse(origin, _o);
var vx:Number = origin.x - transform.d;
var vy:Number = origin.y - transform.h;
var vz:Number = origin.z - transform.l;
var ox:Number = transform.a*vx + transform.e*vy + transform.i*vz;
var oy:Number = transform.b*vx + transform.f*vy + transform.j*vz;
var oz:Number = transform.c*vx + transform.g*vy + transform.k*vz;
// Inlined transform.deltaTransformVectorInverse(vector, _v);
vx = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
vy = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
vz = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
// Проверка параллельности сегмента и плоскости примитива
if (vz > -threshold && vz < threshold) return -1;
var t:Number = -oz/vz;
if (t < 0) return -1;
// Проверка вхождения точки пересечения в прямоугольник
ox += vx*t;
oy += vy*t;
oz = 0;
if (ox < (-hs.x - threshold) || ox > (hs.x + threshold) || oy < (-hs.y - threshold) || oy > (hs.y + threshold)) return -1;
normal.x = transform.c;
normal.y = transform.g;
normal.z = transform.k;
return t;
}
}
}

View File

@@ -0,0 +1,80 @@
package alternativa.physics.collision.primitives {
import alternativa.physics.altphysics;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Сфера.
*/
public class CollisionSphere extends CollisionPrimitive {
// Радиус сферы
public var r:Number = 0;
/**
*
* @param r
* @param collisionGroup
*/
public function CollisionSphere(r:Number, collisionGroup:int) {
super(SPHERE, collisionGroup);
this.r = r;
}
/**
* @return
*/
override public function calculateAABB():BoundBox {
aabb.maxX = transform.d + r;
aabb.minX = transform.d - r;
aabb.maxY = transform.h + r;
aabb.minY = transform.h - r;
aabb.maxZ = transform.l + r;
aabb.minZ = transform.l - r;
return aabb;
}
/**
* @param origin
* @param vector
* @param threshold
* @param normal
* @return
*/
override public function getRayIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
var px:Number = origin.x - transform.d;
var py:Number = origin.y - transform.h;
var pz:Number = origin.z - transform.l;
var k:Number = vector.x*px + vector.y*py + vector.z*pz;
if (k > 0) return -1;
var a:Number = vector.x*vector.x + vector.y*vector.y + vector.z*vector.z;
var D:Number = k*k - a*(px*px + py*py + pz*pz - r*r);
if (D < 0) return -1;
return -(k + Math.sqrt(D))/a;
}
/**
* @param source
* @return
*/
override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
var sphere:CollisionSphere = source as CollisionSphere;
if (sphere == null) return this;
super.copyFrom(sphere);
r = sphere.r;
return this;
}
/**
* @return
*/
override protected function createPrimitive():CollisionPrimitive {
return new CollisionSphere(r, collisionGroup);
}
}
}

View File

@@ -0,0 +1,243 @@
package alternativa.physics.collision.primitives {
import alternativa.physics.altphysics;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
* Примитив треугольник.
*/
public class CollisionTriangle extends CollisionPrimitive {
public var v0:Vector3 = new Vector3();
public var v1:Vector3 = new Vector3();
public var v2:Vector3 = new Vector3();
public var e0:Vector3 = new Vector3();
public var e1:Vector3 = new Vector3();
public var e2:Vector3 = new Vector3();
// public var len0:Number;
// public var len1:Number;
// public var len2:Number;
//
/**
*
* @param v0
* @param v1
* @param v2
* @param collisionGroup
*/
public function CollisionTriangle(v0:Vector3, v1:Vector3, v2:Vector3, collisionGroup:int) {
super(TRIANGLE, collisionGroup);
initVertices(v0, v1, v2);
}
/**
* Рассчитывает AABB примитива.
*
* @return ссылка на свой AABB
*/
override public function calculateAABB():BoundBox {
var epsilon:Number = 0.005;
var a:Number;
var b:Number;
var eps_c:Number = epsilon*transform.c;
var eps_g:Number = epsilon*transform.g;
var eps_k:Number = epsilon*transform.k;
// Вершина 0
// Ось X
a = v0.x*transform.a + v0.y*transform.b;
aabb.minX = aabb.maxX = a + eps_c;
b = a - eps_c;
if (b > aabb.maxX) aabb.maxX = b;
else if (b < aabb.minX) aabb.minX = b;
// Ось Y
a = v0.x*transform.e + v0.y*transform.f;
aabb.minY = aabb.maxY = a + eps_g;
b = a - eps_g;
if (b > aabb.maxY) aabb.maxY = b;
else if (b < aabb.minY) aabb.minY = b;
// Ось Z
a = v0.x*transform.i + v0.y*transform.j;
aabb.minZ = aabb.maxZ = a + eps_k;
b = a - eps_k;
if (b > aabb.maxZ) aabb.maxZ = b;
else if (b < aabb.minZ) aabb.minZ = b;
// Вершина 1
// Ось X
a = v1.x*transform.a + v1.y*transform.b;
b = a + eps_c;
if (b > aabb.maxX) aabb.maxX = b;
else if (b < aabb.minX) aabb.minX = b;
b = a - eps_c;
if (b > aabb.maxX) aabb.maxX = b;
else if (b < aabb.minX) aabb.minX = b;
// Ось Y
a = v1.x*transform.e + v1.y*transform.f;
b = a + eps_g;
if (b > aabb.maxY) aabb.maxY = b;
else if (b < aabb.minY) aabb.minY = b;
b = a - eps_g;
if (b > aabb.maxY) aabb.maxY = b;
else if (b < aabb.minY) aabb.minY = b;
// Ось Z
a = v1.x*transform.i + v1.y*transform.j;
b = a + eps_k;
if (b > aabb.maxZ) aabb.maxZ = b;
else if (b < aabb.minZ) aabb.minZ = b;
b = a - eps_k;
if (b > aabb.maxZ) aabb.maxZ = b;
else if (b < aabb.minZ) aabb.minZ = b;
// Вершина 2
// Ось X
a = v2.x*transform.a + v2.y*transform.b;
b = a + eps_c;
if (b > aabb.maxX) aabb.maxX = b;
else if (b < aabb.minX) aabb.minX = b;
b = a - eps_c;
if (b > aabb.maxX) aabb.maxX = b;
else if (b < aabb.minX) aabb.minX = b;
// Ось Y
a = v2.x*transform.e + v2.y*transform.f;
b = a + eps_g;
if (b > aabb.maxY) aabb.maxY = b;
else if (b < aabb.minY) aabb.minY = b;
b = a - eps_g;
if (b > aabb.maxY) aabb.maxY = b;
else if (b < aabb.minY) aabb.minY = b;
// Ось Z
a = v2.x*transform.i + v2.y*transform.j;
b = a + eps_k;
if (b > aabb.maxZ) aabb.maxZ = b;
else if (b < aabb.minZ) aabb.minZ = b;
b = a - eps_k;
if (b > aabb.maxZ) aabb.maxZ = b;
else if (b < aabb.minZ) aabb.minZ = b;
aabb.minX += transform.d;
aabb.maxX += transform.d;
aabb.minY += transform.h;
aabb.maxY += transform.h;
aabb.minZ += transform.l;
aabb.maxZ += transform.l;
return aabb;
}
/**
* Вычисляет пересечение луча с примитивом.
*
* @param origin начальная точка луча в мировых координатах
* @param vector направляющий вектор луча в мировых координатах. Вектор может быть любой отличной от нуля длины.
* @param epsilon погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную погрешность, считается равной нулю.
* @param normal если пересечение существует, в этот параметр записывается нормаль к примитиву в точке пересечения
* @return если пересечение существует, возвращается неотрицательное время точки пересечения, в противном случае возвращается -1.
*/
override public function getRayIntersection(origin:Vector3, vector:Vector3, epsilon:Number, normal:Vector3):Number {
// Луч трансформируется в систему координат примитива, затем проверяется пересечение
var vz:Number = vector.x*transform.c + vector.y*transform.g + vector.z*transform.k;
// Если луч параллелен плоскости птимитива, то пересечения нет
if (vz < epsilon && vz > -epsilon) return -1;
var tx:Number = origin.x - transform.d;
var ty:Number = origin.y - transform.h;
var tz:Number = origin.z - transform.l;
var oz:Number = tx*transform.c + ty*transform.g + tz*transform.k;
var t:Number = -oz/vz;
if (t < 0) return -1;
var ox:Number = tx*transform.a + ty*transform.e + tz*transform.i;
var oy:Number = tx*transform.b + ty*transform.f + tz*transform.j;
tx = ox + t*(vector.x*transform.a + vector.y*transform.e + vector.z*transform.i);
ty = oy + t*(vector.x*transform.b + vector.y*transform.f + vector.z*transform.j);
tz = oz + t*vz;
// Проверка вхождения точки в треугольник
if ((e0.x*(ty - v0.y) - e0.y*(tx - v0.x) < 0) || (e1.x*(ty - v1.y) - e1.y*(tx - v1.x) < 0) || (e2.x*(ty - v2.y) - e2.y*(tx - v2.x) < 0)) return -1;
// Запись нормали
normal.x = transform.c;
normal.y = transform.g;
normal.z = transform.k;
return t;
}
/**
* Копирует параметры указанного примитива. Объекты копируются по значению.
*
* @param source примитив, чьи параметры копируются
* @return this
*/
override public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
super.copyFrom(source);
var tri:CollisionTriangle = source as CollisionTriangle;
if (tri != null) {
v0.vCopy(tri.v0);
v1.vCopy(tri.v1);
v2.vCopy(tri.v2);
e0.vCopy(tri.e0);
e1.vCopy(tri.e1);
e2.vCopy(tri.e2);
// len0 = tri.len0;
// len1 = tri.len1;
// len2 = tri.len2;
}
return this;
}
/**
* Создаёт строковое представление объекта.
*
* @return строковое представление объекта
*/
override public function toString():String {
return "[CollisionTriangle v0=" + v0 + ", v1=" + v1 + ", v2=" + v2 + "]";
}
/**
* Создаёт новый экземпляр примитива соответствующего типа.
*
* @return новый экземпляр примитива
*/
override protected function createPrimitive():CollisionPrimitive {
return new CollisionTriangle(v0, v1, v2, collisionGroup);
}
/**
*
* @param v0
* @param v1
* @param v2
*/
private function initVertices(v0:Vector3, v1:Vector3, v2:Vector3):void {
this.v0.vCopy(v0);
this.v1.vCopy(v1);
this.v2.vCopy(v2);
e0.vDiff(v1, v0);
// len0 = e0.vLength();
e0.vNormalize();
e1.vDiff(v2, v1);
// len1 = e1.vLength();
e1.vNormalize();
e2.vDiff(v0, v2);
// len2 = e2.vLength();
e2.vNormalize();
}
}
}

View File

@@ -0,0 +1,23 @@
K 25
svn:wc:ra_dav:version-url
V 131
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/types
END
BoundBox.as
K 25
svn:wc:ra_dav:version-url
V 143
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/types/BoundBox.as
END
RayIntersection.as
K 25
svn:wc:ra_dav:version-url
V 150
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/types/RayIntersection.as
END
Ray.as
K 25
svn:wc:ra_dav:version-url
V 138
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/types/Ray.as
END

View File

@@ -0,0 +1,64 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/collision/types
http://svndev.alternativaplatform.com
2009-09-09T14:02:48.134744Z
19633
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
BoundBox.as
file
2010-10-28T04:32:38.000000Z
d92d4ab3dde698feb674fb9488f59678
2009-09-09T14:02:48.134744Z
19633
mike
RayIntersection.as
file
2010-10-28T04:32:38.000000Z
a0a4cf3dd94468fdf44cd06734927c94
2009-06-08T06:46:18.310732Z
14099
mike
Ray.as
file
2010-10-28T04:32:38.000000Z
55eeb1cf79a416bb863bee95f089dbcd
2009-04-17T17:39:21.367569Z
11341
mike

View File

@@ -0,0 +1,75 @@
package alternativa.physics.collision.types {
/**
* Ограничивающий параллелепипед.
*/
public class BoundBox {
public var minX:Number = 1e308;
public var minY:Number = 1e308;
public var minZ:Number = 1e308;
public var maxX:Number = -1e308;
public var maxY:Number = -1e308;
public var maxZ:Number = -1e308;
public function setSize(minX:Number, minY:Number, minZ:Number, maxX:Number, maxY:Number, maxZ:Number):void {
this.minX = minX;
this.minY = minY;
this.minZ = minZ;
this.maxX = maxX;
this.maxY = maxY;
this.maxZ = maxZ;
}
public function addBoundBox(boundBox:BoundBox):void {
minX = (boundBox.minX < minX) ? boundBox.minX : minX;
minY = (boundBox.minY < minY) ? boundBox.minY : minY;
minZ = (boundBox.minZ < minZ) ? boundBox.minZ : minZ;
maxX = (boundBox.maxX > maxX) ? boundBox.maxX : maxX;
maxY = (boundBox.maxY > maxY) ? boundBox.maxY : maxY;
maxZ = (boundBox.maxZ > maxZ) ? boundBox.maxZ : maxZ;
}
public function addPoint(x:Number, y:Number, z:Number):void {
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
if (z < minZ) minZ = z;
if (z > maxZ) maxZ = z;
}
public function infinity():void {
minX = 1e308;
minY = 1e308;
minZ = 1e308;
maxX = -1e308;
maxY = -1e308;
maxZ = -1e308;
}
public function intersects(bb:BoundBox, epsilon:Number):Boolean {
return !(minX > bb.maxX + epsilon || maxX < bb.minX - epsilon || minY > bb.maxY + epsilon || maxY < bb.minY - epsilon || minZ > bb.maxZ + epsilon || maxZ < bb.minZ - epsilon);
}
public function copyFrom(boundBox:BoundBox):void {
minX = boundBox.minX;
minY = boundBox.minY;
minZ = boundBox.minZ;
maxX = boundBox.maxX;
maxY = boundBox.maxY;
maxZ = boundBox.maxZ;
}
public function clone():BoundBox {
var clone:BoundBox = new BoundBox();
clone.copyFrom(this);
return clone;
}
public function toString():String {
return "BoundBox [" + minX + ", " + minY + ", " + minZ + " : " + maxX + ", " + maxY + ", " + maxZ + "]";
}
}
}

View File

@@ -0,0 +1,13 @@
package alternativa.physics.collision.types {
import alternativa.physics.types.Vector3;
/**
* Структура, описывающая луч и сегмент прямой в пространстве.
*/
public class Ray {
// Начало луча. Начальная точка сегмента на прямой луча.
public var origin:Vector3 = new Vector3();
// Направляющий вектор луча. Длина вектора задаёт длину сегмента на луче. Конечная точка сегмента вычисляется как origin + dir.
public var dir:Vector3 = new Vector3();
}
}

View File

@@ -0,0 +1,25 @@
package alternativa.physics.collision.types {
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.types.Vector3;
/**
* Структура описывает точку пересечения луча с физической геометрией.
*/
public class RayIntersection {
// Физический примитив, с которым пересекается луч
public var primitive:CollisionPrimitive;
// Положенеи точки пересечения
public var pos:Vector3 = new Vector3();
// Нормаль поверхности физического примитива в точке пересечения
public var normal:Vector3 = new Vector3();
// Время пересечения (|pos - ray.origin|/|ray.dir|)
public var t:Number = 0;
public function copy(source:RayIntersection):void {
primitive = source.primitive;
pos.vCopy(source.pos);
normal.vCopy(source.normal);
t = source.t;
}
}
}

View File

@@ -0,0 +1,75 @@
package alternativa.physics.collision.types {
/**
* Ограничивающий параллелепипед.
*/
public class BoundBox {
public var minX:Number = 1e308;
public var minY:Number = 1e308;
public var minZ:Number = 1e308;
public var maxX:Number = -1e308;
public var maxY:Number = -1e308;
public var maxZ:Number = -1e308;
public function setSize(minX:Number, minY:Number, minZ:Number, maxX:Number, maxY:Number, maxZ:Number):void {
this.minX = minX;
this.minY = minY;
this.minZ = minZ;
this.maxX = maxX;
this.maxY = maxY;
this.maxZ = maxZ;
}
public function addBoundBox(boundBox:BoundBox):void {
minX = (boundBox.minX < minX) ? boundBox.minX : minX;
minY = (boundBox.minY < minY) ? boundBox.minY : minY;
minZ = (boundBox.minZ < minZ) ? boundBox.minZ : minZ;
maxX = (boundBox.maxX > maxX) ? boundBox.maxX : maxX;
maxY = (boundBox.maxY > maxY) ? boundBox.maxY : maxY;
maxZ = (boundBox.maxZ > maxZ) ? boundBox.maxZ : maxZ;
}
public function addPoint(x:Number, y:Number, z:Number):void {
if (x < minX) minX = x;
if (x > maxX) maxX = x;
if (y < minY) minY = y;
if (y > maxY) maxY = y;
if (z < minZ) minZ = z;
if (z > maxZ) maxZ = z;
}
public function infinity():void {
minX = 1e308;
minY = 1e308;
minZ = 1e308;
maxX = -1e308;
maxY = -1e308;
maxZ = -1e308;
}
public function intersects(bb:BoundBox, epsilon:Number):Boolean {
return !(minX > bb.maxX + epsilon || maxX < bb.minX - epsilon || minY > bb.maxY + epsilon || maxY < bb.minY - epsilon || minZ > bb.maxZ + epsilon || maxZ < bb.minZ - epsilon);
}
public function copyFrom(boundBox:BoundBox):void {
minX = boundBox.minX;
minY = boundBox.minY;
minZ = boundBox.minZ;
maxX = boundBox.maxX;
maxY = boundBox.maxY;
maxZ = boundBox.maxZ;
}
public function clone():BoundBox {
var clone:BoundBox = new BoundBox();
clone.copyFrom(this);
return clone;
}
public function toString():String {
return "BoundBox [" + minX + ", " + minY + ", " + minZ + " : " + maxX + ", " + maxY + ", " + maxZ + "]";
}
}
}

View File

@@ -0,0 +1,13 @@
package alternativa.physics.collision.types {
import alternativa.physics.types.Vector3;
/**
* Структура, описывающая луч и сегмент прямой в пространстве.
*/
public class Ray {
// Начало луча. Начальная точка сегмента на прямой луча.
public var origin:Vector3 = new Vector3();
// Направляющий вектор луча. Длина вектора задаёт длину сегмента на луче. Конечная точка сегмента вычисляется как origin + dir.
public var dir:Vector3 = new Vector3();
}
}

View File

@@ -0,0 +1,25 @@
package alternativa.physics.collision.types {
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.types.Vector3;
/**
* Структура описывает точку пересечения луча с физической геометрией.
*/
public class RayIntersection {
// Физический примитив, с которым пересекается луч
public var primitive:CollisionPrimitive;
// Положенеи точки пересечения
public var pos:Vector3 = new Vector3();
// Нормаль поверхности физического примитива в точке пересечения
public var normal:Vector3 = new Vector3();
// Время пересечения (|pos - ray.origin|/|ray.dir|)
public var t:Number = 0;
public function copy(source:RayIntersection):void {
primitive = source.primitive;
pos.vCopy(source.pos);
normal.vCopy(source.normal);
t = source.t;
}
}
}

View File

@@ -0,0 +1,47 @@
K 25
svn:wc:ra_dav:version-url
V 121
/!svn/ver/19850/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/rigid
END
Body.as
K 25
svn:wc:ra_dav:version-url
V 129
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/rigid/Body.as
END
BodyState.as
K 25
svn:wc:ra_dav:version-url
V 134
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/rigid/BodyState.as
END
PhysicsUtils.as
K 25
svn:wc:ra_dav:version-url
V 137
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/rigid/PhysicsUtils.as
END
ContactPoint.as
K 25
svn:wc:ra_dav:version-url
V 137
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/rigid/ContactPoint.as
END
RigidWorld.as
K 25
svn:wc:ra_dav:version-url
V 135
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/rigid/RigidWorld.as
END
Contact.as
K 25
svn:wc:ra_dav:version-url
V 132
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/rigid/Contact.as
END
BodyMaterial.as
K 25
svn:wc:ra_dav:version-url
V 137
/!svn/ver/19806/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/rigid/BodyMaterial.as
END

View File

@@ -0,0 +1,118 @@
8
dir
46043
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized/src/alternativa/physics/rigid
http://svndev.alternativaplatform.com
2009-09-14T12:08:58.450611Z
19850
mike
svn:special svn:externals svn:needs-lock
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
Body.as
file
2010-10-28T04:32:38.000000Z
23bad2eff16ce73dccb56a7a601cc3c0
2009-06-29T14:07:24.833358Z
15390
mike
BodyState.as
file
2010-10-28T04:32:38.000000Z
8a089205beccac13a05eccfb9cfc6387
2009-06-08T06:46:18.310732Z
14099
mike
PhysicsUtils.as
file
2010-10-28T04:32:38.000000Z
6d88004ddab2ae19e0d15f5b1b7c016a
2009-04-26T21:40:17.813142Z
12023
mike
constraints
dir
ContactPoint.as
file
2010-10-28T04:32:38.000000Z
a94e73e3bfc85be2a79d6cc939512d91
2009-06-08T06:46:18.310732Z
14099
mike
RigidWorld.as
file
2010-10-28T04:32:38.000000Z
b0b528a79a6fce55e5d800a15dc99534
2009-07-03T12:54:42.818772Z
15744
mike
primitives
dir
Contact.as
file
2010-10-28T04:32:38.000000Z
d65ad3d488d4a06addcbfa4aaf958f10
2009-04-13T03:04:08.898183Z
11018
mike
BodyMaterial.as
file
2010-10-28T04:32:38.000000Z
49c7a55b5b26fdb03a60f24bac460f53
2009-04-07T08:34:06.342994Z
10587
mike

View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,340 @@
package alternativa.physics.rigid {
import __AS3__.vec.Vector;
import alternativa.physics.altphysics;
import alternativa.physics.collision.IBodyCollisionPredicate;
import alternativa.physics.collision.primitives.CollisionPrimitive;
import alternativa.physics.collision.types.BoundBox;
import alternativa.physics.types.Matrix3;
import alternativa.physics.types.Matrix4;
import alternativa.physics.types.Quaternion;
import alternativa.physics.types.Vector3;
use namespace altphysics;
/**
*
*/
public class Body {
public static var linDamping:Number = 0.997;
public static var rotDamping:Number = 0.997;
public var name:String;
public var movable:Boolean = true;
public var canFreeze:Boolean = false;
public var freezeCounter:int;
public var frozen:Boolean = false;
public var aabb:BoundBox = new BoundBox();
public var postCollisionPredicate:IBodyCollisionPredicate;
altphysics var id:int;
// Мир, в котором находится тело
altphysics var world:RigidWorld;
// Текущее и предыдущее состояние тела. Промежуточное состояние вычисляется линейной интерполяцией.
altphysics var state:BodyState = new BodyState();
altphysics var prevState:BodyState = new BodyState();
// Линейное и угловое ускорение тела на текущем шаге симуляции
altphysics var accel:Vector3 = new Vector3();
altphysics var angleAccel:Vector3 = new Vector3();
// Материал тела
altphysics var material:BodyMaterial = new BodyMaterial();
altphysics var invMass:Number = 1;
altphysics var invInertia:Matrix3 = new Matrix3();
altphysics var invInertiaWorld:Matrix3 = new Matrix3();
altphysics var baseMatrix:Matrix3 = new Matrix3();
altphysics const MAX_CONTACTS:int = 20;
altphysics var contacts:Vector.<Contact> = new Vector.<Contact>(MAX_CONTACTS);
altphysics var contactsNum:int;
altphysics var collisionPrimitives:Vector.<CollisionPrimitive>;
altphysics var collisionPrimitivesNum:int;
// Аккумулятор сил
altphysics var forceAccum:Vector3 = new Vector3();
// Аккумулятор моментов
altphysics var torqueAccum:Vector3 = new Vector3();
// Внутренние переменные для избежания создания экземпляров
private static var _r:Vector3 = new Vector3();
private static var _f:Vector3 = new Vector3();
/**
*
* @param invMass
* @param invInertia
*/
public function Body(invMass:Number, invInertia:Matrix3) {
this.invMass = invMass;
this.invInertia.copy(invInertia);
}
/**
* @param primitive
* @param localTransform
*/
public function addCollisionPrimitive(primitive:CollisionPrimitive, localTransform:Matrix4 = null):void {
if (primitive == null) throw new ArgumentError("Primitive cannot be null");
if (collisionPrimitives == null) collisionPrimitives = new Vector.<CollisionPrimitive>();
collisionPrimitives[collisionPrimitivesNum++] = primitive;
primitive.setBody(this, localTransform);
}
/**
* @param t
* @param result
*/
public function interpolate(t:Number, pos:Vector3, orientation:Quaternion):void {
var t1:Number = 1 - t;
pos.x = prevState.pos.x*t1 + state.pos.x*t;
pos.y = prevState.pos.y*t1 + state.pos.y*t;
pos.z = prevState.pos.z*t1 + state.pos.z*t;
orientation.w = prevState.orientation.w*t1 + state.orientation.w*t;
orientation.x = prevState.orientation.x*t1 + state.orientation.x*t;
orientation.y = prevState.orientation.y*t1 + state.orientation.y*t;
orientation.z = prevState.orientation.z*t1 + state.orientation.z*t;
}
/**
*
* @param pos
*/
public function setPosition(pos:Vector3):void {
state.pos.vCopy(pos);
}
/**
*
* @param x
* @param y
* @param z
*/
public function setPositionXYZ(x:Number, y:Number, z:Number):void {
state.pos.vReset(x, y, z);
}
/**
*
* @param vel
*/
public function setVelocity(vel:Vector3):void {
state.velocity.vCopy(vel);
}
/**
*
* @param x
* @param y
* @param z
*/
public function setVelocityXYZ(x:Number, y:Number, z:Number):void {
state.velocity.vReset(x, y, z);
}
/**
*
* @param rot
*/
public function setRotation(rot:Vector3):void {
state.rotation.vCopy(rot);
}
/**
*
* @param x
* @param y
* @param z
*/
public function setRotationXYZ(x:Number, y:Number, z:Number):void {
state.rotation.vReset(x, y, z);
}
/**
*
* @param q
*/
public function setOrientation(q:Quaternion):void {
state.orientation.copy(q);
}
/**
* @param r
* @param dir
* @param magnitude
*/
public function applyRelPosWorldImpulse(r:Vector3, dir:Vector3, magnitude:Number):void {
var d:Number = magnitude*invMass;
// Линейная часть
state.velocity.x += d*dir.x;
state.velocity.y += d*dir.y;
state.velocity.z += d*dir.z;
// Вращательная часть
var rx:Number = r.x;
var ry:Number = r.y;
var rz:Number = r.z;
_r.x = (ry*dir.z - rz*dir.y)*magnitude;
_r.y = (rz*dir.x - rx*dir.z)*magnitude;
_r.z = (rx*dir.y - ry*dir.x)*magnitude;
_r.vTransformBy3(invInertiaWorld);
state.rotation.x += _r.x;
state.rotation.y += _r.y;
state.rotation.z += _r.z;
}
/**
* @param f
*/
public function addForce(f:Vector3):void {
forceAccum.vAdd(f);
}
/**
* @param pos
* @param f
*/
public function addWorldForce(pos:Vector3, force:Vector3):void {
forceAccum.vAdd(force);
torqueAccum.vAdd(_r.vDiff(pos, state.pos).vCross(force));
}
/**
* @param pos
* @param f
*/
public function addWorldForceScaled(pos:Vector3, force:Vector3, scale:Number):void {
_f.x = scale*force.x, _f.y = scale*force.y, _f.z = scale*force.z;
forceAccum.vAdd(_f);
torqueAccum.vAdd(_r.vDiff(pos, state.pos).vCross(_f));
}
/**
* @param pos
* @param f
*/
public function addLocalForce(pos:Vector3, force:Vector3):void {
// Трансформируем точку приложения в мировую систему координат
baseMatrix.transformVector(pos, _r);
// Трансформируем вектор силы в мировую систему
baseMatrix.transformVector(force, _f);
// Добавляем силу и момент
forceAccum.vAdd(_f);
torqueAccum.vAdd(_r.vCross(_f));
}
/**
*
* @param localPos
* @param worldForce
*/
public function addWorldForceAtLocalPoint(localPos:Vector3, worldForce:Vector3):void {
// Трансформируем точку приложения в мировую систему координат
baseMatrix.transformVector(localPos, _r);
// Добавляем силу и момент
forceAccum.vAdd(worldForce);
torqueAccum.vAdd(_r.vCross(worldForce));
}
/**
* @param dt
*/
public function addExternalForces(dt:Number):void {
}
/**
* @param t
*/
public function addTorque(t:Vector3):void {
torqueAccum.vAdd(t);
}
/**
*
*/
altphysics function clearAccumulators():void {
forceAccum.vReset();
torqueAccum.vReset();
}
/**
*
*/
altphysics function calcAccelerations():void {
accel.x = forceAccum.x*invMass;
accel.y = forceAccum.y*invMass;
accel.z = forceAccum.z*invMass;
invInertiaWorld.transformVector(torqueAccum, angleAccel);
}
/**
* Вычисляет производные данные.
*/
altphysics function calcDerivedData():void {
// Вычисление базисной матрицы и обратного тензора инерции в мировых координатах
state.orientation.toMatrix3(baseMatrix);
invInertiaWorld.copy(invInertia).append(baseMatrix).prependTransposed(baseMatrix);
if (collisionPrimitives != null) {
aabb.infinity();
for (var i:int = 0; i < collisionPrimitivesNum; i++) {
var primitive:CollisionPrimitive = collisionPrimitives[i];
primitive.transform.setFromMatrix3(baseMatrix, state.pos);
if (primitive.localTransform != null) primitive.transform.prepend(primitive.localTransform);
primitive.calculateAABB();
aabb.addBoundBox(primitive.aabb);
}
}
}
/**
*
*/
altphysics function saveState():void {
prevState.copy(state);
}
/**
*
*/
altphysics function restoreState():void {
state.copy(prevState);
}
/**
* @param dt
*/
altphysics function integrateVelocity(dt:Number):void {
// v = v + a*t
state.velocity.x += accel.x*dt;
state.velocity.y += accel.y*dt;
state.velocity.z += accel.z*dt;
// rot = rot + eps*t
state.rotation.x += angleAccel.x*dt;
state.rotation.y += angleAccel.y*dt;
state.rotation.z += angleAccel.z*dt;
state.velocity.x *= linDamping;
state.velocity.y *= linDamping;
state.velocity.z *= linDamping;
state.rotation.x *= rotDamping;
state.rotation.y *= rotDamping;
state.rotation.z *= rotDamping;
}
/**
*
*/
altphysics function integratePosition(dt:Number):void {
// pos = pos + v*t
state.pos.x += state.velocity.x*dt;
state.pos.y += state.velocity.y*dt;
state.pos.z += state.velocity.z*dt;
// q = q + 0.5*rot*q
state.orientation.addScaledVector(state.rotation, dt);
}
}
}

View File

@@ -0,0 +1,11 @@
package alternativa.physics.rigid {
public class BodyMaterial {
public var restitution:Number = 0;
public var friction:Number = 0.3;
// public var dynamicFriction:Number = 0.2;
// public var dynamicFriction:Number = 0.2;
}
}

View File

@@ -0,0 +1,39 @@
package alternativa.physics.rigid {
import alternativa.physics.types.Quaternion;
import alternativa.physics.types.Vector3;
/**
* Класс описывает состояние твёрдого тела.
*/
public class BodyState {
/**
* Положение тела.
*/
public var pos:Vector3 = new Vector3();
/**
* Ориентация тела.
*/
public var orientation:Quaternion = new Quaternion();
/**
* Скорость тела.
*/
public var velocity:Vector3 = new Vector3();
/**
* Угловая скорость тела.
*/
public var rotation:Vector3 = new Vector3();
/**
* Копирует значение указанного объекта.
*
* @param state
*/
public function copy(state:BodyState):void {
pos.vCopy(state.pos);
orientation.copy(state.orientation);
velocity.vCopy(state.velocity);
rotation.vCopy(state.rotation);
}
}
}

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