mirror of
https://github.com/MapMakersAndProgrammers/alternativaphysics-archive.git
synced 2025-10-26 01:49:13 -07:00
everything i found in leaked code
This commit is contained in:
15
0.0.1.0/.actionScriptProperties
Normal file
15
0.0.1.0/.actionScriptProperties
Normal 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>
|
||||
49
0.0.1.0/.flexLibProperties
Normal file
49
0.0.1.0/.flexLibProperties
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<flexLibProperties version="1">
|
||||
<includeClasses>
|
||||
<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.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.rigid.RigidWorld"/>
|
||||
</includeClasses>
|
||||
<includeResources/>
|
||||
<namespaceManifests/>
|
||||
</flexLibProperties>
|
||||
19
0.0.1.0/.project
Normal file
19
0.0.1.0/.project
Normal 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>
|
||||
11
0.0.1.0/.settings/.svn/all-wcprops
Normal file
11
0.0.1.0/.settings/.svn/all-wcprops
Normal file
@@ -0,0 +1,11 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 89
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.settings
|
||||
END
|
||||
org.eclipse.core.resources.prefs
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 122
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.settings/org.eclipse.core.resources.prefs
|
||||
END
|
||||
40
0.0.1.0/.settings/.svn/entries
Normal file
40
0.0.1.0/.settings/.svn/entries
Normal file
@@ -0,0 +1,40 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.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:39.000000Z
|
||||
4f70b476f3e5075e399505021df2f89b
|
||||
2009-04-01T12:25:29.638016Z
|
||||
10301
|
||||
mike
|
||||
|
||||
1
0.0.1.0/.settings/.svn/format
Normal file
1
0.0.1.0/.settings/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,3 @@
|
||||
#Fri Mar 20 00:19:33 YEKT 2009
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
3
0.0.1.0/.settings/org.eclipse.core.resources.prefs
Normal file
3
0.0.1.0/.settings/org.eclipse.core.resources.prefs
Normal file
@@ -0,0 +1,3 @@
|
||||
#Fri Mar 20 00:19:33 YEKT 2009
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
29
0.0.1.0/.svn/all-wcprops
Normal file
29
0.0.1.0/.svn/all-wcprops
Normal file
@@ -0,0 +1,29 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 79
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0
|
||||
END
|
||||
.flexLibProperties
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 98
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.flexLibProperties
|
||||
END
|
||||
.project
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 88
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.project
|
||||
END
|
||||
pom.xml
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 87
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/pom.xml
|
||||
END
|
||||
.actionScriptProperties
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 103
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/.actionScriptProperties
|
||||
END
|
||||
5
0.0.1.0/.svn/dir-prop-base
Normal file
5
0.0.1.0/.svn/dir-prop-base
Normal file
@@ -0,0 +1,5 @@
|
||||
K 13
|
||||
svn:mergeinfo
|
||||
V 85
|
||||
/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.1.Vector3D:13892-14098
|
||||
END
|
||||
85
0.0.1.0/.svn/entries
Normal file
85
0.0.1.0/.svn/entries
Normal file
@@ -0,0 +1,85 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2009-06-23T08:51:29.377118Z
|
||||
14887
|
||||
mike
|
||||
has-props
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
META-INF
|
||||
dir
|
||||
|
||||
.flexLibProperties
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
fcef7ca7fe61a3459fb00858c28adec1
|
||||
2009-05-26T08:41:33.471001Z
|
||||
13389
|
||||
mike
|
||||
|
||||
.project
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
1e90cc68b52b93173b2b5de88eb5c3a3
|
||||
2009-04-20T17:15:55.260110Z
|
||||
11541
|
||||
mike
|
||||
|
||||
src
|
||||
dir
|
||||
|
||||
pom.xml
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
66fe2d1158b7a4fe76ade73e32168db1
|
||||
2009-06-23T08:50:57.759518Z
|
||||
14886
|
||||
mike
|
||||
|
||||
.actionScriptProperties
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
918335cb945c3f8866aeb8e6cfd1b110
|
||||
2009-06-23T08:49:19.922749Z
|
||||
14884
|
||||
mike
|
||||
|
||||
.settings
|
||||
dir
|
||||
|
||||
1
0.0.1.0/.svn/format
Normal file
1
0.0.1.0/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
15
0.0.1.0/.svn/text-base/.actionScriptProperties.svn-base
Normal file
15
0.0.1.0/.svn/text-base/.actionScriptProperties.svn-base
Normal 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>
|
||||
49
0.0.1.0/.svn/text-base/.flexLibProperties.svn-base
Normal file
49
0.0.1.0/.svn/text-base/.flexLibProperties.svn-base
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<flexLibProperties version="1">
|
||||
<includeClasses>
|
||||
<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.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.rigid.RigidWorld"/>
|
||||
</includeClasses>
|
||||
<includeResources/>
|
||||
<namespaceManifests/>
|
||||
</flexLibProperties>
|
||||
19
0.0.1.0/.svn/text-base/.project.svn-base
Normal file
19
0.0.1.0/.svn/text-base/.project.svn-base
Normal 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>
|
||||
17
0.0.1.0/.svn/text-base/pom.xml.svn-base
Normal file
17
0.0.1.0/.svn/text-base/pom.xml.svn-base
Normal 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.1.0</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/tags/0.0.1.0</connection>
|
||||
</scm>
|
||||
<dependencies>
|
||||
</dependencies>
|
||||
</project>
|
||||
11
0.0.1.0/META-INF/.svn/all-wcprops
Normal file
11
0.0.1.0/META-INF/.svn/all-wcprops
Normal file
@@ -0,0 +1,11 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 88
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/META-INF
|
||||
END
|
||||
MANIFEST.MF
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 100
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/META-INF/MANIFEST.MF
|
||||
END
|
||||
40
0.0.1.0/META-INF/.svn/entries
Normal file
40
0.0.1.0/META-INF/.svn/entries
Normal file
@@ -0,0 +1,40 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/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:39.000000Z
|
||||
7b64dceb50a6905850ff00a0bfbe77eb
|
||||
2009-04-20T17:15:55.260110Z
|
||||
11541
|
||||
mike
|
||||
|
||||
1
0.0.1.0/META-INF/.svn/format
Normal file
1
0.0.1.0/META-INF/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
1
0.0.1.0/META-INF/.svn/text-base/MANIFEST.MF.svn-base
Normal file
1
0.0.1.0/META-INF/.svn/text-base/MANIFEST.MF.svn-base
Normal file
@@ -0,0 +1 @@
|
||||
Bundle-Name: platform.clients.fp10.libraries.AlternativaPhysics
|
||||
1
0.0.1.0/META-INF/MANIFEST.MF
Normal file
1
0.0.1.0/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1 @@
|
||||
Bundle-Name: platform.clients.fp10.libraries.AlternativaPhysics
|
||||
17
0.0.1.0/pom.xml
Normal file
17
0.0.1.0/pom.xml
Normal 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.1.0</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/tags/0.0.1.0</connection>
|
||||
</scm>
|
||||
<dependencies>
|
||||
</dependencies>
|
||||
</project>
|
||||
5
0.0.1.0/src/.svn/all-wcprops
Normal file
5
0.0.1.0/src/.svn/all-wcprops
Normal file
@@ -0,0 +1,5 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 83
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src
|
||||
END
|
||||
31
0.0.1.0/src/.svn/entries
Normal file
31
0.0.1.0/src/.svn/entries
Normal file
@@ -0,0 +1,31 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2009-06-17T08:27:51.730249Z
|
||||
14488
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
alternativa
|
||||
dir
|
||||
|
||||
1
0.0.1.0/src/.svn/format
Normal file
1
0.0.1.0/src/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
5
0.0.1.0/src/alternativa/.svn/all-wcprops
Normal file
5
0.0.1.0/src/alternativa/.svn/all-wcprops
Normal file
@@ -0,0 +1,5 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 95
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa
|
||||
END
|
||||
31
0.0.1.0/src/alternativa/.svn/entries
Normal file
31
0.0.1.0/src/alternativa/.svn/entries
Normal file
@@ -0,0 +1,31 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2009-06-17T08:27:51.730249Z
|
||||
14488
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
physics
|
||||
dir
|
||||
|
||||
1
0.0.1.0/src/alternativa/.svn/format
Normal file
1
0.0.1.0/src/alternativa/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
11
0.0.1.0/src/alternativa/physics/.svn/all-wcprops
Normal file
11
0.0.1.0/src/alternativa/physics/.svn/all-wcprops
Normal file
@@ -0,0 +1,11 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 103
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics
|
||||
END
|
||||
altphysics.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 117
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/altphysics.as
|
||||
END
|
||||
49
0.0.1.0/src/alternativa/physics/.svn/entries
Normal file
49
0.0.1.0/src/alternativa/physics/.svn/entries
Normal file
@@ -0,0 +1,49 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2009-06-17T08:27:51.730249Z
|
||||
14488
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
types
|
||||
dir
|
||||
|
||||
altphysics.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
04fbe40a27502dbbd0dba02a76b2df50
|
||||
2009-04-20T20:09:14.715403Z
|
||||
11579
|
||||
mike
|
||||
|
||||
collision
|
||||
dir
|
||||
|
||||
rigid
|
||||
dir
|
||||
|
||||
1
0.0.1.0/src/alternativa/physics/.svn/format
Normal file
1
0.0.1.0/src/alternativa/physics/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,3 @@
|
||||
package alternativa.physics {
|
||||
public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
|
||||
}
|
||||
3
0.0.1.0/src/alternativa/physics/altphysics.as
Normal file
3
0.0.1.0/src/alternativa/physics/altphysics.as
Normal file
@@ -0,0 +1,3 @@
|
||||
package alternativa.physics {
|
||||
public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
|
||||
}
|
||||
95
0.0.1.0/src/alternativa/physics/collision/.svn/all-wcprops
Normal file
95
0.0.1.0/src/alternativa/physics/collision/.svn/all-wcprops
Normal file
@@ -0,0 +1,95 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 113
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision
|
||||
END
|
||||
BoxSphereCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 134
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BoxSphereCollider.as
|
||||
END
|
||||
BoxBoxCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 131
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BoxBoxCollider.as
|
||||
END
|
||||
CollisionKdNode.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 132
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/CollisionKdNode.as
|
||||
END
|
||||
SpherePlaneCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 136
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/SpherePlaneCollider.as
|
||||
END
|
||||
KdTreeCollisionDetector.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 140
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/KdTreeCollisionDetector.as
|
||||
END
|
||||
BoxRectCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 132
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BoxRectCollider.as
|
||||
END
|
||||
ICollisionPredicate.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 136
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/ICollisionPredicate.as
|
||||
END
|
||||
BoxPlaneCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 133
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BoxPlaneCollider.as
|
||||
END
|
||||
CollisionKdTree.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 132
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/CollisionKdTree.as
|
||||
END
|
||||
BoxTriangleCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 136
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BoxTriangleCollider.as
|
||||
END
|
||||
ICollisionDetector.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 135
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/ICollisionDetector.as
|
||||
END
|
||||
IRayCollisionPredicate.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 139
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/IRayCollisionPredicate.as
|
||||
END
|
||||
ICollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 126
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/ICollider.as
|
||||
END
|
||||
BruteForceCollisionDetector.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 144
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/BruteForceCollisionDetector.as
|
||||
END
|
||||
SphereSphereCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 137
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/SphereSphereCollider.as
|
||||
END
|
||||
214
0.0.1.0/src/alternativa/physics/collision/.svn/entries
Normal file
214
0.0.1.0/src/alternativa/physics/collision/.svn/entries
Normal file
@@ -0,0 +1,214 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2009-06-17T08:27:51.730249Z
|
||||
14488
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
BoxSphereCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
ad1dc1849019ac159d22cd93b1ba38d3
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
BoxBoxCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
e4eb46370e3c7098fa35d0da4e346d09
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
CollisionKdNode.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
7952384e785be1fca93657d94121b246
|
||||
2009-04-17T17:39:21.367569Z
|
||||
11341
|
||||
mike
|
||||
|
||||
BoxRectCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
8554254b88a8ecc31bf005fdee676a67
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
KdTreeCollisionDetector.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
7ba4808350962bc29e80317568ad589c
|
||||
2009-06-17T08:27:51.730249Z
|
||||
14488
|
||||
mike
|
||||
|
||||
SpherePlaneCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
7fbbf70eef5caa9a09ce679819429874
|
||||
2009-04-13T03:07:43.422558Z
|
||||
11019
|
||||
mike
|
||||
|
||||
ICollisionPredicate.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
866471ea582e95e46e94eb7ae437c827
|
||||
2009-04-26T15:39:06.673206Z
|
||||
11995
|
||||
mike
|
||||
|
||||
BoxPlaneCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
4344df47c2b9c1873d2dc711815b4737
|
||||
2009-04-13T03:07:43.422558Z
|
||||
11019
|
||||
mike
|
||||
|
||||
primitives
|
||||
dir
|
||||
|
||||
CollisionKdTree.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
92511d7533028a942e299ca3a4251da8
|
||||
2009-05-18T16:37:02.465501Z
|
||||
13087
|
||||
mike
|
||||
|
||||
BoxTriangleCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
f95da4166903174a2c92c1ab262e719c
|
||||
2009-05-26T08:41:33.471001Z
|
||||
13389
|
||||
mike
|
||||
|
||||
ICollisionDetector.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
c98a3e33a4a54e2e85da149e25b583fe
|
||||
2009-06-08T11:22:42.165256Z
|
||||
14112
|
||||
mike
|
||||
|
||||
IRayCollisionPredicate.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
f7443fb728cf4e6157dcfb4f39665889
|
||||
2009-04-26T15:39:06.673206Z
|
||||
11995
|
||||
mike
|
||||
|
||||
types
|
||||
dir
|
||||
|
||||
BruteForceCollisionDetector.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
c166c05be4909e221c2f129c9e7cac32
|
||||
2009-06-08T11:22:42.165256Z
|
||||
14112
|
||||
mike
|
||||
|
||||
ICollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
bc3785279b9b7810a0c84cb4fe6084fc
|
||||
2009-04-13T03:07:43.422558Z
|
||||
11019
|
||||
mike
|
||||
|
||||
SphereSphereCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
fd6e8cf39dcf007125384bc91b347a00
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
1
0.0.1.0/src/alternativa/physics/collision/.svn/format
Normal file
1
0.0.1.0/src/alternativa/physics/collision/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,791 @@
|
||||
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 implements ICollider {
|
||||
|
||||
private var tolerance:Number = 0.001;
|
||||
|
||||
private var pos1:Vector3 = new Vector3();
|
||||
private var pos2:Vector3 = new Vector3();
|
||||
private var vectorToBox1: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 tmpAxis:Vector3 = new Vector3();
|
||||
private var vector:Vector3 = new Vector3();
|
||||
private var point1:Vector3 = new Vector3();
|
||||
private var point2:Vector3 = new Vector3();
|
||||
|
||||
private var bestAxisIndex:int;
|
||||
private var minOverlap:Number;
|
||||
|
||||
private var verts1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
private var verts2: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();
|
||||
verts1[i] = new Vector3();
|
||||
verts2[i] = new Vector3();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body1
|
||||
* @param body2
|
||||
* @param contactInfo
|
||||
* @return
|
||||
*/
|
||||
public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
minOverlap = 1e10;
|
||||
var box1:CollisionBox;
|
||||
var box2:CollisionBox;
|
||||
if (prim1.body != null) {
|
||||
box1 = prim1 as CollisionBox;
|
||||
box2 = prim2 as CollisionBox;
|
||||
} else {
|
||||
box1 = prim2 as CollisionBox;
|
||||
box2 = prim1 as CollisionBox;
|
||||
}
|
||||
var transform1:Matrix4 = box1.transform;
|
||||
var transform2:Matrix4 = box2.transform;
|
||||
|
||||
// Вектор из центра второго бокса в центр первого
|
||||
pos1.x = transform1.d; pos1.y = transform1.h; pos1.z = transform1.l;
|
||||
pos2.x = transform2.d; pos2.y = transform2.h; pos2.z = transform2.l;
|
||||
vectorToBox1.x = pos1.x - pos2.x;
|
||||
vectorToBox1.y = pos1.y - pos2.y;
|
||||
vectorToBox1.z = pos1.z - pos2.z;
|
||||
|
||||
// Проверка пересечения по основным осям
|
||||
// box1.transform.getAxis(0, axis10);
|
||||
axis10.x = transform1.a; axis10.y = transform1.e; axis10.z = transform1.i;
|
||||
if (!testAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
|
||||
// box1.transform.getAxis(1, axis11);
|
||||
axis11.x = transform1.b; axis11.y = transform1.f; axis11.z = transform1.j;
|
||||
if (!testAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
|
||||
// box1.transform.getAxis(2, axis12);
|
||||
axis12.x = transform1.c; axis12.y = transform1.g; axis12.z = transform1.k;
|
||||
if (!testAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
|
||||
|
||||
// box2.transform.getAxis(0, axis20);
|
||||
axis20.x = transform2.a; axis20.y = transform2.e; axis20.z = transform2.i;
|
||||
if (!testAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
|
||||
// box2.transform.getAxis(1, axis21);
|
||||
axis21.x = transform2.b; axis21.y = transform2.f; axis21.z = transform2.j;
|
||||
if (!testAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
|
||||
// box2.transform.getAxis(2, axis22);
|
||||
axis22.x = transform2.c; axis22.y = transform2.g; axis22.z = transform2.k;
|
||||
if (!testAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
|
||||
|
||||
// Проверка производных осей
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis10, axis20), 6, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis10, axis21), 7, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis10, axis22), 8, vectorToBox1)) return false;
|
||||
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis11, axis20), 9, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis11, axis21), 10, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis11, axis22), 11, vectorToBox1)) return false;
|
||||
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis12, axis20), 12, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis12, axis21), 13, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis12, axis22), 14, vectorToBox1)) return false;
|
||||
|
||||
if (bestAxisIndex < 6) {
|
||||
// Контакт грань-(грань|ребро|вершина)
|
||||
findFaceContactPoints(box1, box2, vectorToBox1, bestAxisIndex, contact);
|
||||
} else {
|
||||
// Контакт ребро-ребро
|
||||
bestAxisIndex -= 6;
|
||||
findEdgesIntersection(box1, box2, vectorToBox1, int(bestAxisIndex/3), bestAxisIndex%3, contact);
|
||||
}
|
||||
contact.body1 = box1.body;
|
||||
contact.body2 = box2.body;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет поиск точек контакта грани одного бокса с гранью/ребром/вершиной другого.
|
||||
*
|
||||
* @param box1 первый бокс
|
||||
* @param box2 второй бокс
|
||||
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
|
||||
* @param faceAxisIdx индекс оси первого бокса, перпендикулярной грани, с которой произошло столкновение
|
||||
* @param contactInfo структура, в которую записывается информация о точках контакта
|
||||
*/
|
||||
private function findFaceContactPoints(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, faceAxisIdx:int, contactInfo:Contact):void {
|
||||
var swapNormal:Boolean = false;
|
||||
if (faceAxisIdx > 2) {
|
||||
// Столкновение с гранью второго бокса. Для дальнейших расчётов боксы меняются местами,
|
||||
// но нормаль контакта всё равно должна быть направлена в сторону первоначального box1
|
||||
var tmpBox:CollisionBox = box1;
|
||||
box1 = box2;
|
||||
box2 = tmpBox;
|
||||
vectorToBox1.vReverse();
|
||||
faceAxisIdx -= 3;
|
||||
swapNormal = true;
|
||||
}
|
||||
box1.transform.getAxis(faceAxisIdx, colAxis);
|
||||
var faceReversed:Boolean = colAxis.vDot(vectorToBox1) > 0;
|
||||
if (!faceReversed) colAxis.vReverse();
|
||||
// Ищем ось второго бокса, определяющую наиболее антипараллельную грань
|
||||
var incFaceAxisIdx:int = 0;
|
||||
var maxDot:Number = 0;
|
||||
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
|
||||
box2.transform.getAxis(axisIdx, axis);
|
||||
var dot:Number = axis.vDot(colAxis);
|
||||
if (dot < 0) dot = -dot;
|
||||
if (dot > maxDot) {
|
||||
maxDot = dot;
|
||||
incFaceAxisIdx = axisIdx;
|
||||
}
|
||||
}
|
||||
// Получаем список вершин грани второго бокса, переводим их в систему координат первого бокса и выполняем обрезку
|
||||
// по грани первого бокса. Таким образом получается список потенциальных точек контакта.
|
||||
box2.transform.getAxis(incFaceAxisIdx, axis);
|
||||
getFaceVertsByAxis(box2, incFaceAxisIdx, axis.vDot(colAxis) < 0, verts1);
|
||||
box2.transform.transformVectors(verts1, verts2);
|
||||
box1.transform.transformVectorsInverse(verts2, verts1);
|
||||
var pnum:int = clip(box1.hs, faceAxisIdx);
|
||||
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
|
||||
var pen:Number;
|
||||
pcount = 0;
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
if ((pen = getPointBoxPenetration(box1.hs, verts1[i], faceAxisIdx, faceReversed)) > -tolerance) {
|
||||
var cp:ContactPoint = tmpPoints[pcount++];
|
||||
box1.transform.transformVector(verts1[i], cp.pos);
|
||||
cp.r1.vDiff(cp.pos, pos1);
|
||||
cp.r2.vDiff(cp.pos, pos2);
|
||||
cp.penetration = pen;
|
||||
}
|
||||
}
|
||||
contactInfo.normal.vCopy(colAxis);
|
||||
if (swapNormal) contactInfo.normal.vReverse();
|
||||
|
||||
if (pcount > 4) reducePoints();
|
||||
for (i = 0; i < pcount; i++) (contactInfo.points[i] as ContactPoint).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 = (tmpPoints[pcount - 1] as ContactPoint).pos;
|
||||
var p2:Vector3;
|
||||
for (i = 0; i < pcount; i++) {
|
||||
p2 = (tmpPoints[i] as ContactPoint).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 ? (pcount - 1) : (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[i - 1] = tmpPoints[i];
|
||||
tmpPoints[pcount - 1] = cp1;
|
||||
}
|
||||
pcount--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
|
||||
*
|
||||
* @param box бокс, в котором ишутся вершины
|
||||
* @param axisIdx индекс нормальной оси
|
||||
* @param reverse если указано значение true, возвращаются вершины противоположной грани
|
||||
* @param result список, в который помещаются вершины
|
||||
*/
|
||||
private function getFaceVertsByAxis(box:CollisionBox, axisIdx:int, reverse:Boolean, result:Vector.<Vector3>):void {
|
||||
var hs:Vector3 = box.hs;
|
||||
switch (axisIdx) {
|
||||
case 0:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет обрезку грани, заданной списком вершин в поле объекта 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, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, verts2, verts1);
|
||||
case 1:
|
||||
if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighX(hs.x, pnum, verts2, verts1);
|
||||
case 2:
|
||||
if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, verts2, verts1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var x1:Number = x - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.x > x1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.x < x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var x1:Number = x + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.x < x1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.x > x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var y1:Number = y - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.y > y1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.y < y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var y1:Number = y + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.y < y1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.y > y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var z1:Number = z - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.z > z1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.z < z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var z1:Number = z + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.z < z1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.z > z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет точку столкновения рёбер двух боксов.
|
||||
*
|
||||
* @param box1 первый бокс
|
||||
* @param box2 второй бокс
|
||||
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
|
||||
* @param axisIdx1 индекс направляющей оси ребра первого бокса
|
||||
* @param axisIdx2 индекс направляющей оси ребра второго бокса
|
||||
* @param contactInfo структура, в которую записывается информация о столкновении
|
||||
*/
|
||||
private function findEdgesIntersection(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
|
||||
box1.transform.getAxis(axisIdx1, axis10);
|
||||
box2.transform.getAxis(axisIdx2, axis20);
|
||||
colAxis.vCross2(axis10, axis20).vNormalize();
|
||||
// Разворот оси в сторону первого бокса
|
||||
if (colAxis.vDot(vectorToBox1) < 0) colAxis.vReverse();
|
||||
|
||||
/* Кодирование рёбер
|
||||
бит 0: 1 (тип контакта ребро-ребро)
|
||||
биты 1-2: индекс направляющей оси ребра
|
||||
бит 3:
|
||||
бит 4:
|
||||
бит 5:
|
||||
*/
|
||||
var edgeCode1:int = 1;
|
||||
var edgeCode2:int = 1;
|
||||
|
||||
// Находим среднюю точку на каждом из пересекающихся рёбер
|
||||
var halfLen1:Number;
|
||||
var halfLen2:Number;
|
||||
point1.vCopy(box1.hs);
|
||||
point2.vCopy(box2.hs);
|
||||
// x
|
||||
if (axisIdx1 == 0) {
|
||||
point1.x = 0;
|
||||
halfLen1 = box1.hs.x;
|
||||
} else {
|
||||
box1.transform.getAxis(0, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.x = -point1.x;
|
||||
edgeCode1 |= 8; // 1 << 3
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 0) {
|
||||
point2.x = 0;
|
||||
halfLen2 = box2.hs.x;
|
||||
} else {
|
||||
box2.transform.getAxis(0, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) {
|
||||
point2.x = -point2.x;
|
||||
edgeCode2 |= 8; // 1 << 3
|
||||
}
|
||||
}
|
||||
// y
|
||||
if (axisIdx1 == 1) {
|
||||
point1.y = 0;
|
||||
halfLen1 = box1.hs.y;
|
||||
edgeCode1 |= 2; // 1 << 1
|
||||
} else {
|
||||
box1.transform.getAxis(1, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.y = -point1.y;
|
||||
edgeCode1 |= 16; // 1 << 4
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 1) {
|
||||
point2.y = 0;
|
||||
halfLen2 = box2.hs.y;
|
||||
edgeCode2 |= 2; // 1 << 1
|
||||
} else {
|
||||
box2.transform.getAxis(1, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) {
|
||||
point2.y = -point2.y;
|
||||
edgeCode2 |= 16; // 1 << 4
|
||||
}
|
||||
}
|
||||
// z
|
||||
if (axisIdx1 == 2) {
|
||||
point1.z = 0;
|
||||
halfLen1 = box1.hs.z;
|
||||
edgeCode1 |= 4; // 2 << 1
|
||||
} else {
|
||||
box1.transform.getAxis(2, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.z = -point1.z;
|
||||
edgeCode1 |= 32; // 1 << 5
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 2) {
|
||||
point2.z = 0;
|
||||
halfLen2 = box2.hs.z;
|
||||
edgeCode2 |= 4; // 2 << 1
|
||||
} else {
|
||||
box2.transform.getAxis(2, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) {
|
||||
point2.z = -point2.z;
|
||||
edgeCode2 |= 32; // 1 << 5
|
||||
}
|
||||
}
|
||||
// Получаем глобальные координаты средних точек рёбер
|
||||
point1.vTransformBy4(box1.transform);
|
||||
point2.vTransformBy4(box2.transform);
|
||||
// Находим точку пересечения рёбер, решая систему уравнений
|
||||
var k:Number = axis10.vDot(axis20);
|
||||
var det:Number = k*k - 1;
|
||||
vector.vDiff(point2, point1);
|
||||
var c1:Number = axis10.vDot(vector);
|
||||
var c2:Number = axis20.vDot(vector);
|
||||
var t1:Number = (c2*k - c1)/det;
|
||||
var t2:Number = (c2 - c1*k)/det;
|
||||
// Запись данных о столкновении
|
||||
contact.normal.vCopy(colAxis);
|
||||
contact.pcount = 1;
|
||||
var cp:ContactPoint = contact.points[0];
|
||||
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
|
||||
cp.pos.x = 0.5*(point1.x + axis10.x*t1 + point2.x + axis20.x*t2);
|
||||
cp.pos.y = 0.5*(point1.y + axis10.y*t1 + point2.y + axis20.y*t2);
|
||||
cp.pos.z = 0.5*(point1.z + axis10.z*t1 + point2.z + axis20.z*t2);
|
||||
cp.r1.vDiff(cp.pos, pos1);
|
||||
cp.r2.vDiff(cp.pos, pos2);
|
||||
cp.penetration = minOverlap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет пересечение боксов вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
|
||||
*
|
||||
* @param box1
|
||||
* @param box2
|
||||
* @param axis
|
||||
* @param axisIndex
|
||||
* @param vectorToBox1
|
||||
* @param bestAxis
|
||||
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
|
||||
*/
|
||||
private function testAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, axisIndex:int, vectorToBox1:Vector3):Boolean {
|
||||
if (axis.vLengthSqr() < 0.0001) {
|
||||
return true;
|
||||
}
|
||||
axis.vNormalize();
|
||||
|
||||
var overlap:Number = overlapOnAxis(box1, box2, axis, vectorToBox1);
|
||||
if (overlap < -tolerance) return false;
|
||||
if (overlap + tolerance < minOverlap) {
|
||||
minOverlap = overlap;
|
||||
bestAxisIndex = axisIndex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет глубину перекрытия двух боксов вдоль заданной оси.
|
||||
*
|
||||
* @param box1 первый бокс
|
||||
* @param box2 второй бокс
|
||||
* @param axis ось
|
||||
* @param vectorToBox1 вектор, соединяющий центр второго бокса с центром первого
|
||||
* @return величина перекрытия боксов вдоль оси
|
||||
*/
|
||||
public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, vectorToBox1: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 = vectorToBox1.x*axis.x + vectorToBox1.y*axis.y + vectorToBox1.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;
|
||||
|
||||
public var feature1:int;
|
||||
public var feature2:int;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
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 collide(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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,734 @@
|
||||
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 implements ICollider {
|
||||
|
||||
private var tolerance:Number = 0.001;
|
||||
|
||||
private var bPos:Vector3 = new Vector3();
|
||||
private var rPos:Vector3 = new Vector3();
|
||||
|
||||
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 colAxis:Vector3 = new Vector3();
|
||||
private var tmpAxis:Vector3 = new Vector3();
|
||||
private var vector:Vector3 = new Vector3();
|
||||
private var point1:Vector3 = new Vector3();
|
||||
private var point2:Vector3 = new Vector3();
|
||||
|
||||
private var bestAxisIndex:int;
|
||||
private var minOverlap:Number;
|
||||
|
||||
private var verts1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
private var verts2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function BoxRectCollider() {
|
||||
for (var i:int = 0; i < 8; i++) {
|
||||
verts1[i] = new Vector3();
|
||||
verts2[i] = new Vector3();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @param contact
|
||||
* @return
|
||||
*/
|
||||
public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):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;
|
||||
}
|
||||
|
||||
// Вектор из центра прямоугольника в центр бокса
|
||||
box.transform.getAxis(3, bPos);
|
||||
rect.transform.getAxis(3, rPos);
|
||||
vectorToBox.vDiff(bPos, rPos);
|
||||
|
||||
// Проверка пересечения по нормали прямоугольника
|
||||
rect.transform.getAxis(2, axis22);
|
||||
if (!testAxis(box, rect, axis22, 0, vectorToBox)) return false;
|
||||
|
||||
// Проверка пересечения по основным осям бокса
|
||||
box.transform.getAxis(0, axis10);
|
||||
if (!testAxis(box, rect, axis10, 1, vectorToBox)) return false;
|
||||
box.transform.getAxis(1, axis11);
|
||||
if (!testAxis(box, rect, axis11, 2, vectorToBox)) return false;
|
||||
box.transform.getAxis(2, axis12);
|
||||
if (!testAxis(box, rect, axis12, 3, vectorToBox)) return false;
|
||||
|
||||
// Получаем направляющие рёбер прямоугольника
|
||||
rect.transform.getAxis(0, axis20);
|
||||
rect.transform.getAxis(1, axis21);
|
||||
|
||||
// Проверка производных осей
|
||||
if (!testAxis(box, rect, axis.vCross2(axis10, axis20), 4, vectorToBox)) return false;
|
||||
if (!testAxis(box, rect, axis.vCross2(axis10, axis21), 5, vectorToBox)) return false;
|
||||
|
||||
if (!testAxis(box, rect, axis.vCross2(axis11, axis20), 6, vectorToBox)) return false;
|
||||
if (!testAxis(box, rect, axis.vCross2(axis11, axis21), 7, vectorToBox)) return false;
|
||||
|
||||
if (!testAxis(box, rect, axis.vCross2(axis12, axis20), 8, vectorToBox)) return false;
|
||||
if (!testAxis(box, rect, axis.vCross2(axis12, axis21), 9, vectorToBox)) return false;
|
||||
|
||||
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 box бокс
|
||||
* @param rect прямоугольник
|
||||
* @param vectorToBox вектор, направленный из центра прямоугольника в центр бокса
|
||||
* @param faceAxisIdx индекс оси, идентифицирующей полскость столкновения (грань бокса или полскость прямоугольника)
|
||||
* @param colInfo структура, в которую записывается информация о точках контакта
|
||||
*/
|
||||
private function findFaceContactPoints(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, faceAxisIdx:int, colInfo:Contact):Boolean {
|
||||
var pnum:int, i:int, v:Vector3, cp:ContactPoint;
|
||||
if (faceAxisIdx == 0) {
|
||||
// Столкновение с плоскостью прямоугольника
|
||||
|
||||
// Проверим положение бокса относительно плоскости прямоугольника
|
||||
rect.transform.getAxis(2, colAxis);
|
||||
var offset:Number = colAxis.vDot(rPos);
|
||||
if (bPos.vDot(colAxis) < offset) return false;
|
||||
|
||||
// Ищем ось бокса, определяющую наиболее антипараллельную грань
|
||||
var incFaceAxisIdx:int = 0;
|
||||
var maxDot:Number = 0;
|
||||
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
|
||||
box.transform.getAxis(axisIdx, axis);
|
||||
var dot:Number = axis.vDot(colAxis);
|
||||
if (dot < 0) dot = -dot;
|
||||
if (dot > maxDot) {
|
||||
maxDot = dot;
|
||||
incFaceAxisIdx = axisIdx;
|
||||
}
|
||||
}
|
||||
// Получаем список вершин грани бокса, переводим их в систему координат прямоугольника и выполняем обрезку
|
||||
// по прямоугольнику. Таким образом получается список потенциальных точек контакта.
|
||||
box.transform.getAxis(incFaceAxisIdx, axis);
|
||||
getFaceVertsByAxis(box.hs, incFaceAxisIdx, axis.vDot(colAxis) > 0, verts1);
|
||||
box.transform.transformVectors(verts1, verts2);
|
||||
rect.transform.transformVectorsInverse(verts2, verts1);
|
||||
pnum = clipByRect(rect.hs);
|
||||
// Проверяем каждую потенциальную точку на принадлежность нижней полуплоскости прямоугольника и добавляем такие точки в список контактов
|
||||
colInfo.pcount = 0;
|
||||
for (i = 0; i < pnum; i++) {
|
||||
v = verts1[i];
|
||||
if (v.z < tolerance) {
|
||||
cp = colInfo.points[colInfo.pcount++];
|
||||
rect.transform.transformVector(v, cp.pos);
|
||||
cp.r1.vDiff(cp.pos, bPos);
|
||||
cp.r2.vDiff(cp.pos, rPos);
|
||||
cp.penetration = -v.z;
|
||||
}
|
||||
}
|
||||
colInfo.normal.vCopy(colAxis);
|
||||
} else {
|
||||
// Столкновение с гранью бокса
|
||||
faceAxisIdx--;
|
||||
box.transform.getAxis(faceAxisIdx, colAxis);
|
||||
var faceReversed:Boolean = colAxis.vDot(vectorToBox) > 0;
|
||||
if (!faceReversed) colAxis.vReverse();
|
||||
|
||||
rect.transform.getAxis(2, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) return false;
|
||||
|
||||
getFaceVertsByAxis(rect.hs, 2, false, verts1);
|
||||
rect.transform.transformVectors(verts1, verts2);
|
||||
box.transform.transformVectorsInverse(verts2, verts1);
|
||||
pnum = clipByBox(box.hs, faceAxisIdx);
|
||||
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
|
||||
var pen:Number;
|
||||
colInfo.pcount = 0;
|
||||
for (i = 0; i < pnum; i++) {
|
||||
if ((pen = getPointBoxPenetration(box.hs, verts1[i], faceAxisIdx, faceReversed)) > -tolerance) {
|
||||
cp = colInfo.points[colInfo.pcount++];
|
||||
box.transform.transformVector(verts1[i], cp.pos);
|
||||
cp.r1.vDiff(cp.pos, bPos);
|
||||
cp.r2.vDiff(cp.pos, rPos);
|
||||
cp.penetration = pen;
|
||||
}
|
||||
}
|
||||
colInfo.normal.vCopy(colAxis);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
|
||||
*
|
||||
* @param box бокс, в котором ишутся вершины
|
||||
* @param axisIdx индекс нормальной оси
|
||||
* @param reverse если указано значение true, возвращаются вершины противоположной грани
|
||||
* @param result список, в который помещаются вершины
|
||||
*/
|
||||
private function getFaceVertsByAxis(hs:Vector3, axisIdx:int, reverse:Boolean, result:Vector.<Vector3>):void {
|
||||
switch (axisIdx) {
|
||||
case 0:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет обрезку грани, заданной списком вершин в поле объекта 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, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, verts2, verts1);
|
||||
case 1:
|
||||
if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighX(hs.x, pnum, verts2, verts1);
|
||||
case 2:
|
||||
if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, verts2, verts1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hs
|
||||
* @return
|
||||
*/
|
||||
private function clipByRect(hs:Vector3):int {
|
||||
var pnum:int = 4;
|
||||
if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, verts2, verts1);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var x1:Number = x - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.x > x1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.x < x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var x1:Number = x + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.x < x1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.x > x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var y1:Number = y - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.y > y1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.y < y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var y1:Number = y + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.y < y1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.y > y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var z1:Number = z - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.z > z1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.z < z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var z1:Number = z + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.z < z1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.z > z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет точку столкновения рёбер двух боксов.
|
||||
*
|
||||
* @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, colInfo:Contact):void {
|
||||
box.transform.getAxis(axisIdx1, axis10);
|
||||
rect.transform.getAxis(axisIdx2, axis20);
|
||||
colAxis.vCross2(axis10, axis20).vNormalize();
|
||||
// Разворот оси в сторону бокса
|
||||
if (colAxis.vDot(vectorToBox) < 0) colAxis.vReverse();
|
||||
|
||||
/* Кодирование рёбер
|
||||
бит 0: 1 (тип контакта ребро-ребро)
|
||||
биты 1-2: индекс направляющей оси ребра
|
||||
бит 3:
|
||||
бит 4:
|
||||
бит 5:
|
||||
*/
|
||||
var edgeCode1:int = 1;
|
||||
var edgeCode2:int = 1;
|
||||
|
||||
// Находим среднюю точку на каждом из пересекающихся рёбер
|
||||
var halfLen1:Number;
|
||||
var halfLen2:Number;
|
||||
point1.vCopy(box.hs);
|
||||
point2.vCopy(rect.hs);
|
||||
// x
|
||||
if (axisIdx1 == 0) {
|
||||
point1.x = 0;
|
||||
halfLen1 = box.hs.x;
|
||||
} else {
|
||||
box.transform.getAxis(0, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.x = -point1.x;
|
||||
edgeCode1 |= 8; // 1 << 3
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 0) {
|
||||
point2.x = 0;
|
||||
halfLen2 = rect.hs.x;
|
||||
} else {
|
||||
rect.transform.getAxis(0, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) {
|
||||
point2.x = -point2.x;
|
||||
edgeCode2 |= 8; // 1 << 3
|
||||
}
|
||||
}
|
||||
// y
|
||||
if (axisIdx1 == 1) {
|
||||
point1.y = 0;
|
||||
halfLen1 = box.hs.y;
|
||||
edgeCode1 |= 2; // 1 << 1
|
||||
} else {
|
||||
box.transform.getAxis(1, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.y = -point1.y;
|
||||
edgeCode1 |= 16; // 1 << 4
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 1) {
|
||||
point2.y = 0;
|
||||
halfLen2 = rect.hs.y;
|
||||
edgeCode2 |= 2; // 1 << 1
|
||||
} else {
|
||||
rect.transform.getAxis(1, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) {
|
||||
point2.y = -point2.y;
|
||||
edgeCode2 |= 16; // 1 << 4
|
||||
}
|
||||
}
|
||||
// z
|
||||
if (axisIdx1 == 2) {
|
||||
point1.z = 0;
|
||||
halfLen1 = box.hs.z;
|
||||
edgeCode1 |= 4; // 2 << 1
|
||||
} else {
|
||||
box.transform.getAxis(2, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.z = -point1.z;
|
||||
edgeCode1 |= 32; // 1 << 5
|
||||
}
|
||||
}
|
||||
// Получаем глобальные координаты средних точек рёбер
|
||||
point1.vTransformBy4(box.transform);
|
||||
point2.vTransformBy4(rect.transform);
|
||||
// Находим точку пересечения рёбер, решая систему уравнений
|
||||
var k:Number = axis10.vDot(axis20);
|
||||
var det:Number = k*k - 1;
|
||||
vector.vDiff(point2, point1);
|
||||
var c1:Number = axis10.vDot(vector);
|
||||
var c2:Number = axis20.vDot(vector);
|
||||
var t1:Number = (c2*k - c1)/det;
|
||||
var t2:Number = (c2 - c1*k)/det;
|
||||
// Запись данных о столкновении
|
||||
colInfo.normal.vCopy(colAxis);
|
||||
colInfo.pcount = 1;
|
||||
var cp:ContactPoint = colInfo.points[0];
|
||||
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
|
||||
cp.pos.x = 0.5*(point1.x + axis10.x*t1 + point2.x + axis20.x*t2);
|
||||
cp.pos.y = 0.5*(point1.y + axis10.y*t1 + point2.y + axis20.y*t2);
|
||||
cp.pos.z = 0.5*(point1.z + axis10.z*t1 + point2.z + axis20.z*t2);
|
||||
cp.r1.vDiff(cp.pos, bPos);
|
||||
cp.r2.vDiff(cp.pos, rPos);
|
||||
cp.penetration = minOverlap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет пересечение вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
|
||||
*
|
||||
* @param box
|
||||
* @param rect
|
||||
* @param axis
|
||||
* @param axisIndex
|
||||
* @param vectorToBox
|
||||
* @param bestAxis
|
||||
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
|
||||
*/
|
||||
private function testAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
|
||||
if (axis.vLengthSqr() < 0.0001) return true;
|
||||
axis.vNormalize();
|
||||
|
||||
var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
|
||||
if (overlap < -tolerance) return false;
|
||||
if (overlap + tolerance < 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
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 collide(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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.rigid.Contact;
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
|
||||
public class BoxTriangleCollider implements ICollider {
|
||||
public function BoxTriangleCollider() {
|
||||
}
|
||||
|
||||
public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package alternativa.physics.collision {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
import alternativa.physics.collision.types.RayIntersection;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package alternativa.physics.collision {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
|
||||
public class CollisionKdNode {
|
||||
public var objects:Vector.<int>;
|
||||
public var boundBox:BoundBox;
|
||||
public var parent:CollisionKdNode;
|
||||
|
||||
public var axis:int = -1; // 0 - x, 1 - y, 2 - z
|
||||
public var coord:Number;
|
||||
public var positiveNode:CollisionKdNode;
|
||||
public var negativeNode:CollisionKdNode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
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.01;
|
||||
public var minPrimitivesPerNode:int = 1;
|
||||
|
||||
public var rootNode:CollisionKdNode;
|
||||
public var staticChildren:Vector.<CollisionPrimitive> = new Vector.<CollisionPrimitive>();
|
||||
public var numStaticChildren:int;
|
||||
private var staticBoundBoxes:Vector.<BoundBox> = new Vector.<BoundBox>();
|
||||
|
||||
/**
|
||||
* @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.objects = 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.objects[i] = i;
|
||||
}
|
||||
staticBoundBoxes.length = numStaticChildren;
|
||||
// Разделяем рутовую ноду
|
||||
splitNode(rootNode);
|
||||
}
|
||||
|
||||
private var splitAxis:int;
|
||||
private var splitCoord:Number;
|
||||
private var splitCost:Number;
|
||||
static private const nodeBoundBoxThreshold:BoundBox = new BoundBox();
|
||||
static private const splitCoordsX:Vector.<Number> = new Vector.<Number>();
|
||||
static private const splitCoordsY:Vector.<Number> = new Vector.<Number>();
|
||||
static private const splitCoordsZ:Vector.<Number> = new Vector.<Number>();
|
||||
/**
|
||||
* @param node
|
||||
*/
|
||||
private function splitNode(node:CollisionKdNode):void {
|
||||
if (node.objects.length <= minPrimitivesPerNode) return;
|
||||
|
||||
var objects:Vector.<int> = node.objects;
|
||||
var i:int, j:int, k:int, length:int = objects.length, c1:Number, c2:Number, ct1:Number, ct2:Number, area:Number, areaNegative:Number, areaPositive:Number, numNegative:int, numPositive:int, conflict:Boolean, cost:Number;
|
||||
|
||||
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 numSplitCoordsX:int = 0, numSplitCoordsY:int = 0, numSplitCoordsZ:int = 0;
|
||||
for (i = 0; i < length; i++) {
|
||||
var boundBox:BoundBox = staticBoundBoxes[objects[i]];
|
||||
if (boundBox.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = boundBox.minX;
|
||||
if (boundBox.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = boundBox.maxX;
|
||||
|
||||
if (boundBox.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = boundBox.minY;
|
||||
if (boundBox.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = boundBox.maxY;
|
||||
|
||||
if (boundBox.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.minZ;
|
||||
if (boundBox.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.maxZ;
|
||||
}
|
||||
|
||||
// Убираем дубликаты координат, ищем наилучший сплит
|
||||
splitAxis = -1; splitCost = 1e308;
|
||||
i = 0; area = (nodeBoundBox.maxY - nodeBoundBox.minY)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
|
||||
while (i < numSplitCoordsX) {
|
||||
if (isNaN(c1 = splitCoordsX[i++])) continue;
|
||||
ct1 = c1 - threshold;
|
||||
ct2 = c1 + threshold;
|
||||
areaNegative = area*(c1 - nodeBoundBox.minX);
|
||||
areaPositive = area*(nodeBoundBox.maxX - c1);
|
||||
numNegative = numPositive = 0;
|
||||
conflict = false;
|
||||
// Проверяем объекты
|
||||
for (j = 0; j < length; j++) {
|
||||
boundBox = staticBoundBoxes[objects[j]];
|
||||
if (boundBox.maxX <= ct2) {
|
||||
if (boundBox.minX < ct1) numNegative++;
|
||||
} else {
|
||||
if (boundBox.minX >= ct1) numPositive++; else {conflict = true; break;}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
|
||||
splitCost = cost;
|
||||
splitAxis = 0;
|
||||
splitCoord = c1;
|
||||
}
|
||||
j = i;
|
||||
while (++j < numSplitCoordsX) if ((c2 = splitCoordsX[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsX[j] = NaN;
|
||||
}
|
||||
i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
|
||||
while (i < numSplitCoordsY) {
|
||||
if (isNaN(c1 = splitCoordsY[i++])) continue;
|
||||
ct1 = c1 - threshold;
|
||||
ct2 = c1 + threshold;
|
||||
areaNegative = area*(c1 - nodeBoundBox.minY);
|
||||
areaPositive = area*(nodeBoundBox.maxY - c1);
|
||||
numNegative = numPositive = 0;
|
||||
conflict = false;
|
||||
// Проверяем объекты
|
||||
for (j = 0; j < length; j++) {
|
||||
boundBox = staticBoundBoxes[objects[j]];
|
||||
if (boundBox.maxY <= ct2) {
|
||||
if (boundBox.minY < ct1) numNegative++;
|
||||
} else {
|
||||
if (boundBox.minY >= ct1) numPositive++; else {conflict = true; break;}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
|
||||
splitCost = cost;
|
||||
splitAxis = 1;
|
||||
splitCoord = c1;
|
||||
}
|
||||
j = i;
|
||||
while (++j < numSplitCoordsY) if ((c2 = splitCoordsY[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsY[j] = NaN;
|
||||
}
|
||||
i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxY - nodeBoundBox.minY);
|
||||
while (i < numSplitCoordsZ) {
|
||||
if (isNaN(c1 = splitCoordsZ[i++])) continue;
|
||||
ct1 = c1 - threshold;
|
||||
ct2 = c1 + threshold;
|
||||
areaNegative = area*(c1 - nodeBoundBox.minZ);
|
||||
areaPositive = area*(nodeBoundBox.maxZ - c1);
|
||||
numNegative = numPositive = 0;
|
||||
conflict = false;
|
||||
// Проверяем объекты
|
||||
for (j = 0; j < length; j++) {
|
||||
boundBox = staticBoundBoxes[objects[j]];
|
||||
if (boundBox.maxZ <= ct2) {
|
||||
if (boundBox.minZ < ct1) numNegative++;
|
||||
} else {
|
||||
if (boundBox.minZ >= ct1) numPositive++; else {conflict = true; break;}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
|
||||
splitCost = cost;
|
||||
splitAxis = 2;
|
||||
splitCoord = c1;
|
||||
}
|
||||
j = i;
|
||||
while (++j < numSplitCoordsZ) if ((c2 = splitCoordsZ[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsZ[j] = NaN;
|
||||
}
|
||||
|
||||
// Если сплит не найден, выходим
|
||||
if (splitAxis < 0) return;
|
||||
|
||||
// Разделяем ноду
|
||||
var axisX:Boolean = splitAxis == 0, axisY:Boolean = splitAxis == 1;
|
||||
node.axis = splitAxis;
|
||||
node.coord = splitCoord;
|
||||
|
||||
// Создаём дочерние ноды
|
||||
node.negativeNode = new CollisionKdNode();
|
||||
node.positiveNode = new CollisionKdNode();
|
||||
node.negativeNode.parent = node;
|
||||
node.positiveNode.parent = node;
|
||||
node.negativeNode.boundBox = nodeBoundBox.clone();
|
||||
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;
|
||||
|
||||
// Распределяем объекты по дочерним нодам
|
||||
ct1 = splitCoord - threshold; ct2 = splitCoord + threshold;
|
||||
for (i = 0; i < length; i++) {
|
||||
boundBox = staticBoundBoxes[objects[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 <= ct2) {
|
||||
// Объект в негативной стороне
|
||||
if (node.negativeNode.objects == null) node.negativeNode.objects = new Vector.<int>();
|
||||
node.negativeNode.objects.push(objects[i]);
|
||||
objects[i] = -1;
|
||||
} else {
|
||||
if (min >= ct1) {
|
||||
// Объект в положительной стороне
|
||||
if (node.positiveNode.objects == null) node.positiveNode.objects = new Vector.<int>();
|
||||
node.positiveNode.objects.push(objects[i]);
|
||||
objects[i] = -1;
|
||||
} else {
|
||||
// Распилился
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Очистка списка объектов
|
||||
j = 0;
|
||||
for (i = 0; i < length; i++) {
|
||||
if (objects[i] >= 0) objects[j++] = objects[i];
|
||||
}
|
||||
if (j > 0) objects.length = j; else node.objects = null;
|
||||
|
||||
// Разделение дочерних нод
|
||||
if (node.negativeNode.objects != null) splitNode(node.negativeNode);
|
||||
if (node.positiveNode.objects != null) splitNode(node.positiveNode);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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.objects);
|
||||
traceNode(str + "-", node.negativeNode);
|
||||
traceNode(str + "+", node.positiveNode);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
import alternativa.physics.rigid.Contact;
|
||||
|
||||
/**
|
||||
* Интерфейс определителя столкновений между двумя примитивами.
|
||||
*/
|
||||
public interface ICollider {
|
||||
function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
import alternativa.physics.collision.types.RayIntersection;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
|
||||
public interface ICollisionPredicate {
|
||||
|
||||
function considerCollision(primitive:CollisionPrimitive):Boolean;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.rigid.Body;
|
||||
|
||||
public interface IRayCollisionPredicate {
|
||||
function considerBody(body:Body):Boolean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
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.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.PLANE, new BoxPlaneCollider());
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.SPHERE, new BoxSphereCollider());
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.RECT, new BoxRectCollider());
|
||||
|
||||
// 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();
|
||||
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.collide(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 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 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.objects != null) {
|
||||
// Поиск столкновений со статическими примитивами узла
|
||||
var primitives:Vector.<CollisionPrimitive> = tree.staticChildren;
|
||||
var indices:Vector.<int> = node.objects;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Тест пересечения луча с динамическими примитивами.
|
||||
*
|
||||
* @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 minTime:Number = maxTime + 1;
|
||||
for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
|
||||
var primitive:CollisionPrimitive = dynamicPrimitives[i];
|
||||
if ((primitive.collisionGroup & collisionGroup) == 0) continue;
|
||||
if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
|
||||
var t:Number = primitive.getSegmentIntersection(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.objects != null && getRayNodeIntersection(origin, dir, collisionGroup, tree.staticChildren, node.objects, 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.getSegmentIntersection(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;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
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 collide(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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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 collide(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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
791
0.0.1.0/src/alternativa/physics/collision/BoxBoxCollider.as
Normal file
791
0.0.1.0/src/alternativa/physics/collision/BoxBoxCollider.as
Normal file
@@ -0,0 +1,791 @@
|
||||
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 implements ICollider {
|
||||
|
||||
private var tolerance:Number = 0.001;
|
||||
|
||||
private var pos1:Vector3 = new Vector3();
|
||||
private var pos2:Vector3 = new Vector3();
|
||||
private var vectorToBox1: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 tmpAxis:Vector3 = new Vector3();
|
||||
private var vector:Vector3 = new Vector3();
|
||||
private var point1:Vector3 = new Vector3();
|
||||
private var point2:Vector3 = new Vector3();
|
||||
|
||||
private var bestAxisIndex:int;
|
||||
private var minOverlap:Number;
|
||||
|
||||
private var verts1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
private var verts2: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();
|
||||
verts1[i] = new Vector3();
|
||||
verts2[i] = new Vector3();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body1
|
||||
* @param body2
|
||||
* @param contactInfo
|
||||
* @return
|
||||
*/
|
||||
public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
minOverlap = 1e10;
|
||||
var box1:CollisionBox;
|
||||
var box2:CollisionBox;
|
||||
if (prim1.body != null) {
|
||||
box1 = prim1 as CollisionBox;
|
||||
box2 = prim2 as CollisionBox;
|
||||
} else {
|
||||
box1 = prim2 as CollisionBox;
|
||||
box2 = prim1 as CollisionBox;
|
||||
}
|
||||
var transform1:Matrix4 = box1.transform;
|
||||
var transform2:Matrix4 = box2.transform;
|
||||
|
||||
// Вектор из центра второго бокса в центр первого
|
||||
pos1.x = transform1.d; pos1.y = transform1.h; pos1.z = transform1.l;
|
||||
pos2.x = transform2.d; pos2.y = transform2.h; pos2.z = transform2.l;
|
||||
vectorToBox1.x = pos1.x - pos2.x;
|
||||
vectorToBox1.y = pos1.y - pos2.y;
|
||||
vectorToBox1.z = pos1.z - pos2.z;
|
||||
|
||||
// Проверка пересечения по основным осям
|
||||
// box1.transform.getAxis(0, axis10);
|
||||
axis10.x = transform1.a; axis10.y = transform1.e; axis10.z = transform1.i;
|
||||
if (!testAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
|
||||
// box1.transform.getAxis(1, axis11);
|
||||
axis11.x = transform1.b; axis11.y = transform1.f; axis11.z = transform1.j;
|
||||
if (!testAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
|
||||
// box1.transform.getAxis(2, axis12);
|
||||
axis12.x = transform1.c; axis12.y = transform1.g; axis12.z = transform1.k;
|
||||
if (!testAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
|
||||
|
||||
// box2.transform.getAxis(0, axis20);
|
||||
axis20.x = transform2.a; axis20.y = transform2.e; axis20.z = transform2.i;
|
||||
if (!testAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
|
||||
// box2.transform.getAxis(1, axis21);
|
||||
axis21.x = transform2.b; axis21.y = transform2.f; axis21.z = transform2.j;
|
||||
if (!testAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
|
||||
// box2.transform.getAxis(2, axis22);
|
||||
axis22.x = transform2.c; axis22.y = transform2.g; axis22.z = transform2.k;
|
||||
if (!testAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
|
||||
|
||||
// Проверка производных осей
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis10, axis20), 6, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis10, axis21), 7, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis10, axis22), 8, vectorToBox1)) return false;
|
||||
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis11, axis20), 9, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis11, axis21), 10, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis11, axis22), 11, vectorToBox1)) return false;
|
||||
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis12, axis20), 12, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis12, axis21), 13, vectorToBox1)) return false;
|
||||
if (!testAxis(box1, box2, axis.vCross2(axis12, axis22), 14, vectorToBox1)) return false;
|
||||
|
||||
if (bestAxisIndex < 6) {
|
||||
// Контакт грань-(грань|ребро|вершина)
|
||||
findFaceContactPoints(box1, box2, vectorToBox1, bestAxisIndex, contact);
|
||||
} else {
|
||||
// Контакт ребро-ребро
|
||||
bestAxisIndex -= 6;
|
||||
findEdgesIntersection(box1, box2, vectorToBox1, int(bestAxisIndex/3), bestAxisIndex%3, contact);
|
||||
}
|
||||
contact.body1 = box1.body;
|
||||
contact.body2 = box2.body;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет поиск точек контакта грани одного бокса с гранью/ребром/вершиной другого.
|
||||
*
|
||||
* @param box1 первый бокс
|
||||
* @param box2 второй бокс
|
||||
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
|
||||
* @param faceAxisIdx индекс оси первого бокса, перпендикулярной грани, с которой произошло столкновение
|
||||
* @param contactInfo структура, в которую записывается информация о точках контакта
|
||||
*/
|
||||
private function findFaceContactPoints(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, faceAxisIdx:int, contactInfo:Contact):void {
|
||||
var swapNormal:Boolean = false;
|
||||
if (faceAxisIdx > 2) {
|
||||
// Столкновение с гранью второго бокса. Для дальнейших расчётов боксы меняются местами,
|
||||
// но нормаль контакта всё равно должна быть направлена в сторону первоначального box1
|
||||
var tmpBox:CollisionBox = box1;
|
||||
box1 = box2;
|
||||
box2 = tmpBox;
|
||||
vectorToBox1.vReverse();
|
||||
faceAxisIdx -= 3;
|
||||
swapNormal = true;
|
||||
}
|
||||
box1.transform.getAxis(faceAxisIdx, colAxis);
|
||||
var faceReversed:Boolean = colAxis.vDot(vectorToBox1) > 0;
|
||||
if (!faceReversed) colAxis.vReverse();
|
||||
// Ищем ось второго бокса, определяющую наиболее антипараллельную грань
|
||||
var incFaceAxisIdx:int = 0;
|
||||
var maxDot:Number = 0;
|
||||
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
|
||||
box2.transform.getAxis(axisIdx, axis);
|
||||
var dot:Number = axis.vDot(colAxis);
|
||||
if (dot < 0) dot = -dot;
|
||||
if (dot > maxDot) {
|
||||
maxDot = dot;
|
||||
incFaceAxisIdx = axisIdx;
|
||||
}
|
||||
}
|
||||
// Получаем список вершин грани второго бокса, переводим их в систему координат первого бокса и выполняем обрезку
|
||||
// по грани первого бокса. Таким образом получается список потенциальных точек контакта.
|
||||
box2.transform.getAxis(incFaceAxisIdx, axis);
|
||||
getFaceVertsByAxis(box2, incFaceAxisIdx, axis.vDot(colAxis) < 0, verts1);
|
||||
box2.transform.transformVectors(verts1, verts2);
|
||||
box1.transform.transformVectorsInverse(verts2, verts1);
|
||||
var pnum:int = clip(box1.hs, faceAxisIdx);
|
||||
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
|
||||
var pen:Number;
|
||||
pcount = 0;
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
if ((pen = getPointBoxPenetration(box1.hs, verts1[i], faceAxisIdx, faceReversed)) > -tolerance) {
|
||||
var cp:ContactPoint = tmpPoints[pcount++];
|
||||
box1.transform.transformVector(verts1[i], cp.pos);
|
||||
cp.r1.vDiff(cp.pos, pos1);
|
||||
cp.r2.vDiff(cp.pos, pos2);
|
||||
cp.penetration = pen;
|
||||
}
|
||||
}
|
||||
contactInfo.normal.vCopy(colAxis);
|
||||
if (swapNormal) contactInfo.normal.vReverse();
|
||||
|
||||
if (pcount > 4) reducePoints();
|
||||
for (i = 0; i < pcount; i++) (contactInfo.points[i] as ContactPoint).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 = (tmpPoints[pcount - 1] as ContactPoint).pos;
|
||||
var p2:Vector3;
|
||||
for (i = 0; i < pcount; i++) {
|
||||
p2 = (tmpPoints[i] as ContactPoint).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 ? (pcount - 1) : (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[i - 1] = tmpPoints[i];
|
||||
tmpPoints[pcount - 1] = cp1;
|
||||
}
|
||||
pcount--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
|
||||
*
|
||||
* @param box бокс, в котором ишутся вершины
|
||||
* @param axisIdx индекс нормальной оси
|
||||
* @param reverse если указано значение true, возвращаются вершины противоположной грани
|
||||
* @param result список, в который помещаются вершины
|
||||
*/
|
||||
private function getFaceVertsByAxis(box:CollisionBox, axisIdx:int, reverse:Boolean, result:Vector.<Vector3>):void {
|
||||
var hs:Vector3 = box.hs;
|
||||
switch (axisIdx) {
|
||||
case 0:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет обрезку грани, заданной списком вершин в поле объекта 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, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, verts2, verts1);
|
||||
case 1:
|
||||
if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighX(hs.x, pnum, verts2, verts1);
|
||||
case 2:
|
||||
if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, verts2, verts1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var x1:Number = x - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.x > x1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.x < x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var x1:Number = x + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.x < x1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.x > x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var y1:Number = y - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.y > y1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.y < y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var y1:Number = y + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.y < y1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.y > y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var z1:Number = z - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.z > z1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.z < z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var z1:Number = z + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.z < z1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.z > z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет точку столкновения рёбер двух боксов.
|
||||
*
|
||||
* @param box1 первый бокс
|
||||
* @param box2 второй бокс
|
||||
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
|
||||
* @param axisIdx1 индекс направляющей оси ребра первого бокса
|
||||
* @param axisIdx2 индекс направляющей оси ребра второго бокса
|
||||
* @param contactInfo структура, в которую записывается информация о столкновении
|
||||
*/
|
||||
private function findEdgesIntersection(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
|
||||
box1.transform.getAxis(axisIdx1, axis10);
|
||||
box2.transform.getAxis(axisIdx2, axis20);
|
||||
colAxis.vCross2(axis10, axis20).vNormalize();
|
||||
// Разворот оси в сторону первого бокса
|
||||
if (colAxis.vDot(vectorToBox1) < 0) colAxis.vReverse();
|
||||
|
||||
/* Кодирование рёбер
|
||||
бит 0: 1 (тип контакта ребро-ребро)
|
||||
биты 1-2: индекс направляющей оси ребра
|
||||
бит 3:
|
||||
бит 4:
|
||||
бит 5:
|
||||
*/
|
||||
var edgeCode1:int = 1;
|
||||
var edgeCode2:int = 1;
|
||||
|
||||
// Находим среднюю точку на каждом из пересекающихся рёбер
|
||||
var halfLen1:Number;
|
||||
var halfLen2:Number;
|
||||
point1.vCopy(box1.hs);
|
||||
point2.vCopy(box2.hs);
|
||||
// x
|
||||
if (axisIdx1 == 0) {
|
||||
point1.x = 0;
|
||||
halfLen1 = box1.hs.x;
|
||||
} else {
|
||||
box1.transform.getAxis(0, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.x = -point1.x;
|
||||
edgeCode1 |= 8; // 1 << 3
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 0) {
|
||||
point2.x = 0;
|
||||
halfLen2 = box2.hs.x;
|
||||
} else {
|
||||
box2.transform.getAxis(0, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) {
|
||||
point2.x = -point2.x;
|
||||
edgeCode2 |= 8; // 1 << 3
|
||||
}
|
||||
}
|
||||
// y
|
||||
if (axisIdx1 == 1) {
|
||||
point1.y = 0;
|
||||
halfLen1 = box1.hs.y;
|
||||
edgeCode1 |= 2; // 1 << 1
|
||||
} else {
|
||||
box1.transform.getAxis(1, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.y = -point1.y;
|
||||
edgeCode1 |= 16; // 1 << 4
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 1) {
|
||||
point2.y = 0;
|
||||
halfLen2 = box2.hs.y;
|
||||
edgeCode2 |= 2; // 1 << 1
|
||||
} else {
|
||||
box2.transform.getAxis(1, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) {
|
||||
point2.y = -point2.y;
|
||||
edgeCode2 |= 16; // 1 << 4
|
||||
}
|
||||
}
|
||||
// z
|
||||
if (axisIdx1 == 2) {
|
||||
point1.z = 0;
|
||||
halfLen1 = box1.hs.z;
|
||||
edgeCode1 |= 4; // 2 << 1
|
||||
} else {
|
||||
box1.transform.getAxis(2, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.z = -point1.z;
|
||||
edgeCode1 |= 32; // 1 << 5
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 2) {
|
||||
point2.z = 0;
|
||||
halfLen2 = box2.hs.z;
|
||||
edgeCode2 |= 4; // 2 << 1
|
||||
} else {
|
||||
box2.transform.getAxis(2, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) {
|
||||
point2.z = -point2.z;
|
||||
edgeCode2 |= 32; // 1 << 5
|
||||
}
|
||||
}
|
||||
// Получаем глобальные координаты средних точек рёбер
|
||||
point1.vTransformBy4(box1.transform);
|
||||
point2.vTransformBy4(box2.transform);
|
||||
// Находим точку пересечения рёбер, решая систему уравнений
|
||||
var k:Number = axis10.vDot(axis20);
|
||||
var det:Number = k*k - 1;
|
||||
vector.vDiff(point2, point1);
|
||||
var c1:Number = axis10.vDot(vector);
|
||||
var c2:Number = axis20.vDot(vector);
|
||||
var t1:Number = (c2*k - c1)/det;
|
||||
var t2:Number = (c2 - c1*k)/det;
|
||||
// Запись данных о столкновении
|
||||
contact.normal.vCopy(colAxis);
|
||||
contact.pcount = 1;
|
||||
var cp:ContactPoint = contact.points[0];
|
||||
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
|
||||
cp.pos.x = 0.5*(point1.x + axis10.x*t1 + point2.x + axis20.x*t2);
|
||||
cp.pos.y = 0.5*(point1.y + axis10.y*t1 + point2.y + axis20.y*t2);
|
||||
cp.pos.z = 0.5*(point1.z + axis10.z*t1 + point2.z + axis20.z*t2);
|
||||
cp.r1.vDiff(cp.pos, pos1);
|
||||
cp.r2.vDiff(cp.pos, pos2);
|
||||
cp.penetration = minOverlap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет пересечение боксов вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
|
||||
*
|
||||
* @param box1
|
||||
* @param box2
|
||||
* @param axis
|
||||
* @param axisIndex
|
||||
* @param vectorToBox1
|
||||
* @param bestAxis
|
||||
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
|
||||
*/
|
||||
private function testAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, axisIndex:int, vectorToBox1:Vector3):Boolean {
|
||||
if (axis.vLengthSqr() < 0.0001) {
|
||||
return true;
|
||||
}
|
||||
axis.vNormalize();
|
||||
|
||||
var overlap:Number = overlapOnAxis(box1, box2, axis, vectorToBox1);
|
||||
if (overlap < -tolerance) return false;
|
||||
if (overlap + tolerance < minOverlap) {
|
||||
minOverlap = overlap;
|
||||
bestAxisIndex = axisIndex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет глубину перекрытия двух боксов вдоль заданной оси.
|
||||
*
|
||||
* @param box1 первый бокс
|
||||
* @param box2 второй бокс
|
||||
* @param axis ось
|
||||
* @param vectorToBox1 вектор, соединяющий центр второго бокса с центром первого
|
||||
* @return величина перекрытия боксов вдоль оси
|
||||
*/
|
||||
public function overlapOnAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, vectorToBox1: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 = vectorToBox1.x*axis.x + vectorToBox1.y*axis.y + vectorToBox1.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;
|
||||
|
||||
public var feature1:int;
|
||||
public var feature2:int;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
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 collide(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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
734
0.0.1.0/src/alternativa/physics/collision/BoxRectCollider.as
Normal file
734
0.0.1.0/src/alternativa/physics/collision/BoxRectCollider.as
Normal file
@@ -0,0 +1,734 @@
|
||||
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 implements ICollider {
|
||||
|
||||
private var tolerance:Number = 0.001;
|
||||
|
||||
private var bPos:Vector3 = new Vector3();
|
||||
private var rPos:Vector3 = new Vector3();
|
||||
|
||||
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 colAxis:Vector3 = new Vector3();
|
||||
private var tmpAxis:Vector3 = new Vector3();
|
||||
private var vector:Vector3 = new Vector3();
|
||||
private var point1:Vector3 = new Vector3();
|
||||
private var point2:Vector3 = new Vector3();
|
||||
|
||||
private var bestAxisIndex:int;
|
||||
private var minOverlap:Number;
|
||||
|
||||
private var verts1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
private var verts2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function BoxRectCollider() {
|
||||
for (var i:int = 0; i < 8; i++) {
|
||||
verts1[i] = new Vector3();
|
||||
verts2[i] = new Vector3();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @param contact
|
||||
* @return
|
||||
*/
|
||||
public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):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;
|
||||
}
|
||||
|
||||
// Вектор из центра прямоугольника в центр бокса
|
||||
box.transform.getAxis(3, bPos);
|
||||
rect.transform.getAxis(3, rPos);
|
||||
vectorToBox.vDiff(bPos, rPos);
|
||||
|
||||
// Проверка пересечения по нормали прямоугольника
|
||||
rect.transform.getAxis(2, axis22);
|
||||
if (!testAxis(box, rect, axis22, 0, vectorToBox)) return false;
|
||||
|
||||
// Проверка пересечения по основным осям бокса
|
||||
box.transform.getAxis(0, axis10);
|
||||
if (!testAxis(box, rect, axis10, 1, vectorToBox)) return false;
|
||||
box.transform.getAxis(1, axis11);
|
||||
if (!testAxis(box, rect, axis11, 2, vectorToBox)) return false;
|
||||
box.transform.getAxis(2, axis12);
|
||||
if (!testAxis(box, rect, axis12, 3, vectorToBox)) return false;
|
||||
|
||||
// Получаем направляющие рёбер прямоугольника
|
||||
rect.transform.getAxis(0, axis20);
|
||||
rect.transform.getAxis(1, axis21);
|
||||
|
||||
// Проверка производных осей
|
||||
if (!testAxis(box, rect, axis.vCross2(axis10, axis20), 4, vectorToBox)) return false;
|
||||
if (!testAxis(box, rect, axis.vCross2(axis10, axis21), 5, vectorToBox)) return false;
|
||||
|
||||
if (!testAxis(box, rect, axis.vCross2(axis11, axis20), 6, vectorToBox)) return false;
|
||||
if (!testAxis(box, rect, axis.vCross2(axis11, axis21), 7, vectorToBox)) return false;
|
||||
|
||||
if (!testAxis(box, rect, axis.vCross2(axis12, axis20), 8, vectorToBox)) return false;
|
||||
if (!testAxis(box, rect, axis.vCross2(axis12, axis21), 9, vectorToBox)) return false;
|
||||
|
||||
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 box бокс
|
||||
* @param rect прямоугольник
|
||||
* @param vectorToBox вектор, направленный из центра прямоугольника в центр бокса
|
||||
* @param faceAxisIdx индекс оси, идентифицирующей полскость столкновения (грань бокса или полскость прямоугольника)
|
||||
* @param colInfo структура, в которую записывается информация о точках контакта
|
||||
*/
|
||||
private function findFaceContactPoints(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, faceAxisIdx:int, colInfo:Contact):Boolean {
|
||||
var pnum:int, i:int, v:Vector3, cp:ContactPoint;
|
||||
if (faceAxisIdx == 0) {
|
||||
// Столкновение с плоскостью прямоугольника
|
||||
|
||||
// Проверим положение бокса относительно плоскости прямоугольника
|
||||
rect.transform.getAxis(2, colAxis);
|
||||
var offset:Number = colAxis.vDot(rPos);
|
||||
if (bPos.vDot(colAxis) < offset) return false;
|
||||
|
||||
// Ищем ось бокса, определяющую наиболее антипараллельную грань
|
||||
var incFaceAxisIdx:int = 0;
|
||||
var maxDot:Number = 0;
|
||||
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
|
||||
box.transform.getAxis(axisIdx, axis);
|
||||
var dot:Number = axis.vDot(colAxis);
|
||||
if (dot < 0) dot = -dot;
|
||||
if (dot > maxDot) {
|
||||
maxDot = dot;
|
||||
incFaceAxisIdx = axisIdx;
|
||||
}
|
||||
}
|
||||
// Получаем список вершин грани бокса, переводим их в систему координат прямоугольника и выполняем обрезку
|
||||
// по прямоугольнику. Таким образом получается список потенциальных точек контакта.
|
||||
box.transform.getAxis(incFaceAxisIdx, axis);
|
||||
getFaceVertsByAxis(box.hs, incFaceAxisIdx, axis.vDot(colAxis) > 0, verts1);
|
||||
box.transform.transformVectors(verts1, verts2);
|
||||
rect.transform.transformVectorsInverse(verts2, verts1);
|
||||
pnum = clipByRect(rect.hs);
|
||||
// Проверяем каждую потенциальную точку на принадлежность нижней полуплоскости прямоугольника и добавляем такие точки в список контактов
|
||||
colInfo.pcount = 0;
|
||||
for (i = 0; i < pnum; i++) {
|
||||
v = verts1[i];
|
||||
if (v.z < tolerance) {
|
||||
cp = colInfo.points[colInfo.pcount++];
|
||||
rect.transform.transformVector(v, cp.pos);
|
||||
cp.r1.vDiff(cp.pos, bPos);
|
||||
cp.r2.vDiff(cp.pos, rPos);
|
||||
cp.penetration = -v.z;
|
||||
}
|
||||
}
|
||||
colInfo.normal.vCopy(colAxis);
|
||||
} else {
|
||||
// Столкновение с гранью бокса
|
||||
faceAxisIdx--;
|
||||
box.transform.getAxis(faceAxisIdx, colAxis);
|
||||
var faceReversed:Boolean = colAxis.vDot(vectorToBox) > 0;
|
||||
if (!faceReversed) colAxis.vReverse();
|
||||
|
||||
rect.transform.getAxis(2, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) return false;
|
||||
|
||||
getFaceVertsByAxis(rect.hs, 2, false, verts1);
|
||||
rect.transform.transformVectors(verts1, verts2);
|
||||
box.transform.transformVectorsInverse(verts2, verts1);
|
||||
pnum = clipByBox(box.hs, faceAxisIdx);
|
||||
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
|
||||
var pen:Number;
|
||||
colInfo.pcount = 0;
|
||||
for (i = 0; i < pnum; i++) {
|
||||
if ((pen = getPointBoxPenetration(box.hs, verts1[i], faceAxisIdx, faceReversed)) > -tolerance) {
|
||||
cp = colInfo.points[colInfo.pcount++];
|
||||
box.transform.transformVector(verts1[i], cp.pos);
|
||||
cp.r1.vDiff(cp.pos, bPos);
|
||||
cp.r2.vDiff(cp.pos, rPos);
|
||||
cp.penetration = pen;
|
||||
}
|
||||
}
|
||||
colInfo.normal.vCopy(colAxis);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
|
||||
*
|
||||
* @param box бокс, в котором ишутся вершины
|
||||
* @param axisIdx индекс нормальной оси
|
||||
* @param reverse если указано значение true, возвращаются вершины противоположной грани
|
||||
* @param result список, в который помещаются вершины
|
||||
*/
|
||||
private function getFaceVertsByAxis(hs:Vector3, axisIdx:int, reverse:Boolean, result:Vector.<Vector3>):void {
|
||||
switch (axisIdx) {
|
||||
case 0:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (reverse) {
|
||||
(result[0] as Vector3).vReset(-hs.x, hs.y, -hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, hs.y, -hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, -hs.y, -hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, -hs.y, -hs.z);
|
||||
} else {
|
||||
(result[0] as Vector3).vReset(-hs.x, -hs.y, hs.z);
|
||||
(result[1] as Vector3).vReset(hs.x, -hs.y, hs.z);
|
||||
(result[2] as Vector3).vReset(hs.x, hs.y, hs.z);
|
||||
(result[3] as Vector3).vReset(-hs.x, hs.y, hs.z);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет обрезку грани, заданной списком вершин в поле объекта 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, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, verts2, verts1);
|
||||
case 1:
|
||||
if ((pnum = clipLowZ(-hs.z, pnum, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighX(hs.x, pnum, verts2, verts1);
|
||||
case 2:
|
||||
if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, verts2, verts1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hs
|
||||
* @return
|
||||
*/
|
||||
private function clipByRect(hs:Vector3):int {
|
||||
var pnum:int = 4;
|
||||
if ((pnum = clipLowX(-hs.x, pnum, verts1, verts2)) == 0) return 0;
|
||||
if ((pnum = clipHighX(hs.x, pnum, verts2, verts1)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, verts1, verts2)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, verts2, verts1);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var x1:Number = x - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.x > x1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.x < x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var x1:Number = x + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.x < x1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.x > x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var y1:Number = y - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.y > y1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.y < y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var y1:Number = y + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.y < y1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.y > y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipLowZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var z1:Number = z - tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.z > z1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.z < z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function clipHighZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>):int {
|
||||
var z1:Number = z + tolerance;
|
||||
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;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.z < z1) {
|
||||
(result[num++] as Vector3).vCopy(p1);
|
||||
if (p2.z > z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, 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;
|
||||
(result[num++] as Vector3).vReset(p1.x + t*dx, p1.y + t*dy, p1.z + t*dz);
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет точку столкновения рёбер двух боксов.
|
||||
*
|
||||
* @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, colInfo:Contact):void {
|
||||
box.transform.getAxis(axisIdx1, axis10);
|
||||
rect.transform.getAxis(axisIdx2, axis20);
|
||||
colAxis.vCross2(axis10, axis20).vNormalize();
|
||||
// Разворот оси в сторону бокса
|
||||
if (colAxis.vDot(vectorToBox) < 0) colAxis.vReverse();
|
||||
|
||||
/* Кодирование рёбер
|
||||
бит 0: 1 (тип контакта ребро-ребро)
|
||||
биты 1-2: индекс направляющей оси ребра
|
||||
бит 3:
|
||||
бит 4:
|
||||
бит 5:
|
||||
*/
|
||||
var edgeCode1:int = 1;
|
||||
var edgeCode2:int = 1;
|
||||
|
||||
// Находим среднюю точку на каждом из пересекающихся рёбер
|
||||
var halfLen1:Number;
|
||||
var halfLen2:Number;
|
||||
point1.vCopy(box.hs);
|
||||
point2.vCopy(rect.hs);
|
||||
// x
|
||||
if (axisIdx1 == 0) {
|
||||
point1.x = 0;
|
||||
halfLen1 = box.hs.x;
|
||||
} else {
|
||||
box.transform.getAxis(0, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.x = -point1.x;
|
||||
edgeCode1 |= 8; // 1 << 3
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 0) {
|
||||
point2.x = 0;
|
||||
halfLen2 = rect.hs.x;
|
||||
} else {
|
||||
rect.transform.getAxis(0, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) {
|
||||
point2.x = -point2.x;
|
||||
edgeCode2 |= 8; // 1 << 3
|
||||
}
|
||||
}
|
||||
// y
|
||||
if (axisIdx1 == 1) {
|
||||
point1.y = 0;
|
||||
halfLen1 = box.hs.y;
|
||||
edgeCode1 |= 2; // 1 << 1
|
||||
} else {
|
||||
box.transform.getAxis(1, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.y = -point1.y;
|
||||
edgeCode1 |= 16; // 1 << 4
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 1) {
|
||||
point2.y = 0;
|
||||
halfLen2 = rect.hs.y;
|
||||
edgeCode2 |= 2; // 1 << 1
|
||||
} else {
|
||||
rect.transform.getAxis(1, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) < 0) {
|
||||
point2.y = -point2.y;
|
||||
edgeCode2 |= 16; // 1 << 4
|
||||
}
|
||||
}
|
||||
// z
|
||||
if (axisIdx1 == 2) {
|
||||
point1.z = 0;
|
||||
halfLen1 = box.hs.z;
|
||||
edgeCode1 |= 4; // 2 << 1
|
||||
} else {
|
||||
box.transform.getAxis(2, tmpAxis);
|
||||
if (tmpAxis.vDot(colAxis) > 0) {
|
||||
point1.z = -point1.z;
|
||||
edgeCode1 |= 32; // 1 << 5
|
||||
}
|
||||
}
|
||||
// Получаем глобальные координаты средних точек рёбер
|
||||
point1.vTransformBy4(box.transform);
|
||||
point2.vTransformBy4(rect.transform);
|
||||
// Находим точку пересечения рёбер, решая систему уравнений
|
||||
var k:Number = axis10.vDot(axis20);
|
||||
var det:Number = k*k - 1;
|
||||
vector.vDiff(point2, point1);
|
||||
var c1:Number = axis10.vDot(vector);
|
||||
var c2:Number = axis20.vDot(vector);
|
||||
var t1:Number = (c2*k - c1)/det;
|
||||
var t2:Number = (c2 - c1*k)/det;
|
||||
// Запись данных о столкновении
|
||||
colInfo.normal.vCopy(colAxis);
|
||||
colInfo.pcount = 1;
|
||||
var cp:ContactPoint = colInfo.points[0];
|
||||
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
|
||||
cp.pos.x = 0.5*(point1.x + axis10.x*t1 + point2.x + axis20.x*t2);
|
||||
cp.pos.y = 0.5*(point1.y + axis10.y*t1 + point2.y + axis20.y*t2);
|
||||
cp.pos.z = 0.5*(point1.z + axis10.z*t1 + point2.z + axis20.z*t2);
|
||||
cp.r1.vDiff(cp.pos, bPos);
|
||||
cp.r2.vDiff(cp.pos, rPos);
|
||||
cp.penetration = minOverlap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет пересечение вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
|
||||
*
|
||||
* @param box
|
||||
* @param rect
|
||||
* @param axis
|
||||
* @param axisIndex
|
||||
* @param vectorToBox
|
||||
* @param bestAxis
|
||||
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
|
||||
*/
|
||||
private function testAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
|
||||
if (axis.vLengthSqr() < 0.0001) return true;
|
||||
axis.vNormalize();
|
||||
|
||||
var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
|
||||
if (overlap < -tolerance) return false;
|
||||
if (overlap + tolerance < 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
104
0.0.1.0/src/alternativa/physics/collision/BoxSphereCollider.as
Normal file
104
0.0.1.0/src/alternativa/physics/collision/BoxSphereCollider.as
Normal file
@@ -0,0 +1,104 @@
|
||||
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 collide(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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.rigid.Contact;
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
|
||||
public class BoxTriangleCollider implements ICollider {
|
||||
public function BoxTriangleCollider() {
|
||||
}
|
||||
|
||||
public function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package alternativa.physics.collision {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
import alternativa.physics.collision.types.RayIntersection;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
16
0.0.1.0/src/alternativa/physics/collision/CollisionKdNode.as
Normal file
16
0.0.1.0/src/alternativa/physics/collision/CollisionKdNode.as
Normal file
@@ -0,0 +1,16 @@
|
||||
package alternativa.physics.collision {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
|
||||
public class CollisionKdNode {
|
||||
public var objects:Vector.<int>;
|
||||
public var boundBox:BoundBox;
|
||||
public var parent:CollisionKdNode;
|
||||
|
||||
public var axis:int = -1; // 0 - x, 1 - y, 2 - z
|
||||
public var coord:Number;
|
||||
public var positiveNode:CollisionKdNode;
|
||||
public var negativeNode:CollisionKdNode;
|
||||
}
|
||||
}
|
||||
259
0.0.1.0/src/alternativa/physics/collision/CollisionKdTree.as
Normal file
259
0.0.1.0/src/alternativa/physics/collision/CollisionKdTree.as
Normal file
@@ -0,0 +1,259 @@
|
||||
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.01;
|
||||
public var minPrimitivesPerNode:int = 1;
|
||||
|
||||
public var rootNode:CollisionKdNode;
|
||||
public var staticChildren:Vector.<CollisionPrimitive> = new Vector.<CollisionPrimitive>();
|
||||
public var numStaticChildren:int;
|
||||
private var staticBoundBoxes:Vector.<BoundBox> = new Vector.<BoundBox>();
|
||||
|
||||
/**
|
||||
* @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.objects = 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.objects[i] = i;
|
||||
}
|
||||
staticBoundBoxes.length = numStaticChildren;
|
||||
// Разделяем рутовую ноду
|
||||
splitNode(rootNode);
|
||||
}
|
||||
|
||||
private var splitAxis:int;
|
||||
private var splitCoord:Number;
|
||||
private var splitCost:Number;
|
||||
static private const nodeBoundBoxThreshold:BoundBox = new BoundBox();
|
||||
static private const splitCoordsX:Vector.<Number> = new Vector.<Number>();
|
||||
static private const splitCoordsY:Vector.<Number> = new Vector.<Number>();
|
||||
static private const splitCoordsZ:Vector.<Number> = new Vector.<Number>();
|
||||
/**
|
||||
* @param node
|
||||
*/
|
||||
private function splitNode(node:CollisionKdNode):void {
|
||||
if (node.objects.length <= minPrimitivesPerNode) return;
|
||||
|
||||
var objects:Vector.<int> = node.objects;
|
||||
var i:int, j:int, k:int, length:int = objects.length, c1:Number, c2:Number, ct1:Number, ct2:Number, area:Number, areaNegative:Number, areaPositive:Number, numNegative:int, numPositive:int, conflict:Boolean, cost:Number;
|
||||
|
||||
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 numSplitCoordsX:int = 0, numSplitCoordsY:int = 0, numSplitCoordsZ:int = 0;
|
||||
for (i = 0; i < length; i++) {
|
||||
var boundBox:BoundBox = staticBoundBoxes[objects[i]];
|
||||
if (boundBox.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = boundBox.minX;
|
||||
if (boundBox.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = boundBox.maxX;
|
||||
|
||||
if (boundBox.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = boundBox.minY;
|
||||
if (boundBox.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = boundBox.maxY;
|
||||
|
||||
if (boundBox.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.minZ;
|
||||
if (boundBox.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.maxZ;
|
||||
}
|
||||
|
||||
// Убираем дубликаты координат, ищем наилучший сплит
|
||||
splitAxis = -1; splitCost = 1e308;
|
||||
i = 0; area = (nodeBoundBox.maxY - nodeBoundBox.minY)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
|
||||
while (i < numSplitCoordsX) {
|
||||
if (isNaN(c1 = splitCoordsX[i++])) continue;
|
||||
ct1 = c1 - threshold;
|
||||
ct2 = c1 + threshold;
|
||||
areaNegative = area*(c1 - nodeBoundBox.minX);
|
||||
areaPositive = area*(nodeBoundBox.maxX - c1);
|
||||
numNegative = numPositive = 0;
|
||||
conflict = false;
|
||||
// Проверяем объекты
|
||||
for (j = 0; j < length; j++) {
|
||||
boundBox = staticBoundBoxes[objects[j]];
|
||||
if (boundBox.maxX <= ct2) {
|
||||
if (boundBox.minX < ct1) numNegative++;
|
||||
} else {
|
||||
if (boundBox.minX >= ct1) numPositive++; else {conflict = true; break;}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
|
||||
splitCost = cost;
|
||||
splitAxis = 0;
|
||||
splitCoord = c1;
|
||||
}
|
||||
j = i;
|
||||
while (++j < numSplitCoordsX) if ((c2 = splitCoordsX[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsX[j] = NaN;
|
||||
}
|
||||
i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxZ - nodeBoundBox.minZ);
|
||||
while (i < numSplitCoordsY) {
|
||||
if (isNaN(c1 = splitCoordsY[i++])) continue;
|
||||
ct1 = c1 - threshold;
|
||||
ct2 = c1 + threshold;
|
||||
areaNegative = area*(c1 - nodeBoundBox.minY);
|
||||
areaPositive = area*(nodeBoundBox.maxY - c1);
|
||||
numNegative = numPositive = 0;
|
||||
conflict = false;
|
||||
// Проверяем объекты
|
||||
for (j = 0; j < length; j++) {
|
||||
boundBox = staticBoundBoxes[objects[j]];
|
||||
if (boundBox.maxY <= ct2) {
|
||||
if (boundBox.minY < ct1) numNegative++;
|
||||
} else {
|
||||
if (boundBox.minY >= ct1) numPositive++; else {conflict = true; break;}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
|
||||
splitCost = cost;
|
||||
splitAxis = 1;
|
||||
splitCoord = c1;
|
||||
}
|
||||
j = i;
|
||||
while (++j < numSplitCoordsY) if ((c2 = splitCoordsY[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsY[j] = NaN;
|
||||
}
|
||||
i = 0; area = (nodeBoundBox.maxX - nodeBoundBox.minX)*(nodeBoundBox.maxY - nodeBoundBox.minY);
|
||||
while (i < numSplitCoordsZ) {
|
||||
if (isNaN(c1 = splitCoordsZ[i++])) continue;
|
||||
ct1 = c1 - threshold;
|
||||
ct2 = c1 + threshold;
|
||||
areaNegative = area*(c1 - nodeBoundBox.minZ);
|
||||
areaPositive = area*(nodeBoundBox.maxZ - c1);
|
||||
numNegative = numPositive = 0;
|
||||
conflict = false;
|
||||
// Проверяем объекты
|
||||
for (j = 0; j < length; j++) {
|
||||
boundBox = staticBoundBoxes[objects[j]];
|
||||
if (boundBox.maxZ <= ct2) {
|
||||
if (boundBox.minZ < ct1) numNegative++;
|
||||
} else {
|
||||
if (boundBox.minZ >= ct1) numPositive++; else {conflict = true; break;}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
if (!conflict && (cost = areaNegative*numNegative + areaPositive*numPositive) < splitCost) {
|
||||
splitCost = cost;
|
||||
splitAxis = 2;
|
||||
splitCoord = c1;
|
||||
}
|
||||
j = i;
|
||||
while (++j < numSplitCoordsZ) if ((c2 = splitCoordsZ[j]) >= c1 - threshold && c2 <= c1 + threshold) splitCoordsZ[j] = NaN;
|
||||
}
|
||||
|
||||
// Если сплит не найден, выходим
|
||||
if (splitAxis < 0) return;
|
||||
|
||||
// Разделяем ноду
|
||||
var axisX:Boolean = splitAxis == 0, axisY:Boolean = splitAxis == 1;
|
||||
node.axis = splitAxis;
|
||||
node.coord = splitCoord;
|
||||
|
||||
// Создаём дочерние ноды
|
||||
node.negativeNode = new CollisionKdNode();
|
||||
node.positiveNode = new CollisionKdNode();
|
||||
node.negativeNode.parent = node;
|
||||
node.positiveNode.parent = node;
|
||||
node.negativeNode.boundBox = nodeBoundBox.clone();
|
||||
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;
|
||||
|
||||
// Распределяем объекты по дочерним нодам
|
||||
ct1 = splitCoord - threshold; ct2 = splitCoord + threshold;
|
||||
for (i = 0; i < length; i++) {
|
||||
boundBox = staticBoundBoxes[objects[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 <= ct2) {
|
||||
// Объект в негативной стороне
|
||||
if (node.negativeNode.objects == null) node.negativeNode.objects = new Vector.<int>();
|
||||
node.negativeNode.objects.push(objects[i]);
|
||||
objects[i] = -1;
|
||||
} else {
|
||||
if (min >= ct1) {
|
||||
// Объект в положительной стороне
|
||||
if (node.positiveNode.objects == null) node.positiveNode.objects = new Vector.<int>();
|
||||
node.positiveNode.objects.push(objects[i]);
|
||||
objects[i] = -1;
|
||||
} else {
|
||||
// Распилился
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Очистка списка объектов
|
||||
j = 0;
|
||||
for (i = 0; i < length; i++) {
|
||||
if (objects[i] >= 0) objects[j++] = objects[i];
|
||||
}
|
||||
if (j > 0) objects.length = j; else node.objects = null;
|
||||
|
||||
// Разделение дочерних нод
|
||||
if (node.negativeNode.objects != null) splitNode(node.negativeNode);
|
||||
if (node.positiveNode.objects != null) splitNode(node.positiveNode);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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.objects);
|
||||
traceNode(str + "-", node.negativeNode);
|
||||
traceNode(str + "+", node.positiveNode);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
12
0.0.1.0/src/alternativa/physics/collision/ICollider.as
Normal file
12
0.0.1.0/src/alternativa/physics/collision/ICollider.as
Normal file
@@ -0,0 +1,12 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
import alternativa.physics.rigid.Contact;
|
||||
|
||||
/**
|
||||
* Интерфейс определителя столкновений между двумя примитивами.
|
||||
*/
|
||||
public interface ICollider {
|
||||
function collide(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
import alternativa.physics.collision.types.RayIntersection;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
|
||||
public interface ICollisionPredicate {
|
||||
|
||||
function considerCollision(primitive:CollisionPrimitive):Boolean;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.rigid.Body;
|
||||
|
||||
public interface IRayCollisionPredicate {
|
||||
function considerBody(body:Body):Boolean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
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.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.PLANE, new BoxPlaneCollider());
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.SPHERE, new BoxSphereCollider());
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.RECT, new BoxRectCollider());
|
||||
|
||||
// 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();
|
||||
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.collide(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 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 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.objects != null) {
|
||||
// Поиск столкновений со статическими примитивами узла
|
||||
var primitives:Vector.<CollisionPrimitive> = tree.staticChildren;
|
||||
var indices:Vector.<int> = node.objects;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Тест пересечения луча с динамическими примитивами.
|
||||
*
|
||||
* @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 minTime:Number = maxTime + 1;
|
||||
for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
|
||||
var primitive:CollisionPrimitive = dynamicPrimitives[i];
|
||||
if ((primitive.collisionGroup & collisionGroup) == 0) continue;
|
||||
if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
|
||||
var t:Number = primitive.getSegmentIntersection(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.objects != null && getRayNodeIntersection(origin, dir, collisionGroup, tree.staticChildren, node.objects, 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.getSegmentIntersection(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;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
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 collide(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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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 collide(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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 124
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives
|
||||
END
|
||||
CollisionTriangle.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 145
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionTriangle.as
|
||||
END
|
||||
CollisionSphere.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 143
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionSphere.as
|
||||
END
|
||||
CollisionBox.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 140
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionBox.as
|
||||
END
|
||||
CollisionPrimitive.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 146
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionPrimitive.as
|
||||
END
|
||||
CollisionRect.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 141
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives/CollisionRect.as
|
||||
END
|
||||
@@ -0,0 +1,88 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/primitives
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2009-06-08T07:53:17.170410Z
|
||||
14100
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
CollisionTriangle.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
a1ccd11eb25d172d2739fce7fef21ee2
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
CollisionSphere.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
1ca67429cc8bf27435b6ec46f8d6f80a
|
||||
2009-05-26T08:41:33.471001Z
|
||||
13389
|
||||
mike
|
||||
|
||||
CollisionBox.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
e6a441a4317416b0d13d365f5b26ace4
|
||||
2009-06-08T07:53:17.170410Z
|
||||
14100
|
||||
mike
|
||||
|
||||
CollisionPrimitive.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
09336a8257b1c0e46da8f1d11975b5b1
|
||||
2009-06-08T07:53:17.170410Z
|
||||
14100
|
||||
mike
|
||||
|
||||
CollisionRect.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
c76d5cb0626964578f2df221c8072142
|
||||
2009-06-08T07:53:17.170410Z
|
||||
14100
|
||||
mike
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -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();
|
||||
|
||||
// Используются в определении пересечения с лучом
|
||||
private static var _o:Vector3 = new Vector3();
|
||||
private static var _v: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 threshold погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную
|
||||
* погрешность, считается равной нулю.
|
||||
* @param normal возвращаемое значение. Нормаль к примитиву в точке пересечения с сегментом.
|
||||
* @return в случае наличия пересечения возвращается время точки пересечения, в противном случае возвращается -1.
|
||||
*/
|
||||
override public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
|
||||
var tMin:Number = -1, tMax:Number = 1e308, t1:Number, t2:Number;
|
||||
// Перевод параметров сегмента в систему координат примитива
|
||||
// Inlined transform.transformVectorInverse(origin, _o);
|
||||
var xx:Number = origin.x - transform.d;
|
||||
var yy:Number = origin.y - transform.h;
|
||||
var zz:Number = origin.z - transform.l;
|
||||
_o.x = transform.a*xx + transform.e*yy + transform.i*zz;
|
||||
_o.y = transform.b*xx + transform.f*yy + transform.j*zz;
|
||||
_o.z = transform.c*xx + transform.g*yy + transform.k*zz;
|
||||
// Inlined transform.deltaTransformVectorInverse(vector, _v);
|
||||
_v.x = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
|
||||
_v.y = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
|
||||
_v.z = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
|
||||
// X
|
||||
if (_v.x < threshold && _v.x > -threshold) {
|
||||
if (_o.x < -hs.x || _o.x > hs.x) return -1;
|
||||
} else {
|
||||
t1 = (-hs.x - _o.x)/_v.x;
|
||||
t2 = (hs.x - _o.x)/_v.x;
|
||||
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 (_v.y < threshold && _v.y > -threshold) {
|
||||
if (_o.y < -hs.y || _o.y > hs.y) return -1;
|
||||
} else {
|
||||
t1 = (-hs.y - _o.y)/_v.y;
|
||||
t2 = (hs.y - _o.y)/_v.y;
|
||||
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 (_v.z < threshold && _v.z > -threshold) {
|
||||
if (_o.z < -hs.z || _o.z > hs.z) return -1;
|
||||
} else {
|
||||
t1 = (-hs.z - _o.z)/_v.z;
|
||||
t2 = (hs.z - _o.z)/_v.z;
|
||||
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);
|
||||
xx = normal.x, yy = normal.y, zz = normal.z;
|
||||
normal.x = transform.a*xx + transform.b*yy + transform.c*zz;
|
||||
normal.y = transform.e*xx + transform.f*yy + transform.g*zz;
|
||||
normal.z = transform.i*xx + transform.j*yy + transform.k*zz;
|
||||
|
||||
return tMin;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package alternativa.physics.collision.primitives {
|
||||
|
||||
import alternativa.physics.altphysics;
|
||||
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;
|
||||
|
||||
use namespace altphysics;
|
||||
|
||||
/**
|
||||
* Базовый класс для примитивов, использующихся детектором столкновений.
|
||||
*/
|
||||
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;
|
||||
|
||||
// Тело, владеющее примитивом. Может быть null.
|
||||
altphysics var body:Body;
|
||||
// Трансформация примитива в системе координат тела, если оно указано. Не допускается масштабирование матрицы.
|
||||
altphysics var localTransform:Matrix4 = new Matrix4();
|
||||
// Полная трансформация примитива. Не допускается масштабирование матрицы.
|
||||
altphysics var transform:Matrix4 = new Matrix4();
|
||||
// AABB в мировой системе координат. Расчитывается системой вызовом функции calculateBoundBox().
|
||||
altphysics 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 (this.localTransform == null) this.localTransform = new Matrix4();
|
||||
if (localTransform != null) this.localTransform.copy(localTransform);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Наследники должны переопределять этот метод, реализуя в нём корректный расчёт ограничивающего бокса, выравненного
|
||||
* по осям мировой системы координат.
|
||||
*
|
||||
* @return ссылка на свой баунд бокс
|
||||
*/
|
||||
public function calculateAABB():BoundBox {
|
||||
return aabb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирует примитив. Переопределять не рекомендуется. Вместо этого переопределяются методы 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 {
|
||||
type = source.type;
|
||||
transform.copy(source.transform);
|
||||
collisionGroup = source.collisionGroup;
|
||||
setBody(source.body, source.localTransform);
|
||||
aabb.copyFrom(source.aabb);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр примитива соотвествующего типа.
|
||||
*
|
||||
* @return новый экземпляр примитива
|
||||
*/
|
||||
protected function createPrimitive():CollisionPrimitive {
|
||||
return new CollisionPrimitive(type, collisionGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод должен вычислять параметры пересечения заданного сегмента с примитивом.
|
||||
*
|
||||
* @param origin начальная точка сегмента в мировых координатах
|
||||
* @param vector вектор сегмента в мировых координатах
|
||||
* @param threshold погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную
|
||||
* погрешность, считается равной нулю.
|
||||
* @param normal возвращаемое значение. Нормаль к примитиву в точке пересечения с сегментом.
|
||||
* @return в случае наличия пересечения возвращается время точки пересечения, в противном случае возвращается -1.
|
||||
*/
|
||||
public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
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.01;
|
||||
// Переменные используются в функции поиска пересечения с лучом
|
||||
private static var _o:Vector3 = new Vector3();
|
||||
private static var _v:Vector3 = new Vector3();
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр примитива.
|
||||
*
|
||||
* @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 getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
|
||||
// Перевод параметров сегмента в систему координат примитива
|
||||
// Inlined transform.transformVectorInverse(origin, _o);
|
||||
var xx:Number = origin.x - transform.d;
|
||||
var yy:Number = origin.y - transform.h;
|
||||
var zz:Number = origin.z - transform.l;
|
||||
_o.x = transform.a*xx + transform.e*yy + transform.i*zz;
|
||||
_o.y = transform.b*xx + transform.f*yy + transform.j*zz;
|
||||
_o.z = transform.c*xx + transform.g*yy + transform.k*zz;
|
||||
// Inlined transform.deltaTransformVectorInverse(vector, _v);
|
||||
_v.x = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
|
||||
_v.y = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
|
||||
_v.z = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
|
||||
|
||||
// Проверка параллельности сегмента и плоскости примитива
|
||||
if (_v.z > -threshold && _v.z < threshold) return -1;
|
||||
var t:Number = -_o.z/_v.z;
|
||||
if (t < 0) return -1;
|
||||
// Проверка вхождения точки пересечения в прямоугольник
|
||||
_o.x += _v.x*t;
|
||||
_o.y += _v.y*t;
|
||||
_o.z = 0;
|
||||
if (_o.x < (-hs.x - EPSILON) || _o.x > (hs.x + EPSILON) || _o.y < (-hs.y - EPSILON) || _o.y > (hs.y + EPSILON)) return -1;
|
||||
|
||||
normal.x = transform.c;
|
||||
normal.y = transform.g;
|
||||
normal.z = transform.k;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 getSegmentIntersection(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package alternativa.physics.collision.primitives {
|
||||
import alternativa.physics.altphysics;
|
||||
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;
|
||||
|
||||
public function CollisionTriangle(v0:Vector3, v1:Vector3, v2:Vector3, collisionGroup:int) {
|
||||
super(TRIANGLE, collisionGroup);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
// Используются в определении пересечения с лучом
|
||||
private static var _o:Vector3 = new Vector3();
|
||||
private static var _v: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 threshold погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную
|
||||
* погрешность, считается равной нулю.
|
||||
* @param normal возвращаемое значение. Нормаль к примитиву в точке пересечения с сегментом.
|
||||
* @return в случае наличия пересечения возвращается время точки пересечения, в противном случае возвращается -1.
|
||||
*/
|
||||
override public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
|
||||
var tMin:Number = -1, tMax:Number = 1e308, t1:Number, t2:Number;
|
||||
// Перевод параметров сегмента в систему координат примитива
|
||||
// Inlined transform.transformVectorInverse(origin, _o);
|
||||
var xx:Number = origin.x - transform.d;
|
||||
var yy:Number = origin.y - transform.h;
|
||||
var zz:Number = origin.z - transform.l;
|
||||
_o.x = transform.a*xx + transform.e*yy + transform.i*zz;
|
||||
_o.y = transform.b*xx + transform.f*yy + transform.j*zz;
|
||||
_o.z = transform.c*xx + transform.g*yy + transform.k*zz;
|
||||
// Inlined transform.deltaTransformVectorInverse(vector, _v);
|
||||
_v.x = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
|
||||
_v.y = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
|
||||
_v.z = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
|
||||
// X
|
||||
if (_v.x < threshold && _v.x > -threshold) {
|
||||
if (_o.x < -hs.x || _o.x > hs.x) return -1;
|
||||
} else {
|
||||
t1 = (-hs.x - _o.x)/_v.x;
|
||||
t2 = (hs.x - _o.x)/_v.x;
|
||||
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 (_v.y < threshold && _v.y > -threshold) {
|
||||
if (_o.y < -hs.y || _o.y > hs.y) return -1;
|
||||
} else {
|
||||
t1 = (-hs.y - _o.y)/_v.y;
|
||||
t2 = (hs.y - _o.y)/_v.y;
|
||||
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 (_v.z < threshold && _v.z > -threshold) {
|
||||
if (_o.z < -hs.z || _o.z > hs.z) return -1;
|
||||
} else {
|
||||
t1 = (-hs.z - _o.z)/_v.z;
|
||||
t2 = (hs.z - _o.z)/_v.z;
|
||||
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);
|
||||
xx = normal.x, yy = normal.y, zz = normal.z;
|
||||
normal.x = transform.a*xx + transform.b*yy + transform.c*zz;
|
||||
normal.y = transform.e*xx + transform.f*yy + transform.g*zz;
|
||||
normal.z = transform.i*xx + transform.j*yy + transform.k*zz;
|
||||
|
||||
return tMin;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package alternativa.physics.collision.primitives {
|
||||
|
||||
import alternativa.physics.altphysics;
|
||||
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;
|
||||
|
||||
use namespace altphysics;
|
||||
|
||||
/**
|
||||
* Базовый класс для примитивов, использующихся детектором столкновений.
|
||||
*/
|
||||
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;
|
||||
|
||||
// Тело, владеющее примитивом. Может быть null.
|
||||
altphysics var body:Body;
|
||||
// Трансформация примитива в системе координат тела, если оно указано. Не допускается масштабирование матрицы.
|
||||
altphysics var localTransform:Matrix4 = new Matrix4();
|
||||
// Полная трансформация примитива. Не допускается масштабирование матрицы.
|
||||
altphysics var transform:Matrix4 = new Matrix4();
|
||||
// AABB в мировой системе координат. Расчитывается системой вызовом функции calculateBoundBox().
|
||||
altphysics 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 (this.localTransform == null) this.localTransform = new Matrix4();
|
||||
if (localTransform != null) this.localTransform.copy(localTransform);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Наследники должны переопределять этот метод, реализуя в нём корректный расчёт ограничивающего бокса, выравненного
|
||||
* по осям мировой системы координат.
|
||||
*
|
||||
* @return ссылка на свой баунд бокс
|
||||
*/
|
||||
public function calculateAABB():BoundBox {
|
||||
return aabb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирует примитив. Переопределять не рекомендуется. Вместо этого переопределяются методы 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 {
|
||||
type = source.type;
|
||||
transform.copy(source.transform);
|
||||
collisionGroup = source.collisionGroup;
|
||||
setBody(source.body, source.localTransform);
|
||||
aabb.copyFrom(source.aabb);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр примитива соотвествующего типа.
|
||||
*
|
||||
* @return новый экземпляр примитива
|
||||
*/
|
||||
protected function createPrimitive():CollisionPrimitive {
|
||||
return new CollisionPrimitive(type, collisionGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод должен вычислять параметры пересечения заданного сегмента с примитивом.
|
||||
*
|
||||
* @param origin начальная точка сегмента в мировых координатах
|
||||
* @param vector вектор сегмента в мировых координатах
|
||||
* @param threshold погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную
|
||||
* погрешность, считается равной нулю.
|
||||
* @param normal возвращаемое значение. Нормаль к примитиву в точке пересечения с сегментом.
|
||||
* @return в случае наличия пересечения возвращается время точки пересечения, в противном случае возвращается -1.
|
||||
*/
|
||||
public function getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
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.01;
|
||||
// Переменные используются в функции поиска пересечения с лучом
|
||||
private static var _o:Vector3 = new Vector3();
|
||||
private static var _v:Vector3 = new Vector3();
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр примитива.
|
||||
*
|
||||
* @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 getSegmentIntersection(origin:Vector3, vector:Vector3, threshold:Number, normal:Vector3):Number {
|
||||
// Перевод параметров сегмента в систему координат примитива
|
||||
// Inlined transform.transformVectorInverse(origin, _o);
|
||||
var xx:Number = origin.x - transform.d;
|
||||
var yy:Number = origin.y - transform.h;
|
||||
var zz:Number = origin.z - transform.l;
|
||||
_o.x = transform.a*xx + transform.e*yy + transform.i*zz;
|
||||
_o.y = transform.b*xx + transform.f*yy + transform.j*zz;
|
||||
_o.z = transform.c*xx + transform.g*yy + transform.k*zz;
|
||||
// Inlined transform.deltaTransformVectorInverse(vector, _v);
|
||||
_v.x = transform.a*vector.x + transform.e*vector.y + transform.i*vector.z;
|
||||
_v.y = transform.b*vector.x + transform.f*vector.y + transform.j*vector.z;
|
||||
_v.z = transform.c*vector.x + transform.g*vector.y + transform.k*vector.z;
|
||||
|
||||
// Проверка параллельности сегмента и плоскости примитива
|
||||
if (_v.z > -threshold && _v.z < threshold) return -1;
|
||||
var t:Number = -_o.z/_v.z;
|
||||
if (t < 0) return -1;
|
||||
// Проверка вхождения точки пересечения в прямоугольник
|
||||
_o.x += _v.x*t;
|
||||
_o.y += _v.y*t;
|
||||
_o.z = 0;
|
||||
if (_o.x < (-hs.x - EPSILON) || _o.x > (hs.x + EPSILON) || _o.y < (-hs.y - EPSILON) || _o.y > (hs.y + EPSILON)) return -1;
|
||||
|
||||
normal.x = transform.c;
|
||||
normal.y = transform.g;
|
||||
normal.z = transform.k;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 getSegmentIntersection(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package alternativa.physics.collision.primitives {
|
||||
import alternativa.physics.altphysics;
|
||||
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;
|
||||
|
||||
public function CollisionTriangle(v0:Vector3, v1:Vector3, v2:Vector3, collisionGroup:int) {
|
||||
super(TRIANGLE, collisionGroup);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 119
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/types
|
||||
END
|
||||
BoundBox.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 131
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/types/BoundBox.as
|
||||
END
|
||||
RayIntersection.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 138
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/types/RayIntersection.as
|
||||
END
|
||||
Ray.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 126
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/types/Ray.as
|
||||
END
|
||||
64
0.0.1.0/src/alternativa/physics/collision/types/.svn/entries
Normal file
64
0.0.1.0/src/alternativa/physics/collision/types/.svn/entries
Normal file
@@ -0,0 +1,64 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/collision/types
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
BoundBox.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
13aaaf1b1923de8a2579543651734ab7
|
||||
2009-05-18T16:37:02.465501Z
|
||||
13087
|
||||
mike
|
||||
|
||||
RayIntersection.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
a0a4cf3dd94468fdf44cd06734927c94
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
Ray.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
55eeb1cf79a416bb863bee95f089dbcd
|
||||
2009-04-17T17:39:21.367569Z
|
||||
11341
|
||||
mike
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,72 @@
|
||||
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, tolerance:Number):Boolean {
|
||||
return !(minX > bb.maxX + tolerance || maxX < bb.minX - tolerance || minY > bb.maxY + tolerance || maxY < bb.minY - tolerance || minZ > bb.maxZ + tolerance || maxZ < bb.minZ - tolerance);
|
||||
}
|
||||
|
||||
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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
72
0.0.1.0/src/alternativa/physics/collision/types/BoundBox.as
Normal file
72
0.0.1.0/src/alternativa/physics/collision/types/BoundBox.as
Normal file
@@ -0,0 +1,72 @@
|
||||
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, tolerance:Number):Boolean {
|
||||
return !(minX > bb.maxX + tolerance || maxX < bb.minX - tolerance || minY > bb.maxY + tolerance || maxY < bb.minY - tolerance || minZ > bb.maxZ + tolerance || maxZ < bb.minZ - tolerance);
|
||||
}
|
||||
|
||||
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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
13
0.0.1.0/src/alternativa/physics/collision/types/Ray.as
Normal file
13
0.0.1.0/src/alternativa/physics/collision/types/Ray.as
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
47
0.0.1.0/src/alternativa/physics/rigid/.svn/all-wcprops
Normal file
47
0.0.1.0/src/alternativa/physics/rigid/.svn/all-wcprops
Normal file
@@ -0,0 +1,47 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 109
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid
|
||||
END
|
||||
Body.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 117
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/Body.as
|
||||
END
|
||||
BodyState.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 122
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/BodyState.as
|
||||
END
|
||||
PhysicsUtils.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 125
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/PhysicsUtils.as
|
||||
END
|
||||
ContactPoint.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 125
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/ContactPoint.as
|
||||
END
|
||||
RigidWorld.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 123
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/RigidWorld.as
|
||||
END
|
||||
Contact.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 120
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/Contact.as
|
||||
END
|
||||
BodyMaterial.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 125
|
||||
/!svn/ver/14887/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid/BodyMaterial.as
|
||||
END
|
||||
118
0.0.1.0/src/alternativa/physics/rigid/.svn/entries
Normal file
118
0.0.1.0/src/alternativa/physics/rigid/.svn/entries
Normal file
@@ -0,0 +1,118 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.1.0/src/alternativa/physics/rigid
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
Body.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
43a884d83ae7d8a1d25eb9f1d26eb755
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
BodyState.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
8a089205beccac13a05eccfb9cfc6387
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
PhysicsUtils.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
6d88004ddab2ae19e0d15f5b1b7c016a
|
||||
2009-04-26T21:40:17.813142Z
|
||||
12023
|
||||
mike
|
||||
|
||||
constraints
|
||||
dir
|
||||
|
||||
ContactPoint.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
a94e73e3bfc85be2a79d6cc939512d91
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
RigidWorld.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
ee25e8fe15553bfa5789bf0e99c1524b
|
||||
2009-06-08T06:46:18.310732Z
|
||||
14099
|
||||
mike
|
||||
|
||||
primitives
|
||||
dir
|
||||
|
||||
Contact.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
d65ad3d488d4a06addcbfa4aaf958f10
|
||||
2009-04-13T03:04:08.898183Z
|
||||
11018
|
||||
mike
|
||||
|
||||
BodyMaterial.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:39.000000Z
|
||||
49c7a55b5b26fdb03a60f24bac460f53
|
||||
2009-04-07T08:34:06.342994Z
|
||||
10587
|
||||
mike
|
||||
|
||||
1
0.0.1.0/src/alternativa/physics/rigid/.svn/format
Normal file
1
0.0.1.0/src/alternativa/physics/rigid/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,329 @@
|
||||
package alternativa.physics.rigid {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.physics.altphysics;
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
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;
|
||||
|
||||
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 prim
|
||||
*/
|
||||
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).prepend(baseMatrix.transpose());
|
||||
baseMatrix.transpose();
|
||||
if (collisionPrimitives != null) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package alternativa.physics.rigid {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.physics.types.Vector3;
|
||||
|
||||
/**
|
||||
* Информация о контакте между двумя телами. Содержит нормаль, множество точек контакта и прочие величины.
|
||||
*/
|
||||
public class Contact {
|
||||
|
||||
// private static var pool:Vector.<ContactInfo> = new Vector.<ContactInfo>();
|
||||
//
|
||||
// public static function create():ContactInfo {
|
||||
// if (pool.length > 0) {
|
||||
// return pool.pop();
|
||||
// }
|
||||
// return new ContactInfo();
|
||||
// }
|
||||
//
|
||||
// public static function destroy(collInfo:ContactInfo):void {
|
||||
// collInfo.body1 = collInfo.body2 = null;
|
||||
// pool.push(collInfo);
|
||||
// }
|
||||
|
||||
// Максимальное количество точек контакта
|
||||
private const N:int = 8;
|
||||
|
||||
public var body1:Body;
|
||||
public var body2:Body;
|
||||
// Взаимный коэффициент отскока
|
||||
public var restitution:Number;
|
||||
// Взаимный коэффициент трения
|
||||
public var friction:Number;
|
||||
// Нормаль контакта. Направлена от второго тела к первому.
|
||||
public var normal:Vector3 = new Vector3();
|
||||
// Список точек контакта
|
||||
public var points:Vector.<ContactPoint> = new Vector.<ContactPoint>(N, true);
|
||||
// Количество точек контакта
|
||||
public var pcount:int;
|
||||
// Максимальная глубина пересечения тел
|
||||
public var maxPenetration:Number = 0;
|
||||
|
||||
public var satisfied:Boolean;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function Contact() {
|
||||
for (var i:int = 0; i < N; i++) points[i] = new ContactPoint();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package alternativa.physics.rigid {
|
||||
import alternativa.physics.types.Vector3;
|
||||
|
||||
public class ContactPoint {
|
||||
|
||||
public var pos:Vector3 = new Vector3();
|
||||
public var penetration:Number;
|
||||
|
||||
public var feature1:int;
|
||||
public var feature2:int;
|
||||
|
||||
// Величины, расчитываемые перед началом фазы решения контактов
|
||||
|
||||
// Требуемая проекция конечной скорости на нормаль для упругого контакта
|
||||
public var normalVel:Number;
|
||||
// Минимальная скорость разделения неупругого контакта
|
||||
public var minSepVel:Number;
|
||||
// Изменение проекции скорости на единицу нормального импульса
|
||||
public var velByUnitImpulseN:Number;
|
||||
|
||||
public var angularInertia1:Number;
|
||||
public var angularInertia2:Number;
|
||||
|
||||
// Радиус-вектор точки контакта относительно центра первого тела
|
||||
public var r1:Vector3 = new Vector3();
|
||||
// Радиус-вектор точки контакта относительно центра второго тела
|
||||
public var r2:Vector3 = new Vector3();
|
||||
|
||||
// Величины, накапливаемые во время фазы решения контактов
|
||||
|
||||
// Накопленный импульс, применяемый для получения требуемой относительной скорости в точке контакта
|
||||
public var accumImpulseN:Number;
|
||||
// Накопленный импульс, применяемый для разделения тел в точке контакта. Не создаёт момента.
|
||||
// public var accumSepImpulse:Number;
|
||||
|
||||
public var satisfied:Boolean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param cp
|
||||
*/
|
||||
public function copyFrom(cp:ContactPoint):void {
|
||||
pos.vCopy(cp.pos);
|
||||
penetration = cp.penetration;
|
||||
feature1 = cp.feature1;
|
||||
feature2 = cp.feature2;
|
||||
r1.vCopy(cp.r1);
|
||||
r2.vCopy(cp.r2);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package alternativa.physics.rigid {
|
||||
import alternativa.physics.types.Matrix3;
|
||||
import alternativa.physics.types.Vector3;
|
||||
|
||||
public class PhysicsUtils {
|
||||
|
||||
/**
|
||||
* @param mass
|
||||
* @param halfSize
|
||||
* @param result
|
||||
*/
|
||||
public static function getBoxInvInertia(mass:Number, halfSize:Vector3, result:Matrix3):void {
|
||||
if (mass <= 0) throw new ArgumentError();
|
||||
/* Момент инерции бокса:
|
||||
|
||||
m*(hy*hy + hz*hz)/3 0 0
|
||||
0 m*(hz*hz + hx*hx)/3 0
|
||||
0 0 m*(hx*hx + hy*hy)/3
|
||||
|
||||
hx, hy, hz -- половина размера бокса вдоль соответствующей оси
|
||||
*/
|
||||
result.copy(Matrix3.ZERO);
|
||||
if (mass == Infinity) return;
|
||||
var xx:Number = halfSize.x*halfSize.x;
|
||||
var yy:Number = halfSize.y*halfSize.y;
|
||||
var zz:Number = halfSize.z*halfSize.z;
|
||||
result.a = 3/(mass*(yy + zz));
|
||||
result.f = 3/(mass*(zz + xx));
|
||||
result.k = 3/(mass*(xx + yy));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mass
|
||||
* @param r
|
||||
* @param h
|
||||
* @param result
|
||||
*/
|
||||
public static function getCylinderInvInertia(mass:Number, r:Number, h:Number, result:Matrix3):void {
|
||||
if (mass <= 0) throw new ArgumentError();
|
||||
|
||||
result.copy(Matrix3.ZERO);
|
||||
if (mass == Infinity) return;
|
||||
|
||||
result.a = result.f = 1/(mass*(h*h/12 + r*r/4));
|
||||
result.k = 2/(mass*r*r);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,603 @@
|
||||
package alternativa.physics.rigid {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.physics.altphysics;
|
||||
import alternativa.physics.collision.ICollisionDetector;
|
||||
import alternativa.physics.collision.KdTreeCollisionDetector;
|
||||
import alternativa.physics.rigid.constraints.Constraint;
|
||||
import alternativa.physics.types.Vector3;
|
||||
|
||||
use namespace altphysics;
|
||||
|
||||
/**
|
||||
* Класс реализует физическую симуляцию поведения твёрдых тел.
|
||||
*/
|
||||
public class RigidWorld {
|
||||
|
||||
private static var lastBodyId:int;
|
||||
|
||||
// Максимальное количество контактов
|
||||
altphysics const MAX_CONTACTS:int = 1000;
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Настроечные параметры симуляции. Могут быть изменены в любой
|
||||
// момент времени без нарушения корректной работы симуляции.
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Количество шагов, за которое пересекающиеся тела должны разделиться
|
||||
public var penResolutionSteps:int = 10;
|
||||
// Величина допустимой глубины пересечения
|
||||
public var allowedPenetration:Number = 0.1;
|
||||
// Максимальная скорость, добавляемая с целью разделения тел
|
||||
public var maxPenResolutionSpeed:Number = 0.5;
|
||||
// Количество итераций для обработки упругих контактов
|
||||
public var collisionIterations:int = 5;
|
||||
// Количество итераций для обработки неупругих контактов
|
||||
public var contactIterations:int = 5;
|
||||
// Флаг использования предсказания состояний
|
||||
public var usePrediction:Boolean = false;
|
||||
|
||||
// Переменные для процедуры разделения тел путём непосредственного их перемещенеия.
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// !!! Экспериментально !!!
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!
|
||||
public var staticSeparationIterations:int = 10;
|
||||
public var staticSeparationSteps:int = 10;
|
||||
public var maxAngleMove:Number = 10;
|
||||
public var useStaticSeparation:Boolean = false;
|
||||
|
||||
// Вектор гравитации
|
||||
altphysics var _gravity:Vector3 = new Vector3(0, 0, -9.8);
|
||||
// Использующийся детектор столкновений
|
||||
altphysics var collisionDetector:ICollisionDetector;
|
||||
// Список тел, участвующих в симуляции
|
||||
altphysics var bodies:Vector.<Body> = new Vector.<Body>();
|
||||
// Количество тел, участвующих в симуляции
|
||||
altphysics var bodiesNum:int;
|
||||
// Список контактов на текущем шаге симуляции
|
||||
altphysics var contacts:Vector.<Contact> = new Vector.<Contact>(MAX_CONTACTS, true);
|
||||
// Количество контактов на текущем шаге симуляции
|
||||
altphysics var contactsNum:int;
|
||||
// Список ограничений
|
||||
altphysics var constraints:Vector.<Constraint> = new Vector.<Constraint>();
|
||||
// Количество ограничений
|
||||
altphysics var constraintsNum:int;
|
||||
// Временная метка. Число прошедших шагов с начала симуляции.
|
||||
altphysics var timeStamp:uint;
|
||||
|
||||
// Временные переменные для избежания создания экземпляров
|
||||
private var _r:Vector3 = new Vector3();
|
||||
private var _t:Vector3 = new Vector3();
|
||||
private var _v:Vector3 = new Vector3();
|
||||
private var _v1:Vector3 = new Vector3();
|
||||
private var _v2:Vector3 = new Vector3();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function RigidWorld() {
|
||||
for (var i:int = 0; i < MAX_CONTACTS; i++) contacts[i] = new Contact();
|
||||
collisionDetector = new KdTreeCollisionDetector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Вектор гравитации.
|
||||
*/
|
||||
public function get gravity():Vector3 {
|
||||
return _gravity.vClone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set gravity(value:Vector3):void {
|
||||
_gravity.vCopy(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет тело в симуляцию.
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
public function addBody(body:Body, isStatic:Boolean):Boolean {
|
||||
var i:int = bodies.indexOf(body);
|
||||
if (i > -1) return false;
|
||||
bodies[bodiesNum++] = body;
|
||||
body.world = this;
|
||||
body.id = lastBodyId++;
|
||||
if (body.collisionPrimitives != null) {
|
||||
for (i = 0; i < body.collisionPrimitivesNum; i++) collisionDetector.addPrimitive(body.collisionPrimitives[i], isStatic);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет тело из симуляции.
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
public function removeBody(body:Body, isStatic:Boolean):Boolean {
|
||||
var i:int = bodies.indexOf(body);
|
||||
if (i == -1) return false;
|
||||
bodies.splice(i, 1);
|
||||
body.world = null;
|
||||
if (body.collisionPrimitives != null) {
|
||||
for (i = 0; i < body.collisionPrimitivesNum; i++) collisionDetector.removePrimitive(body.collisionPrimitives[i], isStatic);
|
||||
}
|
||||
bodiesNum--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет ограничение.
|
||||
* @param c
|
||||
*/
|
||||
public function addConstraint(c:Constraint):void {
|
||||
constraints[constraintsNum++] = c;
|
||||
c.world = this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет ограничение.
|
||||
* @param c
|
||||
*/
|
||||
public function removeConstraint(c:Constraint):Boolean {
|
||||
var idx:int = constraints.indexOf(c);
|
||||
if (idx < 0) return false;
|
||||
constraints.splice(idx, 1)
|
||||
constraintsNum--;
|
||||
c.world = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Применяет к телам действующте на них силы. Аккумуляторы сил и моментов тел очищаются после завершения шага
|
||||
* симуляции, поэтому на момент вызова метода могут уже содержать некоторые значения.
|
||||
*
|
||||
* @param dt промежуток времени, в течении которого действуют силы
|
||||
*/
|
||||
private function applyForces(dt:Number):void {
|
||||
for (var i:int = 0; i < bodiesNum; i++) {
|
||||
var body:Body = bodies[i];
|
||||
body.addExternalForces(dt);
|
||||
body.calcAccelerations();
|
||||
// Ускорение свободного падения применяется только к подвижным телам во избежание некорректного изменения
|
||||
// фиктивной скорости неподвижных тел.
|
||||
if (body.movable) body.accel.vAdd(_gravity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Определяет все столкновения на текущем шаге симуляции и заполняет список получившихся контактов.
|
||||
*
|
||||
* @param dt длительность шага симуляции
|
||||
*/
|
||||
private function detectCollisions(dt:Number):void {
|
||||
var i:int;
|
||||
var j:int;
|
||||
var body:Body;
|
||||
|
||||
for (i = 0; i < bodiesNum; i++) {
|
||||
body = bodies[i];
|
||||
body.contactsNum = 0;
|
||||
body.saveState();
|
||||
// При включённом режиме предсказания состояние тел интегрируется на один шаг вперёд
|
||||
if (usePrediction) {
|
||||
body.integrateVelocity(dt);
|
||||
body.integratePosition(dt);
|
||||
}
|
||||
body.calcDerivedData();
|
||||
}
|
||||
|
||||
contactsNum = collisionDetector.getAllCollisions(contacts);
|
||||
|
||||
// Расчёт относительных векторов точки контакта вынесен сюда из-за необходимости учитывать
|
||||
// положение тел в предсказанном состоянии
|
||||
for (i = 0; i < contactsNum; i++) {
|
||||
var contact:Contact = contacts[i];
|
||||
var b1:Body = contact.body1;
|
||||
var b2:Body = contact.body2;
|
||||
for (j = 0; j < contact.pcount; j++) {
|
||||
var cp:ContactPoint = contact.points[j];
|
||||
cp.r1.x = cp.pos.x - b1.state.pos.x;
|
||||
cp.r1.y = cp.pos.y - b1.state.pos.y;
|
||||
cp.r1.z = cp.pos.z - b1.state.pos.z;
|
||||
if (b2 != null) {
|
||||
cp.r2.x = cp.pos.x - b2.state.pos.x;
|
||||
cp.r2.y = cp.pos.y - b2.state.pos.y;
|
||||
cp.r2.z = cp.pos.z - b2.state.pos.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Восстановление состояния тел
|
||||
if (usePrediction) {
|
||||
for (i = 0; i < bodiesNum; i++) {
|
||||
body = bodies[i];
|
||||
body.restoreState();
|
||||
body.calcDerivedData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Подготваливает полученные из детектора столкновений контакты, расчитывая значения, не меняющиеся
|
||||
* в ходе шага симуляции.
|
||||
*/
|
||||
private function preProcessContacts(dt:Number):void {
|
||||
var i:int;
|
||||
for (i = 0; i < contactsNum; i++) {
|
||||
var contact:Contact = contacts[i];
|
||||
var b1:Body = contact.body1;
|
||||
var b2:Body = contact.body2;
|
||||
contact.restitution = b1.material.restitution;
|
||||
if (b2 != null && b2.material.restitution < contact.restitution) contact.restitution = b2.material.restitution;
|
||||
contact.friction = b1.material.friction;
|
||||
if (b2 != null && b2.material.friction < contact.friction) contact.friction = b2.material.friction;
|
||||
for (var j:int = 0; j < contact.pcount; j++) {
|
||||
var cp:ContactPoint = contact.points[j];
|
||||
cp.accumImpulseN = 0;
|
||||
// Расчитываем изменение нормальной скорости на единицу нормального импульса
|
||||
// dV = b.invMass + ((invI * (r % n)) % r) * n
|
||||
cp.velByUnitImpulseN = 0;
|
||||
if (b1.movable) {
|
||||
cp.angularInertia1 = _v.vCross2(cp.r1, contact.normal).vTransformBy3(b1.invInertiaWorld).vCross(cp.r1).vDot(contact.normal);
|
||||
cp.velByUnitImpulseN += b1.invMass + cp.angularInertia1;
|
||||
}
|
||||
if (b2 != null && b2.movable) {
|
||||
cp.angularInertia2 = _v.vCross2(cp.r2, contact.normal).vTransformBy3(b2.invInertiaWorld).vCross(cp.r2).vDot(contact.normal);
|
||||
cp.velByUnitImpulseN += b2.invMass + cp.angularInertia2;
|
||||
}
|
||||
// Расчёт требуемой конечной скорости для упругого контакта
|
||||
calcSepVelocity(b1, b2, cp, _v);
|
||||
cp.normalVel = _v.vDot(contact.normal);
|
||||
if (cp.normalVel < 0) cp.normalVel = - contact.restitution*cp.normalVel;
|
||||
// Скорость разделения неупругого контакта
|
||||
cp.minSepVel = cp.penetration > allowedPenetration ? (cp.penetration - allowedPenetration)/(penResolutionSteps*dt) : 0;
|
||||
if (cp.minSepVel > maxPenResolutionSpeed) cp.minSepVel = maxPenResolutionSpeed;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < constraintsNum; i++) (constraints[i] as Constraint).preProcess(dt);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dt
|
||||
* @param forceInelastic
|
||||
*/
|
||||
private function processContacts(dt:Number, forceInelastic:Boolean):void {
|
||||
var iterNum:int = forceInelastic ? contactIterations : collisionIterations;
|
||||
var i:int;
|
||||
var forwardLoop:Boolean = false;
|
||||
for (var iter:int = 0; iter < iterNum; iter++) {
|
||||
forwardLoop = !forwardLoop;
|
||||
for (i = 0; i < contactsNum; i++) resolveContact(contacts[i], forceInelastic, forwardLoop);
|
||||
// Ограничения
|
||||
for (i = 0; i < constraintsNum; i++) (constraints[i] as Constraint).apply(dt);
|
||||
}
|
||||
|
||||
// Разделение контактов путём непосредственного изменения координат и ориентации. Экспериментально.
|
||||
if (forceInelastic && useStaticSeparation) performStaticSeparation();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function resolveContact(contactInfo:Contact, forceInelastic:Boolean, forwardLoop:Boolean):void {
|
||||
var b1:Body = contactInfo.body1;
|
||||
var b2:Body = contactInfo.body2;
|
||||
var normal:Vector3 = contactInfo.normal;
|
||||
var i:int;
|
||||
if (forwardLoop) {
|
||||
for (i = 0; i < contactInfo.pcount; i++) resolveContactPoint(i, b1, b2, contactInfo, normal, forceInelastic);
|
||||
} else {
|
||||
for (i = contactInfo.pcount - 1; i >= 0; i--) resolveContactPoint(i, b1, b2, contactInfo, normal, forceInelastic);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param idx
|
||||
* @param b1
|
||||
* @param b2
|
||||
* @param colInfo
|
||||
* @param normal
|
||||
* @param forceInelastic
|
||||
*/
|
||||
private function resolveContactPoint(idx:int, b1:Body, b2:Body, contact:Contact, normal:Vector3, forceInelastic:Boolean):void {
|
||||
var cp:ContactPoint = contact.points[idx];
|
||||
if (!forceInelastic) cp.satisfied = true;
|
||||
|
||||
var newVel:Number = 0;
|
||||
calcSepVelocity(b1, b2, cp, _v);
|
||||
var sepVel:Number = _v.vDot(contact.normal);
|
||||
if (forceInelastic) {
|
||||
var minSpeVel:Number = useStaticSeparation ? 0 : cp.minSepVel;
|
||||
if (sepVel < minSpeVel) cp.satisfied = false;
|
||||
else if (cp.satisfied) return;
|
||||
newVel = minSpeVel;
|
||||
} else {
|
||||
newVel = cp.normalVel;
|
||||
}
|
||||
var deltaVel:Number = newVel - sepVel;
|
||||
var impulse:Number = deltaVel/cp.velByUnitImpulseN;
|
||||
var accumImpulse:Number = cp.accumImpulseN + impulse;
|
||||
if (accumImpulse < 0) accumImpulse = 0;
|
||||
var deltaImpulse:Number = accumImpulse - cp.accumImpulseN;
|
||||
cp.accumImpulseN = accumImpulse;
|
||||
// Применяем импульс к телам
|
||||
if (b1.movable) b1.applyRelPosWorldImpulse(cp.r1, normal, deltaImpulse);
|
||||
if (b2 != null && b2.movable) b2.applyRelPosWorldImpulse(cp.r2, normal, -deltaImpulse);
|
||||
|
||||
// Учёт силы трения
|
||||
calcSepVelocity(b1, b2, cp, _v);
|
||||
// Расчитываем изменение касательной скорости на единицу касательного импульса
|
||||
var tanSpeedByUnitImpulse:Number = 0;
|
||||
_v.vAddScaled(-_v.vDot(contact.normal), contact.normal);
|
||||
var tanSpeed:Number = _v.vLength();
|
||||
if (tanSpeed < 0.001) return;
|
||||
_t.vCopy(_v).vNormalize().vReverse();
|
||||
// dV = b.invMass + ((invI * (r % t)) % r) * t
|
||||
if (b1.movable) {
|
||||
_v.vCross2(cp.r1, _t).vTransformBy3(b1.invInertiaWorld).vCross(cp.r1);
|
||||
tanSpeedByUnitImpulse += b1.invMass + _v.vDot(_t);
|
||||
}
|
||||
if (b2 != null && b2.movable) {
|
||||
_v.vCross2(cp.r2, _t).vTransformBy3(b2.invInertiaWorld).vCross(cp.r2);
|
||||
tanSpeedByUnitImpulse += b2.invMass + _v.vDot(_t);
|
||||
}
|
||||
|
||||
var tanImpulse:Number = tanSpeed/tanSpeedByUnitImpulse;
|
||||
var max:Number = contact.friction*cp.accumImpulseN;
|
||||
if (max < 0) {
|
||||
if (tanImpulse < max) tanImpulse = max;
|
||||
} else {
|
||||
if (tanImpulse > max) tanImpulse = max;
|
||||
}
|
||||
|
||||
// Применяем импульс к телам
|
||||
if (b1.movable) b1.applyRelPosWorldImpulse(cp.r1, _t, tanImpulse);
|
||||
if (b2 != null && b2.movable) b2.applyRelPosWorldImpulse(cp.r2, _t, -tanImpulse);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param cp
|
||||
* @param normal
|
||||
* @return
|
||||
*/
|
||||
private function calcSepVelocity(body1:Body, body2:Body, cp:ContactPoint, result:Vector3):void {
|
||||
// sepVel = (V1 - V2)*normal
|
||||
// V1 = V1_c + w1%r1
|
||||
result.vCopy(body1.state.velocity).vAdd(_v1.vCross2(body1.state.rotation, cp.r1));
|
||||
// V2 = V2_c + w2%r2
|
||||
if (body2 != null) result.vSubtract(body2.state.velocity).vSubtract(_v2.vCross2(body2.state.rotation, cp.r2));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dt
|
||||
*/
|
||||
private function intergateVelocities(dt:Number):void {
|
||||
for (var i:int = 0; i < bodiesNum; i++) {
|
||||
(bodies[i] as Body).integrateVelocity(dt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dt
|
||||
*/
|
||||
private function integratePositions(dt:Number):void {
|
||||
for (var i:int = 0; i < bodiesNum; i++) {
|
||||
var body:Body = bodies[i] as Body;
|
||||
if (body.movable) body.integratePosition(dt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function performStaticSeparation():void {
|
||||
var iterNum:int = staticSeparationIterations;
|
||||
// iterNum = 100;
|
||||
|
||||
// 1. В начале каждой итерации для всех контактов сбрасывается флаг satisfied с одновременным поиском наихудшего контакта, т.е. имеющего наибольшую величину пересечения тел.
|
||||
// 2. Если найденный контакт имеет величину пересечения меньше предельно допустимой процедура прерывается, т.к. разделение контактов не требуется.
|
||||
// 3. Если процедура продолжается, то выполняется разделение найденного контакта и установка его флага satisfied в true, после чего запускается внутренний цикл, состоящий
|
||||
// из contactsNum - 1 итераций. На каждой итерации ищется наихудший контакт среди оставшихся (satisfied == false). Если такой контакт найден, то выполняется его разделение
|
||||
// и переход к следующей итерации внутреннего цикла, иначе внутренний цикл прерывается и выполняется переход к следующей итерации внешнего цикла (шаг 1.).
|
||||
for (var iter:int = 0; iter < iterNum; iter++) {
|
||||
// Ищем контакт с максимальной величиной пересечения тел, одновременно сбрасывая флаги satisfied в false
|
||||
var worstContact:Contact = contacts[0];
|
||||
var i:int;
|
||||
for (i = 1; i < contactsNum; i++) {
|
||||
var contact:Contact = contacts[i];
|
||||
contact.satisfied = false;
|
||||
if (contact.maxPenetration > worstContact.maxPenetration) worstContact = contact;
|
||||
}
|
||||
if (worstContact.maxPenetration <= allowedPenetration) return;
|
||||
|
||||
resolveInterpenetration(worstContact);
|
||||
// Внутренний цикл по оставшимся контактам
|
||||
for (i = 1; i < contactsNum; i++) {
|
||||
worstContact = getWorstContact();
|
||||
if (worstContact == null) break;
|
||||
resolveInterpenetration(worstContact);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function getWorstContact():Contact {
|
||||
var maxPen:Number = 0;
|
||||
var worst:Contact = null;
|
||||
for (var i:int = 0; i < contactsNum; i++) {
|
||||
var c:Contact = contacts[i];
|
||||
if (!c.satisfied && c.maxPenetration > maxPen) {
|
||||
worst = c;
|
||||
maxPen = c.maxPenetration;
|
||||
}
|
||||
}
|
||||
return maxPen > allowedPenetration ? worst : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Разделяет указанный контакт, выполняя staticSeparationSteps итераций по списку точек контакта.
|
||||
* 1. В начале каждой итерации ищется точка с наибольшим пересечением, одновременно сбрасываются флаги satisfied у точек.
|
||||
* 2. Для найденной точки выполняется процедура разделения.
|
||||
* 3. После выполняется pcount - 1 итерация по списку точек, каждый раз ищется наихудшая среди имеющих satisfied == false, для которой выполняется процедура разделения.
|
||||
* Если наихудшая точка не найдена, то выполнется переход к следующей итерации внешнего цикла (шаг 1).
|
||||
*
|
||||
* В конце процедуры разделения обновляются значения пересечений для остальных точек контакта, а также для всех прочих контактов, относящихся к телам текущего.
|
||||
*
|
||||
* @param contact контакт для разделения
|
||||
*/
|
||||
private function resolveInterpenetration(contact:Contact):void {
|
||||
contact.satisfied = true;
|
||||
|
||||
for (var step:int = 0; step < staticSeparationSteps; step++) {
|
||||
var worstCp:ContactPoint = contact.points[0];
|
||||
var cp:ContactPoint;
|
||||
var i:int;
|
||||
for (i = 1; i < contact.pcount; i++) {
|
||||
cp = contact.points[i];
|
||||
cp.satisfied = false;
|
||||
if (cp.penetration > worstCp.penetration) worstCp = cp;
|
||||
}
|
||||
if (worstCp.penetration <= allowedPenetration) break;
|
||||
separateContactPoint(worstCp, contact);
|
||||
// Разделяем оставшиеся точки
|
||||
var maxPen:Number = 0;
|
||||
for (i = 1; i < contact.pcount; i++) {
|
||||
// Поиск наихудшей точки
|
||||
for (var j:int = 0; j < contact.pcount; j++) {
|
||||
cp = contact.points[j];
|
||||
if (cp.satisfied) continue;
|
||||
if (cp.penetration > maxPen) {
|
||||
maxPen = cp.penetration;
|
||||
worstCp = cp;
|
||||
}
|
||||
}
|
||||
if (maxPen <= allowedPenetration) break;
|
||||
separateContactPoint(worstCp, contact);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param cp
|
||||
* @param contact
|
||||
*/
|
||||
private function separateContactPoint(cp:ContactPoint, contact:Contact):void {
|
||||
cp.satisfied = true;
|
||||
|
||||
var b1:Body = contact.body1;
|
||||
var b2:Body = contact.body2;
|
||||
var totalMove:Number = cp.penetration - allowedPenetration;
|
||||
var moveCoeff:Number = totalMove/cp.velByUnitImpulseN;
|
||||
var linMove1:Number;
|
||||
var angleMove1:Number;
|
||||
if (b1.movable) {
|
||||
linMove1 = b1.invMass*moveCoeff;
|
||||
angleMove1 = cp.angularInertia1*moveCoeff;
|
||||
if (angleMove1 > maxAngleMove) {
|
||||
linMove1 += angleMove1 - maxAngleMove;
|
||||
angleMove1 -= maxAngleMove;
|
||||
}
|
||||
b1.state.pos.vAddScaled(linMove1, contact.normal);
|
||||
_v1.vCross2(cp.r1, contact.normal).vTransformBy3(b1.invInertiaWorld).vScale(angleMove1);
|
||||
b1.state.orientation.addScaledVector(_v1, 1);
|
||||
}
|
||||
var linMove2:Number;
|
||||
var angleMove2:Number;
|
||||
if (b2 != null && b2.movable) {
|
||||
linMove2 = b2.invMass*moveCoeff;
|
||||
angleMove2 = cp.angularInertia2*moveCoeff;
|
||||
if (angleMove2 > maxAngleMove) {
|
||||
linMove2 += angleMove2 - maxAngleMove;
|
||||
angleMove2 -= maxAngleMove;
|
||||
}
|
||||
b2.state.pos.vAddScaled(-linMove2, contact.normal);
|
||||
_v2.vCross2(cp.r2, contact.normal).vTransformBy3(b2.invInertiaWorld).vScale(angleMove2);
|
||||
_v2.vReverse();
|
||||
b2.state.orientation.addScaledVector(_v2, 1);
|
||||
}
|
||||
cp.penetration = allowedPenetration;
|
||||
// Обновляем пересечения в других точках
|
||||
var i:int;
|
||||
for (i = 0; i < contact.pcount; i++) {
|
||||
var cp1:ContactPoint = contact.points[i];
|
||||
if (cp1 == cp) continue;
|
||||
var angularMove:Number;
|
||||
if (b1.movable) {
|
||||
angularMove = _v.vCross2(_v1, cp1.r1).vDot(contact.normal);
|
||||
cp1.penetration -= linMove1 + angularMove;
|
||||
}
|
||||
if (b2 != null && b2.movable) {
|
||||
angularMove = _v.vCross2(_v2, cp1.r2).vDot(contact.normal);
|
||||
cp1.penetration -= linMove2 - angularMove;
|
||||
}
|
||||
// Обновление максимального значения для контакта
|
||||
if (cp1.penetration > contact.maxPenetration) contact.maxPenetration = cp1.penetration;
|
||||
}
|
||||
// Обновляем пересечения для других контактов
|
||||
var c:Contact;
|
||||
var j:int;
|
||||
if (b1.movable) {
|
||||
for (i = 0; i < b1.contactsNum; i++) {
|
||||
c = b1.contacts[i];
|
||||
if (c == contact) continue;
|
||||
for (j = 0; j < c.pcount; j++) {
|
||||
cp1 = c.points[j];
|
||||
if (b1 == c.body1) cp1.penetration -= linMove1*contact.normal.vDot(c.normal) + _v.vCross2(_v1, cp1.r1).vDot(c.normal);
|
||||
else cp1.penetration += linMove1*contact.normal.vDot(c.normal) + _v.vCross2(_v1, cp1.r2).vDot(c.normal);
|
||||
if (c.maxPenetration < cp1.penetration) c.maxPenetration = cp1.penetration;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (b2 != null && b2.movable) {
|
||||
for (i = 0; i < b2.contactsNum; i++) {
|
||||
c = b2.contacts[i];
|
||||
if (c == contact) continue;
|
||||
for (j = 0; j < c.pcount; j++) {
|
||||
cp1 = c.points[j];
|
||||
if (b2 == c.body1) cp1.penetration -= linMove2*contact.normal.vDot(c.normal) + _v.vCross2(_v2, cp1.r1).vDot(c.normal);
|
||||
else cp1.penetration += linMove2*contact.normal.vDot(c.normal) + _v.vCross2(_v2, cp1.r2).vDot(c.normal);
|
||||
if (c.maxPenetration < cp1.penetration) c.maxPenetration = cp1.penetration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private function postPhysics():void {
|
||||
for (var i:int = 0; i < bodiesNum; i++) {
|
||||
var body:Body = bodies[i];
|
||||
body.clearAccumulators();
|
||||
body.calcDerivedData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dt
|
||||
*/
|
||||
public function runPhysics(dt:Number):void {
|
||||
timeStamp++;
|
||||
applyForces(dt);
|
||||
detectCollisions(dt);
|
||||
preProcessContacts(dt);
|
||||
processContacts(dt, false);
|
||||
intergateVelocities(dt);
|
||||
processContacts(dt, true);
|
||||
integratePositions(dt);
|
||||
postPhysics();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
329
0.0.1.0/src/alternativa/physics/rigid/Body.as
Normal file
329
0.0.1.0/src/alternativa/physics/rigid/Body.as
Normal file
@@ -0,0 +1,329 @@
|
||||
package alternativa.physics.rigid {
|
||||
import __AS3__.vec.Vector;
|
||||
|
||||
import alternativa.physics.altphysics;
|
||||
import alternativa.physics.collision.primitives.CollisionPrimitive;
|
||||
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;
|
||||
|
||||
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 prim
|
||||
*/
|
||||
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).prepend(baseMatrix.transpose());
|
||||
baseMatrix.transpose();
|
||||
if (collisionPrimitives != null) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
0.0.1.0/src/alternativa/physics/rigid/BodyMaterial.as
Normal file
11
0.0.1.0/src/alternativa/physics/rigid/BodyMaterial.as
Normal 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;
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user