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.9.2/.actionScriptProperties
Normal file
15
0.0.9.2/.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>
|
||||
55
0.0.9.2/.flexLibProperties
Normal file
55
0.0.9.2/.flexLibProperties
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<flexLibProperties version="1">
|
||||
<includeClasses>
|
||||
<classEntry path="alternativa.math.Matrix3"/>
|
||||
<classEntry path="alternativa.math.Matrix4"/>
|
||||
<classEntry path="alternativa.math.Quaternion"/>
|
||||
<classEntry path="alternativa.math.Vector3"/>
|
||||
<classEntry path="alternativa.physics.Body"/>
|
||||
<classEntry path="alternativa.physics.BodyList"/>
|
||||
<classEntry path="alternativa.physics.BodyListItem"/>
|
||||
<classEntry path="alternativa.physics.BodyMaterial"/>
|
||||
<classEntry path="alternativa.physics.BodyState"/>
|
||||
<classEntry path="alternativa.physics.CollisionPrimitiveList"/>
|
||||
<classEntry path="alternativa.physics.CollisionPrimitiveListItem"/>
|
||||
<classEntry path="alternativa.physics.Contact"/>
|
||||
<classEntry path="alternativa.physics.ContactPoint"/>
|
||||
<classEntry path="alternativa.physics.PhysicsScene"/>
|
||||
<classEntry path="alternativa.physics.PhysicsUtils"/>
|
||||
<classEntry path="alternativa.physics.altphysics"/>
|
||||
<classEntry path="alternativa.physics.collision.CollisionKdNode"/>
|
||||
<classEntry path="alternativa.physics.collision.CollisionKdTree"/>
|
||||
<classEntry path="alternativa.physics.collision.CollisionKdTree2D"/>
|
||||
<classEntry path="alternativa.physics.collision.CollisionPrimitive"/>
|
||||
<classEntry path="alternativa.physics.collision.IBodyCollisionPredicate"/>
|
||||
<classEntry path="alternativa.physics.collision.ICollider"/>
|
||||
<classEntry path="alternativa.physics.collision.ICollisionDetector"/>
|
||||
<classEntry path="alternativa.physics.collision.ICollisionPredicate"/>
|
||||
<classEntry path="alternativa.physics.collision.IRayCollisionPredicate"/>
|
||||
<classEntry path="alternativa.physics.collision.KdTreeCollisionDetector"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxBoxCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxPlaneCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxRectCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxSphereCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxTriangleCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.SpherePlaneCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.SphereSphereCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.primitives.CollisionBox"/>
|
||||
<classEntry path="alternativa.physics.collision.primitives.CollisionRect"/>
|
||||
<classEntry path="alternativa.physics.collision.primitives.CollisionSphere"/>
|
||||
<classEntry path="alternativa.physics.collision.primitives.CollisionTriangle"/>
|
||||
<classEntry path="alternativa.physics.collision.types.BoundBox"/>
|
||||
<classEntry path="alternativa.physics.collision.types.Ray"/>
|
||||
<classEntry path="alternativa.physics.collision.types.RayIntersection"/>
|
||||
<classEntry path="alternativa.physics.constraints.Constraint"/>
|
||||
<classEntry path="alternativa.physics.constraints.MaxDistanceConstraint"/>
|
||||
<classEntry path="alternativa.physics.primitives.RigidBox"/>
|
||||
<classEntry path="alternativa.physics.primitives.RigidCylinder"/>
|
||||
<classEntry path="alternativa.physics.primitives.RigidPlane"/>
|
||||
<classEntry path="alternativa.physics.primitives.RigidRect"/>
|
||||
<classEntry path="alternativa.physics.primitives.RigidSphere"/>
|
||||
</includeClasses>
|
||||
<includeResources/>
|
||||
<namespaceManifests/>
|
||||
</flexLibProperties>
|
||||
19
0.0.9.2/.project
Normal file
19
0.0.9.2/.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.9.2/.settings/.svn/all-wcprops
Normal file
11
0.0.9.2/.settings/.svn/all-wcprops
Normal file
@@ -0,0 +1,11 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 89
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/.settings
|
||||
END
|
||||
org.eclipse.core.resources.prefs
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 122
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/.settings/org.eclipse.core.resources.prefs
|
||||
END
|
||||
40
0.0.9.2/.settings/.svn/entries
Normal file
40
0.0.9.2/.settings/.svn/entries
Normal file
@@ -0,0 +1,40 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/.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:44.000000Z
|
||||
4f70b476f3e5075e399505021df2f89b
|
||||
2009-04-01T12:25:29.638016Z
|
||||
10301
|
||||
mike
|
||||
|
||||
1
0.0.9.2/.settings/.svn/format
Normal file
1
0.0.9.2/.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.9.2/.settings/org.eclipse.core.resources.prefs
Normal file
3
0.0.9.2/.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
|
||||
35
0.0.9.2/.svn/all-wcprops
Normal file
35
0.0.9.2/.svn/all-wcprops
Normal file
@@ -0,0 +1,35 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 79
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2
|
||||
END
|
||||
.flexLibProperties
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 98
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/.flexLibProperties
|
||||
END
|
||||
.project
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 88
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/.project
|
||||
END
|
||||
pom.xml
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 87
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/pom.xml
|
||||
END
|
||||
.actionScriptProperties
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 103
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/.actionScriptProperties
|
||||
END
|
||||
AlternativaPhysics.iml
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 102
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/AlternativaPhysics.iml
|
||||
END
|
||||
7
0.0.9.2/.svn/dir-prop-base
Normal file
7
0.0.9.2/.svn/dir-prop-base
Normal file
@@ -0,0 +1,7 @@
|
||||
K 13
|
||||
svn:mergeinfo
|
||||
V 278
|
||||
/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.5.Optimized:19806-19860
|
||||
/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.0.7.0.LinkedList:20360-22217,22226-22252
|
||||
/platform/clients/fp10/libraries/AlternativaPhysics/branches/0.1.Vector3D:13892-14098
|
||||
END
|
||||
97
0.0.9.2/.svn/entries
Normal file
97
0.0.9.2/.svn/entries
Normal file
@@ -0,0 +1,97 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2010-03-06T18:35:38.097601Z
|
||||
29411
|
||||
mike
|
||||
has-props
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
META-INF
|
||||
dir
|
||||
|
||||
.flexLibProperties
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
b170dccb732dcb12462ac421cdca9723
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
.project
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
1e90cc68b52b93173b2b5de88eb5c3a3
|
||||
2009-04-20T17:15:55.260110Z
|
||||
11541
|
||||
mike
|
||||
|
||||
src
|
||||
dir
|
||||
|
||||
pom.xml
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
dc7965d6172d265e83100b9fdee16d5f
|
||||
2010-03-06T18:35:38.097601Z
|
||||
29411
|
||||
mike
|
||||
|
||||
.actionScriptProperties
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
b8656f882fff2baedd57a92db86fcf8c
|
||||
2010-01-14T07:05:07.823716Z
|
||||
26358
|
||||
mike
|
||||
|
||||
AlternativaPhysics.iml
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
5162d434bd39d50f59a8057c1fa2c961
|
||||
2010-02-19T13:52:11.808507Z
|
||||
28758
|
||||
mike
|
||||
|
||||
.settings
|
||||
dir
|
||||
|
||||
1
0.0.9.2/.svn/format
Normal file
1
0.0.9.2/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
15
0.0.9.2/.svn/text-base/.actionScriptProperties.svn-base
Normal file
15
0.0.9.2/.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>
|
||||
55
0.0.9.2/.svn/text-base/.flexLibProperties.svn-base
Normal file
55
0.0.9.2/.svn/text-base/.flexLibProperties.svn-base
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<flexLibProperties version="1">
|
||||
<includeClasses>
|
||||
<classEntry path="alternativa.math.Matrix3"/>
|
||||
<classEntry path="alternativa.math.Matrix4"/>
|
||||
<classEntry path="alternativa.math.Quaternion"/>
|
||||
<classEntry path="alternativa.math.Vector3"/>
|
||||
<classEntry path="alternativa.physics.Body"/>
|
||||
<classEntry path="alternativa.physics.BodyList"/>
|
||||
<classEntry path="alternativa.physics.BodyListItem"/>
|
||||
<classEntry path="alternativa.physics.BodyMaterial"/>
|
||||
<classEntry path="alternativa.physics.BodyState"/>
|
||||
<classEntry path="alternativa.physics.CollisionPrimitiveList"/>
|
||||
<classEntry path="alternativa.physics.CollisionPrimitiveListItem"/>
|
||||
<classEntry path="alternativa.physics.Contact"/>
|
||||
<classEntry path="alternativa.physics.ContactPoint"/>
|
||||
<classEntry path="alternativa.physics.PhysicsScene"/>
|
||||
<classEntry path="alternativa.physics.PhysicsUtils"/>
|
||||
<classEntry path="alternativa.physics.altphysics"/>
|
||||
<classEntry path="alternativa.physics.collision.CollisionKdNode"/>
|
||||
<classEntry path="alternativa.physics.collision.CollisionKdTree"/>
|
||||
<classEntry path="alternativa.physics.collision.CollisionKdTree2D"/>
|
||||
<classEntry path="alternativa.physics.collision.CollisionPrimitive"/>
|
||||
<classEntry path="alternativa.physics.collision.IBodyCollisionPredicate"/>
|
||||
<classEntry path="alternativa.physics.collision.ICollider"/>
|
||||
<classEntry path="alternativa.physics.collision.ICollisionDetector"/>
|
||||
<classEntry path="alternativa.physics.collision.ICollisionPredicate"/>
|
||||
<classEntry path="alternativa.physics.collision.IRayCollisionPredicate"/>
|
||||
<classEntry path="alternativa.physics.collision.KdTreeCollisionDetector"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxBoxCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxPlaneCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxRectCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxSphereCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.BoxTriangleCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.SpherePlaneCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.colliders.SphereSphereCollider"/>
|
||||
<classEntry path="alternativa.physics.collision.primitives.CollisionBox"/>
|
||||
<classEntry path="alternativa.physics.collision.primitives.CollisionRect"/>
|
||||
<classEntry path="alternativa.physics.collision.primitives.CollisionSphere"/>
|
||||
<classEntry path="alternativa.physics.collision.primitives.CollisionTriangle"/>
|
||||
<classEntry path="alternativa.physics.collision.types.BoundBox"/>
|
||||
<classEntry path="alternativa.physics.collision.types.Ray"/>
|
||||
<classEntry path="alternativa.physics.collision.types.RayIntersection"/>
|
||||
<classEntry path="alternativa.physics.constraints.Constraint"/>
|
||||
<classEntry path="alternativa.physics.constraints.MaxDistanceConstraint"/>
|
||||
<classEntry path="alternativa.physics.primitives.RigidBox"/>
|
||||
<classEntry path="alternativa.physics.primitives.RigidCylinder"/>
|
||||
<classEntry path="alternativa.physics.primitives.RigidPlane"/>
|
||||
<classEntry path="alternativa.physics.primitives.RigidRect"/>
|
||||
<classEntry path="alternativa.physics.primitives.RigidSphere"/>
|
||||
</includeClasses>
|
||||
<includeResources/>
|
||||
<namespaceManifests/>
|
||||
</flexLibProperties>
|
||||
19
0.0.9.2/.svn/text-base/.project.svn-base
Normal file
19
0.0.9.2/.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>
|
||||
65
0.0.9.2/.svn/text-base/AlternativaPhysics.iml.svn-base
Normal file
65
0.0.9.2/.svn/text-base/AlternativaPhysics.iml.svn-base
Normal file
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="flex" name="Flex">
|
||||
<configuration>
|
||||
<option name="DO_BUILD" value="true" />
|
||||
<option name="OUTPUT_TYPE" value="Library" />
|
||||
<option name="USE_DEFAULT_SDK_CONFIG_FILE" value="true" />
|
||||
<option name="USE_CUSTOM_CONFIG_FILE" value="false" />
|
||||
<option name="CUSTOM_CONFIG_FILE" value="" />
|
||||
<option name="USE_CUSTOM_CONFIG_FILE_FOR_TESTS" value="false" />
|
||||
<option name="CUSTOM_CONFIG_FILE_FOR_TESTS" value="" />
|
||||
<option name="APPLICATION_ENTRY_POINT" value="" />
|
||||
<option name="MAIN_CLASS" value="" />
|
||||
<option name="OUTPUT_FILE_NAME" value="AlternativaPhysics-0.0.9.0-SNAPSHOT.swc" />
|
||||
<option name="USE_FACET_COMPILE_OUTPUT_PATH" value="true" />
|
||||
<option name="FACET_COMPILE_OUTPUT_PATH" value="$MODULE_DIR$/target" />
|
||||
<option name="INCLUDE_RESOURCE_FILES_IN_SWC" value="false" />
|
||||
<option name="TARGET_PLAYER_VERSION" value="" />
|
||||
<option name="STATIC_LINK_RUNTIME_SHARED_LIBRARIES" value="false" />
|
||||
<option name="USE_LOCALE_SETTINGS" value="false" />
|
||||
<option name="LOCALE" value="en_US" />
|
||||
<option name="ADDITIONAL_COMPILER_OPTIONS" value="" />
|
||||
<option name="VERSION" value="0" />
|
||||
<option name="PATH_TO_SERVICES_CONFIG_XML" value="" />
|
||||
<option name="CONTEXT_ROOT" value="" />
|
||||
<NAMESPACE_AND_MANIFEST_FILE_INFO_LIST />
|
||||
<CONDITIONAL_COMPILATION_DEFINITION_LIST />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.maven.wagon:wagon-webdav:1.0-beta-2" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: slide:slide-webdavlib:2.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-httpclient:commons-httpclient:2.0.2" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-logging:commons-logging:1.0.4" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: jdom:jdom:1.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: de.zeigermann.xml:xml-im-exporter:1.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.maven.wagon:wagon-provider-api:1.0-beta-2" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.codehaus.plexus:plexus-utils:1.0.4" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:playerglobal:swc:10-4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_conversion:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_core:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:flex4:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:framework:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:applicationupdater:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:applicationupdater_ui:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_edit:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:flex:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:utilities:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:rpc:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:rpc:rb.swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:framework:rb.swc:4.0.0.4021" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
17
0.0.9.2/.svn/text-base/pom.xml.svn-base
Normal file
17
0.0.9.2/.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.9.2</version>
|
||||
<parent>
|
||||
<groupId>platform.tools.maven</groupId>
|
||||
<artifactId>FlashBasePom</artifactId>
|
||||
<version>2.0.0.0</version>
|
||||
</parent>
|
||||
<scm>
|
||||
<connection>scm:svn:http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2</connection>
|
||||
</scm>
|
||||
<dependencies>
|
||||
</dependencies>
|
||||
</project>
|
||||
65
0.0.9.2/AlternativaPhysics.iml
Normal file
65
0.0.9.2/AlternativaPhysics.iml
Normal file
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="flex" name="Flex">
|
||||
<configuration>
|
||||
<option name="DO_BUILD" value="true" />
|
||||
<option name="OUTPUT_TYPE" value="Library" />
|
||||
<option name="USE_DEFAULT_SDK_CONFIG_FILE" value="true" />
|
||||
<option name="USE_CUSTOM_CONFIG_FILE" value="false" />
|
||||
<option name="CUSTOM_CONFIG_FILE" value="" />
|
||||
<option name="USE_CUSTOM_CONFIG_FILE_FOR_TESTS" value="false" />
|
||||
<option name="CUSTOM_CONFIG_FILE_FOR_TESTS" value="" />
|
||||
<option name="APPLICATION_ENTRY_POINT" value="" />
|
||||
<option name="MAIN_CLASS" value="" />
|
||||
<option name="OUTPUT_FILE_NAME" value="AlternativaPhysics-0.0.9.0-SNAPSHOT.swc" />
|
||||
<option name="USE_FACET_COMPILE_OUTPUT_PATH" value="true" />
|
||||
<option name="FACET_COMPILE_OUTPUT_PATH" value="$MODULE_DIR$/target" />
|
||||
<option name="INCLUDE_RESOURCE_FILES_IN_SWC" value="false" />
|
||||
<option name="TARGET_PLAYER_VERSION" value="" />
|
||||
<option name="STATIC_LINK_RUNTIME_SHARED_LIBRARIES" value="false" />
|
||||
<option name="USE_LOCALE_SETTINGS" value="false" />
|
||||
<option name="LOCALE" value="en_US" />
|
||||
<option name="ADDITIONAL_COMPILER_OPTIONS" value="" />
|
||||
<option name="VERSION" value="0" />
|
||||
<option name="PATH_TO_SERVICES_CONFIG_XML" value="" />
|
||||
<option name="CONTEXT_ROOT" value="" />
|
||||
<NAMESPACE_AND_MANIFEST_FILE_INFO_LIST />
|
||||
<CONDITIONAL_COMPILATION_DEFINITION_LIST />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.maven.wagon:wagon-webdav:1.0-beta-2" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: slide:slide-webdavlib:2.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-httpclient:commons-httpclient:2.0.2" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-logging:commons-logging:1.0.4" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: jdom:jdom:1.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: de.zeigermann.xml:xml-im-exporter:1.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.apache.maven.wagon:wagon-provider-api:1.0-beta-2" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.codehaus.plexus:plexus-utils:1.0.4" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:playerglobal:swc:10-4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_conversion:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_core:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:flex4:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:framework:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:applicationupdater:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:applicationupdater_ui:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:textLayout_edit:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:flex:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:utilities:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:rpc:swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:rpc:rb.swc:4.0.0.4021" level="project" />
|
||||
<orderEntry type="library" exported="" name="Maven: com.adobe.flex.framework:framework:rb.swc:4.0.0.4021" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
11
0.0.9.2/META-INF/.svn/all-wcprops
Normal file
11
0.0.9.2/META-INF/.svn/all-wcprops
Normal file
@@ -0,0 +1,11 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 88
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/META-INF
|
||||
END
|
||||
MANIFEST.MF
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 100
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/META-INF/MANIFEST.MF
|
||||
END
|
||||
40
0.0.9.2/META-INF/.svn/entries
Normal file
40
0.0.9.2/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.9.2/META-INF
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2010-03-06T18:33:59.318892Z
|
||||
29409
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
MANIFEST.MF
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
7b64dceb50a6905850ff00a0bfbe77eb
|
||||
2009-04-20T17:15:55.260110Z
|
||||
11541
|
||||
mike
|
||||
|
||||
1
0.0.9.2/META-INF/.svn/format
Normal file
1
0.0.9.2/META-INF/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
1
0.0.9.2/META-INF/.svn/text-base/MANIFEST.MF.svn-base
Normal file
1
0.0.9.2/META-INF/.svn/text-base/MANIFEST.MF.svn-base
Normal file
@@ -0,0 +1 @@
|
||||
Bundle-Name: platform.clients.fp10.libraries.AlternativaPhysics
|
||||
1
0.0.9.2/META-INF/MANIFEST.MF
Normal file
1
0.0.9.2/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1 @@
|
||||
Bundle-Name: platform.clients.fp10.libraries.AlternativaPhysics
|
||||
17
0.0.9.2/pom.xml
Normal file
17
0.0.9.2/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.9.2</version>
|
||||
<parent>
|
||||
<groupId>platform.tools.maven</groupId>
|
||||
<artifactId>FlashBasePom</artifactId>
|
||||
<version>2.0.0.0</version>
|
||||
</parent>
|
||||
<scm>
|
||||
<connection>scm:svn:http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2</connection>
|
||||
</scm>
|
||||
<dependencies>
|
||||
</dependencies>
|
||||
</project>
|
||||
5
0.0.9.2/src/.svn/all-wcprops
Normal file
5
0.0.9.2/src/.svn/all-wcprops
Normal file
@@ -0,0 +1,5 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 83
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src
|
||||
END
|
||||
31
0.0.9.2/src/.svn/entries
Normal file
31
0.0.9.2/src/.svn/entries
Normal file
@@ -0,0 +1,31 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2010-03-06T16:51:19.781174Z
|
||||
29404
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
alternativa
|
||||
dir
|
||||
|
||||
1
0.0.9.2/src/.svn/format
Normal file
1
0.0.9.2/src/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
5
0.0.9.2/src/alternativa/.svn/all-wcprops
Normal file
5
0.0.9.2/src/alternativa/.svn/all-wcprops
Normal file
@@ -0,0 +1,5 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 95
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa
|
||||
END
|
||||
34
0.0.9.2/src/alternativa/.svn/entries
Normal file
34
0.0.9.2/src/alternativa/.svn/entries
Normal file
@@ -0,0 +1,34 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2010-03-06T16:51:19.781174Z
|
||||
29404
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
physics
|
||||
dir
|
||||
|
||||
math
|
||||
dir
|
||||
|
||||
1
0.0.9.2/src/alternativa/.svn/format
Normal file
1
0.0.9.2/src/alternativa/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
29
0.0.9.2/src/alternativa/math/.svn/all-wcprops
Normal file
29
0.0.9.2/src/alternativa/math/.svn/all-wcprops
Normal file
@@ -0,0 +1,29 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 100
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/math
|
||||
END
|
||||
Quaternion.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 114
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/math/Quaternion.as
|
||||
END
|
||||
Vector3.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 111
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/math/Vector3.as
|
||||
END
|
||||
Matrix3.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 111
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/math/Matrix3.as
|
||||
END
|
||||
Matrix4.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 111
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/math/Matrix4.as
|
||||
END
|
||||
76
0.0.9.2/src/alternativa/math/.svn/entries
Normal file
76
0.0.9.2/src/alternativa/math/.svn/entries
Normal file
@@ -0,0 +1,76 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/math
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
Quaternion.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
7332c9620420bd10062d913decfb9501
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
Vector3.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
a1571b527786783cef1a9f56db3f2af6
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
Matrix3.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
0054bd91fe694474a28c8ec17697292b
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
Matrix4.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
47b31d4350499ebe77a5973a938e4b44
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
1
0.0.9.2/src/alternativa/math/.svn/format
Normal file
1
0.0.9.2/src/alternativa/math/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
390
0.0.9.2/src/alternativa/math/.svn/text-base/Matrix3.as.svn-base
Normal file
390
0.0.9.2/src/alternativa/math/.svn/text-base/Matrix3.as.svn-base
Normal file
@@ -0,0 +1,390 @@
|
||||
package alternativa.math {
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Matrix3 {
|
||||
|
||||
public static const ZERO:Matrix3 = new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
public static const IDENTITY:Matrix3 = new Matrix3();
|
||||
|
||||
public var a:Number;
|
||||
public var b:Number;
|
||||
public var c:Number;
|
||||
|
||||
public var e:Number;
|
||||
public var f:Number;
|
||||
public var g:Number;
|
||||
|
||||
public var i:Number;
|
||||
public var j:Number;
|
||||
public var k:Number;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @param c
|
||||
* @param e
|
||||
* @param f
|
||||
* @param g
|
||||
* @param i
|
||||
* @param j
|
||||
* @param k
|
||||
*/
|
||||
public function Matrix3(a:Number = 1, b:Number = 0, c:Number = 0, e:Number = 0, f:Number = 1, g:Number = 0, i:Number = 0, j:Number = 0, k:Number = 1) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
|
||||
this.e = e;
|
||||
this.f = f;
|
||||
this.g = g;
|
||||
|
||||
this.i = i;
|
||||
this.j = j;
|
||||
this.k = k;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function toIdentity():Matrix3 {
|
||||
a = f = k = 1;
|
||||
b = c = e = g = i = j = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Преобразование матрицы в обратную.
|
||||
*/
|
||||
public function invert():Matrix3 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
|
||||
var det:Number = 1/(-cc*ff*ii + bb*gg*ii + cc*ee*jj - aa*gg*jj - bb*ee*kk + aa*ff*kk);
|
||||
|
||||
a = (ff*kk - gg*jj)*det;
|
||||
b = (cc*jj - bb*kk)*det;
|
||||
c = (bb*gg - cc*ff)*det;
|
||||
e = (gg*ii - ee*kk)*det;
|
||||
f = (aa*kk - cc*ii)*det;
|
||||
g = (cc*ee - aa*gg)*det;
|
||||
i = (ee*jj - ff*ii)*det;
|
||||
j = (bb*ii - aa*jj)*det;
|
||||
k = (aa*ff - bb*ee)*det;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на матрицу справа: M * this
|
||||
*
|
||||
* @param m левый операнд умножения
|
||||
*/
|
||||
public function append(m:Matrix3):Matrix3 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
|
||||
a = m.a*aa + m.b*ee + m.c*ii;
|
||||
b = m.a*bb + m.b*ff + m.c*jj;
|
||||
c = m.a*cc + m.b*gg + m.c*kk;
|
||||
e = m.e*aa + m.f*ee + m.g*ii;
|
||||
f = m.e*bb + m.f*ff + m.g*jj;
|
||||
g = m.e*cc + m.f*gg + m.g*kk;
|
||||
i = m.i*aa + m.j*ee + m.k*ii;
|
||||
j = m.i*bb + m.j*ff + m.k*jj;
|
||||
k = m.i*cc + m.j*gg + m.k*kk;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на матрицу слева: this * M
|
||||
*
|
||||
* @param matrix правый операнд умножения
|
||||
*/
|
||||
public function prepend(m:Matrix3):Matrix3 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
|
||||
a = aa*m.a + bb*m.e + cc*m.i;
|
||||
b = aa*m.b + bb*m.f + cc*m.j;
|
||||
c = aa*m.c + bb*m.g + cc*m.k;
|
||||
e = ee*m.a + ff*m.e + gg*m.i;
|
||||
f = ee*m.b + ff*m.f + gg*m.j;
|
||||
g = ee*m.c + ff*m.g + gg*m.k;
|
||||
i = ii*m.a + jj*m.e + kk*m.i;
|
||||
j = ii*m.b + jj*m.f + kk*m.j;
|
||||
k = ii*m.c + jj*m.g + kk*m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на матрицу слева: this * M
|
||||
*
|
||||
* @param matrix правый операнд умножения
|
||||
*/
|
||||
public function prependTransposed(m:Matrix3):Matrix3 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
|
||||
a = aa*m.a + bb*m.b + cc*m.c;
|
||||
b = aa*m.e + bb*m.f + cc*m.g;
|
||||
c = aa*m.i + bb*m.j + cc*m.k;
|
||||
e = ee*m.a + ff*m.b + gg*m.c;
|
||||
f = ee*m.e + ff*m.f + gg*m.g;
|
||||
g = ee*m.i + ff*m.j + gg*m.k;
|
||||
i = ii*m.a + jj*m.b + kk*m.c;
|
||||
j = ii*m.e + jj*m.f + kk*m.g;
|
||||
k = ii*m.i + jj*m.j + kk*m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
*/
|
||||
public function add(m:Matrix3):Matrix3 {
|
||||
a += m.a;
|
||||
b += m.b;
|
||||
c += m.c;
|
||||
e += m.e;
|
||||
f += m.f;
|
||||
g += m.g;
|
||||
i += m.i;
|
||||
j += m.j;
|
||||
k += m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
*/
|
||||
public function subtract(m:Matrix3):Matrix3 {
|
||||
a -= m.a;
|
||||
b -= m.b;
|
||||
c -= m.c;
|
||||
e -= m.e;
|
||||
f -= m.f;
|
||||
g -= m.g;
|
||||
i -= m.i;
|
||||
j -= m.j;
|
||||
k -= m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function transpose():Matrix3 {
|
||||
var tmp:Number = b;
|
||||
b = e;
|
||||
e = tmp;
|
||||
tmp = c;
|
||||
c = i;
|
||||
i = tmp;
|
||||
tmp = g;
|
||||
g = j;
|
||||
j = tmp;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформирует заданный вектор.
|
||||
*
|
||||
* @param vin входной вектор
|
||||
* @param vout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function transformVector(vin:Vector3, vout:Vector3):void {
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет обратную трансформацию заданного вектора. При этом подразумевается, что квадратная матрица содержит только вращения.
|
||||
*
|
||||
* @param vin входной вектор
|
||||
* @param vout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function transformVectorInverse(vin:Vector3, vout:Vector3):void {
|
||||
vout.x = a*vin.x + e*vin.y + i*vin.z;
|
||||
vout.y = b*vin.x + f*vin.y + j*vin.z;
|
||||
vout.z = c*vin.x + g*vin.y + k*vin.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформирует заданный вектор.
|
||||
*
|
||||
* @param vin входной вектор
|
||||
* @param vout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function transformVector3To3D(vin:Vector3, vout:Vector3D):void {
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param matrix
|
||||
*/
|
||||
public function createSkewSymmetric(v:Vector3):Matrix3 {
|
||||
a = f = k = 0;
|
||||
b = -v.z;
|
||||
c = v.y;
|
||||
e = v.z;
|
||||
g = -v.x;
|
||||
i = -v.y;
|
||||
j = v.x;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирование значений указанной матрицы.
|
||||
*
|
||||
* @param matrix матрица, значения которой копируются
|
||||
*/
|
||||
public function copy(m:Matrix3):Matrix3 {
|
||||
a = m.a;
|
||||
b = m.b;
|
||||
c = m.c;
|
||||
e = m.e;
|
||||
f = m.f;
|
||||
g = m.g;
|
||||
i = m.i;
|
||||
j = m.j;
|
||||
k = m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public function setRotationMatrix(rx:Number, ry:Number, rz:Number):Matrix3 {
|
||||
var cosX:Number = Math.cos(rx);
|
||||
var sinX:Number = Math.sin(rx);
|
||||
var cosY:Number = Math.cos(ry);
|
||||
var sinY:Number = Math.sin(ry);
|
||||
var cosZ:Number = Math.cos(rz);
|
||||
var sinZ:Number = Math.sin(rz);
|
||||
|
||||
var cosZsinY:Number = cosZ*sinY;
|
||||
var sinZsinY:Number = sinZ*sinY;
|
||||
|
||||
a = cosZ*cosY;
|
||||
b = cosZsinY*sinX - sinZ*cosX;
|
||||
c = cosZsinY*cosX + sinZ*sinX;
|
||||
|
||||
e = sinZ*cosY;
|
||||
f = sinZsinY*sinX + cosZ*cosX;
|
||||
g = sinZsinY*cosX - cosZ*sinX;
|
||||
|
||||
i = -sinY;
|
||||
j = cosY*sinX;
|
||||
k = cosY*cosX;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод формирует матрицу поворота на заданный угол относительно заданной оси.
|
||||
*
|
||||
* @param axis нормализованный вектор, задающий ось, относительно которой выполняется поворот
|
||||
* @param angle угол поворота в радианах
|
||||
*/
|
||||
public function fromAxisAngle(axis:Vector3, angle:Number):void {
|
||||
var c1:Number = Math.cos(angle);
|
||||
var s:Number = Math.sin(angle);
|
||||
var t:Number = 1 - c1;
|
||||
var x:Number = axis.x;
|
||||
var y:Number = axis.y;
|
||||
var z:Number = axis.z;
|
||||
|
||||
a = t*x*x + c1;
|
||||
b = t*x*y - z*s;
|
||||
c = t*x*z + y*s;
|
||||
|
||||
e = t*x*y + z*s;
|
||||
f = t*y*y + c1;
|
||||
g = t*y*z - x*s;
|
||||
|
||||
i = t*x*z - y*s;
|
||||
j = t*y*z + x*s;
|
||||
k = t*z*z + c1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирование матрицы.
|
||||
*
|
||||
* @return клон матрицы
|
||||
*/
|
||||
public function clone():Matrix3 {
|
||||
return new Matrix3(a, b, c, e, f, g, i, j, k);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[Matrix3 (" + a + ", " + b + ", " + c + "), (" + e + ", " + f + ", " + g + "), (" + i + ", " + j + ", " + k + ")]";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param angles
|
||||
*/
|
||||
public function getEulerAngles(angles:Vector3):void {
|
||||
if (-1 < i && i < 1) {
|
||||
angles.x = Math.atan2(j, k);
|
||||
angles.y = -Math.asin(i);
|
||||
angles.z = Math.atan2(e, a);
|
||||
} else {
|
||||
angles.x = 0;
|
||||
angles.y = (i <= -1) ? Math.PI : -Math.PI;
|
||||
angles.y *= 0.5;
|
||||
angles.z = Math.atan2(-b, f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
475
0.0.9.2/src/alternativa/math/.svn/text-base/Matrix4.as.svn-base
Normal file
475
0.0.9.2/src/alternativa/math/.svn/text-base/Matrix4.as.svn-base
Normal file
@@ -0,0 +1,475 @@
|
||||
package alternativa.math {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Matrix4 {
|
||||
|
||||
public static const IDENTITY:Matrix4 = new Matrix4();
|
||||
|
||||
public var a:Number;
|
||||
public var b:Number;
|
||||
public var c:Number;
|
||||
public var d:Number;
|
||||
|
||||
public var e:Number;
|
||||
public var f:Number;
|
||||
public var g:Number;
|
||||
public var h:Number;
|
||||
|
||||
public var i:Number;
|
||||
public var j:Number;
|
||||
public var k:Number;
|
||||
public var l:Number;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @param c
|
||||
* @param e
|
||||
* @param f
|
||||
* @param g
|
||||
* @param i
|
||||
* @param j
|
||||
* @param k
|
||||
*/
|
||||
public function Matrix4(a:Number = 1, b:Number = 0, c:Number = 0, d:Number = 0, e:Number = 0, f:Number = 1, g:Number = 0, h:Number = 0, i:Number = 0, j:Number = 0, k:Number = 1, l:Number = 0) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
this.d = d;
|
||||
|
||||
this.e = e;
|
||||
this.f = f;
|
||||
this.g = g;
|
||||
this.h = h;
|
||||
|
||||
this.i = i;
|
||||
this.j = j;
|
||||
this.k = k;
|
||||
this.l = l;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function toIdentity():Matrix4 {
|
||||
a = f = k = 1;
|
||||
b = c = e = g = i = j = d = h = l = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Преобразование матрицы в обратную.
|
||||
*/
|
||||
public function invert():Matrix4 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var dd:Number = d;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var hh:Number = h;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
var ll:Number = l;
|
||||
|
||||
var det:Number = -cc*ff*ii + bb*gg*ii + cc*ee*jj - aa*gg*jj - bb*ee*kk + aa*ff*kk;
|
||||
|
||||
a = (-gg*jj + ff*kk)/det;
|
||||
b = (cc*jj - bb*kk)/det;
|
||||
c = (-cc*ff + bb*gg)/det;
|
||||
d = (dd*gg*jj - cc*hh*jj - dd*ff*kk + bb*hh*kk + cc*ff*ll - bb*gg*ll)/det;
|
||||
e = (gg*ii - ee*kk)/det;
|
||||
f = (-cc*ii + aa*kk)/det;
|
||||
g = (cc*ee - aa*gg)/det;
|
||||
h = (cc*hh*ii - dd*gg*ii + dd*ee*kk - aa*hh*kk - cc*ee*ll + aa*gg*ll)/det;
|
||||
i = (-ff*ii + ee*jj)/det;
|
||||
j = (bb*ii - aa*jj)/det;
|
||||
k = (-bb*ee + aa*ff)/det;
|
||||
l = (dd*ff*ii - bb*hh*ii - dd*ee*jj + aa*hh*jj + bb*ee*ll - aa*ff*ll)/det;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на матрицу справа: M * this
|
||||
*
|
||||
* @param m левый операнд умножения
|
||||
*/
|
||||
public function append(m:Matrix4):Matrix4 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var dd:Number = d;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var hh:Number = h;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
var ll:Number = l;
|
||||
|
||||
a = m.a*aa + m.b*ee + m.c*ii;
|
||||
b = m.a*bb + m.b*ff + m.c*jj;
|
||||
c = m.a*cc + m.b*gg + m.c*kk;
|
||||
d = m.a*dd + m.b*hh + m.c*ll + m.d;
|
||||
e = m.e*aa + m.f*ee + m.g*ii;
|
||||
f = m.e*bb + m.f*ff + m.g*jj;
|
||||
g = m.e*cc + m.f*gg + m.g*kk;
|
||||
h = m.e*dd + m.f*hh + m.g*ll + m.h;
|
||||
i = m.i*aa + m.j*ee + m.k*ii;
|
||||
j = m.i*bb + m.j*ff + m.k*jj;
|
||||
k = m.i*cc + m.j*gg + m.k*kk;
|
||||
l = m.i*dd + m.j*hh + m.k*ll + m.l;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на матрицу слева: this * M
|
||||
*
|
||||
* @param matrix правый операнд умножения
|
||||
*/
|
||||
public function prepend(m:Matrix4):Matrix4 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var dd:Number = d;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var hh:Number = h;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
var ll:Number = l;
|
||||
|
||||
a = aa*m.a + bb*m.e + cc*m.i;
|
||||
b = aa*m.b + bb*m.f + cc*m.j;
|
||||
c = aa*m.c + bb*m.g + cc*m.k;
|
||||
d = aa*m.d + bb*m.h + cc*m.l + dd;
|
||||
e = ee*m.a + ff*m.e + gg*m.i;
|
||||
f = ee*m.b + ff*m.f + gg*m.j;
|
||||
g = ee*m.c + ff*m.g + gg*m.k;
|
||||
h = ee*m.d + ff*m.h + gg*m.l + hh;
|
||||
i = ii*m.a + jj*m.e + kk*m.i;
|
||||
j = ii*m.b + jj*m.f + kk*m.j;
|
||||
k = ii*m.c + jj*m.g + kk*m.k;
|
||||
l = ii*m.d + jj*m.h + kk*m.l + ll;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
*/
|
||||
public function add(m:Matrix4):Matrix4 {
|
||||
a += m.a;
|
||||
b += m.b;
|
||||
c += m.c;
|
||||
d += m.d;
|
||||
e += m.e;
|
||||
f += m.f;
|
||||
g += m.g;
|
||||
h += m.h;
|
||||
i += m.i;
|
||||
j += m.j;
|
||||
k += m.k;
|
||||
l += m.l;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
*/
|
||||
public function subtract(m:Matrix4):Matrix4 {
|
||||
a -= m.a;
|
||||
b -= m.b;
|
||||
c -= m.c;
|
||||
d -= m.d;
|
||||
e -= m.e;
|
||||
f -= m.f;
|
||||
g -= m.g;
|
||||
h -= m.h;
|
||||
i -= m.i;
|
||||
j -= m.j;
|
||||
k -= m.k;
|
||||
l -= m.l;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформирует заданный вектор.
|
||||
*
|
||||
* @param vin входной вектор
|
||||
* @param vout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function transformVector(vin:Vector3, vout:Vector3):void {
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z + d;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z + h;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z + l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет обратную трансформацию заданного вектора. При этом подразумевается, что квадратная матрица содержит только вращения.
|
||||
*
|
||||
* @param vin входной вектор
|
||||
* @param vout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function transformVectorInverse(vin:Vector3, vout:Vector3):void {
|
||||
var xx:Number = vin.x - d;
|
||||
var yy:Number = vin.y - h;
|
||||
var zz:Number = vin.z - l;
|
||||
vout.x = a*xx + e*yy + i*zz;
|
||||
vout.y = b*xx + f*yy + j*zz;
|
||||
vout.z = c*xx + g*yy + k*zz;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param arrin
|
||||
* @param Vector3
|
||||
* @param arrout
|
||||
* @param Vector3
|
||||
*/
|
||||
public function transformVectors(arrin:Vector.<Vector3>, arrout:Vector.<Vector3>):void {
|
||||
var len:int = arrin.length;
|
||||
var vin:Vector3;
|
||||
var vout:Vector3;
|
||||
for (var idx:int = 0; idx < len; idx++) {
|
||||
vin = arrin[idx];
|
||||
vout = arrout[idx];
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z + d;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z + h;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z + l;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param arrin
|
||||
* @param Vector3
|
||||
* @param arrout
|
||||
* @param Vector3
|
||||
* @param len
|
||||
*/
|
||||
public function transformVectorsN(arrin:Vector.<Vector3>, arrout:Vector.<Vector3>, len:int):void {
|
||||
var vin:Vector3;
|
||||
var vout:Vector3;
|
||||
for (var idx:int = 0; idx < len; idx++) {
|
||||
vin = arrin[idx];
|
||||
vout = arrout[idx];
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z + d;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z + h;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z + l;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param arrin
|
||||
* @param Vector3
|
||||
* @param arrout
|
||||
* @param Vector3
|
||||
*/
|
||||
public function transformVectorsInverse(arrin:Vector.<Vector3>, arrout:Vector.<Vector3>):void {
|
||||
var len:int = arrin.length;
|
||||
var vin:Vector3;
|
||||
var vout:Vector3;
|
||||
for (var idx:int = 0; idx < len; idx++) {
|
||||
vin = arrin[idx];
|
||||
vout = arrout[idx];
|
||||
var xx:Number = vin.x - d;
|
||||
var yy:Number = vin.y - h;
|
||||
var zz:Number = vin.z - l;
|
||||
vout.x = a*xx + e*yy + i*zz;
|
||||
vout.y = b*xx + f*yy + j*zz;
|
||||
vout.z = c*xx + g*yy + k*zz;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param arrin
|
||||
* @param Vector3
|
||||
* @param arrout
|
||||
* @param Vector3
|
||||
*/
|
||||
public function transformVectorsInverseN(arrin:Vector.<Vector3>, arrout:Vector.<Vector3>, len:int):void {
|
||||
var vin:Vector3;
|
||||
var vout:Vector3;
|
||||
for (var idx:int = 0; idx < len; idx++) {
|
||||
vin = arrin[idx];
|
||||
vout = arrout[idx];
|
||||
var xx:Number = vin.x - d;
|
||||
var yy:Number = vin.y - h;
|
||||
var zz:Number = vin.z - l;
|
||||
vout.x = a*xx + e*yy + i*zz;
|
||||
vout.y = b*xx + f*yy + j*zz;
|
||||
vout.z = c*xx + g*yy + k*zz;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param i
|
||||
* @param axis
|
||||
*/
|
||||
public function getAxis(idx:int, axis:Vector3):void {
|
||||
switch (idx) {
|
||||
case 0:
|
||||
axis.x = a;
|
||||
axis.y = e;
|
||||
axis.z = i;
|
||||
return;
|
||||
case 1:
|
||||
axis.x = b;
|
||||
axis.y = f;
|
||||
axis.z = j;
|
||||
return;
|
||||
case 2:
|
||||
axis.x = c;
|
||||
axis.y = g;
|
||||
axis.z = k;
|
||||
return;
|
||||
case 3:
|
||||
axis.x = d;
|
||||
axis.y = h;
|
||||
axis.z = l;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформирует заданный вектор без учёта смещения центра матрицы.
|
||||
*
|
||||
* @param pin входной вектор
|
||||
* @param pout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function deltaTransformVector(vin:Vector3, vout:Vector3):void {
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z + d;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z + h;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z + l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформирует заданный вектор без учёта смещения центра матрицы.
|
||||
*
|
||||
* @param pin входной вектор
|
||||
* @param pout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function deltaTransformVectorInverse(vin:Vector3, vout:Vector3):void {
|
||||
vout.x = a*vin.x + e*vin.y + i*vin.z;
|
||||
vout.y = b*vin.x + f*vin.y + j*vin.z;
|
||||
vout.z = c*vin.x + g*vin.y + k*vin.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирование значений указанной матрицы.
|
||||
*
|
||||
* @param matrix матрица, значения которой копируются
|
||||
*/
|
||||
public function copy(m:Matrix4):Matrix4 {
|
||||
a = m.a;
|
||||
b = m.b;
|
||||
c = m.c;
|
||||
d = m.d;
|
||||
e = m.e;
|
||||
f = m.f;
|
||||
g = m.g;
|
||||
h = m.h;
|
||||
i = m.i;
|
||||
j = m.j;
|
||||
k = m.k;
|
||||
l = m.l;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public function setFromMatrix3(m:Matrix3, offset:Vector3):Matrix4 {
|
||||
a = m.a;
|
||||
b = m.b;
|
||||
c = m.c;
|
||||
d = offset.x;
|
||||
e = m.e;
|
||||
f = m.f;
|
||||
g = m.g;
|
||||
h = offset.y;
|
||||
i = m.i;
|
||||
j = m.j;
|
||||
k = m.k;
|
||||
l = offset.z;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public function setOrientationFromMatrix3(m:Matrix3):Matrix4 {
|
||||
a = m.a;
|
||||
b = m.b;
|
||||
c = m.c;
|
||||
e = m.e;
|
||||
f = m.f;
|
||||
g = m.g;
|
||||
i = m.i;
|
||||
j = m.j;
|
||||
k = m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param angles
|
||||
*/
|
||||
public function getEulerAngles(angles:Vector3):void {
|
||||
if (-1 < i && i < 1) {
|
||||
angles.x = Math.atan2(j, k);
|
||||
angles.y = -Math.asin(i);
|
||||
angles.z = Math.atan2(e, a);
|
||||
} else {
|
||||
angles.x = 0;
|
||||
angles.y = (i <= -1) ? Math.PI : -Math.PI;
|
||||
angles.y *= 0.5;
|
||||
angles.z = Math.atan2(-b, f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает координаты матрицы.
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public function setPosition(pos:Vector3):void {
|
||||
d = pos.x;
|
||||
h = pos.y;
|
||||
l = pos.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирование матрицы.
|
||||
*
|
||||
* @return клон матрицы
|
||||
*/
|
||||
public function clone():Matrix4 {
|
||||
return new Matrix4(a, b, c, d, e, f, g, h, i, j, k, l);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[Matrix4 [" + a.toFixed(3) + " " + b.toFixed(3) + " " + c.toFixed(3) + " " + d.toFixed(3) + "] [" + e.toFixed(3) + " " + f.toFixed(3) + " " + g.toFixed(3) + " " + h.toFixed(3) + "] [" + i.toFixed(3) + " " + j.toFixed(3) + " " + k.toFixed(3) + " " + l.toFixed(3) + "]]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,458 @@
|
||||
package alternativa.math {
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
public class Quaternion {
|
||||
|
||||
public var w:Number;
|
||||
public var x:Number;
|
||||
public var y:Number;
|
||||
public var z:Number;
|
||||
|
||||
public static function multiply(q1:Quaternion, q2:Quaternion, result:Quaternion):void {
|
||||
result.w = q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z;
|
||||
result.x = q1.w*q2.x + q1.x*q2.w + q1.y*q2.z - q1.z*q2.y;
|
||||
result.y = q1.w*q2.y + q1.y*q2.w + q1.z*q2.x - q1.x*q2.z;
|
||||
result.z = q1.w*q2.z + q1.z*q2.w + q1.x*q2.y - q1.y*q2.x;
|
||||
}
|
||||
|
||||
public static function createFromAxisAngle(axis:Vector3, angle:Number):Quaternion {
|
||||
var q:Quaternion = new Quaternion();
|
||||
q.setFromAxisAngle(axis, angle);
|
||||
return q;
|
||||
}
|
||||
|
||||
public static function createFromAxisAngleComponents(x:Number, y:Number, z:Number, angle:Number):Quaternion {
|
||||
var q:Quaternion = new Quaternion();
|
||||
q.setFromAxisAngleComponents(x, y, z, angle);
|
||||
return q;
|
||||
}
|
||||
|
||||
private static var _q:Quaternion = new Quaternion();
|
||||
|
||||
public function Quaternion(w:Number = 1, x:Number = 0, y:Number = 0, z:Number = 0) {
|
||||
this.w = w;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public function reset(w:Number = 1, x:Number = 0, y:Number = 0, z:Number = 0):Quaternion {
|
||||
this.w = w;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public function normalize():Quaternion {
|
||||
var d:Number = w*w + x*x + y*y + z*z;
|
||||
if (d == 0) {
|
||||
w = 1;
|
||||
} else {
|
||||
d = 1/Math.sqrt(d);
|
||||
w *= d;
|
||||
x *= d;
|
||||
y *= d;
|
||||
z *= d;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножает на указанный кватернион слева: this * q
|
||||
*
|
||||
* @param q множитель
|
||||
*/
|
||||
public function prepend(q:Quaternion):Quaternion {
|
||||
var ww:Number = w*q.w - x*q.x - y*q.y - z*q.z;
|
||||
var xx:Number = w*q.x + x*q.w + y*q.z - z*q.y;
|
||||
var yy:Number = w*q.y + y*q.w + z*q.x - x*q.z;
|
||||
var zz:Number = w*q.z + z*q.w + x*q.y - y*q.x;
|
||||
w = ww;
|
||||
x = xx;
|
||||
y = yy;
|
||||
z = zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножает на указанный кватернион справа: q * this
|
||||
*
|
||||
* @param q множитель
|
||||
*/
|
||||
public function append(q:Quaternion):Quaternion {
|
||||
var ww:Number = q.w*w - q.x*x - q.y*y - q.z*z;
|
||||
var xx:Number = q.w*x + q.x*w + q.y*z - q.z*y;
|
||||
var yy:Number = q.w*y + q.y*w + q.z*x - q.x*z;
|
||||
var zz:Number = q.w*z + q.z*w + q.x*y - q.y*x;
|
||||
w = ww;
|
||||
x = xx;
|
||||
y = yy;
|
||||
z = zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param vector
|
||||
*/
|
||||
public function rotateByVector(v:Vector3):Quaternion {
|
||||
var ww:Number = -v.x*x - v.y*y - v.z*z;
|
||||
var xx:Number = v.x*w + v.y*z - v.z*y;
|
||||
var yy:Number = v.y*w + v.z*x - v.x*z;
|
||||
var zz:Number = v.z*w + v.x*y - v.y*x;
|
||||
w = ww;
|
||||
x = xx;
|
||||
y = yy;
|
||||
z = zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет вращение, приданное вектором угловой скорости за указанное время.
|
||||
*
|
||||
* @param v
|
||||
* @param scale
|
||||
*/
|
||||
public function addScaledVector(v:Vector3, scale:Number):Quaternion {
|
||||
var vx:Number = v.x*scale;
|
||||
var vy:Number = v.y*scale;
|
||||
var vz:Number = v.z*scale;
|
||||
var ww:Number = -x*vx - y*vy - z*vz;
|
||||
var xx:Number = vx*w + vy*z - vz*y;
|
||||
var yy:Number = vy*w + vz*x - vx*z;
|
||||
var zz:Number = vz*w + vx*y - vy*x;
|
||||
w += 0.5*ww;
|
||||
x += 0.5*xx;
|
||||
y += 0.5*yy;
|
||||
z += 0.5*zz;
|
||||
// inlined normalize
|
||||
var d:Number = w*w + x*x + y*y + z*z;
|
||||
if (d == 0) {
|
||||
w = 1;
|
||||
} else {
|
||||
d = 1/Math.sqrt(d);
|
||||
w *= d;
|
||||
x *= d;
|
||||
y *= d;
|
||||
z *= d;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
* @return
|
||||
*/
|
||||
public function toMatrix3(m:Matrix3):Quaternion {
|
||||
var xx2:Number = 2.0*x*x;
|
||||
var yy2:Number = 2.0*y*y;
|
||||
var zz2:Number = 2.0*z*z;
|
||||
var xy2:Number = 2.0*x*y;
|
||||
var yz2:Number = 2.0*y*z;
|
||||
var zx2:Number = 2.0*z*x;
|
||||
var wx2:Number = 2.0*w*x;
|
||||
var wy2:Number = 2.0*w*y;
|
||||
var wz2:Number = 2.0*w*z;
|
||||
|
||||
m.a = 1.0 - yy2 - zz2;
|
||||
m.b = xy2 - wz2;
|
||||
m.c = zx2 + wy2;
|
||||
|
||||
m.e = xy2 + wz2;
|
||||
m.f = 1.0 - xx2 - zz2;
|
||||
m.g = yz2 - wx2;
|
||||
|
||||
m.i = zx2 - wy2;
|
||||
m.j = yz2 + wx2;
|
||||
m.k = 1.0 - xx2 - yy2;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function length():Number {
|
||||
return Math.sqrt(w*w + x*x + y*y + z*z);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function lengthSqr():Number {
|
||||
return w*w + x*x + y*y + z*z;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param axis
|
||||
* @param angle
|
||||
* @return
|
||||
*/
|
||||
public function setFromAxisAngle(axis:Vector3, angle:Number):Quaternion {
|
||||
w = Math.cos(0.5*angle);
|
||||
var k:Number = Math.sin(0.5*angle)/Math.sqrt(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z);
|
||||
x = axis.x*k;
|
||||
y = axis.y*k;
|
||||
z = axis.z*k;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
* @param angle
|
||||
*/
|
||||
public function setFromAxisAngleComponents(x:Number, y:Number, z:Number, angle:Number):Quaternion {
|
||||
w = Math.cos(0.5*angle);
|
||||
var k:Number = Math.sin(0.5*angle)/Math.sqrt(x*x + y*y + z*z);
|
||||
this.x = x*k;
|
||||
this.y = y*k;
|
||||
this.z = z*k;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param vector
|
||||
*/
|
||||
public function toAxisVector(v:Vector3 = null):Vector3 {
|
||||
if (w < -1 || w > 1) {
|
||||
normalize();
|
||||
}
|
||||
if (v == null) {
|
||||
v = new Vector3();
|
||||
}
|
||||
if (w > -1 && w < 1) {
|
||||
if (w == 0) {
|
||||
v.x = x;
|
||||
v.y = y;
|
||||
v.z = z;
|
||||
} else {
|
||||
var angle:Number = 2*Math.acos(w);
|
||||
var coeff:Number = 1/Math.sqrt(1 - w*w);
|
||||
v.x = x*coeff*angle;
|
||||
v.y = y*coeff*angle;
|
||||
v.z = z*coeff*angle;
|
||||
}
|
||||
} else {
|
||||
v.x = 0;
|
||||
v.y = 0;
|
||||
v.z = 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param rotations
|
||||
*/
|
||||
public function getEulerAngles(angles:Vector3):Vector3 {
|
||||
var qi2:Number = 2*x*x;
|
||||
var qj2:Number = 2*y*y;
|
||||
var qk2:Number = 2*z*z;
|
||||
var qij:Number = 2*x*y;
|
||||
var qjk:Number = 2*y*z;
|
||||
var qki:Number = 2*z*x;
|
||||
var qri:Number = 2*w*x;
|
||||
var qrj:Number = 2*w*y;
|
||||
var qrk:Number = 2*w*z;
|
||||
|
||||
var aa:Number = 1 - qj2 - qk2;
|
||||
var bb:Number = qij - qrk;
|
||||
var ee:Number = qij + qrk;
|
||||
var ff:Number = 1 - qi2 - qk2;
|
||||
var ii:Number = qki - qrj;
|
||||
var jj:Number = qjk + qri;
|
||||
var kk:Number = 1 - qi2 - qj2;
|
||||
|
||||
if (-1 < ii && ii < 1) {
|
||||
if (angles == null) {
|
||||
angles = new Vector3(Math.atan2(jj, kk), -Math.asin(ii), Math.atan2(ee, aa));
|
||||
} else {
|
||||
angles.x = Math.atan2(jj, kk);
|
||||
angles.y = -Math.asin(ii);
|
||||
angles.z = Math.atan2(ee, aa);
|
||||
}
|
||||
} else {
|
||||
if (angles == null) {
|
||||
angles = new Vector3(0, 0.5*((ii <= -1) ? Math.PI : -Math.PI), Math.atan2(-bb, ff));
|
||||
} else {
|
||||
angles.x = 0;
|
||||
angles.y = (ii <= -1) ? Math.PI : -Math.PI;
|
||||
angles.y *= 0.5;
|
||||
angles.z = Math.atan2(-bb, ff);
|
||||
}
|
||||
}
|
||||
return angles;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
*/
|
||||
public function setFromEulerAnglesXYZ(x:Number, y:Number, z:Number):void {
|
||||
setFromAxisAngleComponents(1, 0, 0, x);
|
||||
|
||||
_q.setFromAxisAngleComponents(0, 1, 0, y);
|
||||
append(_q);
|
||||
normalize();
|
||||
|
||||
_q.setFromAxisAngleComponents(0, 0, 1, z);
|
||||
append(_q);
|
||||
normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function conjugate():void {
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет линейную интерполяцию.
|
||||
*
|
||||
* @param q1 начало отрезка
|
||||
* @param q2 конец отрезка
|
||||
* @param t время, обычно задаётся в интервале [0, 1]
|
||||
*/
|
||||
public function nlerp(q1:Quaternion, q2:Quaternion, t:Number):Quaternion {
|
||||
var d:Number = 1 - t;
|
||||
w = q1.w*d + q2.w*t;
|
||||
x = q1.x*d + q2.x*t;
|
||||
y = q1.y*d + q2.y*t;
|
||||
z = q1.z*d + q2.z*t;
|
||||
// inlined normalize
|
||||
d = w*w + x*x + y*y + z*z;
|
||||
if (d == 0) {
|
||||
w = 1;
|
||||
} else {
|
||||
d = 1/Math.sqrt(d);
|
||||
w *= d;
|
||||
x *= d;
|
||||
y *= d;
|
||||
z *= d;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param q
|
||||
* @return
|
||||
*/
|
||||
public function subtract(q:Quaternion):Quaternion {
|
||||
w -= q.w;
|
||||
x -= q.x;
|
||||
y -= q.y;
|
||||
z -= q.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param q1
|
||||
* @param q2
|
||||
* @return
|
||||
*/
|
||||
public function diff(q1:Quaternion, q2:Quaternion):Quaternion {
|
||||
w = q2.w - q1.w;
|
||||
x = q2.x - q1.x;
|
||||
y = q2.y - q1.y;
|
||||
z = q2.z - q1.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param q
|
||||
* @return
|
||||
*/
|
||||
public function copy(q:Quaternion):Quaternion {
|
||||
w = q.w;
|
||||
x = q.x;
|
||||
y = q.y;
|
||||
z = q.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reslult
|
||||
*/
|
||||
public function toVector3D(result:Vector3D):Vector3D {
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
result.z = z;
|
||||
result.w = w;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function clone():Quaternion {
|
||||
return new Quaternion(w, x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[" + w + ", " + x + ", " + y + ", " + z + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет сферическую интерполяцию между двумя заданными кватернионами по наименьшему расстоянию.
|
||||
*
|
||||
* @param a первый кватерион
|
||||
* @param b второй кватернион
|
||||
* @param t параметр интерполяции, обычно принадлежит отрезку [0, 1]
|
||||
* @return this
|
||||
*/
|
||||
public function slerp(a:Quaternion, b:Quaternion, t:Number):Quaternion {
|
||||
var flip:Number = 1;
|
||||
// Так как одна и та же ориентация представляется двумя значениями q и -q, нужно сменить знак одного из кватернионов
|
||||
// если скалярное произведение отрицательно. Иначе будет получено интерполированное значение по наибольшему расстоянию.
|
||||
var cosine:Number = a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
if (cosine < 0) {
|
||||
cosine = -cosine;
|
||||
flip = -1;
|
||||
}
|
||||
|
||||
if ((1 - cosine) < 0.001) {
|
||||
// Вблизи нуля используется линейная интерполяция
|
||||
var k1:Number = 1 - t;
|
||||
var k2:Number = t*flip;
|
||||
w = a.w*k1 + b.w*k2;
|
||||
x = a.x*k1 + b.x*k2;
|
||||
y = a.y*k1 + b.y*k2;
|
||||
z = a.z*k1 + b.z*k2;
|
||||
normalize();
|
||||
} else {
|
||||
var theta:Number = Math.acos(cosine);
|
||||
var sine:Number = Math.sin(theta);
|
||||
var beta:Number = Math.sin((1 - t)*theta)/sine;
|
||||
var alpha:Number = Math.sin(t*theta)/sine*flip;
|
||||
w = a.w*beta + b.w*alpha;
|
||||
x = a.x*beta + b.x*alpha;
|
||||
y = a.y*beta + b.y*alpha;
|
||||
z = a.z*beta + b.z*alpha;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
334
0.0.9.2/src/alternativa/math/.svn/text-base/Vector3.as.svn-base
Normal file
334
0.0.9.2/src/alternativa/math/.svn/text-base/Vector3.as.svn-base
Normal file
@@ -0,0 +1,334 @@
|
||||
package alternativa.math {
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Vector3 extends Vector3D {
|
||||
|
||||
public static const ZERO:Vector3 = new Vector3(0, 0, 0);
|
||||
public static const X_AXIS:Vector3 = new Vector3(1, 0, 0);
|
||||
public static const Y_AXIS:Vector3 = new Vector3(0, 1, 0);
|
||||
public static const Z_AXIS:Vector3 = new Vector3(0, 0, 1);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
*/
|
||||
public function Vector3(x:Number = 0, y:Number = 0, z:Number = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function vLength():Number {
|
||||
return Math.sqrt(x*x + y*y + z*z);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function vLengthSqr():Number {
|
||||
return x*x + y*y + z*z;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
public function vSetLength(length:Number):Vector3 {
|
||||
var d:Number = x*x + y*y + z*z;
|
||||
if (d == 0) {
|
||||
x = length;
|
||||
} else {
|
||||
var k:Number = length/Math.sqrt(x*x + y*y + z*z);
|
||||
x *= k;
|
||||
y *= k;
|
||||
z *= k;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function vNormalize():Vector3 {
|
||||
var d:Number = x*x + y*y + z*z;
|
||||
if (d == 0) {
|
||||
x = 1;
|
||||
} else {
|
||||
d = Math.sqrt(d);
|
||||
x /= d;
|
||||
y /= d;
|
||||
z /= d;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сложение координат.
|
||||
*
|
||||
* @param v точка, координаты которой прибавляются к собственным
|
||||
*/
|
||||
public function vAdd(v:Vector3):Vector3 {
|
||||
x += v.x;
|
||||
y += v.y;
|
||||
z += v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param k
|
||||
* @param v
|
||||
* @return
|
||||
*/
|
||||
public function vAddScaled(k:Number, v:Vector3):Vector3 {
|
||||
x += k*v.x;
|
||||
y += k*v.y;
|
||||
z += k*v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычитание координат.
|
||||
*
|
||||
* @param v точка, координаты которой вычитаются из собственных
|
||||
*/
|
||||
public function vSubtract(v:Vector3):Vector3 {
|
||||
x -= v.x;
|
||||
y -= v.y;
|
||||
z -= v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет сумму векторов.
|
||||
*
|
||||
* @param a уменьшаемый вектор
|
||||
* @param b вычитаемый вектор
|
||||
* @return this
|
||||
*/
|
||||
public function vSum(a:Vector3, b:Vector3):Vector3 {
|
||||
x = a.x + b.x;
|
||||
y = a.y + b.y;
|
||||
z = a.z + b.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисление разности векторов.
|
||||
*
|
||||
* @param a уменьшаемый вектор
|
||||
* @param b вычитаемый вектор
|
||||
*/
|
||||
public function vDiff(a:Vector3, b:Vector3):Vector3 {
|
||||
x = a.x - b.x;
|
||||
y = a.y - b.y;
|
||||
z = a.z - b.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на скаляр.
|
||||
*
|
||||
* @param k число, на которое умножаются координаты
|
||||
*/
|
||||
public function vScale(k:Number):Vector3 {
|
||||
x *= k;
|
||||
y *= k;
|
||||
z *= k;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Инвертирование вектора.
|
||||
*/
|
||||
public function vReverse():Vector3 {
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param v
|
||||
* @return
|
||||
*/
|
||||
public function vDot(v:Vector3):Number {
|
||||
return x*v.x + y*v.y + z*v.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет векторное произведение с заданным вектором и записывает результат в текущий вектор.
|
||||
*
|
||||
* @param v
|
||||
*/
|
||||
public function vCross(v:Vector3):Vector3 {
|
||||
var xx:Number = y*v.z - z*v.y;
|
||||
var yy:Number = z*v.x - x*v.z;
|
||||
var zz:Number = x*v.y - y*v.x;
|
||||
x = xx;
|
||||
y = yy;
|
||||
z = zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет векторное произведение с заданным вектором и записывает результат в текущий вектор.
|
||||
*
|
||||
* @param v
|
||||
*/
|
||||
public function vCross2(a:Vector3, b:Vector3):Vector3 {
|
||||
x = a.y*b.z - a.z*b.y;
|
||||
y = a.z*b.x - a.x*b.z;
|
||||
z = a.x*b.y - a.y*b.x;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформация точки (вектора). Новым значением координат становится результат умножения матрицы на вектор вида
|
||||
* <code>M × r</code>.
|
||||
*
|
||||
* @param m матрица трансформации
|
||||
*/
|
||||
public function vTransformBy3(m:Matrix3):Vector3 {
|
||||
var xx:Number = x;
|
||||
var yy:Number = y;
|
||||
var zz:Number = z;
|
||||
x = m.a*xx + m.b*yy + m.c*zz;
|
||||
y = m.e*xx + m.f*yy + m.g*zz;
|
||||
z = m.i*xx + m.j*yy + m.k*zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param matrix
|
||||
*/
|
||||
public function vTransformBy3Tr(m:Matrix3):Vector3 {
|
||||
var xx:Number = x;
|
||||
var yy:Number = y;
|
||||
var zz:Number = z;
|
||||
x = m.a*xx + m.e*yy + m.i*zz;
|
||||
y = m.b*xx + m.f*yy + m.j*zz;
|
||||
z = m.c*xx + m.g*yy + m.k*zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформация точки (вектора). Новым значением координат становится результат умножения матрицы на вектор вида
|
||||
* <code>M × r</code>.
|
||||
*
|
||||
* @param m матрица трансформации
|
||||
*/
|
||||
public function vTransformBy4(m:Matrix4):Vector3 {
|
||||
var xx:Number = x;
|
||||
var yy:Number = y;
|
||||
var zz:Number = z;
|
||||
x = m.a*xx + m.b*yy + m.c*zz + m.d;
|
||||
y = m.e*xx + m.f*yy + m.g*zz + m.h;
|
||||
z = m.i*xx + m.j*yy + m.k*zz + m.l;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
* @return
|
||||
*/
|
||||
public function vTransformInverseBy4(m:Matrix4):Vector3 {
|
||||
var xx:Number = x - m.d;
|
||||
var yy:Number = y - m.h;
|
||||
var zz:Number = z - m.l;
|
||||
x = m.a*xx + m.e*yy + m.i*zz;
|
||||
y = m.b*xx + m.f*yy + m.j*zz;
|
||||
z = m.c*xx + m.g*yy + m.k*zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
*/
|
||||
public function vDeltaTransformBy4(m:Matrix4):Vector3 {
|
||||
var xx:Number = x;
|
||||
var yy:Number = y;
|
||||
var zz:Number = z;
|
||||
x = m.a*xx + m.b*yy + m.c*zz;
|
||||
y = m.e*xx + m.f*yy + m.g*zz;
|
||||
z = m.i*xx + m.j*yy + m.k*zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка координат.
|
||||
*/
|
||||
public function vReset(x:Number = 0, y:Number = 0, z:Number = 0):Vector3 {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирование координат точки.
|
||||
*
|
||||
* @param v точка, координаты которой копируются
|
||||
*/
|
||||
public function vCopy(v:Vector3):Vector3 {
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
z = v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирование точки.
|
||||
*
|
||||
* @return клонированная точка
|
||||
*/
|
||||
public function vClone():Vector3 {
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирует координаты вектора в экземпляр класса Vector3D.
|
||||
*
|
||||
* @param result экземпляр, в который копируются координаты
|
||||
* @return переданный параметр
|
||||
*/
|
||||
public function toVector3D(result:Vector3D):Vector3D {
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
result.z = z;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
public function copyFromVector3D(source:Vector3D):Vector3 {
|
||||
x = source.x;
|
||||
y = source.y;
|
||||
z = source.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
override public function toString():String {
|
||||
return "Vector3(" + x + "," + y + ", " + z + ")";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
390
0.0.9.2/src/alternativa/math/Matrix3.as
Normal file
390
0.0.9.2/src/alternativa/math/Matrix3.as
Normal file
@@ -0,0 +1,390 @@
|
||||
package alternativa.math {
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Matrix3 {
|
||||
|
||||
public static const ZERO:Matrix3 = new Matrix3(0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
public static const IDENTITY:Matrix3 = new Matrix3();
|
||||
|
||||
public var a:Number;
|
||||
public var b:Number;
|
||||
public var c:Number;
|
||||
|
||||
public var e:Number;
|
||||
public var f:Number;
|
||||
public var g:Number;
|
||||
|
||||
public var i:Number;
|
||||
public var j:Number;
|
||||
public var k:Number;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @param c
|
||||
* @param e
|
||||
* @param f
|
||||
* @param g
|
||||
* @param i
|
||||
* @param j
|
||||
* @param k
|
||||
*/
|
||||
public function Matrix3(a:Number = 1, b:Number = 0, c:Number = 0, e:Number = 0, f:Number = 1, g:Number = 0, i:Number = 0, j:Number = 0, k:Number = 1) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
|
||||
this.e = e;
|
||||
this.f = f;
|
||||
this.g = g;
|
||||
|
||||
this.i = i;
|
||||
this.j = j;
|
||||
this.k = k;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function toIdentity():Matrix3 {
|
||||
a = f = k = 1;
|
||||
b = c = e = g = i = j = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Преобразование матрицы в обратную.
|
||||
*/
|
||||
public function invert():Matrix3 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
|
||||
var det:Number = 1/(-cc*ff*ii + bb*gg*ii + cc*ee*jj - aa*gg*jj - bb*ee*kk + aa*ff*kk);
|
||||
|
||||
a = (ff*kk - gg*jj)*det;
|
||||
b = (cc*jj - bb*kk)*det;
|
||||
c = (bb*gg - cc*ff)*det;
|
||||
e = (gg*ii - ee*kk)*det;
|
||||
f = (aa*kk - cc*ii)*det;
|
||||
g = (cc*ee - aa*gg)*det;
|
||||
i = (ee*jj - ff*ii)*det;
|
||||
j = (bb*ii - aa*jj)*det;
|
||||
k = (aa*ff - bb*ee)*det;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на матрицу справа: M * this
|
||||
*
|
||||
* @param m левый операнд умножения
|
||||
*/
|
||||
public function append(m:Matrix3):Matrix3 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
|
||||
a = m.a*aa + m.b*ee + m.c*ii;
|
||||
b = m.a*bb + m.b*ff + m.c*jj;
|
||||
c = m.a*cc + m.b*gg + m.c*kk;
|
||||
e = m.e*aa + m.f*ee + m.g*ii;
|
||||
f = m.e*bb + m.f*ff + m.g*jj;
|
||||
g = m.e*cc + m.f*gg + m.g*kk;
|
||||
i = m.i*aa + m.j*ee + m.k*ii;
|
||||
j = m.i*bb + m.j*ff + m.k*jj;
|
||||
k = m.i*cc + m.j*gg + m.k*kk;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на матрицу слева: this * M
|
||||
*
|
||||
* @param matrix правый операнд умножения
|
||||
*/
|
||||
public function prepend(m:Matrix3):Matrix3 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
|
||||
a = aa*m.a + bb*m.e + cc*m.i;
|
||||
b = aa*m.b + bb*m.f + cc*m.j;
|
||||
c = aa*m.c + bb*m.g + cc*m.k;
|
||||
e = ee*m.a + ff*m.e + gg*m.i;
|
||||
f = ee*m.b + ff*m.f + gg*m.j;
|
||||
g = ee*m.c + ff*m.g + gg*m.k;
|
||||
i = ii*m.a + jj*m.e + kk*m.i;
|
||||
j = ii*m.b + jj*m.f + kk*m.j;
|
||||
k = ii*m.c + jj*m.g + kk*m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на матрицу слева: this * M
|
||||
*
|
||||
* @param matrix правый операнд умножения
|
||||
*/
|
||||
public function prependTransposed(m:Matrix3):Matrix3 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
|
||||
a = aa*m.a + bb*m.b + cc*m.c;
|
||||
b = aa*m.e + bb*m.f + cc*m.g;
|
||||
c = aa*m.i + bb*m.j + cc*m.k;
|
||||
e = ee*m.a + ff*m.b + gg*m.c;
|
||||
f = ee*m.e + ff*m.f + gg*m.g;
|
||||
g = ee*m.i + ff*m.j + gg*m.k;
|
||||
i = ii*m.a + jj*m.b + kk*m.c;
|
||||
j = ii*m.e + jj*m.f + kk*m.g;
|
||||
k = ii*m.i + jj*m.j + kk*m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
*/
|
||||
public function add(m:Matrix3):Matrix3 {
|
||||
a += m.a;
|
||||
b += m.b;
|
||||
c += m.c;
|
||||
e += m.e;
|
||||
f += m.f;
|
||||
g += m.g;
|
||||
i += m.i;
|
||||
j += m.j;
|
||||
k += m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
*/
|
||||
public function subtract(m:Matrix3):Matrix3 {
|
||||
a -= m.a;
|
||||
b -= m.b;
|
||||
c -= m.c;
|
||||
e -= m.e;
|
||||
f -= m.f;
|
||||
g -= m.g;
|
||||
i -= m.i;
|
||||
j -= m.j;
|
||||
k -= m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function transpose():Matrix3 {
|
||||
var tmp:Number = b;
|
||||
b = e;
|
||||
e = tmp;
|
||||
tmp = c;
|
||||
c = i;
|
||||
i = tmp;
|
||||
tmp = g;
|
||||
g = j;
|
||||
j = tmp;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформирует заданный вектор.
|
||||
*
|
||||
* @param vin входной вектор
|
||||
* @param vout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function transformVector(vin:Vector3, vout:Vector3):void {
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет обратную трансформацию заданного вектора. При этом подразумевается, что квадратная матрица содержит только вращения.
|
||||
*
|
||||
* @param vin входной вектор
|
||||
* @param vout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function transformVectorInverse(vin:Vector3, vout:Vector3):void {
|
||||
vout.x = a*vin.x + e*vin.y + i*vin.z;
|
||||
vout.y = b*vin.x + f*vin.y + j*vin.z;
|
||||
vout.z = c*vin.x + g*vin.y + k*vin.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформирует заданный вектор.
|
||||
*
|
||||
* @param vin входной вектор
|
||||
* @param vout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function transformVector3To3D(vin:Vector3, vout:Vector3D):void {
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param matrix
|
||||
*/
|
||||
public function createSkewSymmetric(v:Vector3):Matrix3 {
|
||||
a = f = k = 0;
|
||||
b = -v.z;
|
||||
c = v.y;
|
||||
e = v.z;
|
||||
g = -v.x;
|
||||
i = -v.y;
|
||||
j = v.x;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирование значений указанной матрицы.
|
||||
*
|
||||
* @param matrix матрица, значения которой копируются
|
||||
*/
|
||||
public function copy(m:Matrix3):Matrix3 {
|
||||
a = m.a;
|
||||
b = m.b;
|
||||
c = m.c;
|
||||
e = m.e;
|
||||
f = m.f;
|
||||
g = m.g;
|
||||
i = m.i;
|
||||
j = m.j;
|
||||
k = m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public function setRotationMatrix(rx:Number, ry:Number, rz:Number):Matrix3 {
|
||||
var cosX:Number = Math.cos(rx);
|
||||
var sinX:Number = Math.sin(rx);
|
||||
var cosY:Number = Math.cos(ry);
|
||||
var sinY:Number = Math.sin(ry);
|
||||
var cosZ:Number = Math.cos(rz);
|
||||
var sinZ:Number = Math.sin(rz);
|
||||
|
||||
var cosZsinY:Number = cosZ*sinY;
|
||||
var sinZsinY:Number = sinZ*sinY;
|
||||
|
||||
a = cosZ*cosY;
|
||||
b = cosZsinY*sinX - sinZ*cosX;
|
||||
c = cosZsinY*cosX + sinZ*sinX;
|
||||
|
||||
e = sinZ*cosY;
|
||||
f = sinZsinY*sinX + cosZ*cosX;
|
||||
g = sinZsinY*cosX - cosZ*sinX;
|
||||
|
||||
i = -sinY;
|
||||
j = cosY*sinX;
|
||||
k = cosY*cosX;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Метод формирует матрицу поворота на заданный угол относительно заданной оси.
|
||||
*
|
||||
* @param axis нормализованный вектор, задающий ось, относительно которой выполняется поворот
|
||||
* @param angle угол поворота в радианах
|
||||
*/
|
||||
public function fromAxisAngle(axis:Vector3, angle:Number):void {
|
||||
var c1:Number = Math.cos(angle);
|
||||
var s:Number = Math.sin(angle);
|
||||
var t:Number = 1 - c1;
|
||||
var x:Number = axis.x;
|
||||
var y:Number = axis.y;
|
||||
var z:Number = axis.z;
|
||||
|
||||
a = t*x*x + c1;
|
||||
b = t*x*y - z*s;
|
||||
c = t*x*z + y*s;
|
||||
|
||||
e = t*x*y + z*s;
|
||||
f = t*y*y + c1;
|
||||
g = t*y*z - x*s;
|
||||
|
||||
i = t*x*z - y*s;
|
||||
j = t*y*z + x*s;
|
||||
k = t*z*z + c1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирование матрицы.
|
||||
*
|
||||
* @return клон матрицы
|
||||
*/
|
||||
public function clone():Matrix3 {
|
||||
return new Matrix3(a, b, c, e, f, g, i, j, k);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[Matrix3 (" + a + ", " + b + ", " + c + "), (" + e + ", " + f + ", " + g + "), (" + i + ", " + j + ", " + k + ")]";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param angles
|
||||
*/
|
||||
public function getEulerAngles(angles:Vector3):void {
|
||||
if (-1 < i && i < 1) {
|
||||
angles.x = Math.atan2(j, k);
|
||||
angles.y = -Math.asin(i);
|
||||
angles.z = Math.atan2(e, a);
|
||||
} else {
|
||||
angles.x = 0;
|
||||
angles.y = (i <= -1) ? Math.PI : -Math.PI;
|
||||
angles.y *= 0.5;
|
||||
angles.z = Math.atan2(-b, f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
475
0.0.9.2/src/alternativa/math/Matrix4.as
Normal file
475
0.0.9.2/src/alternativa/math/Matrix4.as
Normal file
@@ -0,0 +1,475 @@
|
||||
package alternativa.math {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Matrix4 {
|
||||
|
||||
public static const IDENTITY:Matrix4 = new Matrix4();
|
||||
|
||||
public var a:Number;
|
||||
public var b:Number;
|
||||
public var c:Number;
|
||||
public var d:Number;
|
||||
|
||||
public var e:Number;
|
||||
public var f:Number;
|
||||
public var g:Number;
|
||||
public var h:Number;
|
||||
|
||||
public var i:Number;
|
||||
public var j:Number;
|
||||
public var k:Number;
|
||||
public var l:Number;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @param c
|
||||
* @param e
|
||||
* @param f
|
||||
* @param g
|
||||
* @param i
|
||||
* @param j
|
||||
* @param k
|
||||
*/
|
||||
public function Matrix4(a:Number = 1, b:Number = 0, c:Number = 0, d:Number = 0, e:Number = 0, f:Number = 1, g:Number = 0, h:Number = 0, i:Number = 0, j:Number = 0, k:Number = 1, l:Number = 0) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
this.c = c;
|
||||
this.d = d;
|
||||
|
||||
this.e = e;
|
||||
this.f = f;
|
||||
this.g = g;
|
||||
this.h = h;
|
||||
|
||||
this.i = i;
|
||||
this.j = j;
|
||||
this.k = k;
|
||||
this.l = l;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function toIdentity():Matrix4 {
|
||||
a = f = k = 1;
|
||||
b = c = e = g = i = j = d = h = l = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Преобразование матрицы в обратную.
|
||||
*/
|
||||
public function invert():Matrix4 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var dd:Number = d;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var hh:Number = h;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
var ll:Number = l;
|
||||
|
||||
var det:Number = -cc*ff*ii + bb*gg*ii + cc*ee*jj - aa*gg*jj - bb*ee*kk + aa*ff*kk;
|
||||
|
||||
a = (-gg*jj + ff*kk)/det;
|
||||
b = (cc*jj - bb*kk)/det;
|
||||
c = (-cc*ff + bb*gg)/det;
|
||||
d = (dd*gg*jj - cc*hh*jj - dd*ff*kk + bb*hh*kk + cc*ff*ll - bb*gg*ll)/det;
|
||||
e = (gg*ii - ee*kk)/det;
|
||||
f = (-cc*ii + aa*kk)/det;
|
||||
g = (cc*ee - aa*gg)/det;
|
||||
h = (cc*hh*ii - dd*gg*ii + dd*ee*kk - aa*hh*kk - cc*ee*ll + aa*gg*ll)/det;
|
||||
i = (-ff*ii + ee*jj)/det;
|
||||
j = (bb*ii - aa*jj)/det;
|
||||
k = (-bb*ee + aa*ff)/det;
|
||||
l = (dd*ff*ii - bb*hh*ii - dd*ee*jj + aa*hh*jj + bb*ee*ll - aa*ff*ll)/det;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на матрицу справа: M * this
|
||||
*
|
||||
* @param m левый операнд умножения
|
||||
*/
|
||||
public function append(m:Matrix4):Matrix4 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var dd:Number = d;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var hh:Number = h;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
var ll:Number = l;
|
||||
|
||||
a = m.a*aa + m.b*ee + m.c*ii;
|
||||
b = m.a*bb + m.b*ff + m.c*jj;
|
||||
c = m.a*cc + m.b*gg + m.c*kk;
|
||||
d = m.a*dd + m.b*hh + m.c*ll + m.d;
|
||||
e = m.e*aa + m.f*ee + m.g*ii;
|
||||
f = m.e*bb + m.f*ff + m.g*jj;
|
||||
g = m.e*cc + m.f*gg + m.g*kk;
|
||||
h = m.e*dd + m.f*hh + m.g*ll + m.h;
|
||||
i = m.i*aa + m.j*ee + m.k*ii;
|
||||
j = m.i*bb + m.j*ff + m.k*jj;
|
||||
k = m.i*cc + m.j*gg + m.k*kk;
|
||||
l = m.i*dd + m.j*hh + m.k*ll + m.l;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на матрицу слева: this * M
|
||||
*
|
||||
* @param matrix правый операнд умножения
|
||||
*/
|
||||
public function prepend(m:Matrix4):Matrix4 {
|
||||
var aa:Number = a;
|
||||
var bb:Number = b;
|
||||
var cc:Number = c;
|
||||
var dd:Number = d;
|
||||
var ee:Number = e;
|
||||
var ff:Number = f;
|
||||
var gg:Number = g;
|
||||
var hh:Number = h;
|
||||
var ii:Number = i;
|
||||
var jj:Number = j;
|
||||
var kk:Number = k;
|
||||
var ll:Number = l;
|
||||
|
||||
a = aa*m.a + bb*m.e + cc*m.i;
|
||||
b = aa*m.b + bb*m.f + cc*m.j;
|
||||
c = aa*m.c + bb*m.g + cc*m.k;
|
||||
d = aa*m.d + bb*m.h + cc*m.l + dd;
|
||||
e = ee*m.a + ff*m.e + gg*m.i;
|
||||
f = ee*m.b + ff*m.f + gg*m.j;
|
||||
g = ee*m.c + ff*m.g + gg*m.k;
|
||||
h = ee*m.d + ff*m.h + gg*m.l + hh;
|
||||
i = ii*m.a + jj*m.e + kk*m.i;
|
||||
j = ii*m.b + jj*m.f + kk*m.j;
|
||||
k = ii*m.c + jj*m.g + kk*m.k;
|
||||
l = ii*m.d + jj*m.h + kk*m.l + ll;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
*/
|
||||
public function add(m:Matrix4):Matrix4 {
|
||||
a += m.a;
|
||||
b += m.b;
|
||||
c += m.c;
|
||||
d += m.d;
|
||||
e += m.e;
|
||||
f += m.f;
|
||||
g += m.g;
|
||||
h += m.h;
|
||||
i += m.i;
|
||||
j += m.j;
|
||||
k += m.k;
|
||||
l += m.l;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
*/
|
||||
public function subtract(m:Matrix4):Matrix4 {
|
||||
a -= m.a;
|
||||
b -= m.b;
|
||||
c -= m.c;
|
||||
d -= m.d;
|
||||
e -= m.e;
|
||||
f -= m.f;
|
||||
g -= m.g;
|
||||
h -= m.h;
|
||||
i -= m.i;
|
||||
j -= m.j;
|
||||
k -= m.k;
|
||||
l -= m.l;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформирует заданный вектор.
|
||||
*
|
||||
* @param vin входной вектор
|
||||
* @param vout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function transformVector(vin:Vector3, vout:Vector3):void {
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z + d;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z + h;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z + l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет обратную трансформацию заданного вектора. При этом подразумевается, что квадратная матрица содержит только вращения.
|
||||
*
|
||||
* @param vin входной вектор
|
||||
* @param vout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function transformVectorInverse(vin:Vector3, vout:Vector3):void {
|
||||
var xx:Number = vin.x - d;
|
||||
var yy:Number = vin.y - h;
|
||||
var zz:Number = vin.z - l;
|
||||
vout.x = a*xx + e*yy + i*zz;
|
||||
vout.y = b*xx + f*yy + j*zz;
|
||||
vout.z = c*xx + g*yy + k*zz;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param arrin
|
||||
* @param Vector3
|
||||
* @param arrout
|
||||
* @param Vector3
|
||||
*/
|
||||
public function transformVectors(arrin:Vector.<Vector3>, arrout:Vector.<Vector3>):void {
|
||||
var len:int = arrin.length;
|
||||
var vin:Vector3;
|
||||
var vout:Vector3;
|
||||
for (var idx:int = 0; idx < len; idx++) {
|
||||
vin = arrin[idx];
|
||||
vout = arrout[idx];
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z + d;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z + h;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z + l;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param arrin
|
||||
* @param Vector3
|
||||
* @param arrout
|
||||
* @param Vector3
|
||||
* @param len
|
||||
*/
|
||||
public function transformVectorsN(arrin:Vector.<Vector3>, arrout:Vector.<Vector3>, len:int):void {
|
||||
var vin:Vector3;
|
||||
var vout:Vector3;
|
||||
for (var idx:int = 0; idx < len; idx++) {
|
||||
vin = arrin[idx];
|
||||
vout = arrout[idx];
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z + d;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z + h;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z + l;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param arrin
|
||||
* @param Vector3
|
||||
* @param arrout
|
||||
* @param Vector3
|
||||
*/
|
||||
public function transformVectorsInverse(arrin:Vector.<Vector3>, arrout:Vector.<Vector3>):void {
|
||||
var len:int = arrin.length;
|
||||
var vin:Vector3;
|
||||
var vout:Vector3;
|
||||
for (var idx:int = 0; idx < len; idx++) {
|
||||
vin = arrin[idx];
|
||||
vout = arrout[idx];
|
||||
var xx:Number = vin.x - d;
|
||||
var yy:Number = vin.y - h;
|
||||
var zz:Number = vin.z - l;
|
||||
vout.x = a*xx + e*yy + i*zz;
|
||||
vout.y = b*xx + f*yy + j*zz;
|
||||
vout.z = c*xx + g*yy + k*zz;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param arrin
|
||||
* @param Vector3
|
||||
* @param arrout
|
||||
* @param Vector3
|
||||
*/
|
||||
public function transformVectorsInverseN(arrin:Vector.<Vector3>, arrout:Vector.<Vector3>, len:int):void {
|
||||
var vin:Vector3;
|
||||
var vout:Vector3;
|
||||
for (var idx:int = 0; idx < len; idx++) {
|
||||
vin = arrin[idx];
|
||||
vout = arrout[idx];
|
||||
var xx:Number = vin.x - d;
|
||||
var yy:Number = vin.y - h;
|
||||
var zz:Number = vin.z - l;
|
||||
vout.x = a*xx + e*yy + i*zz;
|
||||
vout.y = b*xx + f*yy + j*zz;
|
||||
vout.z = c*xx + g*yy + k*zz;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param i
|
||||
* @param axis
|
||||
*/
|
||||
public function getAxis(idx:int, axis:Vector3):void {
|
||||
switch (idx) {
|
||||
case 0:
|
||||
axis.x = a;
|
||||
axis.y = e;
|
||||
axis.z = i;
|
||||
return;
|
||||
case 1:
|
||||
axis.x = b;
|
||||
axis.y = f;
|
||||
axis.z = j;
|
||||
return;
|
||||
case 2:
|
||||
axis.x = c;
|
||||
axis.y = g;
|
||||
axis.z = k;
|
||||
return;
|
||||
case 3:
|
||||
axis.x = d;
|
||||
axis.y = h;
|
||||
axis.z = l;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформирует заданный вектор без учёта смещения центра матрицы.
|
||||
*
|
||||
* @param pin входной вектор
|
||||
* @param pout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function deltaTransformVector(vin:Vector3, vout:Vector3):void {
|
||||
vout.x = a*vin.x + b*vin.y + c*vin.z + d;
|
||||
vout.y = e*vin.x + f*vin.y + g*vin.z + h;
|
||||
vout.z = i*vin.x + j*vin.y + k*vin.z + l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформирует заданный вектор без учёта смещения центра матрицы.
|
||||
*
|
||||
* @param pin входной вектор
|
||||
* @param pout вектор, в который записывается результат трансформации
|
||||
*/
|
||||
public function deltaTransformVectorInverse(vin:Vector3, vout:Vector3):void {
|
||||
vout.x = a*vin.x + e*vin.y + i*vin.z;
|
||||
vout.y = b*vin.x + f*vin.y + j*vin.z;
|
||||
vout.z = c*vin.x + g*vin.y + k*vin.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирование значений указанной матрицы.
|
||||
*
|
||||
* @param matrix матрица, значения которой копируются
|
||||
*/
|
||||
public function copy(m:Matrix4):Matrix4 {
|
||||
a = m.a;
|
||||
b = m.b;
|
||||
c = m.c;
|
||||
d = m.d;
|
||||
e = m.e;
|
||||
f = m.f;
|
||||
g = m.g;
|
||||
h = m.h;
|
||||
i = m.i;
|
||||
j = m.j;
|
||||
k = m.k;
|
||||
l = m.l;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public function setFromMatrix3(m:Matrix3, offset:Vector3):Matrix4 {
|
||||
a = m.a;
|
||||
b = m.b;
|
||||
c = m.c;
|
||||
d = offset.x;
|
||||
e = m.e;
|
||||
f = m.f;
|
||||
g = m.g;
|
||||
h = offset.y;
|
||||
i = m.i;
|
||||
j = m.j;
|
||||
k = m.k;
|
||||
l = offset.z;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public function setOrientationFromMatrix3(m:Matrix3):Matrix4 {
|
||||
a = m.a;
|
||||
b = m.b;
|
||||
c = m.c;
|
||||
e = m.e;
|
||||
f = m.f;
|
||||
g = m.g;
|
||||
i = m.i;
|
||||
j = m.j;
|
||||
k = m.k;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param angles
|
||||
*/
|
||||
public function getEulerAngles(angles:Vector3):void {
|
||||
if (-1 < i && i < 1) {
|
||||
angles.x = Math.atan2(j, k);
|
||||
angles.y = -Math.asin(i);
|
||||
angles.z = Math.atan2(e, a);
|
||||
} else {
|
||||
angles.x = 0;
|
||||
angles.y = (i <= -1) ? Math.PI : -Math.PI;
|
||||
angles.y *= 0.5;
|
||||
angles.z = Math.atan2(-b, f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает координаты матрицы.
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public function setPosition(pos:Vector3):void {
|
||||
d = pos.x;
|
||||
h = pos.y;
|
||||
l = pos.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирование матрицы.
|
||||
*
|
||||
* @return клон матрицы
|
||||
*/
|
||||
public function clone():Matrix4 {
|
||||
return new Matrix4(a, b, c, d, e, f, g, h, i, j, k, l);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[Matrix4 [" + a.toFixed(3) + " " + b.toFixed(3) + " " + c.toFixed(3) + " " + d.toFixed(3) + "] [" + e.toFixed(3) + " " + f.toFixed(3) + " " + g.toFixed(3) + " " + h.toFixed(3) + "] [" + i.toFixed(3) + " " + j.toFixed(3) + " " + k.toFixed(3) + " " + l.toFixed(3) + "]]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
458
0.0.9.2/src/alternativa/math/Quaternion.as
Normal file
458
0.0.9.2/src/alternativa/math/Quaternion.as
Normal file
@@ -0,0 +1,458 @@
|
||||
package alternativa.math {
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
public class Quaternion {
|
||||
|
||||
public var w:Number;
|
||||
public var x:Number;
|
||||
public var y:Number;
|
||||
public var z:Number;
|
||||
|
||||
public static function multiply(q1:Quaternion, q2:Quaternion, result:Quaternion):void {
|
||||
result.w = q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z;
|
||||
result.x = q1.w*q2.x + q1.x*q2.w + q1.y*q2.z - q1.z*q2.y;
|
||||
result.y = q1.w*q2.y + q1.y*q2.w + q1.z*q2.x - q1.x*q2.z;
|
||||
result.z = q1.w*q2.z + q1.z*q2.w + q1.x*q2.y - q1.y*q2.x;
|
||||
}
|
||||
|
||||
public static function createFromAxisAngle(axis:Vector3, angle:Number):Quaternion {
|
||||
var q:Quaternion = new Quaternion();
|
||||
q.setFromAxisAngle(axis, angle);
|
||||
return q;
|
||||
}
|
||||
|
||||
public static function createFromAxisAngleComponents(x:Number, y:Number, z:Number, angle:Number):Quaternion {
|
||||
var q:Quaternion = new Quaternion();
|
||||
q.setFromAxisAngleComponents(x, y, z, angle);
|
||||
return q;
|
||||
}
|
||||
|
||||
private static var _q:Quaternion = new Quaternion();
|
||||
|
||||
public function Quaternion(w:Number = 1, x:Number = 0, y:Number = 0, z:Number = 0) {
|
||||
this.w = w;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public function reset(w:Number = 1, x:Number = 0, y:Number = 0, z:Number = 0):Quaternion {
|
||||
this.w = w;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public function normalize():Quaternion {
|
||||
var d:Number = w*w + x*x + y*y + z*z;
|
||||
if (d == 0) {
|
||||
w = 1;
|
||||
} else {
|
||||
d = 1/Math.sqrt(d);
|
||||
w *= d;
|
||||
x *= d;
|
||||
y *= d;
|
||||
z *= d;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножает на указанный кватернион слева: this * q
|
||||
*
|
||||
* @param q множитель
|
||||
*/
|
||||
public function prepend(q:Quaternion):Quaternion {
|
||||
var ww:Number = w*q.w - x*q.x - y*q.y - z*q.z;
|
||||
var xx:Number = w*q.x + x*q.w + y*q.z - z*q.y;
|
||||
var yy:Number = w*q.y + y*q.w + z*q.x - x*q.z;
|
||||
var zz:Number = w*q.z + z*q.w + x*q.y - y*q.x;
|
||||
w = ww;
|
||||
x = xx;
|
||||
y = yy;
|
||||
z = zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножает на указанный кватернион справа: q * this
|
||||
*
|
||||
* @param q множитель
|
||||
*/
|
||||
public function append(q:Quaternion):Quaternion {
|
||||
var ww:Number = q.w*w - q.x*x - q.y*y - q.z*z;
|
||||
var xx:Number = q.w*x + q.x*w + q.y*z - q.z*y;
|
||||
var yy:Number = q.w*y + q.y*w + q.z*x - q.x*z;
|
||||
var zz:Number = q.w*z + q.z*w + q.x*y - q.y*x;
|
||||
w = ww;
|
||||
x = xx;
|
||||
y = yy;
|
||||
z = zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param vector
|
||||
*/
|
||||
public function rotateByVector(v:Vector3):Quaternion {
|
||||
var ww:Number = -v.x*x - v.y*y - v.z*z;
|
||||
var xx:Number = v.x*w + v.y*z - v.z*y;
|
||||
var yy:Number = v.y*w + v.z*x - v.x*z;
|
||||
var zz:Number = v.z*w + v.x*y - v.y*x;
|
||||
w = ww;
|
||||
x = xx;
|
||||
y = yy;
|
||||
z = zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет вращение, приданное вектором угловой скорости за указанное время.
|
||||
*
|
||||
* @param v
|
||||
* @param scale
|
||||
*/
|
||||
public function addScaledVector(v:Vector3, scale:Number):Quaternion {
|
||||
var vx:Number = v.x*scale;
|
||||
var vy:Number = v.y*scale;
|
||||
var vz:Number = v.z*scale;
|
||||
var ww:Number = -x*vx - y*vy - z*vz;
|
||||
var xx:Number = vx*w + vy*z - vz*y;
|
||||
var yy:Number = vy*w + vz*x - vx*z;
|
||||
var zz:Number = vz*w + vx*y - vy*x;
|
||||
w += 0.5*ww;
|
||||
x += 0.5*xx;
|
||||
y += 0.5*yy;
|
||||
z += 0.5*zz;
|
||||
// inlined normalize
|
||||
var d:Number = w*w + x*x + y*y + z*z;
|
||||
if (d == 0) {
|
||||
w = 1;
|
||||
} else {
|
||||
d = 1/Math.sqrt(d);
|
||||
w *= d;
|
||||
x *= d;
|
||||
y *= d;
|
||||
z *= d;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
* @return
|
||||
*/
|
||||
public function toMatrix3(m:Matrix3):Quaternion {
|
||||
var xx2:Number = 2.0*x*x;
|
||||
var yy2:Number = 2.0*y*y;
|
||||
var zz2:Number = 2.0*z*z;
|
||||
var xy2:Number = 2.0*x*y;
|
||||
var yz2:Number = 2.0*y*z;
|
||||
var zx2:Number = 2.0*z*x;
|
||||
var wx2:Number = 2.0*w*x;
|
||||
var wy2:Number = 2.0*w*y;
|
||||
var wz2:Number = 2.0*w*z;
|
||||
|
||||
m.a = 1.0 - yy2 - zz2;
|
||||
m.b = xy2 - wz2;
|
||||
m.c = zx2 + wy2;
|
||||
|
||||
m.e = xy2 + wz2;
|
||||
m.f = 1.0 - xx2 - zz2;
|
||||
m.g = yz2 - wx2;
|
||||
|
||||
m.i = zx2 - wy2;
|
||||
m.j = yz2 + wx2;
|
||||
m.k = 1.0 - xx2 - yy2;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function length():Number {
|
||||
return Math.sqrt(w*w + x*x + y*y + z*z);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function lengthSqr():Number {
|
||||
return w*w + x*x + y*y + z*z;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param axis
|
||||
* @param angle
|
||||
* @return
|
||||
*/
|
||||
public function setFromAxisAngle(axis:Vector3, angle:Number):Quaternion {
|
||||
w = Math.cos(0.5*angle);
|
||||
var k:Number = Math.sin(0.5*angle)/Math.sqrt(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z);
|
||||
x = axis.x*k;
|
||||
y = axis.y*k;
|
||||
z = axis.z*k;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
* @param angle
|
||||
*/
|
||||
public function setFromAxisAngleComponents(x:Number, y:Number, z:Number, angle:Number):Quaternion {
|
||||
w = Math.cos(0.5*angle);
|
||||
var k:Number = Math.sin(0.5*angle)/Math.sqrt(x*x + y*y + z*z);
|
||||
this.x = x*k;
|
||||
this.y = y*k;
|
||||
this.z = z*k;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param vector
|
||||
*/
|
||||
public function toAxisVector(v:Vector3 = null):Vector3 {
|
||||
if (w < -1 || w > 1) {
|
||||
normalize();
|
||||
}
|
||||
if (v == null) {
|
||||
v = new Vector3();
|
||||
}
|
||||
if (w > -1 && w < 1) {
|
||||
if (w == 0) {
|
||||
v.x = x;
|
||||
v.y = y;
|
||||
v.z = z;
|
||||
} else {
|
||||
var angle:Number = 2*Math.acos(w);
|
||||
var coeff:Number = 1/Math.sqrt(1 - w*w);
|
||||
v.x = x*coeff*angle;
|
||||
v.y = y*coeff*angle;
|
||||
v.z = z*coeff*angle;
|
||||
}
|
||||
} else {
|
||||
v.x = 0;
|
||||
v.y = 0;
|
||||
v.z = 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param rotations
|
||||
*/
|
||||
public function getEulerAngles(angles:Vector3):Vector3 {
|
||||
var qi2:Number = 2*x*x;
|
||||
var qj2:Number = 2*y*y;
|
||||
var qk2:Number = 2*z*z;
|
||||
var qij:Number = 2*x*y;
|
||||
var qjk:Number = 2*y*z;
|
||||
var qki:Number = 2*z*x;
|
||||
var qri:Number = 2*w*x;
|
||||
var qrj:Number = 2*w*y;
|
||||
var qrk:Number = 2*w*z;
|
||||
|
||||
var aa:Number = 1 - qj2 - qk2;
|
||||
var bb:Number = qij - qrk;
|
||||
var ee:Number = qij + qrk;
|
||||
var ff:Number = 1 - qi2 - qk2;
|
||||
var ii:Number = qki - qrj;
|
||||
var jj:Number = qjk + qri;
|
||||
var kk:Number = 1 - qi2 - qj2;
|
||||
|
||||
if (-1 < ii && ii < 1) {
|
||||
if (angles == null) {
|
||||
angles = new Vector3(Math.atan2(jj, kk), -Math.asin(ii), Math.atan2(ee, aa));
|
||||
} else {
|
||||
angles.x = Math.atan2(jj, kk);
|
||||
angles.y = -Math.asin(ii);
|
||||
angles.z = Math.atan2(ee, aa);
|
||||
}
|
||||
} else {
|
||||
if (angles == null) {
|
||||
angles = new Vector3(0, 0.5*((ii <= -1) ? Math.PI : -Math.PI), Math.atan2(-bb, ff));
|
||||
} else {
|
||||
angles.x = 0;
|
||||
angles.y = (ii <= -1) ? Math.PI : -Math.PI;
|
||||
angles.y *= 0.5;
|
||||
angles.z = Math.atan2(-bb, ff);
|
||||
}
|
||||
}
|
||||
return angles;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
*/
|
||||
public function setFromEulerAnglesXYZ(x:Number, y:Number, z:Number):void {
|
||||
setFromAxisAngleComponents(1, 0, 0, x);
|
||||
|
||||
_q.setFromAxisAngleComponents(0, 1, 0, y);
|
||||
append(_q);
|
||||
normalize();
|
||||
|
||||
_q.setFromAxisAngleComponents(0, 0, 1, z);
|
||||
append(_q);
|
||||
normalize();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function conjugate():void {
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет линейную интерполяцию.
|
||||
*
|
||||
* @param q1 начало отрезка
|
||||
* @param q2 конец отрезка
|
||||
* @param t время, обычно задаётся в интервале [0, 1]
|
||||
*/
|
||||
public function nlerp(q1:Quaternion, q2:Quaternion, t:Number):Quaternion {
|
||||
var d:Number = 1 - t;
|
||||
w = q1.w*d + q2.w*t;
|
||||
x = q1.x*d + q2.x*t;
|
||||
y = q1.y*d + q2.y*t;
|
||||
z = q1.z*d + q2.z*t;
|
||||
// inlined normalize
|
||||
d = w*w + x*x + y*y + z*z;
|
||||
if (d == 0) {
|
||||
w = 1;
|
||||
} else {
|
||||
d = 1/Math.sqrt(d);
|
||||
w *= d;
|
||||
x *= d;
|
||||
y *= d;
|
||||
z *= d;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param q
|
||||
* @return
|
||||
*/
|
||||
public function subtract(q:Quaternion):Quaternion {
|
||||
w -= q.w;
|
||||
x -= q.x;
|
||||
y -= q.y;
|
||||
z -= q.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param q1
|
||||
* @param q2
|
||||
* @return
|
||||
*/
|
||||
public function diff(q1:Quaternion, q2:Quaternion):Quaternion {
|
||||
w = q2.w - q1.w;
|
||||
x = q2.x - q1.x;
|
||||
y = q2.y - q1.y;
|
||||
z = q2.z - q1.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param q
|
||||
* @return
|
||||
*/
|
||||
public function copy(q:Quaternion):Quaternion {
|
||||
w = q.w;
|
||||
x = q.x;
|
||||
y = q.y;
|
||||
z = q.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param reslult
|
||||
*/
|
||||
public function toVector3D(result:Vector3D):Vector3D {
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
result.z = z;
|
||||
result.w = w;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function clone():Quaternion {
|
||||
return new Quaternion(w, x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[" + w + ", " + x + ", " + y + ", " + z + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет сферическую интерполяцию между двумя заданными кватернионами по наименьшему расстоянию.
|
||||
*
|
||||
* @param a первый кватерион
|
||||
* @param b второй кватернион
|
||||
* @param t параметр интерполяции, обычно принадлежит отрезку [0, 1]
|
||||
* @return this
|
||||
*/
|
||||
public function slerp(a:Quaternion, b:Quaternion, t:Number):Quaternion {
|
||||
var flip:Number = 1;
|
||||
// Так как одна и та же ориентация представляется двумя значениями q и -q, нужно сменить знак одного из кватернионов
|
||||
// если скалярное произведение отрицательно. Иначе будет получено интерполированное значение по наибольшему расстоянию.
|
||||
var cosine:Number = a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
if (cosine < 0) {
|
||||
cosine = -cosine;
|
||||
flip = -1;
|
||||
}
|
||||
|
||||
if ((1 - cosine) < 0.001) {
|
||||
// Вблизи нуля используется линейная интерполяция
|
||||
var k1:Number = 1 - t;
|
||||
var k2:Number = t*flip;
|
||||
w = a.w*k1 + b.w*k2;
|
||||
x = a.x*k1 + b.x*k2;
|
||||
y = a.y*k1 + b.y*k2;
|
||||
z = a.z*k1 + b.z*k2;
|
||||
normalize();
|
||||
} else {
|
||||
var theta:Number = Math.acos(cosine);
|
||||
var sine:Number = Math.sin(theta);
|
||||
var beta:Number = Math.sin((1 - t)*theta)/sine;
|
||||
var alpha:Number = Math.sin(t*theta)/sine*flip;
|
||||
w = a.w*beta + b.w*alpha;
|
||||
x = a.x*beta + b.x*alpha;
|
||||
y = a.y*beta + b.y*alpha;
|
||||
z = a.z*beta + b.z*alpha;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
334
0.0.9.2/src/alternativa/math/Vector3.as
Normal file
334
0.0.9.2/src/alternativa/math/Vector3.as
Normal file
@@ -0,0 +1,334 @@
|
||||
package alternativa.math {
|
||||
import flash.geom.Vector3D;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Vector3 extends Vector3D {
|
||||
|
||||
public static const ZERO:Vector3 = new Vector3(0, 0, 0);
|
||||
public static const X_AXIS:Vector3 = new Vector3(1, 0, 0);
|
||||
public static const Y_AXIS:Vector3 = new Vector3(0, 1, 0);
|
||||
public static const Z_AXIS:Vector3 = new Vector3(0, 0, 1);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
*/
|
||||
public function Vector3(x:Number = 0, y:Number = 0, z:Number = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function vLength():Number {
|
||||
return Math.sqrt(x*x + y*y + z*z);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function vLengthSqr():Number {
|
||||
return x*x + y*y + z*z;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
public function vSetLength(length:Number):Vector3 {
|
||||
var d:Number = x*x + y*y + z*z;
|
||||
if (d == 0) {
|
||||
x = length;
|
||||
} else {
|
||||
var k:Number = length/Math.sqrt(x*x + y*y + z*z);
|
||||
x *= k;
|
||||
y *= k;
|
||||
z *= k;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public function vNormalize():Vector3 {
|
||||
var d:Number = x*x + y*y + z*z;
|
||||
if (d == 0) {
|
||||
x = 1;
|
||||
} else {
|
||||
d = Math.sqrt(d);
|
||||
x /= d;
|
||||
y /= d;
|
||||
z /= d;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Сложение координат.
|
||||
*
|
||||
* @param v точка, координаты которой прибавляются к собственным
|
||||
*/
|
||||
public function vAdd(v:Vector3):Vector3 {
|
||||
x += v.x;
|
||||
y += v.y;
|
||||
z += v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param k
|
||||
* @param v
|
||||
* @return
|
||||
*/
|
||||
public function vAddScaled(k:Number, v:Vector3):Vector3 {
|
||||
x += k*v.x;
|
||||
y += k*v.y;
|
||||
z += k*v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычитание координат.
|
||||
*
|
||||
* @param v точка, координаты которой вычитаются из собственных
|
||||
*/
|
||||
public function vSubtract(v:Vector3):Vector3 {
|
||||
x -= v.x;
|
||||
y -= v.y;
|
||||
z -= v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет сумму векторов.
|
||||
*
|
||||
* @param a уменьшаемый вектор
|
||||
* @param b вычитаемый вектор
|
||||
* @return this
|
||||
*/
|
||||
public function vSum(a:Vector3, b:Vector3):Vector3 {
|
||||
x = a.x + b.x;
|
||||
y = a.y + b.y;
|
||||
z = a.z + b.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисление разности векторов.
|
||||
*
|
||||
* @param a уменьшаемый вектор
|
||||
* @param b вычитаемый вектор
|
||||
*/
|
||||
public function vDiff(a:Vector3, b:Vector3):Vector3 {
|
||||
x = a.x - b.x;
|
||||
y = a.y - b.y;
|
||||
z = a.z - b.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Умножение на скаляр.
|
||||
*
|
||||
* @param k число, на которое умножаются координаты
|
||||
*/
|
||||
public function vScale(k:Number):Vector3 {
|
||||
x *= k;
|
||||
y *= k;
|
||||
z *= k;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Инвертирование вектора.
|
||||
*/
|
||||
public function vReverse():Vector3 {
|
||||
x = -x;
|
||||
y = -y;
|
||||
z = -z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param v
|
||||
* @return
|
||||
*/
|
||||
public function vDot(v:Vector3):Number {
|
||||
return x*v.x + y*v.y + z*v.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет векторное произведение с заданным вектором и записывает результат в текущий вектор.
|
||||
*
|
||||
* @param v
|
||||
*/
|
||||
public function vCross(v:Vector3):Vector3 {
|
||||
var xx:Number = y*v.z - z*v.y;
|
||||
var yy:Number = z*v.x - x*v.z;
|
||||
var zz:Number = x*v.y - y*v.x;
|
||||
x = xx;
|
||||
y = yy;
|
||||
z = zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет векторное произведение с заданным вектором и записывает результат в текущий вектор.
|
||||
*
|
||||
* @param v
|
||||
*/
|
||||
public function vCross2(a:Vector3, b:Vector3):Vector3 {
|
||||
x = a.y*b.z - a.z*b.y;
|
||||
y = a.z*b.x - a.x*b.z;
|
||||
z = a.x*b.y - a.y*b.x;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформация точки (вектора). Новым значением координат становится результат умножения матрицы на вектор вида
|
||||
* <code>M × r</code>.
|
||||
*
|
||||
* @param m матрица трансформации
|
||||
*/
|
||||
public function vTransformBy3(m:Matrix3):Vector3 {
|
||||
var xx:Number = x;
|
||||
var yy:Number = y;
|
||||
var zz:Number = z;
|
||||
x = m.a*xx + m.b*yy + m.c*zz;
|
||||
y = m.e*xx + m.f*yy + m.g*zz;
|
||||
z = m.i*xx + m.j*yy + m.k*zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param matrix
|
||||
*/
|
||||
public function vTransformBy3Tr(m:Matrix3):Vector3 {
|
||||
var xx:Number = x;
|
||||
var yy:Number = y;
|
||||
var zz:Number = z;
|
||||
x = m.a*xx + m.e*yy + m.i*zz;
|
||||
y = m.b*xx + m.f*yy + m.j*zz;
|
||||
z = m.c*xx + m.g*yy + m.k*zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Трансформация точки (вектора). Новым значением координат становится результат умножения матрицы на вектор вида
|
||||
* <code>M × r</code>.
|
||||
*
|
||||
* @param m матрица трансформации
|
||||
*/
|
||||
public function vTransformBy4(m:Matrix4):Vector3 {
|
||||
var xx:Number = x;
|
||||
var yy:Number = y;
|
||||
var zz:Number = z;
|
||||
x = m.a*xx + m.b*yy + m.c*zz + m.d;
|
||||
y = m.e*xx + m.f*yy + m.g*zz + m.h;
|
||||
z = m.i*xx + m.j*yy + m.k*zz + m.l;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
* @return
|
||||
*/
|
||||
public function vTransformInverseBy4(m:Matrix4):Vector3 {
|
||||
var xx:Number = x - m.d;
|
||||
var yy:Number = y - m.h;
|
||||
var zz:Number = z - m.l;
|
||||
x = m.a*xx + m.e*yy + m.i*zz;
|
||||
y = m.b*xx + m.f*yy + m.j*zz;
|
||||
z = m.c*xx + m.g*yy + m.k*zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m
|
||||
*/
|
||||
public function vDeltaTransformBy4(m:Matrix4):Vector3 {
|
||||
var xx:Number = x;
|
||||
var yy:Number = y;
|
||||
var zz:Number = z;
|
||||
x = m.a*xx + m.b*yy + m.c*zz;
|
||||
y = m.e*xx + m.f*yy + m.g*zz;
|
||||
z = m.i*xx + m.j*yy + m.k*zz;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Установка координат.
|
||||
*/
|
||||
public function vReset(x:Number = 0, y:Number = 0, z:Number = 0):Vector3 {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирование координат точки.
|
||||
*
|
||||
* @param v точка, координаты которой копируются
|
||||
*/
|
||||
public function vCopy(v:Vector3):Vector3 {
|
||||
x = v.x;
|
||||
y = v.y;
|
||||
z = v.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирование точки.
|
||||
*
|
||||
* @return клонированная точка
|
||||
*/
|
||||
public function vClone():Vector3 {
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирует координаты вектора в экземпляр класса Vector3D.
|
||||
*
|
||||
* @param result экземпляр, в который копируются координаты
|
||||
* @return переданный параметр
|
||||
*/
|
||||
public function toVector3D(result:Vector3D):Vector3D {
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
result.z = z;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
public function copyFromVector3D(source:Vector3D):Vector3 {
|
||||
x = source.x;
|
||||
y = source.y;
|
||||
z = source.z;
|
||||
return this;
|
||||
}
|
||||
|
||||
override public function toString():String {
|
||||
return "Vector3(" + x + "," + y + ", " + z + ")";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
77
0.0.9.2/src/alternativa/physics/.svn/all-wcprops
Normal file
77
0.0.9.2/src/alternativa/physics/.svn/all-wcprops
Normal file
@@ -0,0 +1,77 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 103
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics
|
||||
END
|
||||
Body.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 111
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/Body.as
|
||||
END
|
||||
BodyState.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 116
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/BodyState.as
|
||||
END
|
||||
CollisionPrimitiveListItem.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 133
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/CollisionPrimitiveListItem.as
|
||||
END
|
||||
CollisionPrimitiveList.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 129
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/CollisionPrimitiveList.as
|
||||
END
|
||||
PhysicsScene.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 119
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/PhysicsScene.as
|
||||
END
|
||||
PhysicsUtils.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 119
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/PhysicsUtils.as
|
||||
END
|
||||
altphysics.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 117
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/altphysics.as
|
||||
END
|
||||
ContactPoint.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 119
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/ContactPoint.as
|
||||
END
|
||||
BodyListItem.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 119
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/BodyListItem.as
|
||||
END
|
||||
BodyList.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 115
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/BodyList.as
|
||||
END
|
||||
Contact.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 114
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/Contact.as
|
||||
END
|
||||
BodyMaterial.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 119
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/BodyMaterial.as
|
||||
END
|
||||
181
0.0.9.2/src/alternativa/physics/.svn/entries
Normal file
181
0.0.9.2/src/alternativa/physics/.svn/entries
Normal file
@@ -0,0 +1,181 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2010-03-06T16:51:19.781174Z
|
||||
29404
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
CollisionPrimitiveList.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
5752b1f4909c5146720b58fbfffed468
|
||||
2009-11-01T12:35:52.270571Z
|
||||
22955
|
||||
mike
|
||||
|
||||
altphysics.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
04fbe40a27502dbbd0dba02a76b2df50
|
||||
2009-04-20T20:09:14.715403Z
|
||||
11579
|
||||
mike
|
||||
|
||||
collision
|
||||
dir
|
||||
|
||||
primitives
|
||||
dir
|
||||
|
||||
BodyList.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
08a90332660bd0273728006d1402dd25
|
||||
2009-11-01T12:35:52.270571Z
|
||||
22955
|
||||
mike
|
||||
|
||||
Contact.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
eb89f4f37bcf2d21eadc733f244fcf64
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
Body.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
355d0fb70dbb4946d4090a7c94c0d58a
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
BodyState.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
5be68820e6f6185d62b3928c39dde285
|
||||
2009-11-24T08:44:15.941480Z
|
||||
23758
|
||||
mike
|
||||
|
||||
CollisionPrimitiveListItem.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
cec5dbfefef812d87481478455f367b1
|
||||
2009-10-19T07:17:33.113306Z
|
||||
22253
|
||||
mike
|
||||
|
||||
PhysicsScene.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
96c74784bb71c06141cc469eec57ccb8
|
||||
2010-02-19T13:09:00.412754Z
|
||||
28731
|
||||
mike
|
||||
|
||||
PhysicsUtils.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
dce0a94f8a861291a2a8942ed0ba54a5
|
||||
2009-11-24T08:44:15.941480Z
|
||||
23758
|
||||
mike
|
||||
|
||||
constraints
|
||||
dir
|
||||
|
||||
ContactPoint.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
14f23db94810328dedd7da2992812ced
|
||||
2009-11-24T08:44:15.941480Z
|
||||
23758
|
||||
mike
|
||||
|
||||
BodyListItem.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
2fc2b598310b24aef3f8b90e14a8ba4f
|
||||
2009-10-19T07:17:33.113306Z
|
||||
22253
|
||||
mike
|
||||
|
||||
BodyMaterial.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
9b700692ebb8017882f3e8cec095e1a9
|
||||
2009-10-19T07:17:33.113306Z
|
||||
22253
|
||||
mike
|
||||
|
||||
1
0.0.9.2/src/alternativa/physics/.svn/format
Normal file
1
0.0.9.2/src/alternativa/physics/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
405
0.0.9.2/src/alternativa/physics/.svn/text-base/Body.as.svn-base
Normal file
405
0.0.9.2/src/alternativa/physics/.svn/text-base/Body.as.svn-base
Normal file
@@ -0,0 +1,405 @@
|
||||
package alternativa.physics {
|
||||
|
||||
import alternativa.math.Matrix3;
|
||||
import alternativa.math.Matrix4;
|
||||
import alternativa.math.Quaternion;
|
||||
import alternativa.math.Vector3;
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
import alternativa.physics.collision.IBodyCollisionPredicate;
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
|
||||
use namespace altphysics;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Body {
|
||||
|
||||
public static var linDamping:Number = 0.997;
|
||||
public static var rotDamping:Number = 0.997;
|
||||
|
||||
// Идентификатор тела, уникальный в пределах мира
|
||||
public var id:int;
|
||||
// Имя тела
|
||||
public var name:String;
|
||||
// Мир, в котором находится тело
|
||||
public var world:PhysicsScene;
|
||||
// Флаг подвижности тела
|
||||
public var movable:Boolean = true;
|
||||
// Флаг указывает, может ли тело быть заморожено
|
||||
public var canFreeze:Boolean = false;
|
||||
|
||||
public var freezeCounter:int;
|
||||
public var frozen:Boolean = false;
|
||||
// Ограничивающий бокс тела
|
||||
public var aabb:BoundBox = new BoundBox();
|
||||
// Предикат, через который тело получает сообщения о столкновениях
|
||||
public var postCollisionPredicate:IBodyCollisionPredicate;
|
||||
|
||||
// Текущее состояние тела
|
||||
public var state:BodyState = new BodyState();
|
||||
// Предыдущее состояние тела
|
||||
public var prevState:BodyState = new BodyState();
|
||||
// Линейное ускорение тела на текущем шаге симуляции
|
||||
public var accel:Vector3 = new Vector3();
|
||||
// Угловое ускорение тела на текущем шаге симуляции
|
||||
public var angleAccel:Vector3 = new Vector3();
|
||||
// Физический материал тела
|
||||
public var material:BodyMaterial = new BodyMaterial();
|
||||
// Обратная масса тела
|
||||
public var invMass:Number = 1;
|
||||
// Обратная матрица тензора инерции в локальных координатах
|
||||
public var invInertia:Matrix3 = new Matrix3();
|
||||
// Обратная матрица тензора инерции в мировых координатах
|
||||
public var invInertiaWorld:Matrix3 = new Matrix3();
|
||||
// Базисная матрица тела в мировых координатах
|
||||
public var baseMatrix:Matrix3 = new Matrix3();
|
||||
|
||||
public const MAX_CONTACTS:int = 20;
|
||||
public var contacts:Vector.<Contact> = new Vector.<Contact>(MAX_CONTACTS);
|
||||
public var contactsNum:int;
|
||||
|
||||
public var collisionPrimitives:CollisionPrimitiveList;
|
||||
|
||||
// Аккумулятор сил
|
||||
public var forceAccum:Vector3 = new Vector3();
|
||||
// Аккумулятор моментов
|
||||
public var torqueAccum:Vector3 = new Vector3();
|
||||
|
||||
// Внутренние переменные для избежания создания экземпляров
|
||||
private static var _r:Vector3 = new Vector3();
|
||||
private static var _f:Vector3 = new Vector3();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param invMass
|
||||
* @param invInertia
|
||||
*/
|
||||
public function Body(invMass:Number, invInertia:Matrix3) {
|
||||
this.invMass = invMass;
|
||||
this.invInertia.copy(invInertia);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param primitive
|
||||
* @param localTransform
|
||||
*/
|
||||
public function addCollisionPrimitive(primitive:CollisionPrimitive, localTransform:Matrix4 = null):void {
|
||||
if (primitive == null) {
|
||||
throw new ArgumentError("Primitive cannot be null");
|
||||
}
|
||||
if (collisionPrimitives == null) {
|
||||
collisionPrimitives = new CollisionPrimitiveList();
|
||||
}
|
||||
collisionPrimitives.append(primitive);
|
||||
primitive.setBody(this, localTransform);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param primitive
|
||||
*/
|
||||
public function removeCollisionPrimitive(primitive:CollisionPrimitive):void {
|
||||
if (collisionPrimitives == null) return;
|
||||
primitive.setBody(null);
|
||||
collisionPrimitives.remove(primitive);
|
||||
if (collisionPrimitives.size == 0) {
|
||||
collisionPrimitives = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 x:Number = (r.y*dir.z - r.z*dir.y)*magnitude;
|
||||
var y:Number = (r.z*dir.x - r.x*dir.z)*magnitude;
|
||||
var z:Number = (r.x*dir.y - r.y*dir.x)*magnitude;
|
||||
|
||||
state.rotation.x += invInertiaWorld.a*x + invInertiaWorld.b*y + invInertiaWorld.c*z;
|
||||
state.rotation.y += invInertiaWorld.e*x + invInertiaWorld.f*y + invInertiaWorld.g*z;
|
||||
state.rotation.z += invInertiaWorld.i*x + invInertiaWorld.j*y + invInertiaWorld.k*z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param f
|
||||
*/
|
||||
public function addForce(f:Vector3):void {
|
||||
forceAccum.vAdd(f);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fx
|
||||
* @param fy
|
||||
* @param fz
|
||||
*/
|
||||
public function addForceXYZ(fx:Number, fy:Number, fz:Number):void {
|
||||
forceAccum.x += fx;
|
||||
forceAccum.y += fy;
|
||||
forceAccum.z += fz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pos
|
||||
* @param f
|
||||
*/
|
||||
public function addWorldForceXYZ(px:Number, py:Number, pz:Number, fx:Number, fy:Number, fz:Number):void {
|
||||
forceAccum.x += fx;
|
||||
forceAccum.y += fy;
|
||||
forceAccum.z += fz;
|
||||
|
||||
var pos:Vector3 = state.pos;
|
||||
var rx:Number = px - pos.x;
|
||||
var ry:Number = py - pos.y;
|
||||
var rz:Number = pz - pos.z;
|
||||
|
||||
// var x:Number = ry*fz - rz*fy;
|
||||
// var y:Number = rz*fx - rx*fz;
|
||||
// var z:Number = rx*fy - ry*fx;
|
||||
|
||||
torqueAccum.x += ry*fz - rz*fy;
|
||||
torqueAccum.y += rz*fx - rx*fz;
|
||||
torqueAccum.z += rx*fy - ry*fx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 beforePhysicsStep(dt:Number):void {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param t
|
||||
*/
|
||||
public function addTorque(t:Vector3):void {
|
||||
torqueAccum.vAdd(t);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
altphysics function clearAccumulators():void {
|
||||
forceAccum.x = forceAccum.y = forceAccum.z = 0;
|
||||
torqueAccum.x = torqueAccum.y = torqueAccum.z = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
altphysics function calcAccelerations():void {
|
||||
accel.x = forceAccum.x*invMass;
|
||||
accel.y = forceAccum.y*invMass;
|
||||
accel.z = forceAccum.z*invMass;
|
||||
angleAccel.x = invInertiaWorld.a*torqueAccum.x + invInertiaWorld.b*torqueAccum.y + invInertiaWorld.c*torqueAccum.z;
|
||||
angleAccel.y = invInertiaWorld.e*torqueAccum.x + invInertiaWorld.f*torqueAccum.y + invInertiaWorld.g*torqueAccum.z;
|
||||
angleAccel.z = invInertiaWorld.i*torqueAccum.x + invInertiaWorld.j*torqueAccum.y + invInertiaWorld.k*torqueAccum.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет производные данные.
|
||||
*/
|
||||
public function calcDerivedData():void {
|
||||
// Вычисление базисной матрицы и обратного тензора инерции в мировых координатах
|
||||
state.orientation.toMatrix3(baseMatrix);
|
||||
invInertiaWorld.copy(invInertia).append(baseMatrix).prependTransposed(baseMatrix);
|
||||
if (collisionPrimitives != null) {
|
||||
aabb.infinity();
|
||||
var item:CollisionPrimitiveListItem = collisionPrimitives.head;
|
||||
while (item != null) {
|
||||
var primitive:CollisionPrimitive = item.primitive;
|
||||
primitive.transform.setFromMatrix3(baseMatrix, state.pos);
|
||||
if (primitive.localTransform != null) {
|
||||
primitive.transform.prepend(primitive.localTransform);
|
||||
}
|
||||
primitive.calculateAABB();
|
||||
aabb.addBoundBox(primitive.aabb);
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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,76 @@
|
||||
package alternativa.physics {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BodyList {
|
||||
|
||||
public var head:BodyListItem;
|
||||
public var tail:BodyListItem;
|
||||
public var size:int;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function BodyList() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
*/
|
||||
public function append(body:Body):void {
|
||||
var item:BodyListItem = BodyListItem.create(body);
|
||||
if (head == null) {
|
||||
head = tail = item;
|
||||
} else {
|
||||
tail.next = item;
|
||||
item.prev = tail;
|
||||
tail = item;
|
||||
}
|
||||
size++;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
*/
|
||||
public function remove(body:Body):Boolean {
|
||||
var item:BodyListItem = findItem(body);
|
||||
if (item == null) return false;
|
||||
if (item == head) {
|
||||
if (size == 1) {
|
||||
head = tail = null;
|
||||
} else {
|
||||
head = item.next;
|
||||
head.prev = null;
|
||||
}
|
||||
} else {
|
||||
if (item == tail) {
|
||||
tail = item.prev;
|
||||
tail.next = null;
|
||||
} else {
|
||||
item.prev.next = item.next;
|
||||
item.next.prev = item.prev;
|
||||
}
|
||||
}
|
||||
item.dispose();
|
||||
size--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
public function findItem(body:Body):BodyListItem {
|
||||
var item:BodyListItem = head;
|
||||
while (item != null && item.body != body) {
|
||||
item = item.next;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package alternativa.physics {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BodyListItem {
|
||||
|
||||
// Верхний элемент хранилища
|
||||
private static var poolTop:BodyListItem;
|
||||
|
||||
/**
|
||||
* Создаёт новый элемент списка.
|
||||
*
|
||||
* @param primitive примитив, содержащийся в элементе
|
||||
* @return новый элемент списка
|
||||
*/
|
||||
public static function create(body:Body):BodyListItem {
|
||||
var item:BodyListItem;
|
||||
if (poolTop == null) {
|
||||
item = new BodyListItem(body);
|
||||
} else {
|
||||
item = poolTop;
|
||||
poolTop = item.next;
|
||||
item.next = null;
|
||||
item.body = body;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает хранилище.
|
||||
*/
|
||||
public static function clearPool():void {
|
||||
var item:BodyListItem = poolTop;
|
||||
while (item != null) {
|
||||
poolTop = item.next;
|
||||
item.next = null;
|
||||
item = poolTop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public var body:Body;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public var next:BodyListItem;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public var prev:BodyListItem;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
*/
|
||||
public function BodyListItem(body:Body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function dispose():void {
|
||||
body = null;
|
||||
prev = null;
|
||||
next = poolTop;
|
||||
poolTop = this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package alternativa.physics {
|
||||
|
||||
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 {
|
||||
import alternativa.math.Quaternion;
|
||||
import alternativa.math.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,89 @@
|
||||
package alternativa.physics {
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class CollisionPrimitiveList {
|
||||
|
||||
public var head:CollisionPrimitiveListItem;
|
||||
public var tail:CollisionPrimitiveListItem;
|
||||
public var size:int;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function CollisionPrimitiveList() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param primitive
|
||||
*/
|
||||
public function append(primitive:CollisionPrimitive):void {
|
||||
var item:CollisionPrimitiveListItem = CollisionPrimitiveListItem.create(primitive);
|
||||
if (head == null) {
|
||||
head = tail = item;
|
||||
} else {
|
||||
tail.next = item;
|
||||
item.prev = tail;
|
||||
tail = item;
|
||||
}
|
||||
size++;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param primitve
|
||||
*/
|
||||
public function remove(primitve:CollisionPrimitive):void {
|
||||
var item:CollisionPrimitiveListItem = findItem(primitve);
|
||||
if (item == null) return;
|
||||
if (item == head) {
|
||||
if (size == 1) {
|
||||
head = tail = null;
|
||||
} else {
|
||||
head = item.next;
|
||||
head.prev = null;
|
||||
}
|
||||
} else {
|
||||
if (item == tail) {
|
||||
tail = tail.prev;
|
||||
tail.next = null;
|
||||
} else {
|
||||
item.prev.next = item.next;
|
||||
item.next.prev = item.prev;
|
||||
}
|
||||
}
|
||||
item.dispose();
|
||||
size--;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param primitive
|
||||
* @return
|
||||
*/
|
||||
public function findItem(primitive:CollisionPrimitive):CollisionPrimitiveListItem {
|
||||
var item:CollisionPrimitiveListItem = head;
|
||||
while (item != null && item.primitive != primitive) {
|
||||
item = item.next;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function clear():void {
|
||||
while (head != null) {
|
||||
var item:CollisionPrimitiveListItem = head;
|
||||
head = head.next;
|
||||
item.dispose();
|
||||
}
|
||||
tail = null;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package alternativa.physics {
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
|
||||
/**
|
||||
* Элемент списка примитивов.
|
||||
*/
|
||||
public class CollisionPrimitiveListItem {
|
||||
|
||||
// Верхний элемент хранилища
|
||||
private static var poolTop:CollisionPrimitiveListItem;
|
||||
|
||||
/**
|
||||
* Создаёт новый элемент списка.
|
||||
*
|
||||
* @param primitive примитив, содержащийся в элементе
|
||||
* @return новый элемент списка
|
||||
*/
|
||||
public static function create(primitive:CollisionPrimitive):CollisionPrimitiveListItem {
|
||||
var item:CollisionPrimitiveListItem;
|
||||
if (poolTop == null) {
|
||||
item = new CollisionPrimitiveListItem(primitive);
|
||||
} else {
|
||||
item = poolTop;
|
||||
item.primitive = primitive;
|
||||
poolTop = item.next;
|
||||
item.next = null;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает хранилище.
|
||||
*/
|
||||
public static function clearPool():void {
|
||||
var curr:CollisionPrimitiveListItem = poolTop;
|
||||
while (curr != null) {
|
||||
poolTop = curr.next;
|
||||
curr.next = null;
|
||||
curr = poolTop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Примитив, хранящийся в элементе списка.
|
||||
*/
|
||||
public var primitive:CollisionPrimitive;
|
||||
/**
|
||||
* Ссылка не следующий элемент списка.
|
||||
*/
|
||||
public var next:CollisionPrimitiveListItem;
|
||||
/**
|
||||
* Ссылка не предыдущий элемент списка.
|
||||
*/
|
||||
public var prev:CollisionPrimitiveListItem;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр.
|
||||
*
|
||||
* @param primitive примитив, хранящийся в элементе списка
|
||||
*/
|
||||
public function CollisionPrimitiveListItem(primitive:CollisionPrimitive) {
|
||||
this.primitive = primitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает внутренние ссылки и помещает элемент в хранилище для дальнейшего использования.
|
||||
*/
|
||||
public function dispose():void {
|
||||
primitive = null;
|
||||
prev = null;
|
||||
next = poolTop;
|
||||
poolTop = this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package alternativa.physics {
|
||||
|
||||
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
/**
|
||||
* Описывает контакт одного или двух тел.
|
||||
*/
|
||||
public class Contact {
|
||||
/**
|
||||
* Первое тело контакта, не может быть равно null.
|
||||
*/
|
||||
public var body1:Body;
|
||||
/**
|
||||
* Второе тело контакта, может быть равно null.
|
||||
*/
|
||||
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>(MAX_POINTS, true);
|
||||
/**
|
||||
* Количество точек контакта.
|
||||
*/
|
||||
public var pcount:int;
|
||||
/**
|
||||
* Максимальная глубина пересечения.
|
||||
*/
|
||||
public var maxPenetration:Number = 0;
|
||||
/**
|
||||
* Флаг показывает, разрешён контакт или нет.
|
||||
*/
|
||||
public var satisfied:Boolean;
|
||||
/**
|
||||
* Следующий контакт в списке.
|
||||
*/
|
||||
public var next:Contact;
|
||||
/**
|
||||
* Индекс контакта. Первый контакт в списке имеет индекс 0.
|
||||
*/
|
||||
public var index:int;
|
||||
|
||||
// Максимальное количество точек контакта
|
||||
private const MAX_POINTS:int = 8;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function Contact(index:int) {
|
||||
this.index = index;
|
||||
for (var i:int = 0; i < MAX_POINTS; i++) {
|
||||
points[i] = new ContactPoint();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package alternativa.physics {
|
||||
import alternativa.math.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,743 @@
|
||||
package alternativa.physics {
|
||||
|
||||
|
||||
import alternativa.physics.collision.ICollisionDetector;
|
||||
import alternativa.physics.collision.KdTreeCollisionDetector;
|
||||
import alternativa.physics.constraints.Constraint;
|
||||
import alternativa.math.Matrix3;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
use namespace altphysics;
|
||||
|
||||
/**
|
||||
* Класс реализует физическую симуляцию поведения твёрдых тел.
|
||||
*/
|
||||
public class PhysicsScene {
|
||||
|
||||
private static var lastBodyId:int;
|
||||
|
||||
// Максимальное количество контактов
|
||||
public 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 freezeSteps:int = 10;
|
||||
public var linSpeedFreezeLimit:Number = 1;
|
||||
public var angSpeedFreezeLimit:Number = 0.01;
|
||||
|
||||
// Переменные для процедуры разделения тел путём непосредственного их перемещенеия.
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// !!! Экспериментально !!!
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!
|
||||
public var staticSeparationIterations:int = 10;
|
||||
public var staticSeparationSteps:int = 10;
|
||||
public var maxAngleMove:Number = 10;
|
||||
public var useStaticSeparation:Boolean = false;
|
||||
|
||||
// Вектор гравитации
|
||||
public var _gravity:Vector3 = new Vector3(0, 0, -9.8);
|
||||
// Модуль вектора гравитации
|
||||
public var _gravityMagnitude:Number = 9.8;
|
||||
|
||||
// Использующийся детектор столкновений
|
||||
public var collisionDetector:ICollisionDetector;
|
||||
|
||||
// Список тел, участвующих в симуляции
|
||||
public var bodies:BodyList = new BodyList();
|
||||
|
||||
// Список контактов на текущем шаге симуляции
|
||||
altphysics var contacts:Contact;
|
||||
// Количество контактов на текущем шаге симуляции
|
||||
// altphysics var contactsNum:int;
|
||||
// Список ограничений
|
||||
altphysics var constraints:Vector.<Constraint> = new Vector.<Constraint>();
|
||||
// Количество ограничений
|
||||
altphysics var constraintsNum:int;
|
||||
// Временная метка. Число прошедших шагов с начала симуляции.
|
||||
public var timeStamp:int;
|
||||
// Время с начала симуляции, мс
|
||||
public var time:int;
|
||||
// Первый неиспользованный контакт на текущем шаге симуляции
|
||||
private var borderContact:Contact;
|
||||
|
||||
// Временные переменные для избежания создания экземпляров
|
||||
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 PhysicsScene() {
|
||||
contacts = new Contact(0);
|
||||
var contact:Contact = contacts;
|
||||
for (var i:int = 1; i < MAX_CONTACTS; i++) {
|
||||
contact.next = new Contact(i);
|
||||
contact = contact.next;
|
||||
}
|
||||
collisionDetector = new KdTreeCollisionDetector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Вектор гравитации.
|
||||
*/
|
||||
public function get gravity():Vector3 {
|
||||
return _gravity.vClone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set gravity(value:Vector3):void {
|
||||
_gravity.vCopy(value);
|
||||
_gravityMagnitude = _gravity.vLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет тело в симуляцию.
|
||||
*
|
||||
* @param body
|
||||
*/
|
||||
public function addBody(body:Body):void {
|
||||
body.id = lastBodyId++;
|
||||
body.world = this;
|
||||
bodies.append(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет тело из симуляции.
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
public function removeBody(body:Body):void {
|
||||
if (bodies.remove(body)) {
|
||||
body.world = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет ограничение.
|
||||
* @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 {
|
||||
var item:BodyListItem = bodies.head;
|
||||
while (item != null) {
|
||||
var body:Body = item.body;
|
||||
body.beforePhysicsStep(dt);
|
||||
body.calcAccelerations();
|
||||
// Ускорение свободного падения применяется только к подвижным телам во избежание некорректного изменения
|
||||
// фиктивной скорости неподвижных тел.
|
||||
if (body.movable && !body.frozen) {
|
||||
body.accel.x += _gravity.x;
|
||||
body.accel.y += _gravity.y;
|
||||
body.accel.z += _gravity.z;
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Определяет все столкновения на текущем шаге симуляции и заполняет список получившихся контактов.
|
||||
*
|
||||
* @param dt длительность шага симуляции
|
||||
*/
|
||||
private function detectCollisions(dt:Number):void {
|
||||
var item:BodyListItem = bodies.head;
|
||||
while (item != null) {
|
||||
var body:Body = item.body;
|
||||
if (!body.frozen) {
|
||||
body.contactsNum = 0;
|
||||
body.saveState();
|
||||
// При включённом режиме предсказания состояние тел интегрируется на один шаг вперёд
|
||||
if (usePrediction) {
|
||||
body.integrateVelocity(dt);
|
||||
body.integratePosition(dt);
|
||||
}
|
||||
body.calcDerivedData();
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
|
||||
borderContact = collisionDetector.getAllContacts(contacts);
|
||||
|
||||
// Расчёт относительных векторов точки контакта вынесен сюда из-за необходимости учитывать
|
||||
// положение тел в предсказанном состоянии
|
||||
var contact:Contact = contacts;
|
||||
while (contact != borderContact) {
|
||||
var b1:Body = contact.body1;
|
||||
var b2:Body = contact.body2;
|
||||
for (var j:int = 0; j < contact.pcount; j++) {
|
||||
var cp:ContactPoint = contact.points[j];
|
||||
var bPos:Vector3 = b1.state.pos;
|
||||
cp.r1.x = cp.pos.x - bPos.x;
|
||||
cp.r1.y = cp.pos.y - bPos.y;
|
||||
cp.r1.z = cp.pos.z - bPos.z;
|
||||
if (b2 != null) {
|
||||
bPos = b2.state.pos;
|
||||
cp.r2.x = cp.pos.x - bPos.x;
|
||||
cp.r2.y = cp.pos.y - bPos.y;
|
||||
cp.r2.z = cp.pos.z - bPos.z;
|
||||
}
|
||||
}
|
||||
contact = contact.next;
|
||||
}
|
||||
|
||||
// Восстановление состояния тел
|
||||
if (usePrediction) {
|
||||
item = bodies.head;
|
||||
while (item != null) {
|
||||
body = item.body;
|
||||
if (!body.frozen) {
|
||||
body.restoreState();
|
||||
body.calcDerivedData();
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Подготваливает полученные из детектора столкновений контакты, расчитывая значения, не меняющиеся
|
||||
* в ходе шага симуляции.
|
||||
*/
|
||||
private function preProcessContacts(dt:Number):void {
|
||||
var contact:Contact = contacts;
|
||||
while (contact != borderContact) {
|
||||
var b1:Body = contact.body1;
|
||||
var b2:Body = contact.body2;
|
||||
// Столкнувшиеся тела размораживаются
|
||||
if (b1.frozen) {
|
||||
b1.frozen = false;
|
||||
b1.freezeCounter = 0;
|
||||
}
|
||||
if (b2 != null && b2.frozen) {
|
||||
b2.frozen = false;
|
||||
b2.freezeCounter = 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
contact = contact.next;
|
||||
}
|
||||
for (var i:int = 0; i < constraintsNum; i++) {
|
||||
var constraint:Constraint = constraints[i];
|
||||
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;
|
||||
var contact:Contact = contacts;
|
||||
while (contact != borderContact) {
|
||||
resolveContact(contact, forceInelastic, forwardLoop);
|
||||
contact = contact.next;
|
||||
}
|
||||
// Ограничения
|
||||
for (i = 0; i < constraintsNum; i++) {
|
||||
var constraint:Constraint = constraints[i];
|
||||
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 cnormal:Vector3 = contact.normal;
|
||||
var sepVel:Number = _v.x*cnormal.x + _v.y*cnormal.y + _v.z*cnormal.z;
|
||||
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 dot:Number = _v.x*cnormal.x + _v.y*cnormal.y + _v.z*cnormal.z;
|
||||
_v.x -= dot*cnormal.x;
|
||||
_v.y -= dot*cnormal.y;
|
||||
_v.z -= dot*cnormal.z;
|
||||
|
||||
var tanSpeed:Number = _v.vLength();
|
||||
if (tanSpeed < 0.001) return;
|
||||
|
||||
// _t.vCopy(_v).vNormalize().vReverse();
|
||||
_t.x = -_v.x;
|
||||
_t.y = -_v.y;
|
||||
_t.z = -_v.z;
|
||||
_t.vNormalize();
|
||||
|
||||
var r:Vector3;
|
||||
var m:Matrix3;
|
||||
var xx:Number;
|
||||
var yy:Number;
|
||||
var zz:Number;
|
||||
// dV = b.invMass + ((invI * (r % t)) % r) * t
|
||||
if (b1.movable) {
|
||||
// _v.vCross2(cp.r1, _t).vTransformBy3(b1.invInertiaWorld).vCross(cp.r1);
|
||||
r = cp.r1;
|
||||
m = b1.invInertiaWorld;
|
||||
|
||||
_v.x = r.y*_t.z - r.z*_t.y;
|
||||
_v.y = r.z*_t.x - r.x*_t.z;
|
||||
_v.z = r.x*_t.y - r.y*_t.x;
|
||||
|
||||
xx = m.a*_v.x + m.b*_v.y + m.c*_v.z;
|
||||
yy = m.e*_v.x + m.f*_v.y + m.g*_v.z;
|
||||
zz = m.i*_v.x + m.j*_v.y + m.k*_v.z;
|
||||
|
||||
_v.x = yy*r.z - zz*r.y;
|
||||
_v.y = zz*r.x - xx*r.z;
|
||||
_v.z = xx*r.y - yy*r.x;
|
||||
|
||||
tanSpeedByUnitImpulse += b1.invMass + _v.x*_t.x + _v.y*_t.y + _v.z*_t.z;
|
||||
}
|
||||
if (b2 != null && b2.movable) {
|
||||
// _v.vCross2(cp.r2, _t).vTransformBy3(b2.invInertiaWorld).vCross(cp.r2);
|
||||
|
||||
r = cp.r2;
|
||||
m = b2.invInertiaWorld;
|
||||
|
||||
_v.x = r.y*_t.z - r.z*_t.y;
|
||||
_v.y = r.z*_t.x - r.x*_t.z;
|
||||
_v.z = r.x*_t.y - r.y*_t.x;
|
||||
|
||||
xx = m.a*_v.x + m.b*_v.y + m.c*_v.z;
|
||||
yy = m.e*_v.x + m.f*_v.y + m.g*_v.z;
|
||||
zz = m.i*_v.x + m.j*_v.y + m.k*_v.z;
|
||||
|
||||
_v.x = yy*r.z - zz*r.y;
|
||||
_v.y = zz*r.x - xx*r.z;
|
||||
_v.z = xx*r.y - yy*r.x;
|
||||
|
||||
tanSpeedByUnitImpulse += b2.invMass + _v.x*_t.x + _v.y*_t.y + _v.z*_t.z;
|
||||
}
|
||||
|
||||
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));
|
||||
var rot:Vector3 = body1.state.rotation;
|
||||
var v:Vector3 = cp.r1;
|
||||
var x:Number = rot.y*v.z - rot.z*v.y;
|
||||
var y:Number = rot.z*v.x - rot.x*v.z;
|
||||
var z:Number = rot.x*v.y - rot.y*v.x;
|
||||
v = body1.state.velocity;
|
||||
result.x = v.x + x;
|
||||
result.y = v.y + y;
|
||||
result.z = v.z + z;
|
||||
// V2 = V2_c + w2%r2
|
||||
if (body2 != null) {
|
||||
// result.vSubtract(body2.state.velocity).vSubtract(_v2.vCross2(body2.state.rotation, cp.r2));
|
||||
rot = body2.state.rotation;
|
||||
v = cp.r2;
|
||||
x = rot.y*v.z - rot.z*v.y;
|
||||
y = rot.z*v.x - rot.x*v.z;
|
||||
z = rot.x*v.y - rot.y*v.x;
|
||||
v = body2.state.velocity;
|
||||
result.x -= v.x + x;
|
||||
result.y -= v.y + y;
|
||||
result.z -= v.z + z;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dt
|
||||
*/
|
||||
private function intergateVelocities(dt:Number):void {
|
||||
var item:BodyListItem = bodies.head;
|
||||
while (item != null) {
|
||||
item.body.integrateVelocity(dt);
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dt
|
||||
*/
|
||||
private function integratePositions(dt:Number):void {
|
||||
var item:BodyListItem = bodies.head;
|
||||
while (item != null) {
|
||||
var body:Body = item.body;
|
||||
if (body.movable && !body.frozen) {
|
||||
body.integratePosition(dt);
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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 {
|
||||
var item:BodyListItem = bodies.head;
|
||||
while (item != null) {
|
||||
var body:Body = item.body;
|
||||
body.clearAccumulators();
|
||||
body.calcDerivedData();
|
||||
if (body.canFreeze) {
|
||||
if (body.state.velocity.vLength() < linSpeedFreezeLimit && body.state.rotation.vLength() < angSpeedFreezeLimit) {
|
||||
if (!body.frozen) {
|
||||
body.freezeCounter++;
|
||||
if (body.freezeCounter >= freezeSteps) body.frozen = true;
|
||||
}
|
||||
} else {
|
||||
body.freezeCounter = 0;
|
||||
body.frozen = false;
|
||||
}
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param delta
|
||||
*/
|
||||
public function update(delta:int):void {
|
||||
timeStamp++;
|
||||
time += delta;
|
||||
var dt:Number = 0.001*delta;
|
||||
applyForces(dt);
|
||||
detectCollisions(dt);
|
||||
preProcessContacts(dt);
|
||||
processContacts(dt, false);
|
||||
intergateVelocities(dt);
|
||||
processContacts(dt, true);
|
||||
integratePositions(dt);
|
||||
postPhysics();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package alternativa.physics {
|
||||
import alternativa.math.Matrix3;
|
||||
import alternativa.math.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,3 @@
|
||||
package alternativa.physics {
|
||||
public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
|
||||
}
|
||||
405
0.0.9.2/src/alternativa/physics/Body.as
Normal file
405
0.0.9.2/src/alternativa/physics/Body.as
Normal file
@@ -0,0 +1,405 @@
|
||||
package alternativa.physics {
|
||||
|
||||
import alternativa.math.Matrix3;
|
||||
import alternativa.math.Matrix4;
|
||||
import alternativa.math.Quaternion;
|
||||
import alternativa.math.Vector3;
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
import alternativa.physics.collision.IBodyCollisionPredicate;
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
|
||||
use namespace altphysics;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Body {
|
||||
|
||||
public static var linDamping:Number = 0.997;
|
||||
public static var rotDamping:Number = 0.997;
|
||||
|
||||
// Идентификатор тела, уникальный в пределах мира
|
||||
public var id:int;
|
||||
// Имя тела
|
||||
public var name:String;
|
||||
// Мир, в котором находится тело
|
||||
public var world:PhysicsScene;
|
||||
// Флаг подвижности тела
|
||||
public var movable:Boolean = true;
|
||||
// Флаг указывает, может ли тело быть заморожено
|
||||
public var canFreeze:Boolean = false;
|
||||
|
||||
public var freezeCounter:int;
|
||||
public var frozen:Boolean = false;
|
||||
// Ограничивающий бокс тела
|
||||
public var aabb:BoundBox = new BoundBox();
|
||||
// Предикат, через который тело получает сообщения о столкновениях
|
||||
public var postCollisionPredicate:IBodyCollisionPredicate;
|
||||
|
||||
// Текущее состояние тела
|
||||
public var state:BodyState = new BodyState();
|
||||
// Предыдущее состояние тела
|
||||
public var prevState:BodyState = new BodyState();
|
||||
// Линейное ускорение тела на текущем шаге симуляции
|
||||
public var accel:Vector3 = new Vector3();
|
||||
// Угловое ускорение тела на текущем шаге симуляции
|
||||
public var angleAccel:Vector3 = new Vector3();
|
||||
// Физический материал тела
|
||||
public var material:BodyMaterial = new BodyMaterial();
|
||||
// Обратная масса тела
|
||||
public var invMass:Number = 1;
|
||||
// Обратная матрица тензора инерции в локальных координатах
|
||||
public var invInertia:Matrix3 = new Matrix3();
|
||||
// Обратная матрица тензора инерции в мировых координатах
|
||||
public var invInertiaWorld:Matrix3 = new Matrix3();
|
||||
// Базисная матрица тела в мировых координатах
|
||||
public var baseMatrix:Matrix3 = new Matrix3();
|
||||
|
||||
public const MAX_CONTACTS:int = 20;
|
||||
public var contacts:Vector.<Contact> = new Vector.<Contact>(MAX_CONTACTS);
|
||||
public var contactsNum:int;
|
||||
|
||||
public var collisionPrimitives:CollisionPrimitiveList;
|
||||
|
||||
// Аккумулятор сил
|
||||
public var forceAccum:Vector3 = new Vector3();
|
||||
// Аккумулятор моментов
|
||||
public var torqueAccum:Vector3 = new Vector3();
|
||||
|
||||
// Внутренние переменные для избежания создания экземпляров
|
||||
private static var _r:Vector3 = new Vector3();
|
||||
private static var _f:Vector3 = new Vector3();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param invMass
|
||||
* @param invInertia
|
||||
*/
|
||||
public function Body(invMass:Number, invInertia:Matrix3) {
|
||||
this.invMass = invMass;
|
||||
this.invInertia.copy(invInertia);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param primitive
|
||||
* @param localTransform
|
||||
*/
|
||||
public function addCollisionPrimitive(primitive:CollisionPrimitive, localTransform:Matrix4 = null):void {
|
||||
if (primitive == null) {
|
||||
throw new ArgumentError("Primitive cannot be null");
|
||||
}
|
||||
if (collisionPrimitives == null) {
|
||||
collisionPrimitives = new CollisionPrimitiveList();
|
||||
}
|
||||
collisionPrimitives.append(primitive);
|
||||
primitive.setBody(this, localTransform);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param primitive
|
||||
*/
|
||||
public function removeCollisionPrimitive(primitive:CollisionPrimitive):void {
|
||||
if (collisionPrimitives == null) return;
|
||||
primitive.setBody(null);
|
||||
collisionPrimitives.remove(primitive);
|
||||
if (collisionPrimitives.size == 0) {
|
||||
collisionPrimitives = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 x:Number = (r.y*dir.z - r.z*dir.y)*magnitude;
|
||||
var y:Number = (r.z*dir.x - r.x*dir.z)*magnitude;
|
||||
var z:Number = (r.x*dir.y - r.y*dir.x)*magnitude;
|
||||
|
||||
state.rotation.x += invInertiaWorld.a*x + invInertiaWorld.b*y + invInertiaWorld.c*z;
|
||||
state.rotation.y += invInertiaWorld.e*x + invInertiaWorld.f*y + invInertiaWorld.g*z;
|
||||
state.rotation.z += invInertiaWorld.i*x + invInertiaWorld.j*y + invInertiaWorld.k*z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param f
|
||||
*/
|
||||
public function addForce(f:Vector3):void {
|
||||
forceAccum.vAdd(f);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fx
|
||||
* @param fy
|
||||
* @param fz
|
||||
*/
|
||||
public function addForceXYZ(fx:Number, fy:Number, fz:Number):void {
|
||||
forceAccum.x += fx;
|
||||
forceAccum.y += fy;
|
||||
forceAccum.z += fz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pos
|
||||
* @param f
|
||||
*/
|
||||
public function addWorldForceXYZ(px:Number, py:Number, pz:Number, fx:Number, fy:Number, fz:Number):void {
|
||||
forceAccum.x += fx;
|
||||
forceAccum.y += fy;
|
||||
forceAccum.z += fz;
|
||||
|
||||
var pos:Vector3 = state.pos;
|
||||
var rx:Number = px - pos.x;
|
||||
var ry:Number = py - pos.y;
|
||||
var rz:Number = pz - pos.z;
|
||||
|
||||
// var x:Number = ry*fz - rz*fy;
|
||||
// var y:Number = rz*fx - rx*fz;
|
||||
// var z:Number = rx*fy - ry*fx;
|
||||
|
||||
torqueAccum.x += ry*fz - rz*fy;
|
||||
torqueAccum.y += rz*fx - rx*fz;
|
||||
torqueAccum.z += rx*fy - ry*fx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 beforePhysicsStep(dt:Number):void {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param t
|
||||
*/
|
||||
public function addTorque(t:Vector3):void {
|
||||
torqueAccum.vAdd(t);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
altphysics function clearAccumulators():void {
|
||||
forceAccum.x = forceAccum.y = forceAccum.z = 0;
|
||||
torqueAccum.x = torqueAccum.y = torqueAccum.z = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
altphysics function calcAccelerations():void {
|
||||
accel.x = forceAccum.x*invMass;
|
||||
accel.y = forceAccum.y*invMass;
|
||||
accel.z = forceAccum.z*invMass;
|
||||
angleAccel.x = invInertiaWorld.a*torqueAccum.x + invInertiaWorld.b*torqueAccum.y + invInertiaWorld.c*torqueAccum.z;
|
||||
angleAccel.y = invInertiaWorld.e*torqueAccum.x + invInertiaWorld.f*torqueAccum.y + invInertiaWorld.g*torqueAccum.z;
|
||||
angleAccel.z = invInertiaWorld.i*torqueAccum.x + invInertiaWorld.j*torqueAccum.y + invInertiaWorld.k*torqueAccum.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет производные данные.
|
||||
*/
|
||||
public function calcDerivedData():void {
|
||||
// Вычисление базисной матрицы и обратного тензора инерции в мировых координатах
|
||||
state.orientation.toMatrix3(baseMatrix);
|
||||
invInertiaWorld.copy(invInertia).append(baseMatrix).prependTransposed(baseMatrix);
|
||||
if (collisionPrimitives != null) {
|
||||
aabb.infinity();
|
||||
var item:CollisionPrimitiveListItem = collisionPrimitives.head;
|
||||
while (item != null) {
|
||||
var primitive:CollisionPrimitive = item.primitive;
|
||||
primitive.transform.setFromMatrix3(baseMatrix, state.pos);
|
||||
if (primitive.localTransform != null) {
|
||||
primitive.transform.prepend(primitive.localTransform);
|
||||
}
|
||||
primitive.calculateAABB();
|
||||
aabb.addBoundBox(primitive.aabb);
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
76
0.0.9.2/src/alternativa/physics/BodyList.as
Normal file
76
0.0.9.2/src/alternativa/physics/BodyList.as
Normal file
@@ -0,0 +1,76 @@
|
||||
package alternativa.physics {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BodyList {
|
||||
|
||||
public var head:BodyListItem;
|
||||
public var tail:BodyListItem;
|
||||
public var size:int;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function BodyList() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
*/
|
||||
public function append(body:Body):void {
|
||||
var item:BodyListItem = BodyListItem.create(body);
|
||||
if (head == null) {
|
||||
head = tail = item;
|
||||
} else {
|
||||
tail.next = item;
|
||||
item.prev = tail;
|
||||
tail = item;
|
||||
}
|
||||
size++;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
*/
|
||||
public function remove(body:Body):Boolean {
|
||||
var item:BodyListItem = findItem(body);
|
||||
if (item == null) return false;
|
||||
if (item == head) {
|
||||
if (size == 1) {
|
||||
head = tail = null;
|
||||
} else {
|
||||
head = item.next;
|
||||
head.prev = null;
|
||||
}
|
||||
} else {
|
||||
if (item == tail) {
|
||||
tail = item.prev;
|
||||
tail.next = null;
|
||||
} else {
|
||||
item.prev.next = item.next;
|
||||
item.next.prev = item.prev;
|
||||
}
|
||||
}
|
||||
item.dispose();
|
||||
size--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
public function findItem(body:Body):BodyListItem {
|
||||
var item:BodyListItem = head;
|
||||
while (item != null && item.body != body) {
|
||||
item = item.next;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
74
0.0.9.2/src/alternativa/physics/BodyListItem.as
Normal file
74
0.0.9.2/src/alternativa/physics/BodyListItem.as
Normal file
@@ -0,0 +1,74 @@
|
||||
package alternativa.physics {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BodyListItem {
|
||||
|
||||
// Верхний элемент хранилища
|
||||
private static var poolTop:BodyListItem;
|
||||
|
||||
/**
|
||||
* Создаёт новый элемент списка.
|
||||
*
|
||||
* @param primitive примитив, содержащийся в элементе
|
||||
* @return новый элемент списка
|
||||
*/
|
||||
public static function create(body:Body):BodyListItem {
|
||||
var item:BodyListItem;
|
||||
if (poolTop == null) {
|
||||
item = new BodyListItem(body);
|
||||
} else {
|
||||
item = poolTop;
|
||||
poolTop = item.next;
|
||||
item.next = null;
|
||||
item.body = body;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает хранилище.
|
||||
*/
|
||||
public static function clearPool():void {
|
||||
var item:BodyListItem = poolTop;
|
||||
while (item != null) {
|
||||
poolTop = item.next;
|
||||
item.next = null;
|
||||
item = poolTop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public var body:Body;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public var next:BodyListItem;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public var prev:BodyListItem;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
*/
|
||||
public function BodyListItem(body:Body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function dispose():void {
|
||||
body = null;
|
||||
prev = null;
|
||||
next = poolTop;
|
||||
poolTop = this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
0.0.9.2/src/alternativa/physics/BodyMaterial.as
Normal file
11
0.0.9.2/src/alternativa/physics/BodyMaterial.as
Normal file
@@ -0,0 +1,11 @@
|
||||
package alternativa.physics {
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
}
|
||||
39
0.0.9.2/src/alternativa/physics/BodyState.as
Normal file
39
0.0.9.2/src/alternativa/physics/BodyState.as
Normal file
@@ -0,0 +1,39 @@
|
||||
package alternativa.physics {
|
||||
import alternativa.math.Quaternion;
|
||||
import alternativa.math.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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
89
0.0.9.2/src/alternativa/physics/CollisionPrimitiveList.as
Normal file
89
0.0.9.2/src/alternativa/physics/CollisionPrimitiveList.as
Normal file
@@ -0,0 +1,89 @@
|
||||
package alternativa.physics {
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class CollisionPrimitiveList {
|
||||
|
||||
public var head:CollisionPrimitiveListItem;
|
||||
public var tail:CollisionPrimitiveListItem;
|
||||
public var size:int;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function CollisionPrimitiveList() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param primitive
|
||||
*/
|
||||
public function append(primitive:CollisionPrimitive):void {
|
||||
var item:CollisionPrimitiveListItem = CollisionPrimitiveListItem.create(primitive);
|
||||
if (head == null) {
|
||||
head = tail = item;
|
||||
} else {
|
||||
tail.next = item;
|
||||
item.prev = tail;
|
||||
tail = item;
|
||||
}
|
||||
size++;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param primitve
|
||||
*/
|
||||
public function remove(primitve:CollisionPrimitive):void {
|
||||
var item:CollisionPrimitiveListItem = findItem(primitve);
|
||||
if (item == null) return;
|
||||
if (item == head) {
|
||||
if (size == 1) {
|
||||
head = tail = null;
|
||||
} else {
|
||||
head = item.next;
|
||||
head.prev = null;
|
||||
}
|
||||
} else {
|
||||
if (item == tail) {
|
||||
tail = tail.prev;
|
||||
tail.next = null;
|
||||
} else {
|
||||
item.prev.next = item.next;
|
||||
item.next.prev = item.prev;
|
||||
}
|
||||
}
|
||||
item.dispose();
|
||||
size--;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param primitive
|
||||
* @return
|
||||
*/
|
||||
public function findItem(primitive:CollisionPrimitive):CollisionPrimitiveListItem {
|
||||
var item:CollisionPrimitiveListItem = head;
|
||||
while (item != null && item.primitive != primitive) {
|
||||
item = item.next;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function clear():void {
|
||||
while (head != null) {
|
||||
var item:CollisionPrimitiveListItem = head;
|
||||
head = head.next;
|
||||
item.dispose();
|
||||
}
|
||||
tail = null;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package alternativa.physics {
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
|
||||
/**
|
||||
* Элемент списка примитивов.
|
||||
*/
|
||||
public class CollisionPrimitiveListItem {
|
||||
|
||||
// Верхний элемент хранилища
|
||||
private static var poolTop:CollisionPrimitiveListItem;
|
||||
|
||||
/**
|
||||
* Создаёт новый элемент списка.
|
||||
*
|
||||
* @param primitive примитив, содержащийся в элементе
|
||||
* @return новый элемент списка
|
||||
*/
|
||||
public static function create(primitive:CollisionPrimitive):CollisionPrimitiveListItem {
|
||||
var item:CollisionPrimitiveListItem;
|
||||
if (poolTop == null) {
|
||||
item = new CollisionPrimitiveListItem(primitive);
|
||||
} else {
|
||||
item = poolTop;
|
||||
item.primitive = primitive;
|
||||
poolTop = item.next;
|
||||
item.next = null;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает хранилище.
|
||||
*/
|
||||
public static function clearPool():void {
|
||||
var curr:CollisionPrimitiveListItem = poolTop;
|
||||
while (curr != null) {
|
||||
poolTop = curr.next;
|
||||
curr.next = null;
|
||||
curr = poolTop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Примитив, хранящийся в элементе списка.
|
||||
*/
|
||||
public var primitive:CollisionPrimitive;
|
||||
/**
|
||||
* Ссылка не следующий элемент списка.
|
||||
*/
|
||||
public var next:CollisionPrimitiveListItem;
|
||||
/**
|
||||
* Ссылка не предыдущий элемент списка.
|
||||
*/
|
||||
public var prev:CollisionPrimitiveListItem;
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр.
|
||||
*
|
||||
* @param primitive примитив, хранящийся в элементе списка
|
||||
*/
|
||||
public function CollisionPrimitiveListItem(primitive:CollisionPrimitive) {
|
||||
this.primitive = primitive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Очищает внутренние ссылки и помещает элемент в хранилище для дальнейшего использования.
|
||||
*/
|
||||
public function dispose():void {
|
||||
primitive = null;
|
||||
prev = null;
|
||||
next = poolTop;
|
||||
poolTop = this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
69
0.0.9.2/src/alternativa/physics/Contact.as
Normal file
69
0.0.9.2/src/alternativa/physics/Contact.as
Normal file
@@ -0,0 +1,69 @@
|
||||
package alternativa.physics {
|
||||
|
||||
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
/**
|
||||
* Описывает контакт одного или двух тел.
|
||||
*/
|
||||
public class Contact {
|
||||
/**
|
||||
* Первое тело контакта, не может быть равно null.
|
||||
*/
|
||||
public var body1:Body;
|
||||
/**
|
||||
* Второе тело контакта, может быть равно null.
|
||||
*/
|
||||
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>(MAX_POINTS, true);
|
||||
/**
|
||||
* Количество точек контакта.
|
||||
*/
|
||||
public var pcount:int;
|
||||
/**
|
||||
* Максимальная глубина пересечения.
|
||||
*/
|
||||
public var maxPenetration:Number = 0;
|
||||
/**
|
||||
* Флаг показывает, разрешён контакт или нет.
|
||||
*/
|
||||
public var satisfied:Boolean;
|
||||
/**
|
||||
* Следующий контакт в списке.
|
||||
*/
|
||||
public var next:Contact;
|
||||
/**
|
||||
* Индекс контакта. Первый контакт в списке имеет индекс 0.
|
||||
*/
|
||||
public var index:int;
|
||||
|
||||
// Максимальное количество точек контакта
|
||||
private const MAX_POINTS:int = 8;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function Contact(index:int) {
|
||||
this.index = index;
|
||||
for (var i:int = 0; i < MAX_POINTS; i++) {
|
||||
points[i] = new ContactPoint();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
55
0.0.9.2/src/alternativa/physics/ContactPoint.as
Normal file
55
0.0.9.2/src/alternativa/physics/ContactPoint.as
Normal file
@@ -0,0 +1,55 @@
|
||||
package alternativa.physics {
|
||||
import alternativa.math.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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
743
0.0.9.2/src/alternativa/physics/PhysicsScene.as
Normal file
743
0.0.9.2/src/alternativa/physics/PhysicsScene.as
Normal file
@@ -0,0 +1,743 @@
|
||||
package alternativa.physics {
|
||||
|
||||
|
||||
import alternativa.physics.collision.ICollisionDetector;
|
||||
import alternativa.physics.collision.KdTreeCollisionDetector;
|
||||
import alternativa.physics.constraints.Constraint;
|
||||
import alternativa.math.Matrix3;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
use namespace altphysics;
|
||||
|
||||
/**
|
||||
* Класс реализует физическую симуляцию поведения твёрдых тел.
|
||||
*/
|
||||
public class PhysicsScene {
|
||||
|
||||
private static var lastBodyId:int;
|
||||
|
||||
// Максимальное количество контактов
|
||||
public 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 freezeSteps:int = 10;
|
||||
public var linSpeedFreezeLimit:Number = 1;
|
||||
public var angSpeedFreezeLimit:Number = 0.01;
|
||||
|
||||
// Переменные для процедуры разделения тел путём непосредственного их перемещенеия.
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// !!! Экспериментально !!!
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!
|
||||
public var staticSeparationIterations:int = 10;
|
||||
public var staticSeparationSteps:int = 10;
|
||||
public var maxAngleMove:Number = 10;
|
||||
public var useStaticSeparation:Boolean = false;
|
||||
|
||||
// Вектор гравитации
|
||||
public var _gravity:Vector3 = new Vector3(0, 0, -9.8);
|
||||
// Модуль вектора гравитации
|
||||
public var _gravityMagnitude:Number = 9.8;
|
||||
|
||||
// Использующийся детектор столкновений
|
||||
public var collisionDetector:ICollisionDetector;
|
||||
|
||||
// Список тел, участвующих в симуляции
|
||||
public var bodies:BodyList = new BodyList();
|
||||
|
||||
// Список контактов на текущем шаге симуляции
|
||||
altphysics var contacts:Contact;
|
||||
// Количество контактов на текущем шаге симуляции
|
||||
// altphysics var contactsNum:int;
|
||||
// Список ограничений
|
||||
altphysics var constraints:Vector.<Constraint> = new Vector.<Constraint>();
|
||||
// Количество ограничений
|
||||
altphysics var constraintsNum:int;
|
||||
// Временная метка. Число прошедших шагов с начала симуляции.
|
||||
public var timeStamp:int;
|
||||
// Время с начала симуляции, мс
|
||||
public var time:int;
|
||||
// Первый неиспользованный контакт на текущем шаге симуляции
|
||||
private var borderContact:Contact;
|
||||
|
||||
// Временные переменные для избежания создания экземпляров
|
||||
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 PhysicsScene() {
|
||||
contacts = new Contact(0);
|
||||
var contact:Contact = contacts;
|
||||
for (var i:int = 1; i < MAX_CONTACTS; i++) {
|
||||
contact.next = new Contact(i);
|
||||
contact = contact.next;
|
||||
}
|
||||
collisionDetector = new KdTreeCollisionDetector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Вектор гравитации.
|
||||
*/
|
||||
public function get gravity():Vector3 {
|
||||
return _gravity.vClone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function set gravity(value:Vector3):void {
|
||||
_gravity.vCopy(value);
|
||||
_gravityMagnitude = _gravity.vLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет тело в симуляцию.
|
||||
*
|
||||
* @param body
|
||||
*/
|
||||
public function addBody(body:Body):void {
|
||||
body.id = lastBodyId++;
|
||||
body.world = this;
|
||||
bodies.append(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет тело из симуляции.
|
||||
* @param body
|
||||
* @return
|
||||
*/
|
||||
public function removeBody(body:Body):void {
|
||||
if (bodies.remove(body)) {
|
||||
body.world = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет ограничение.
|
||||
* @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 {
|
||||
var item:BodyListItem = bodies.head;
|
||||
while (item != null) {
|
||||
var body:Body = item.body;
|
||||
body.beforePhysicsStep(dt);
|
||||
body.calcAccelerations();
|
||||
// Ускорение свободного падения применяется только к подвижным телам во избежание некорректного изменения
|
||||
// фиктивной скорости неподвижных тел.
|
||||
if (body.movable && !body.frozen) {
|
||||
body.accel.x += _gravity.x;
|
||||
body.accel.y += _gravity.y;
|
||||
body.accel.z += _gravity.z;
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Определяет все столкновения на текущем шаге симуляции и заполняет список получившихся контактов.
|
||||
*
|
||||
* @param dt длительность шага симуляции
|
||||
*/
|
||||
private function detectCollisions(dt:Number):void {
|
||||
var item:BodyListItem = bodies.head;
|
||||
while (item != null) {
|
||||
var body:Body = item.body;
|
||||
if (!body.frozen) {
|
||||
body.contactsNum = 0;
|
||||
body.saveState();
|
||||
// При включённом режиме предсказания состояние тел интегрируется на один шаг вперёд
|
||||
if (usePrediction) {
|
||||
body.integrateVelocity(dt);
|
||||
body.integratePosition(dt);
|
||||
}
|
||||
body.calcDerivedData();
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
|
||||
borderContact = collisionDetector.getAllContacts(contacts);
|
||||
|
||||
// Расчёт относительных векторов точки контакта вынесен сюда из-за необходимости учитывать
|
||||
// положение тел в предсказанном состоянии
|
||||
var contact:Contact = contacts;
|
||||
while (contact != borderContact) {
|
||||
var b1:Body = contact.body1;
|
||||
var b2:Body = contact.body2;
|
||||
for (var j:int = 0; j < contact.pcount; j++) {
|
||||
var cp:ContactPoint = contact.points[j];
|
||||
var bPos:Vector3 = b1.state.pos;
|
||||
cp.r1.x = cp.pos.x - bPos.x;
|
||||
cp.r1.y = cp.pos.y - bPos.y;
|
||||
cp.r1.z = cp.pos.z - bPos.z;
|
||||
if (b2 != null) {
|
||||
bPos = b2.state.pos;
|
||||
cp.r2.x = cp.pos.x - bPos.x;
|
||||
cp.r2.y = cp.pos.y - bPos.y;
|
||||
cp.r2.z = cp.pos.z - bPos.z;
|
||||
}
|
||||
}
|
||||
contact = contact.next;
|
||||
}
|
||||
|
||||
// Восстановление состояния тел
|
||||
if (usePrediction) {
|
||||
item = bodies.head;
|
||||
while (item != null) {
|
||||
body = item.body;
|
||||
if (!body.frozen) {
|
||||
body.restoreState();
|
||||
body.calcDerivedData();
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Подготваливает полученные из детектора столкновений контакты, расчитывая значения, не меняющиеся
|
||||
* в ходе шага симуляции.
|
||||
*/
|
||||
private function preProcessContacts(dt:Number):void {
|
||||
var contact:Contact = contacts;
|
||||
while (contact != borderContact) {
|
||||
var b1:Body = contact.body1;
|
||||
var b2:Body = contact.body2;
|
||||
// Столкнувшиеся тела размораживаются
|
||||
if (b1.frozen) {
|
||||
b1.frozen = false;
|
||||
b1.freezeCounter = 0;
|
||||
}
|
||||
if (b2 != null && b2.frozen) {
|
||||
b2.frozen = false;
|
||||
b2.freezeCounter = 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
contact = contact.next;
|
||||
}
|
||||
for (var i:int = 0; i < constraintsNum; i++) {
|
||||
var constraint:Constraint = constraints[i];
|
||||
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;
|
||||
var contact:Contact = contacts;
|
||||
while (contact != borderContact) {
|
||||
resolveContact(contact, forceInelastic, forwardLoop);
|
||||
contact = contact.next;
|
||||
}
|
||||
// Ограничения
|
||||
for (i = 0; i < constraintsNum; i++) {
|
||||
var constraint:Constraint = constraints[i];
|
||||
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 cnormal:Vector3 = contact.normal;
|
||||
var sepVel:Number = _v.x*cnormal.x + _v.y*cnormal.y + _v.z*cnormal.z;
|
||||
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 dot:Number = _v.x*cnormal.x + _v.y*cnormal.y + _v.z*cnormal.z;
|
||||
_v.x -= dot*cnormal.x;
|
||||
_v.y -= dot*cnormal.y;
|
||||
_v.z -= dot*cnormal.z;
|
||||
|
||||
var tanSpeed:Number = _v.vLength();
|
||||
if (tanSpeed < 0.001) return;
|
||||
|
||||
// _t.vCopy(_v).vNormalize().vReverse();
|
||||
_t.x = -_v.x;
|
||||
_t.y = -_v.y;
|
||||
_t.z = -_v.z;
|
||||
_t.vNormalize();
|
||||
|
||||
var r:Vector3;
|
||||
var m:Matrix3;
|
||||
var xx:Number;
|
||||
var yy:Number;
|
||||
var zz:Number;
|
||||
// dV = b.invMass + ((invI * (r % t)) % r) * t
|
||||
if (b1.movable) {
|
||||
// _v.vCross2(cp.r1, _t).vTransformBy3(b1.invInertiaWorld).vCross(cp.r1);
|
||||
r = cp.r1;
|
||||
m = b1.invInertiaWorld;
|
||||
|
||||
_v.x = r.y*_t.z - r.z*_t.y;
|
||||
_v.y = r.z*_t.x - r.x*_t.z;
|
||||
_v.z = r.x*_t.y - r.y*_t.x;
|
||||
|
||||
xx = m.a*_v.x + m.b*_v.y + m.c*_v.z;
|
||||
yy = m.e*_v.x + m.f*_v.y + m.g*_v.z;
|
||||
zz = m.i*_v.x + m.j*_v.y + m.k*_v.z;
|
||||
|
||||
_v.x = yy*r.z - zz*r.y;
|
||||
_v.y = zz*r.x - xx*r.z;
|
||||
_v.z = xx*r.y - yy*r.x;
|
||||
|
||||
tanSpeedByUnitImpulse += b1.invMass + _v.x*_t.x + _v.y*_t.y + _v.z*_t.z;
|
||||
}
|
||||
if (b2 != null && b2.movable) {
|
||||
// _v.vCross2(cp.r2, _t).vTransformBy3(b2.invInertiaWorld).vCross(cp.r2);
|
||||
|
||||
r = cp.r2;
|
||||
m = b2.invInertiaWorld;
|
||||
|
||||
_v.x = r.y*_t.z - r.z*_t.y;
|
||||
_v.y = r.z*_t.x - r.x*_t.z;
|
||||
_v.z = r.x*_t.y - r.y*_t.x;
|
||||
|
||||
xx = m.a*_v.x + m.b*_v.y + m.c*_v.z;
|
||||
yy = m.e*_v.x + m.f*_v.y + m.g*_v.z;
|
||||
zz = m.i*_v.x + m.j*_v.y + m.k*_v.z;
|
||||
|
||||
_v.x = yy*r.z - zz*r.y;
|
||||
_v.y = zz*r.x - xx*r.z;
|
||||
_v.z = xx*r.y - yy*r.x;
|
||||
|
||||
tanSpeedByUnitImpulse += b2.invMass + _v.x*_t.x + _v.y*_t.y + _v.z*_t.z;
|
||||
}
|
||||
|
||||
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));
|
||||
var rot:Vector3 = body1.state.rotation;
|
||||
var v:Vector3 = cp.r1;
|
||||
var x:Number = rot.y*v.z - rot.z*v.y;
|
||||
var y:Number = rot.z*v.x - rot.x*v.z;
|
||||
var z:Number = rot.x*v.y - rot.y*v.x;
|
||||
v = body1.state.velocity;
|
||||
result.x = v.x + x;
|
||||
result.y = v.y + y;
|
||||
result.z = v.z + z;
|
||||
// V2 = V2_c + w2%r2
|
||||
if (body2 != null) {
|
||||
// result.vSubtract(body2.state.velocity).vSubtract(_v2.vCross2(body2.state.rotation, cp.r2));
|
||||
rot = body2.state.rotation;
|
||||
v = cp.r2;
|
||||
x = rot.y*v.z - rot.z*v.y;
|
||||
y = rot.z*v.x - rot.x*v.z;
|
||||
z = rot.x*v.y - rot.y*v.x;
|
||||
v = body2.state.velocity;
|
||||
result.x -= v.x + x;
|
||||
result.y -= v.y + y;
|
||||
result.z -= v.z + z;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dt
|
||||
*/
|
||||
private function intergateVelocities(dt:Number):void {
|
||||
var item:BodyListItem = bodies.head;
|
||||
while (item != null) {
|
||||
item.body.integrateVelocity(dt);
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param dt
|
||||
*/
|
||||
private function integratePositions(dt:Number):void {
|
||||
var item:BodyListItem = bodies.head;
|
||||
while (item != null) {
|
||||
var body:Body = item.body;
|
||||
if (body.movable && !body.frozen) {
|
||||
body.integratePosition(dt);
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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 {
|
||||
var item:BodyListItem = bodies.head;
|
||||
while (item != null) {
|
||||
var body:Body = item.body;
|
||||
body.clearAccumulators();
|
||||
body.calcDerivedData();
|
||||
if (body.canFreeze) {
|
||||
if (body.state.velocity.vLength() < linSpeedFreezeLimit && body.state.rotation.vLength() < angSpeedFreezeLimit) {
|
||||
if (!body.frozen) {
|
||||
body.freezeCounter++;
|
||||
if (body.freezeCounter >= freezeSteps) body.frozen = true;
|
||||
}
|
||||
} else {
|
||||
body.freezeCounter = 0;
|
||||
body.frozen = false;
|
||||
}
|
||||
}
|
||||
item = item.next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param delta
|
||||
*/
|
||||
public function update(delta:int):void {
|
||||
timeStamp++;
|
||||
time += delta;
|
||||
var dt:Number = 0.001*delta;
|
||||
applyForces(dt);
|
||||
detectCollisions(dt);
|
||||
preProcessContacts(dt);
|
||||
processContacts(dt, false);
|
||||
intergateVelocities(dt);
|
||||
processContacts(dt, true);
|
||||
integratePositions(dt);
|
||||
postPhysics();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
49
0.0.9.2/src/alternativa/physics/PhysicsUtils.as
Normal file
49
0.0.9.2/src/alternativa/physics/PhysicsUtils.as
Normal file
@@ -0,0 +1,49 @@
|
||||
package alternativa.physics {
|
||||
import alternativa.math.Matrix3;
|
||||
import alternativa.math.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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
3
0.0.9.2/src/alternativa/physics/altphysics.as
Normal file
3
0.0.9.2/src/alternativa/physics/altphysics.as
Normal file
@@ -0,0 +1,3 @@
|
||||
package alternativa.physics {
|
||||
public namespace altphysics = "http://alternativaplatform.com/en/altphysics";
|
||||
}
|
||||
65
0.0.9.2/src/alternativa/physics/collision/.svn/all-wcprops
Normal file
65
0.0.9.2/src/alternativa/physics/collision/.svn/all-wcprops
Normal file
@@ -0,0 +1,65 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 113
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision
|
||||
END
|
||||
ICollisionDetector.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 135
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/ICollisionDetector.as
|
||||
END
|
||||
CollisionKdNode.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 132
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/CollisionKdNode.as
|
||||
END
|
||||
CollisionKdTree2D.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 134
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/CollisionKdTree2D.as
|
||||
END
|
||||
IRayCollisionPredicate.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 139
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/IRayCollisionPredicate.as
|
||||
END
|
||||
IBodyCollisionPredicate.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 140
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/IBodyCollisionPredicate.as
|
||||
END
|
||||
CollisionPrimitive.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 135
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/CollisionPrimitive.as
|
||||
END
|
||||
KdTreeCollisionDetector.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 140
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/KdTreeCollisionDetector.as
|
||||
END
|
||||
ICollisionPredicate.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 136
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/ICollisionPredicate.as
|
||||
END
|
||||
ICollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 126
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/ICollider.as
|
||||
END
|
||||
CollisionKdTree.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 132
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/CollisionKdTree.as
|
||||
END
|
||||
157
0.0.9.2/src/alternativa/physics/collision/.svn/entries
Normal file
157
0.0.9.2/src/alternativa/physics/collision/.svn/entries
Normal file
@@ -0,0 +1,157 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2010-03-06T16:51:19.781174Z
|
||||
29404
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
colliders
|
||||
dir
|
||||
|
||||
ICollisionDetector.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
9f76503bdaf92b4170b05b6e66c45de1
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
CollisionKdNode.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
a6fab491cd9bd45a5472bf4ffc321e08
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
CollisionKdTree2D.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
82bb636d8b49cdfbc22694dd8b01e71d
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
IRayCollisionPredicate.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
32f9b1eaeacfcb40e7ae1927f52b8ab8
|
||||
2009-10-19T07:17:33.113306Z
|
||||
22253
|
||||
mike
|
||||
|
||||
types
|
||||
dir
|
||||
|
||||
IBodyCollisionPredicate.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
618f907a588979a8d06d4f4bcf6618a0
|
||||
2009-10-19T07:17:33.113306Z
|
||||
22253
|
||||
mike
|
||||
|
||||
CollisionPrimitive.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
8501ce34a3effa2380c472cc9b823afa
|
||||
2009-11-24T08:44:15.941480Z
|
||||
23758
|
||||
mike
|
||||
|
||||
KdTreeCollisionDetector.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
ea670ad5d0f72f6a6df5163d65f204f9
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
ICollisionPredicate.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
65f3250d36556fffc457c740fb7d38b3
|
||||
2009-10-19T07:17:33.113306Z
|
||||
22253
|
||||
mike
|
||||
|
||||
ICollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
128a7d7479e13caeccecb45161b27b22
|
||||
2009-10-19T07:17:33.113306Z
|
||||
22253
|
||||
mike
|
||||
|
||||
primitives
|
||||
dir
|
||||
|
||||
CollisionKdTree.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
8e815c1f1e527b0884b3f0124257a603
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
1
0.0.9.2/src/alternativa/physics/collision/.svn/format
Normal file
1
0.0.9.2/src/alternativa/physics/collision/.svn/format
Normal file
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,18 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
|
||||
public class CollisionKdNode {
|
||||
public var indices:Vector.<int>;
|
||||
public var splitIndices:Vector.<int>;
|
||||
public var boundBox:BoundBox;
|
||||
public var parent:CollisionKdNode;
|
||||
public var splitTree:CollisionKdTree2D;
|
||||
|
||||
public var axis:int = -1; // 0 - x, 1 - y, 2 - z
|
||||
public var coord:Number;
|
||||
public var positiveNode:CollisionKdNode;
|
||||
public var negativeNode:CollisionKdNode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
|
||||
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
|
||||
/**
|
||||
* @author mike
|
||||
*/
|
||||
public class CollisionKdTree {
|
||||
|
||||
public var threshold:Number = 0.1;
|
||||
public var minPrimitivesPerNode:int = 1;
|
||||
public var rootNode:CollisionKdNode;
|
||||
public var staticChildren:Vector.<CollisionPrimitive>;
|
||||
public var numStaticChildren:int;
|
||||
public var staticBoundBoxes:Vector.<BoundBox> = new Vector.<BoundBox>();
|
||||
|
||||
private var splitAxis:int;
|
||||
private var splitCoord:Number;
|
||||
private var splitCost:Number;
|
||||
|
||||
private static const nodeBoundBoxThreshold:BoundBox = new BoundBox();
|
||||
private static const splitCoordsX:Vector.<Number> = new Vector.<Number>();
|
||||
private static const splitCoordsY:Vector.<Number> = new Vector.<Number>();
|
||||
private static const splitCoordsZ:Vector.<Number> = new Vector.<Number>();
|
||||
private static const _nodeBB:Vector.<Number> = new Vector.<Number>(6);
|
||||
private static const _bb:Vector.<Number> = new Vector.<Number>(6);
|
||||
|
||||
/**
|
||||
* @param boundBox
|
||||
*/
|
||||
public function createTree(collisionPrimitives:Vector.<CollisionPrimitive>, boundBox:BoundBox = null):void {
|
||||
staticChildren = collisionPrimitives.concat();
|
||||
numStaticChildren = staticChildren.length;
|
||||
// Создаём корневую ноду
|
||||
rootNode = new CollisionKdNode();
|
||||
rootNode.indices = new Vector.<int>();
|
||||
// Расчитываем баунды объектов и рутовой ноды
|
||||
var rootNodeBoundBox:BoundBox = rootNode.boundBox = (boundBox != null) ? boundBox : new BoundBox();
|
||||
for (var i:int = 0; i < numStaticChildren; ++i) {
|
||||
var child:CollisionPrimitive = staticChildren[i];
|
||||
var childBoundBox:BoundBox = staticBoundBoxes[i] = child.calculateAABB();
|
||||
rootNodeBoundBox.addBoundBox(childBoundBox);
|
||||
rootNode.indices[i] = i;
|
||||
}
|
||||
staticBoundBoxes.length = numStaticChildren;
|
||||
// Разделяем рутовую ноду
|
||||
splitNode(rootNode);
|
||||
|
||||
splitCoordsX.length = splitCoordsY.length = splitCoordsZ.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node
|
||||
*/
|
||||
private function splitNode(node:CollisionKdNode):void {
|
||||
var indices:Vector.<int> = node.indices;
|
||||
var numPrimitives:int = indices.length;
|
||||
if (numPrimitives <= minPrimitivesPerNode) return;
|
||||
|
||||
// Подготовка баунда с погрешностями
|
||||
var nodeBoundBox:BoundBox = node.boundBox;
|
||||
nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
|
||||
nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
|
||||
nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
|
||||
nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
|
||||
nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
|
||||
nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
|
||||
var doubleThreshold:Number = threshold*2;
|
||||
|
||||
// Собираем опорные координаты
|
||||
var i:int;
|
||||
var j:int;
|
||||
var numSplitCoordsX:int = 0
|
||||
var numSplitCoordsY:int = 0;
|
||||
var numSplitCoordsZ:int = 0;
|
||||
for (i = 0; i < numPrimitives; ++i) {
|
||||
var boundBox:BoundBox = staticBoundBoxes[indices[i]];
|
||||
|
||||
if (boundBox.maxX - boundBox.minX <= doubleThreshold) {
|
||||
if (boundBox.minX <= nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = nodeBoundBox.minX;
|
||||
else if (boundBox.maxX >= nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = nodeBoundBox.maxX;
|
||||
else splitCoordsX[numSplitCoordsX++] = (boundBox.minX + boundBox.maxX)*0.5;
|
||||
} else {
|
||||
if (boundBox.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = boundBox.minX;
|
||||
if (boundBox.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = boundBox.maxX;
|
||||
}
|
||||
|
||||
if (boundBox.maxY - boundBox.minY <= doubleThreshold) {
|
||||
if (boundBox.minY <= nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = nodeBoundBox.minY;
|
||||
else if (boundBox.maxY >= nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = nodeBoundBox.maxY;
|
||||
else splitCoordsY[numSplitCoordsY++] = (boundBox.minY + boundBox.maxY)*0.5;
|
||||
} else {
|
||||
if (boundBox.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = boundBox.minY;
|
||||
if (boundBox.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = boundBox.maxY;
|
||||
}
|
||||
|
||||
if (boundBox.maxZ - boundBox.minZ <= doubleThreshold) {
|
||||
if (boundBox.minZ <= nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = nodeBoundBox.minZ;
|
||||
else if (boundBox.maxZ >= nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = nodeBoundBox.maxZ;
|
||||
else splitCoordsZ[numSplitCoordsZ++] = (boundBox.minZ + boundBox.maxZ)*0.5;
|
||||
} else {
|
||||
if (boundBox.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.minZ;
|
||||
if (boundBox.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.maxZ;
|
||||
}
|
||||
}
|
||||
|
||||
// Поиск наилучшего сплита
|
||||
splitAxis = -1;
|
||||
splitCost = 1e308;
|
||||
_nodeBB[0] = nodeBoundBox.minX;
|
||||
_nodeBB[1] = nodeBoundBox.minY;
|
||||
_nodeBB[2] = nodeBoundBox.minZ;
|
||||
_nodeBB[3] = nodeBoundBox.maxX;
|
||||
_nodeBB[4] = nodeBoundBox.maxY;
|
||||
_nodeBB[5] = nodeBoundBox.maxZ;
|
||||
checkNodeAxis(node, 0, numSplitCoordsX, splitCoordsX, _nodeBB);
|
||||
checkNodeAxis(node, 1, numSplitCoordsY, splitCoordsY, _nodeBB);
|
||||
checkNodeAxis(node, 2, numSplitCoordsZ, splitCoordsZ, _nodeBB);
|
||||
|
||||
// Если сплит не найден, выходим
|
||||
if (splitAxis < 0) return;
|
||||
|
||||
// Сплиттер найден. Разделение узла.
|
||||
var axisX:Boolean = splitAxis == 0
|
||||
var axisY:Boolean = splitAxis == 1;
|
||||
node.axis = splitAxis;
|
||||
node.coord = splitCoord;
|
||||
// Создаём дочерние ноды
|
||||
node.negativeNode = new CollisionKdNode();
|
||||
node.negativeNode.parent = node;
|
||||
node.negativeNode.boundBox = nodeBoundBox.clone();
|
||||
node.positiveNode = new CollisionKdNode();
|
||||
node.positiveNode.parent = node;
|
||||
node.positiveNode.boundBox = nodeBoundBox.clone();
|
||||
if (axisX) node.negativeNode.boundBox.maxX = node.positiveNode.boundBox.minX = splitCoord;
|
||||
else if (axisY) node.negativeNode.boundBox.maxY = node.positiveNode.boundBox.minY = splitCoord;
|
||||
else node.negativeNode.boundBox.maxZ = node.positiveNode.boundBox.minZ = splitCoord;
|
||||
|
||||
// Распределяем объекты по дочерним нодам
|
||||
var coordMin:Number = splitCoord - threshold;
|
||||
var coordMax:Number = splitCoord + threshold;
|
||||
for (i = 0; i < numPrimitives; ++i) {
|
||||
boundBox = staticBoundBoxes[indices[i]];
|
||||
var min:Number = axisX ? boundBox.minX : (axisY ? boundBox.minY : boundBox.minZ);
|
||||
var max:Number = axisX ? boundBox.maxX : (axisY ? boundBox.maxY : boundBox.maxZ);
|
||||
if (max <= coordMax) {
|
||||
if (min < coordMin) {
|
||||
// Объект в негативной стороне
|
||||
if (node.negativeNode.indices == null) node.negativeNode.indices = new Vector.<int>();
|
||||
node.negativeNode.indices.push(indices[i]);
|
||||
indices[i] = -1;
|
||||
} else {
|
||||
if (node.splitIndices == null) node.splitIndices = new Vector.<int>();
|
||||
node.splitIndices.push(indices[i]);
|
||||
indices[i] = -1;
|
||||
}
|
||||
} else {
|
||||
if (min >= coordMin) {
|
||||
// Объект в положительной стороне
|
||||
if (node.positiveNode.indices == null) node.positiveNode.indices = new Vector.<int>();
|
||||
node.positiveNode.indices.push(indices[i]);
|
||||
indices[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Очистка списка объектов
|
||||
for (i = 0, j = 0; i < numPrimitives; ++i) {
|
||||
if (indices[i] >= 0) indices[j++] = indices[i];
|
||||
}
|
||||
if (j > 0) indices.length = j;
|
||||
else node.indices = null;
|
||||
|
||||
if (node.splitIndices != null) {
|
||||
node.splitTree = new CollisionKdTree2D(this, node);
|
||||
node.splitTree.createTree();
|
||||
}
|
||||
|
||||
// Разделение дочерних нод
|
||||
if (node.negativeNode.indices != null) splitNode(node.negativeNode);
|
||||
if (node.positiveNode.indices != null) splitNode(node.positiveNode);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @param axis
|
||||
* @param numSplitCoords
|
||||
* @param splitCoords
|
||||
* @param bb
|
||||
*/
|
||||
private function checkNodeAxis(node:CollisionKdNode, axis:int, numSplitCoords:int, splitCoords:Vector.<Number>, bb:Vector.<Number>):void {
|
||||
var axis1:int = (axis + 1)%3;
|
||||
var axis2:int = (axis + 2)%3;
|
||||
var area:Number = (bb[axis1 + 3] - bb[axis1])*(bb[axis2 + 3] - bb[axis2]);
|
||||
for (var i:int = 0; i < numSplitCoords; ++i) {
|
||||
var currSplitCoord:Number = splitCoords[i];
|
||||
if (isNaN(currSplitCoord)) continue;
|
||||
var minCoord:Number = currSplitCoord - threshold;
|
||||
var maxCoord:Number = currSplitCoord + threshold;
|
||||
var areaNegative:Number = area*(currSplitCoord - bb[axis]);
|
||||
var areaPositive:Number = area*(bb[int(axis + 3)] - currSplitCoord);
|
||||
var numNegative:int = 0;
|
||||
var numPositive:int = 0;
|
||||
var conflict:Boolean = false;
|
||||
// Проверяем объекты
|
||||
var numObjects:int = node.indices.length;
|
||||
for (var j:int = 0; j < numObjects; j++) {
|
||||
var boundBox:BoundBox = staticBoundBoxes[node.indices[j]];
|
||||
_bb[0] = boundBox.minX;
|
||||
_bb[1] = boundBox.minY;
|
||||
_bb[2] = boundBox.minZ;
|
||||
_bb[3] = boundBox.maxX;
|
||||
_bb[4] = boundBox.maxY;
|
||||
_bb[5] = boundBox.maxZ;
|
||||
if (_bb[axis + 3] <= maxCoord) {
|
||||
if (_bb[axis] < minCoord) numNegative++;
|
||||
} else {
|
||||
if (_bb[axis] >= minCoord) {
|
||||
numPositive++;
|
||||
} else {
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
var cost:Number = areaNegative*numNegative + areaPositive*numPositive;
|
||||
if (!conflict && cost < splitCost) {
|
||||
splitAxis = axis;
|
||||
splitCost = cost;
|
||||
splitCoord = currSplitCoord;
|
||||
}
|
||||
for (j = i + 1; j < numSplitCoords; ++j) {
|
||||
if (splitCoords[j] >= currSplitCoord - threshold && splitCoords[j] <= currSplitCoord + threshold) splitCoords[j] = NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function traceTree():void {
|
||||
traceNode("", rootNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param str
|
||||
* @param node
|
||||
*/
|
||||
private function traceNode(str:String, node:CollisionKdNode):void {
|
||||
if (node == null) return;
|
||||
trace(str, node.axis == -1 ? "end" : ((node.axis == 0) ? "X" : ((node.axis == 1) ? "Y" : "Z")), "splitCoord=" + splitCoord, "bound", node.boundBox, "objs:", node.indices);
|
||||
traceNode(str + "-", node.negativeNode);
|
||||
traceNode(str + "+", node.positiveNode);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class CollisionKdTree2D {
|
||||
|
||||
public var threshold:Number = 0.1;
|
||||
public var minPrimitivesPerNode:int = 1;
|
||||
|
||||
public var parentTree:CollisionKdTree;
|
||||
public var parentNode:CollisionKdNode;
|
||||
public var rootNode:CollisionKdNode;
|
||||
|
||||
private var splitAxis:int;
|
||||
private var splitCost:Number;
|
||||
private var splitCoord:Number;
|
||||
|
||||
private static const nodeBoundBoxThreshold:BoundBox = new BoundBox();
|
||||
private static const splitCoordsX:Vector.<Number> = new Vector.<Number>();
|
||||
private static const splitCoordsY:Vector.<Number> = new Vector.<Number>();
|
||||
private static const splitCoordsZ:Vector.<Number> = new Vector.<Number>();
|
||||
private static const _nodeBB:Vector.<Number> = new Vector.<Number>(6);
|
||||
private static const _bb:Vector.<Number> = new Vector.<Number>(6);
|
||||
/**
|
||||
*
|
||||
* @param parentTree
|
||||
* @param parentNode
|
||||
*/
|
||||
public function CollisionKdTree2D(parentTree:CollisionKdTree, parentNode:CollisionKdNode) {
|
||||
this.parentTree = parentTree;
|
||||
this.parentNode = parentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function createTree():void {
|
||||
rootNode = new CollisionKdNode();
|
||||
rootNode.boundBox = parentNode.boundBox.clone();
|
||||
rootNode.indices = new Vector.<int>();
|
||||
var numObjects:int = parentNode.splitIndices.length;
|
||||
for (var i:int = 0; i < numObjects; ++i) rootNode.indices[i] = parentNode.splitIndices[i];
|
||||
|
||||
splitNode(rootNode);
|
||||
splitCoordsX.length = splitCoordsY.length = splitCoordsZ.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node
|
||||
*/
|
||||
private function splitNode(node:CollisionKdNode):void {
|
||||
if (node.indices.length <= minPrimitivesPerNode) return;
|
||||
|
||||
var objects:Vector.<int> = node.indices;
|
||||
var i:int;
|
||||
var j:int;
|
||||
|
||||
var nodeBoundBox:BoundBox = node.boundBox;
|
||||
|
||||
// Подготовка баунда с погрешностями
|
||||
nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
|
||||
nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
|
||||
nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
|
||||
nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
|
||||
nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
|
||||
nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
|
||||
var doubleThreshold:Number = threshold*2;
|
||||
|
||||
var staticBoundBoxes:Vector.<BoundBox> = parentTree.staticBoundBoxes;
|
||||
// Собираем опорные координаты
|
||||
var numSplitCoordsX:int;
|
||||
var numSplitCoordsY:int;
|
||||
var numSplitCoordsZ:int;
|
||||
var numObjects:int = objects.length;
|
||||
for (i = 0; i < numObjects; ++i) {
|
||||
var bb:BoundBox = staticBoundBoxes[objects[i]];
|
||||
|
||||
if (parentNode.axis != 0) {
|
||||
if (bb.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = bb.minX;
|
||||
if (bb.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = bb.maxX;
|
||||
}
|
||||
|
||||
if (parentNode.axis != 1) {
|
||||
if (bb.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = bb.minY;
|
||||
if (bb.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = bb.maxY;
|
||||
}
|
||||
|
||||
if (parentNode.axis != 2) {
|
||||
if (bb.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = bb.minZ;
|
||||
if (bb.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = bb.maxZ;
|
||||
}
|
||||
}
|
||||
|
||||
// Поиск наилучшего сплита
|
||||
splitAxis = -1;
|
||||
splitCost = 1e308;
|
||||
_nodeBB[0] = nodeBoundBox.minX;
|
||||
_nodeBB[1] = nodeBoundBox.minY;
|
||||
_nodeBB[2] = nodeBoundBox.minZ;
|
||||
_nodeBB[3] = nodeBoundBox.maxX;
|
||||
_nodeBB[4] = nodeBoundBox.maxY;
|
||||
_nodeBB[5] = nodeBoundBox.maxZ;
|
||||
if (parentNode.axis != 0) checkNodeAxis(node, 0, numSplitCoordsX, splitCoordsX, _nodeBB);
|
||||
if (parentNode.axis != 1) checkNodeAxis(node, 1, numSplitCoordsY, splitCoordsY, _nodeBB);
|
||||
if (parentNode.axis != 2) checkNodeAxis(node, 2, numSplitCoordsZ, splitCoordsZ, _nodeBB);
|
||||
|
||||
// Если сплит не найден, выходим
|
||||
if (splitAxis < 0) return;
|
||||
|
||||
// Сплиттер найден. Разделение узла.
|
||||
var axisX:Boolean = splitAxis == 0
|
||||
var axisY:Boolean = splitAxis == 1;
|
||||
node.axis = splitAxis;
|
||||
node.coord = splitCoord;
|
||||
// Создаём дочерние ноды
|
||||
node.negativeNode = new CollisionKdNode();
|
||||
node.negativeNode.parent = node;
|
||||
node.negativeNode.boundBox = nodeBoundBox.clone();
|
||||
node.positiveNode = new CollisionKdNode();
|
||||
node.positiveNode.parent = node;
|
||||
node.positiveNode.boundBox = nodeBoundBox.clone();
|
||||
if (axisX) node.negativeNode.boundBox.maxX = node.positiveNode.boundBox.minX = splitCoord;
|
||||
else if (axisY) node.negativeNode.boundBox.maxY = node.positiveNode.boundBox.minY = splitCoord;
|
||||
else node.negativeNode.boundBox.maxZ = node.positiveNode.boundBox.minZ = splitCoord;
|
||||
|
||||
// Распределяем объекты по дочерним нодам
|
||||
var coordMin:Number = splitCoord - threshold;
|
||||
var coordMax:Number = splitCoord + threshold;
|
||||
for (i = 0; i < numObjects; ++i) {
|
||||
bb = staticBoundBoxes[objects[i]];
|
||||
var min:Number = axisX ? bb.minX : (axisY ? bb.minY : bb.minZ);
|
||||
var max:Number = axisX ? bb.maxX : (axisY ? bb.maxY : bb.maxZ);
|
||||
if (max <= coordMax) {
|
||||
if (min < coordMin) {
|
||||
// Объект в негативной стороне
|
||||
if (node.negativeNode.indices == null) node.negativeNode.indices = new Vector.<int>();
|
||||
node.negativeNode.indices.push(objects[i]);
|
||||
objects[i] = -1;
|
||||
}
|
||||
} else {
|
||||
if (min >= coordMin) {
|
||||
if (max > coordMax) {
|
||||
// Объект в положительной стороне
|
||||
if (node.positiveNode.indices == null) node.positiveNode.indices = new Vector.<int>();
|
||||
node.positiveNode.indices.push(objects[i]);
|
||||
objects[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Очистка списка объектов
|
||||
for (i = 0, j = 0; i < numObjects; ++i) {
|
||||
if (objects[i] >= 0) objects[j++] = objects[i];
|
||||
}
|
||||
if (j > 0) objects.length = j;
|
||||
else node.indices = null;
|
||||
|
||||
// Разделение дочерних нод
|
||||
if (node.negativeNode.indices != null) splitNode(node.negativeNode);
|
||||
if (node.positiveNode.indices != null) splitNode(node.positiveNode);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @param axis
|
||||
* @param numSplitCoords
|
||||
* @param splitCoords
|
||||
* @param bb
|
||||
*/
|
||||
private function checkNodeAxis(node:CollisionKdNode, axis:int, numSplitCoords:int, splitCoords:Vector.<Number>, bb:Vector.<Number>):void {
|
||||
var axis1:int = (axis + 1)%3;
|
||||
var axis2:int = (axis + 2)%3;
|
||||
var area:Number = (bb[axis1 + 3] - bb[axis1])*(bb[axis2 + 3] - bb[axis2]);
|
||||
var staticBoundBoxes:Vector.<BoundBox> = parentTree.staticBoundBoxes;
|
||||
for (var i:int = 0; i < numSplitCoords; ++i) {
|
||||
var currSplitCoord:Number = splitCoords[i];
|
||||
if (isNaN(currSplitCoord)) continue;
|
||||
var minCoord:Number = currSplitCoord - threshold;
|
||||
var maxCoord:Number = currSplitCoord + threshold;
|
||||
var areaNegative:Number = area*(currSplitCoord - bb[axis]);
|
||||
var areaPositive:Number = area*(bb[int(axis + 3)] - currSplitCoord);
|
||||
var numNegative:int = 0;
|
||||
var numPositive:int = 0;
|
||||
var conflict:Boolean = false;
|
||||
// Проверяем объекты
|
||||
var numObjects:int = node.indices.length;
|
||||
for (var j:int = 0; j < numObjects; j++) {
|
||||
var boundBox:BoundBox = staticBoundBoxes[node.indices[j]];
|
||||
_bb[0] = boundBox.minX;
|
||||
_bb[1] = boundBox.minY;
|
||||
_bb[2] = boundBox.minZ;
|
||||
_bb[3] = boundBox.maxX;
|
||||
_bb[4] = boundBox.maxY;
|
||||
_bb[5] = boundBox.maxZ;
|
||||
if (_bb[axis + 3] <= maxCoord) {
|
||||
if (_bb[axis] < minCoord) numNegative++;
|
||||
} else {
|
||||
if (_bb[axis] >= minCoord) {
|
||||
numPositive++;
|
||||
} else {
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
var cost:Number = areaNegative*numNegative + areaPositive*numPositive;
|
||||
if (!conflict && cost < splitCost) {
|
||||
splitAxis = axis;
|
||||
splitCost = cost;
|
||||
splitCoord = currSplitCoord;
|
||||
}
|
||||
for (j = i + 1; j < numSplitCoords; ++j) {
|
||||
if (splitCoords[j] >= currSplitCoord - threshold && splitCoords[j] <= currSplitCoord + threshold) splitCoords[j] = NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
import alternativa.physics.Body;
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
import alternativa.math.Matrix4;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
/**
|
||||
* Базовый класс для примитивов, использующихся детектором столкновений.
|
||||
*/
|
||||
public class CollisionPrimitive {
|
||||
// Константы типов примитива
|
||||
public static const BOX:int = 1;
|
||||
public static const PLANE:int = 2;
|
||||
public static const SPHERE:int = 3;
|
||||
public static const RECT:int = 4;
|
||||
public static const TRIANGLE:int = 5;
|
||||
|
||||
// Тип примитива
|
||||
public var type:int;
|
||||
// Группы примитива. Каждая группа определяется установленным битом. Столкновения проверяются только для примитивов,
|
||||
// имеющих хотя бы одну общую группу.
|
||||
public var collisionGroup:int;
|
||||
// Предикат примитива, вызывающийся детектором при нахождении столкновения. В зависимости от возвращённого
|
||||
// предикатом результата столкновение либо регистрируется, либо игнорируется.
|
||||
// Nullable
|
||||
public var postCollisionPredicate:ICollisionPredicate;
|
||||
// Тело, владеющее примитивом. Поле сделано открытым для быстрого доступа на чтение. Установка значения должна выполняться вызовом метода setBody().
|
||||
// Nullable
|
||||
public var body:Body;
|
||||
// Трансформация примитива в системе координат тела, если оно указано. Не допускается масштабирование матрицы.
|
||||
public var localTransform:Matrix4;
|
||||
// Полная трансформация примитива. Не допускается масштабирование матрицы.
|
||||
public var transform:Matrix4 = new Matrix4();
|
||||
// AABB в мировой системе координат. Расчитывается системой вызовом функции calculateBoundBox().
|
||||
public var aabb:BoundBox = new BoundBox();
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр примитива.
|
||||
*
|
||||
* @param type тип примитива
|
||||
* @param collisionGroup группа примитива
|
||||
*/
|
||||
public function CollisionPrimitive(type:int, collisionGroup:int) {
|
||||
this.type = type;
|
||||
this.collisionGroup = collisionGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает тело, владеющее примитивом.
|
||||
*
|
||||
* @param body тело, которое владеет примитивом
|
||||
* @param localTransform трансформация примитива в системе координат тела. Указание значения null равносильно
|
||||
* заданию единичной матрицы, однако в первом случае не будет дополнительного умножения матриц при вычислении полной трансформации примитива.
|
||||
*/
|
||||
public function setBody(body:Body, localTransform:Matrix4 = null):void {
|
||||
if (this.body == body) return;
|
||||
this.body = body;
|
||||
if (body != null) {
|
||||
if (localTransform != null) {
|
||||
if (this.localTransform == null) {
|
||||
this.localTransform = new Matrix4();
|
||||
}
|
||||
this.localTransform.copy(localTransform);
|
||||
} else {
|
||||
this.localTransform = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Рассчитывает AABB примитива. Наследники должны переопределять этот метод, реализуя в нём корректный рассчёт.
|
||||
*
|
||||
* @return ссылка на свой AABB
|
||||
*/
|
||||
public function calculateAABB():BoundBox {
|
||||
return aabb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет пересечение луча с примитивом.
|
||||
*
|
||||
* @param origin начальная точка луча в мировых координатах
|
||||
* @param vector направляющий вектор луча в мировых координатах. Вектор может быть любой отличной от нуля длины.
|
||||
* @param epsilon погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную погрешность, считается равной нулю.
|
||||
* @param normal если пересечение существует, в этот параметр записывается нормаль к примитиву в точке пересечения
|
||||
* @return если пересечение существует, возвращается неотрицательное время точки пересечения, в противном случае возвращается -1.
|
||||
*/
|
||||
public function getRayIntersection(origin:Vector3, vector:Vector3, epsilon:Number, normal:Vector3):Number {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирует примитив. Переопределять не рекомендуется. Вместо этого переопределяются методы createPrimitive() и copyFrom().
|
||||
*
|
||||
* @return клон примитива
|
||||
*/
|
||||
public function clone():CollisionPrimitive {
|
||||
var p:CollisionPrimitive = createPrimitive();
|
||||
return p.copyFrom(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирует параметры указанного примитива. Объекты копируются по значению.
|
||||
*
|
||||
* @param source примитив, чьи параметры копируются
|
||||
* @return this
|
||||
*/
|
||||
public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
|
||||
if (source == null) {
|
||||
throw new ArgumentError("Parameter source cannot be null");
|
||||
}
|
||||
type = source.type;
|
||||
transform.copy(source.transform);
|
||||
collisionGroup = source.collisionGroup;
|
||||
setBody(source.body, source.localTransform);
|
||||
aabb.copyFrom(source.aabb);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт строковое представление объекта.
|
||||
*
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[CollisionPrimitive type=" + type + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр примитива соответствующего типа.
|
||||
*
|
||||
* @return новый экземпляр примитива
|
||||
*/
|
||||
protected function createPrimitive():CollisionPrimitive {
|
||||
return new CollisionPrimitive(type, collisionGroup);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.Body;
|
||||
|
||||
public interface IBodyCollisionPredicate {
|
||||
function considerBodies(body1:Body, body2:Body):Boolean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
import alternativa.physics.Contact;
|
||||
|
||||
/**
|
||||
* Интерфейс определителя столкновений между двумя примитивами.
|
||||
*/
|
||||
public interface ICollider {
|
||||
|
||||
/**
|
||||
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
|
||||
|
||||
/**
|
||||
* Выполняет быстрый тест на наличие пересечения двух примитивов.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.Contact;
|
||||
import alternativa.physics.collision.types.RayIntersection;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
/**
|
||||
* Интерфейс детектора столкновений.
|
||||
*/
|
||||
public interface ICollisionDetector {
|
||||
|
||||
/**
|
||||
* Получает все столкновения в текущей конфигурации физической геометрии.
|
||||
*
|
||||
* @param contacts список контактов, в кторые будет записана информация о столкновении
|
||||
* @return количество найденных столкновений
|
||||
*/
|
||||
function getAllContacts(contacts:Contact):Contact;
|
||||
|
||||
/**
|
||||
* Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
|
||||
* В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
|
||||
*
|
||||
* @param origin начальная точка луча в мировых координатах
|
||||
* @param direction направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
|
||||
* @param collisionGroup идентификатор группы
|
||||
* @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
|
||||
* @param predicate предикат, применяемый к столкновениям
|
||||
* @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
|
||||
* переданной структуре не гарантируется.
|
||||
* @return true в случае наличия пересечения, иначе false
|
||||
*/
|
||||
function intersectRay(origin:Vector3, direction:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param origin
|
||||
* @param direction
|
||||
* @param collisionGroup
|
||||
* @param maxTime
|
||||
* @param predicate
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
function intersectRayWithStatic(origin:Vector3, direction:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @param contact
|
||||
* @return
|
||||
*/
|
||||
function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @return
|
||||
*/
|
||||
function testCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
public interface ICollisionPredicate {
|
||||
|
||||
function considerCollision(primitive:CollisionPrimitive):Boolean;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.Body;
|
||||
|
||||
public interface IRayCollisionPredicate {
|
||||
function considerBody(body:Body):Boolean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
|
||||
import alternativa.physics.Body;
|
||||
import alternativa.physics.Contact;
|
||||
import alternativa.physics.ContactPoint;
|
||||
import alternativa.physics.altphysics;
|
||||
import alternativa.physics.collision.colliders.BoxBoxCollider;
|
||||
import alternativa.physics.collision.colliders.BoxRectCollider;
|
||||
import alternativa.physics.collision.colliders.BoxSphereCollider;
|
||||
import alternativa.physics.collision.colliders.BoxTriangleCollider;
|
||||
import alternativa.physics.collision.colliders.SphereSphereCollider;
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
import alternativa.physics.collision.types.RayIntersection;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
use namespace altphysics;
|
||||
|
||||
/**
|
||||
* Детектор, хранящий статическую геометрию в kD-дереве и использующий дерево для ускорения тестов на пересечения.
|
||||
*/
|
||||
public class KdTreeCollisionDetector implements ICollisionDetector {
|
||||
|
||||
altphysics var tree:CollisionKdTree;
|
||||
altphysics var dynamicPrimitives:Vector.<CollisionPrimitive>;
|
||||
altphysics var dynamicPrimitivesNum:int;
|
||||
altphysics var threshold:Number = 0.0001;
|
||||
private var colliders:Object = {};
|
||||
|
||||
private var _time:MinMax = new MinMax();
|
||||
private var _n:Vector3 = new Vector3();
|
||||
private var _o:Vector3 = new Vector3();
|
||||
private var _dynamicIntersection:RayIntersection = new RayIntersection();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function KdTreeCollisionDetector() {
|
||||
tree = new CollisionKdTree();
|
||||
dynamicPrimitives = new Vector.<CollisionPrimitive>();
|
||||
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.BOX, new BoxBoxCollider());
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.SPHERE, new BoxSphereCollider());
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.RECT, new BoxRectCollider());
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.TRIANGLE, new BoxTriangleCollider());
|
||||
// addCollider(CollisionPrimitive.BOX, CollisionPrimitive.PLANE, new BoxPlaneCollider());
|
||||
|
||||
// addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.PLANE, new SpherePlaneCollider());
|
||||
addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.SPHERE, new SphereSphereCollider());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param primitive
|
||||
* @param isStatic
|
||||
* @return
|
||||
*/
|
||||
public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
|
||||
// if (isStatic) tree.addStaticPrimitive(primitive);
|
||||
// else dynamicPrimitives[dynamicPrimitivesNum++] = primitive;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param primitive
|
||||
* @param isStatic
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
|
||||
// if (isStatic) return tree.removeStaticPrimitive(primitive);
|
||||
// var idx:int = dynamicPrimitives.indexOf(primitive);
|
||||
// if (idx < 0) return false;
|
||||
// dynamicPrimitives.splice(idx, 1);
|
||||
// dynamicPrimitivesNum--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function init(collisionPrimitives:Vector.<CollisionPrimitive>):void {
|
||||
tree.createTree(collisionPrimitives);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param contacts
|
||||
* @return
|
||||
*/
|
||||
public function getAllContacts(contacts:Contact):Contact {
|
||||
// for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
|
||||
// var primitive:CollisionPrimitive = dynamicPrimitives[i];
|
||||
// primitive.calculateAABB();
|
||||
// if (primitive.body != null && primitive.body.frozen) continue;
|
||||
// var contact:Contact = getPrimitiveNodeCollisions(tree.rootNode, primitive, contacts);
|
||||
//
|
||||
// // Столкновения динамических примитивов между собой
|
||||
// // TODO: Попробовать различные оптимизации (сортировка по баундам, встраивание в дерево)
|
||||
// for (var j:int = i + 1; j < dynamicPrimitivesNum; j++) {
|
||||
// if (getContact(primitive, dynamicPrimitives[j], contacts[colNum])) colNum++;
|
||||
// }
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @param contact
|
||||
* @return
|
||||
*/
|
||||
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
if ((prim1.collisionGroup & prim2.collisionGroup) == 0) return false;
|
||||
if (prim1.body != null && prim1.body == prim2.body) return false;
|
||||
if (!prim1.aabb.intersects(prim2.aabb, 0.01)) return false;
|
||||
var collider:ICollider = colliders[prim1.type <= prim2.type ? (prim1.type << 16) | prim2.type : (prim2.type << 16) | prim1.type] as ICollider;
|
||||
if (collider != null && collider.getContact(prim1, prim2, contact)) {
|
||||
if (prim1.postCollisionPredicate != null && !prim1.postCollisionPredicate.considerCollision(prim2)) return false;
|
||||
if (prim2.postCollisionPredicate != null && !prim2.postCollisionPredicate.considerCollision(prim1)) return false;
|
||||
// Сохраняем ссылку на контакт для каждого тела
|
||||
// if (prim1.body != null) prim1.body.contacts[prim1.body.contactsNum++] = contact;
|
||||
// if (prim2.body != null) prim2.body.contacts[prim2.body.contactsNum++] = contact;
|
||||
// // Вычисляем максимальную глубину пересечения для контакта
|
||||
// contact.maxPenetration = ContactPoint(contact.points[0]).penetration;
|
||||
// var pen:Number;
|
||||
// for (var i:int = contact.pcount - 1; i >= 1; i--) {
|
||||
// if ((pen = (contact.points[i] as ContactPoint).penetration) > contact.maxPenetration) contact.maxPenetration = pen;
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @param contact
|
||||
* @return
|
||||
*/
|
||||
public function testCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
|
||||
if ((prim1.collisionGroup & prim2.collisionGroup) == 0) return false;
|
||||
if (prim1.body != null && prim1.body == prim2.body) return false;
|
||||
if (!prim1.aabb.intersects(prim2.aabb, 0.01)) return false;
|
||||
var collider:ICollider = colliders[prim1.type <= prim2.type ? (prim1.type << 16) | prim2.type : (prim2.type << 16) | prim1.type] as ICollider;
|
||||
if (collider != null && collider.haveCollision(prim1, prim2)) {
|
||||
if (prim1.postCollisionPredicate != null && !prim1.postCollisionPredicate.considerCollision(prim2)) return false;
|
||||
if (prim2.postCollisionPredicate != null && !prim2.postCollisionPredicate.considerCollision(prim1)) return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
|
||||
* В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча.
|
||||
*
|
||||
* @param origin
|
||||
* @param dir
|
||||
* @param collisionGroup идентификатор группы
|
||||
* @param maxTime параметр, задающий длину проверяемого сегмента
|
||||
* @param predicate
|
||||
* @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
|
||||
* переданной структуре не гарантируется.
|
||||
* @return true в случае наличия пересечения, иначе false
|
||||
*/
|
||||
public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
|
||||
var hasStaticIntersection:Boolean = intersectRayWithStatic(origin, dir, collisionGroup, maxTime, predicate, result);
|
||||
var hasDynamicIntersection:Boolean = intersectRayWithDynamic(origin, dir, collisionGroup, maxTime, predicate, _dynamicIntersection);
|
||||
|
||||
if (!(hasDynamicIntersection || hasStaticIntersection)) return false;
|
||||
|
||||
if (hasDynamicIntersection && hasStaticIntersection) {
|
||||
if (result.t > _dynamicIntersection.t) result.copy(_dynamicIntersection);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasStaticIntersection) return true;
|
||||
|
||||
result.copy(_dynamicIntersection);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param origin
|
||||
* @param dir
|
||||
* @param collisionGroup
|
||||
* @param maxTime
|
||||
* @param predicate
|
||||
* @param result
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
|
||||
// Вычисление точки пересечения с корневм узлом
|
||||
if (!getRayBoundBoxIntersection(origin, dir, tree.rootNode.boundBox, _time)) return false;
|
||||
if (_time.max < 0 || _time.min > maxTime) return false;
|
||||
|
||||
if (_time.min <= 0) {
|
||||
_time.min = 0;
|
||||
_o.x = origin.x;
|
||||
_o.y = origin.y;
|
||||
_o.z = origin.z;
|
||||
} else {
|
||||
_o.x = origin.x + _time.min*dir.x;
|
||||
_o.y = origin.y + _time.min*dir.y;
|
||||
_o.z = origin.z + _time.min*dir.z;
|
||||
}
|
||||
if (_time.max > maxTime) _time.max = maxTime;
|
||||
|
||||
var hasIntersection:Boolean = testRayAgainstNode(tree.rootNode, origin, _o, dir, collisionGroup, _time.min, _time.max, predicate, result);
|
||||
return hasIntersection ? result.t <= maxTime : false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
* @param primitive
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public function testBodyPrimitiveCollision(body:Body, primitive:CollisionPrimitive):Boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param type1
|
||||
* @param type2
|
||||
* @param collider
|
||||
*/
|
||||
private function addCollider(type1:int, type2:int, collider:ICollider):void {
|
||||
colliders[type1 <= type2 ? (type1 << 16) | type2 : (type2 << 16) | type1] = collider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет поиск столкновений динамического примитива с примитивами узла дерева.
|
||||
*
|
||||
* @param node
|
||||
* @param primitive
|
||||
* @param contacts
|
||||
* @param startIndex
|
||||
* @return
|
||||
*/
|
||||
private function getPrimitiveNodeCollisions(node:CollisionKdNode, primitive:CollisionPrimitive, contacts:Contact):Contact {
|
||||
// var colNum:int = 0;
|
||||
// if (node.indices != null) {
|
||||
// // Поиск столкновений со статическими примитивами узла
|
||||
// var primitives:Vector.<CollisionPrimitive> = tree.staticChildren;
|
||||
// var indices:Vector.<int> = node.indices;
|
||||
// for (var i:int = indices.length - 1; i >= 0; i--)
|
||||
// if (getContact(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;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static var _rayAABB:BoundBox = new BoundBox();
|
||||
/**
|
||||
* Тест пересечения луча с динамическими примитивами.
|
||||
*
|
||||
* @param origin
|
||||
* @param dir
|
||||
* @param collisionGroup
|
||||
* @param maxTime
|
||||
* @param predicate
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function intersectRayWithDynamic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
|
||||
var xx:Number = origin.x + dir.x*maxTime;
|
||||
var yy:Number = origin.y + dir.y*maxTime;
|
||||
var zz:Number = origin.z + dir.z*maxTime;
|
||||
if (xx < origin.x) {
|
||||
_rayAABB.minX = xx;
|
||||
_rayAABB.maxX = origin.x;
|
||||
} else {
|
||||
_rayAABB.minX = origin.x;
|
||||
_rayAABB.maxX = xx;
|
||||
}
|
||||
if (yy < origin.y) {
|
||||
_rayAABB.minY = yy;
|
||||
_rayAABB.maxY = origin.y;
|
||||
} else {
|
||||
_rayAABB.minY = origin.y;
|
||||
_rayAABB.maxY = yy;
|
||||
}
|
||||
if (zz < origin.z) {
|
||||
_rayAABB.minZ = zz;
|
||||
_rayAABB.maxZ = origin.z;
|
||||
} else {
|
||||
_rayAABB.minZ = origin.z;
|
||||
_rayAABB.maxZ = zz;
|
||||
}
|
||||
|
||||
var minTime:Number = maxTime + 1;
|
||||
for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
|
||||
var primitive:CollisionPrimitive = dynamicPrimitives[i];
|
||||
if ((primitive.collisionGroup & collisionGroup) == 0) continue;
|
||||
var paabb:BoundBox = primitive.aabb;
|
||||
if (_rayAABB.maxX < paabb.minX || _rayAABB.minX > paabb.maxX || _rayAABB.maxY < paabb.minY || _rayAABB.minY > paabb.maxY || _rayAABB.maxZ < paabb.minZ || _rayAABB.minZ > paabb.maxZ) continue;
|
||||
if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
|
||||
var t:Number = primitive.getRayIntersection(origin, dir, threshold, _n);
|
||||
if (t > 0 && t < minTime) {
|
||||
minTime = t;
|
||||
result.primitive = primitive;
|
||||
result.normal.x = _n.x;
|
||||
result.normal.y = _n.y;
|
||||
result.normal.z = _n.z;
|
||||
}
|
||||
}
|
||||
if (minTime > maxTime) return false;
|
||||
result.pos.x = origin.x + dir.x*minTime;
|
||||
result.pos.y = origin.y + dir.y*minTime;
|
||||
result.pos.z = origin.z + dir.z*minTime;
|
||||
result.t = minTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет точки пересечения луча с AABB.
|
||||
*
|
||||
* @param origin точка начала луча
|
||||
* @param dir направляющий вектор луча. Вектор может иметь любую отличную от нуля длину.
|
||||
* @param bb AABB, с которым пересекается луч
|
||||
* @param time возвращаемое значение. В эту переменную записывается минимальное и максимальное время пересечения
|
||||
* @return true в случае наличия хотя бы одного пересечения, иначе false
|
||||
*/
|
||||
private function getRayBoundBoxIntersection(origin:Vector3, dir:Vector3, bb:BoundBox, time:MinMax):Boolean {
|
||||
time.min = -1;
|
||||
time.max = 1e308;
|
||||
var t1:Number;
|
||||
var t2:Number;
|
||||
// Цикл по осям бокса
|
||||
for (var i:int = 0; i < 3; i++) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
if (dir.x < threshold && dir.x > -threshold) {
|
||||
if (origin.x < bb.minX || origin.x > bb.maxX) return false;
|
||||
else continue;
|
||||
}
|
||||
t1 = (bb.minX - origin.x)/dir.x;
|
||||
t2 = (bb.maxX - origin.x)/dir.x;
|
||||
break;
|
||||
case 1:
|
||||
if (dir.y < threshold && dir.y > -threshold) {
|
||||
if (origin.y < bb.minY || origin.y > bb.maxY) return false;
|
||||
else continue;
|
||||
}
|
||||
t1 = (bb.minY - origin.y)/dir.y;
|
||||
t2 = (bb.maxY - origin.y)/dir.y;
|
||||
break;
|
||||
case 2:
|
||||
if (dir.z < threshold && dir.z > -threshold) {
|
||||
if (origin.z < bb.minZ || origin.z > bb.maxZ) return false;
|
||||
else continue;
|
||||
}
|
||||
t1 = (bb.minZ - origin.z)/dir.z;
|
||||
t2 = (bb.maxZ - origin.z)/dir.z;
|
||||
break;
|
||||
}
|
||||
if (t1 < t2) {
|
||||
if (t1 > time.min) time.min = t1;
|
||||
if (t2 < time.max) time.max = t2;
|
||||
} else {
|
||||
if (t2 > time.min) time.min = t2;
|
||||
if (t1 < time.max) time.max = t1;
|
||||
}
|
||||
if (time.max < time.min) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @param origin
|
||||
* @param dir
|
||||
* @param collisionGroup
|
||||
* @param t1 время входа луча в узел
|
||||
* @param t2 время выхода луча из узла
|
||||
* @param intersection
|
||||
*/
|
||||
private function testRayAgainstNode(node:CollisionKdNode, origin:Vector3, localOrigin:Vector3, dir:Vector3, collisionGroup:int, t1:Number, t2:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
|
||||
// При наличии в узле объектов, проверяем пересечение с ними
|
||||
if (node.indices != null && getRayNodeIntersection(origin, dir, collisionGroup, tree.staticChildren, node.indices, predicate, result)) return true;
|
||||
// Выход из функции если это конечный узел
|
||||
if (node.axis == -1) return false;
|
||||
|
||||
// Определение времени пересечения луча и плоскости разделения узла
|
||||
var splitTime:Number;
|
||||
var currChildNode:CollisionKdNode;
|
||||
switch (node.axis) {
|
||||
case 0:
|
||||
if (dir.x > -threshold && dir.x < threshold) splitTime = t2 + 1;
|
||||
else splitTime = (node.coord - origin.x)/dir.x;
|
||||
currChildNode = localOrigin.x < node.coord ? node.negativeNode : node.positiveNode;
|
||||
break;
|
||||
case 1:
|
||||
if (dir.y > -threshold && dir.y < threshold) splitTime = t2 + 1;
|
||||
else splitTime = (node.coord - origin.y)/dir.y;
|
||||
currChildNode = localOrigin.y < node.coord ? node.negativeNode : node.positiveNode;
|
||||
break;
|
||||
case 2:
|
||||
if (dir.z > -threshold && dir.z < threshold) splitTime = t2 + 1;
|
||||
else splitTime = (node.coord - origin.z)/dir.z;
|
||||
currChildNode = localOrigin.z < node.coord ? node.negativeNode : node.positiveNode;
|
||||
break;
|
||||
}
|
||||
// Определение порядка проверки
|
||||
if (splitTime < t1 || splitTime > t2) {
|
||||
// Луч не переходит в соседний дочерний узел
|
||||
return testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, t2, predicate, result);
|
||||
} else {
|
||||
// Луч переходит из одного дочернего узла в другой
|
||||
var intersects:Boolean = testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, splitTime, predicate, result);
|
||||
if (intersects) return true;
|
||||
_o.x = origin.x + splitTime*dir.x;
|
||||
_o.y = origin.y + splitTime*dir.y;
|
||||
_o.z = origin.z + splitTime*dir.z;
|
||||
return testRayAgainstNode(currChildNode == node.negativeNode ? node.positiveNode : node.negativeNode, origin, _o, dir, collisionGroup, splitTime, t2, predicate, result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param origin
|
||||
* @param dir
|
||||
* @param collisionGroup
|
||||
* @param primitives
|
||||
* @param indices
|
||||
* @param intersection
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
private function getRayNodeIntersection(origin:Vector3, dir:Vector3, collisionGroup:int, primitives:Vector.<CollisionPrimitive>, indices:Vector.<int>, predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
|
||||
var pnum:int = indices.length;
|
||||
var minTime:Number = 1e308;
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
var primitive:CollisionPrimitive = primitives[indices[i]];
|
||||
if ((primitive.collisionGroup & collisionGroup) == 0) continue;
|
||||
if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
|
||||
var t:Number = primitive.getRayIntersection(origin, dir, threshold, _n);
|
||||
if (t > 0 && t < minTime) {
|
||||
minTime = t;
|
||||
intersection.primitive = primitive;
|
||||
intersection.normal.x = _n.x;
|
||||
intersection.normal.y = _n.y;
|
||||
intersection.normal.z = _n.z;
|
||||
}
|
||||
}
|
||||
if (minTime == 1e308) return false;
|
||||
intersection.pos.x = origin.x + dir.x*minTime;
|
||||
intersection.pos.y = origin.y + dir.y*minTime;
|
||||
intersection.pos.z = origin.z + dir.z*minTime;
|
||||
intersection.t = minTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class MinMax {
|
||||
public var min:Number = 0;
|
||||
public var max:Number = 0;
|
||||
}
|
||||
18
0.0.9.2/src/alternativa/physics/collision/CollisionKdNode.as
Normal file
18
0.0.9.2/src/alternativa/physics/collision/CollisionKdNode.as
Normal file
@@ -0,0 +1,18 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
|
||||
public class CollisionKdNode {
|
||||
public var indices:Vector.<int>;
|
||||
public var splitIndices:Vector.<int>;
|
||||
public var boundBox:BoundBox;
|
||||
public var parent:CollisionKdNode;
|
||||
public var splitTree:CollisionKdTree2D;
|
||||
|
||||
public var axis:int = -1; // 0 - x, 1 - y, 2 - z
|
||||
public var coord:Number;
|
||||
public var positiveNode:CollisionKdNode;
|
||||
public var negativeNode:CollisionKdNode;
|
||||
}
|
||||
}
|
||||
261
0.0.9.2/src/alternativa/physics/collision/CollisionKdTree.as
Normal file
261
0.0.9.2/src/alternativa/physics/collision/CollisionKdTree.as
Normal file
@@ -0,0 +1,261 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
|
||||
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
|
||||
/**
|
||||
* @author mike
|
||||
*/
|
||||
public class CollisionKdTree {
|
||||
|
||||
public var threshold:Number = 0.1;
|
||||
public var minPrimitivesPerNode:int = 1;
|
||||
public var rootNode:CollisionKdNode;
|
||||
public var staticChildren:Vector.<CollisionPrimitive>;
|
||||
public var numStaticChildren:int;
|
||||
public var staticBoundBoxes:Vector.<BoundBox> = new Vector.<BoundBox>();
|
||||
|
||||
private var splitAxis:int;
|
||||
private var splitCoord:Number;
|
||||
private var splitCost:Number;
|
||||
|
||||
private static const nodeBoundBoxThreshold:BoundBox = new BoundBox();
|
||||
private static const splitCoordsX:Vector.<Number> = new Vector.<Number>();
|
||||
private static const splitCoordsY:Vector.<Number> = new Vector.<Number>();
|
||||
private static const splitCoordsZ:Vector.<Number> = new Vector.<Number>();
|
||||
private static const _nodeBB:Vector.<Number> = new Vector.<Number>(6);
|
||||
private static const _bb:Vector.<Number> = new Vector.<Number>(6);
|
||||
|
||||
/**
|
||||
* @param boundBox
|
||||
*/
|
||||
public function createTree(collisionPrimitives:Vector.<CollisionPrimitive>, boundBox:BoundBox = null):void {
|
||||
staticChildren = collisionPrimitives.concat();
|
||||
numStaticChildren = staticChildren.length;
|
||||
// Создаём корневую ноду
|
||||
rootNode = new CollisionKdNode();
|
||||
rootNode.indices = new Vector.<int>();
|
||||
// Расчитываем баунды объектов и рутовой ноды
|
||||
var rootNodeBoundBox:BoundBox = rootNode.boundBox = (boundBox != null) ? boundBox : new BoundBox();
|
||||
for (var i:int = 0; i < numStaticChildren; ++i) {
|
||||
var child:CollisionPrimitive = staticChildren[i];
|
||||
var childBoundBox:BoundBox = staticBoundBoxes[i] = child.calculateAABB();
|
||||
rootNodeBoundBox.addBoundBox(childBoundBox);
|
||||
rootNode.indices[i] = i;
|
||||
}
|
||||
staticBoundBoxes.length = numStaticChildren;
|
||||
// Разделяем рутовую ноду
|
||||
splitNode(rootNode);
|
||||
|
||||
splitCoordsX.length = splitCoordsY.length = splitCoordsZ.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node
|
||||
*/
|
||||
private function splitNode(node:CollisionKdNode):void {
|
||||
var indices:Vector.<int> = node.indices;
|
||||
var numPrimitives:int = indices.length;
|
||||
if (numPrimitives <= minPrimitivesPerNode) return;
|
||||
|
||||
// Подготовка баунда с погрешностями
|
||||
var nodeBoundBox:BoundBox = node.boundBox;
|
||||
nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
|
||||
nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
|
||||
nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
|
||||
nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
|
||||
nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
|
||||
nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
|
||||
var doubleThreshold:Number = threshold*2;
|
||||
|
||||
// Собираем опорные координаты
|
||||
var i:int;
|
||||
var j:int;
|
||||
var numSplitCoordsX:int = 0
|
||||
var numSplitCoordsY:int = 0;
|
||||
var numSplitCoordsZ:int = 0;
|
||||
for (i = 0; i < numPrimitives; ++i) {
|
||||
var boundBox:BoundBox = staticBoundBoxes[indices[i]];
|
||||
|
||||
if (boundBox.maxX - boundBox.minX <= doubleThreshold) {
|
||||
if (boundBox.minX <= nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = nodeBoundBox.minX;
|
||||
else if (boundBox.maxX >= nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = nodeBoundBox.maxX;
|
||||
else splitCoordsX[numSplitCoordsX++] = (boundBox.minX + boundBox.maxX)*0.5;
|
||||
} else {
|
||||
if (boundBox.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = boundBox.minX;
|
||||
if (boundBox.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = boundBox.maxX;
|
||||
}
|
||||
|
||||
if (boundBox.maxY - boundBox.minY <= doubleThreshold) {
|
||||
if (boundBox.minY <= nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = nodeBoundBox.minY;
|
||||
else if (boundBox.maxY >= nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = nodeBoundBox.maxY;
|
||||
else splitCoordsY[numSplitCoordsY++] = (boundBox.minY + boundBox.maxY)*0.5;
|
||||
} else {
|
||||
if (boundBox.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = boundBox.minY;
|
||||
if (boundBox.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = boundBox.maxY;
|
||||
}
|
||||
|
||||
if (boundBox.maxZ - boundBox.minZ <= doubleThreshold) {
|
||||
if (boundBox.minZ <= nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = nodeBoundBox.minZ;
|
||||
else if (boundBox.maxZ >= nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = nodeBoundBox.maxZ;
|
||||
else splitCoordsZ[numSplitCoordsZ++] = (boundBox.minZ + boundBox.maxZ)*0.5;
|
||||
} else {
|
||||
if (boundBox.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.minZ;
|
||||
if (boundBox.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = boundBox.maxZ;
|
||||
}
|
||||
}
|
||||
|
||||
// Поиск наилучшего сплита
|
||||
splitAxis = -1;
|
||||
splitCost = 1e308;
|
||||
_nodeBB[0] = nodeBoundBox.minX;
|
||||
_nodeBB[1] = nodeBoundBox.minY;
|
||||
_nodeBB[2] = nodeBoundBox.minZ;
|
||||
_nodeBB[3] = nodeBoundBox.maxX;
|
||||
_nodeBB[4] = nodeBoundBox.maxY;
|
||||
_nodeBB[5] = nodeBoundBox.maxZ;
|
||||
checkNodeAxis(node, 0, numSplitCoordsX, splitCoordsX, _nodeBB);
|
||||
checkNodeAxis(node, 1, numSplitCoordsY, splitCoordsY, _nodeBB);
|
||||
checkNodeAxis(node, 2, numSplitCoordsZ, splitCoordsZ, _nodeBB);
|
||||
|
||||
// Если сплит не найден, выходим
|
||||
if (splitAxis < 0) return;
|
||||
|
||||
// Сплиттер найден. Разделение узла.
|
||||
var axisX:Boolean = splitAxis == 0
|
||||
var axisY:Boolean = splitAxis == 1;
|
||||
node.axis = splitAxis;
|
||||
node.coord = splitCoord;
|
||||
// Создаём дочерние ноды
|
||||
node.negativeNode = new CollisionKdNode();
|
||||
node.negativeNode.parent = node;
|
||||
node.negativeNode.boundBox = nodeBoundBox.clone();
|
||||
node.positiveNode = new CollisionKdNode();
|
||||
node.positiveNode.parent = node;
|
||||
node.positiveNode.boundBox = nodeBoundBox.clone();
|
||||
if (axisX) node.negativeNode.boundBox.maxX = node.positiveNode.boundBox.minX = splitCoord;
|
||||
else if (axisY) node.negativeNode.boundBox.maxY = node.positiveNode.boundBox.minY = splitCoord;
|
||||
else node.negativeNode.boundBox.maxZ = node.positiveNode.boundBox.minZ = splitCoord;
|
||||
|
||||
// Распределяем объекты по дочерним нодам
|
||||
var coordMin:Number = splitCoord - threshold;
|
||||
var coordMax:Number = splitCoord + threshold;
|
||||
for (i = 0; i < numPrimitives; ++i) {
|
||||
boundBox = staticBoundBoxes[indices[i]];
|
||||
var min:Number = axisX ? boundBox.minX : (axisY ? boundBox.minY : boundBox.minZ);
|
||||
var max:Number = axisX ? boundBox.maxX : (axisY ? boundBox.maxY : boundBox.maxZ);
|
||||
if (max <= coordMax) {
|
||||
if (min < coordMin) {
|
||||
// Объект в негативной стороне
|
||||
if (node.negativeNode.indices == null) node.negativeNode.indices = new Vector.<int>();
|
||||
node.negativeNode.indices.push(indices[i]);
|
||||
indices[i] = -1;
|
||||
} else {
|
||||
if (node.splitIndices == null) node.splitIndices = new Vector.<int>();
|
||||
node.splitIndices.push(indices[i]);
|
||||
indices[i] = -1;
|
||||
}
|
||||
} else {
|
||||
if (min >= coordMin) {
|
||||
// Объект в положительной стороне
|
||||
if (node.positiveNode.indices == null) node.positiveNode.indices = new Vector.<int>();
|
||||
node.positiveNode.indices.push(indices[i]);
|
||||
indices[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Очистка списка объектов
|
||||
for (i = 0, j = 0; i < numPrimitives; ++i) {
|
||||
if (indices[i] >= 0) indices[j++] = indices[i];
|
||||
}
|
||||
if (j > 0) indices.length = j;
|
||||
else node.indices = null;
|
||||
|
||||
if (node.splitIndices != null) {
|
||||
node.splitTree = new CollisionKdTree2D(this, node);
|
||||
node.splitTree.createTree();
|
||||
}
|
||||
|
||||
// Разделение дочерних нод
|
||||
if (node.negativeNode.indices != null) splitNode(node.negativeNode);
|
||||
if (node.positiveNode.indices != null) splitNode(node.positiveNode);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @param axis
|
||||
* @param numSplitCoords
|
||||
* @param splitCoords
|
||||
* @param bb
|
||||
*/
|
||||
private function checkNodeAxis(node:CollisionKdNode, axis:int, numSplitCoords:int, splitCoords:Vector.<Number>, bb:Vector.<Number>):void {
|
||||
var axis1:int = (axis + 1)%3;
|
||||
var axis2:int = (axis + 2)%3;
|
||||
var area:Number = (bb[axis1 + 3] - bb[axis1])*(bb[axis2 + 3] - bb[axis2]);
|
||||
for (var i:int = 0; i < numSplitCoords; ++i) {
|
||||
var currSplitCoord:Number = splitCoords[i];
|
||||
if (isNaN(currSplitCoord)) continue;
|
||||
var minCoord:Number = currSplitCoord - threshold;
|
||||
var maxCoord:Number = currSplitCoord + threshold;
|
||||
var areaNegative:Number = area*(currSplitCoord - bb[axis]);
|
||||
var areaPositive:Number = area*(bb[int(axis + 3)] - currSplitCoord);
|
||||
var numNegative:int = 0;
|
||||
var numPositive:int = 0;
|
||||
var conflict:Boolean = false;
|
||||
// Проверяем объекты
|
||||
var numObjects:int = node.indices.length;
|
||||
for (var j:int = 0; j < numObjects; j++) {
|
||||
var boundBox:BoundBox = staticBoundBoxes[node.indices[j]];
|
||||
_bb[0] = boundBox.minX;
|
||||
_bb[1] = boundBox.minY;
|
||||
_bb[2] = boundBox.minZ;
|
||||
_bb[3] = boundBox.maxX;
|
||||
_bb[4] = boundBox.maxY;
|
||||
_bb[5] = boundBox.maxZ;
|
||||
if (_bb[axis + 3] <= maxCoord) {
|
||||
if (_bb[axis] < minCoord) numNegative++;
|
||||
} else {
|
||||
if (_bb[axis] >= minCoord) {
|
||||
numPositive++;
|
||||
} else {
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
var cost:Number = areaNegative*numNegative + areaPositive*numPositive;
|
||||
if (!conflict && cost < splitCost) {
|
||||
splitAxis = axis;
|
||||
splitCost = cost;
|
||||
splitCoord = currSplitCoord;
|
||||
}
|
||||
for (j = i + 1; j < numSplitCoords; ++j) {
|
||||
if (splitCoords[j] >= currSplitCoord - threshold && splitCoords[j] <= currSplitCoord + threshold) splitCoords[j] = NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function traceTree():void {
|
||||
traceNode("", rootNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param str
|
||||
* @param node
|
||||
*/
|
||||
private function traceNode(str:String, node:CollisionKdNode):void {
|
||||
if (node == null) return;
|
||||
trace(str, node.axis == -1 ? "end" : ((node.axis == 0) ? "X" : ((node.axis == 1) ? "Y" : "Z")), "splitCoord=" + splitCoord, "bound", node.boundBox, "objs:", node.indices);
|
||||
traceNode(str + "-", node.negativeNode);
|
||||
traceNode(str + "+", node.positiveNode);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
226
0.0.9.2/src/alternativa/physics/collision/CollisionKdTree2D.as
Normal file
226
0.0.9.2/src/alternativa/physics/collision/CollisionKdTree2D.as
Normal file
@@ -0,0 +1,226 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class CollisionKdTree2D {
|
||||
|
||||
public var threshold:Number = 0.1;
|
||||
public var minPrimitivesPerNode:int = 1;
|
||||
|
||||
public var parentTree:CollisionKdTree;
|
||||
public var parentNode:CollisionKdNode;
|
||||
public var rootNode:CollisionKdNode;
|
||||
|
||||
private var splitAxis:int;
|
||||
private var splitCost:Number;
|
||||
private var splitCoord:Number;
|
||||
|
||||
private static const nodeBoundBoxThreshold:BoundBox = new BoundBox();
|
||||
private static const splitCoordsX:Vector.<Number> = new Vector.<Number>();
|
||||
private static const splitCoordsY:Vector.<Number> = new Vector.<Number>();
|
||||
private static const splitCoordsZ:Vector.<Number> = new Vector.<Number>();
|
||||
private static const _nodeBB:Vector.<Number> = new Vector.<Number>(6);
|
||||
private static const _bb:Vector.<Number> = new Vector.<Number>(6);
|
||||
/**
|
||||
*
|
||||
* @param parentTree
|
||||
* @param parentNode
|
||||
*/
|
||||
public function CollisionKdTree2D(parentTree:CollisionKdTree, parentNode:CollisionKdNode) {
|
||||
this.parentTree = parentTree;
|
||||
this.parentNode = parentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function createTree():void {
|
||||
rootNode = new CollisionKdNode();
|
||||
rootNode.boundBox = parentNode.boundBox.clone();
|
||||
rootNode.indices = new Vector.<int>();
|
||||
var numObjects:int = parentNode.splitIndices.length;
|
||||
for (var i:int = 0; i < numObjects; ++i) rootNode.indices[i] = parentNode.splitIndices[i];
|
||||
|
||||
splitNode(rootNode);
|
||||
splitCoordsX.length = splitCoordsY.length = splitCoordsZ.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node
|
||||
*/
|
||||
private function splitNode(node:CollisionKdNode):void {
|
||||
if (node.indices.length <= minPrimitivesPerNode) return;
|
||||
|
||||
var objects:Vector.<int> = node.indices;
|
||||
var i:int;
|
||||
var j:int;
|
||||
|
||||
var nodeBoundBox:BoundBox = node.boundBox;
|
||||
|
||||
// Подготовка баунда с погрешностями
|
||||
nodeBoundBoxThreshold.minX = nodeBoundBox.minX + threshold;
|
||||
nodeBoundBoxThreshold.minY = nodeBoundBox.minY + threshold;
|
||||
nodeBoundBoxThreshold.minZ = nodeBoundBox.minZ + threshold;
|
||||
nodeBoundBoxThreshold.maxX = nodeBoundBox.maxX - threshold;
|
||||
nodeBoundBoxThreshold.maxY = nodeBoundBox.maxY - threshold;
|
||||
nodeBoundBoxThreshold.maxZ = nodeBoundBox.maxZ - threshold;
|
||||
var doubleThreshold:Number = threshold*2;
|
||||
|
||||
var staticBoundBoxes:Vector.<BoundBox> = parentTree.staticBoundBoxes;
|
||||
// Собираем опорные координаты
|
||||
var numSplitCoordsX:int;
|
||||
var numSplitCoordsY:int;
|
||||
var numSplitCoordsZ:int;
|
||||
var numObjects:int = objects.length;
|
||||
for (i = 0; i < numObjects; ++i) {
|
||||
var bb:BoundBox = staticBoundBoxes[objects[i]];
|
||||
|
||||
if (parentNode.axis != 0) {
|
||||
if (bb.minX > nodeBoundBoxThreshold.minX) splitCoordsX[numSplitCoordsX++] = bb.minX;
|
||||
if (bb.maxX < nodeBoundBoxThreshold.maxX) splitCoordsX[numSplitCoordsX++] = bb.maxX;
|
||||
}
|
||||
|
||||
if (parentNode.axis != 1) {
|
||||
if (bb.minY > nodeBoundBoxThreshold.minY) splitCoordsY[numSplitCoordsY++] = bb.minY;
|
||||
if (bb.maxY < nodeBoundBoxThreshold.maxY) splitCoordsY[numSplitCoordsY++] = bb.maxY;
|
||||
}
|
||||
|
||||
if (parentNode.axis != 2) {
|
||||
if (bb.minZ > nodeBoundBoxThreshold.minZ) splitCoordsZ[numSplitCoordsZ++] = bb.minZ;
|
||||
if (bb.maxZ < nodeBoundBoxThreshold.maxZ) splitCoordsZ[numSplitCoordsZ++] = bb.maxZ;
|
||||
}
|
||||
}
|
||||
|
||||
// Поиск наилучшего сплита
|
||||
splitAxis = -1;
|
||||
splitCost = 1e308;
|
||||
_nodeBB[0] = nodeBoundBox.minX;
|
||||
_nodeBB[1] = nodeBoundBox.minY;
|
||||
_nodeBB[2] = nodeBoundBox.minZ;
|
||||
_nodeBB[3] = nodeBoundBox.maxX;
|
||||
_nodeBB[4] = nodeBoundBox.maxY;
|
||||
_nodeBB[5] = nodeBoundBox.maxZ;
|
||||
if (parentNode.axis != 0) checkNodeAxis(node, 0, numSplitCoordsX, splitCoordsX, _nodeBB);
|
||||
if (parentNode.axis != 1) checkNodeAxis(node, 1, numSplitCoordsY, splitCoordsY, _nodeBB);
|
||||
if (parentNode.axis != 2) checkNodeAxis(node, 2, numSplitCoordsZ, splitCoordsZ, _nodeBB);
|
||||
|
||||
// Если сплит не найден, выходим
|
||||
if (splitAxis < 0) return;
|
||||
|
||||
// Сплиттер найден. Разделение узла.
|
||||
var axisX:Boolean = splitAxis == 0
|
||||
var axisY:Boolean = splitAxis == 1;
|
||||
node.axis = splitAxis;
|
||||
node.coord = splitCoord;
|
||||
// Создаём дочерние ноды
|
||||
node.negativeNode = new CollisionKdNode();
|
||||
node.negativeNode.parent = node;
|
||||
node.negativeNode.boundBox = nodeBoundBox.clone();
|
||||
node.positiveNode = new CollisionKdNode();
|
||||
node.positiveNode.parent = node;
|
||||
node.positiveNode.boundBox = nodeBoundBox.clone();
|
||||
if (axisX) node.negativeNode.boundBox.maxX = node.positiveNode.boundBox.minX = splitCoord;
|
||||
else if (axisY) node.negativeNode.boundBox.maxY = node.positiveNode.boundBox.minY = splitCoord;
|
||||
else node.negativeNode.boundBox.maxZ = node.positiveNode.boundBox.minZ = splitCoord;
|
||||
|
||||
// Распределяем объекты по дочерним нодам
|
||||
var coordMin:Number = splitCoord - threshold;
|
||||
var coordMax:Number = splitCoord + threshold;
|
||||
for (i = 0; i < numObjects; ++i) {
|
||||
bb = staticBoundBoxes[objects[i]];
|
||||
var min:Number = axisX ? bb.minX : (axisY ? bb.minY : bb.minZ);
|
||||
var max:Number = axisX ? bb.maxX : (axisY ? bb.maxY : bb.maxZ);
|
||||
if (max <= coordMax) {
|
||||
if (min < coordMin) {
|
||||
// Объект в негативной стороне
|
||||
if (node.negativeNode.indices == null) node.negativeNode.indices = new Vector.<int>();
|
||||
node.negativeNode.indices.push(objects[i]);
|
||||
objects[i] = -1;
|
||||
}
|
||||
} else {
|
||||
if (min >= coordMin) {
|
||||
if (max > coordMax) {
|
||||
// Объект в положительной стороне
|
||||
if (node.positiveNode.indices == null) node.positiveNode.indices = new Vector.<int>();
|
||||
node.positiveNode.indices.push(objects[i]);
|
||||
objects[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Очистка списка объектов
|
||||
for (i = 0, j = 0; i < numObjects; ++i) {
|
||||
if (objects[i] >= 0) objects[j++] = objects[i];
|
||||
}
|
||||
if (j > 0) objects.length = j;
|
||||
else node.indices = null;
|
||||
|
||||
// Разделение дочерних нод
|
||||
if (node.negativeNode.indices != null) splitNode(node.negativeNode);
|
||||
if (node.positiveNode.indices != null) splitNode(node.positiveNode);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @param axis
|
||||
* @param numSplitCoords
|
||||
* @param splitCoords
|
||||
* @param bb
|
||||
*/
|
||||
private function checkNodeAxis(node:CollisionKdNode, axis:int, numSplitCoords:int, splitCoords:Vector.<Number>, bb:Vector.<Number>):void {
|
||||
var axis1:int = (axis + 1)%3;
|
||||
var axis2:int = (axis + 2)%3;
|
||||
var area:Number = (bb[axis1 + 3] - bb[axis1])*(bb[axis2 + 3] - bb[axis2]);
|
||||
var staticBoundBoxes:Vector.<BoundBox> = parentTree.staticBoundBoxes;
|
||||
for (var i:int = 0; i < numSplitCoords; ++i) {
|
||||
var currSplitCoord:Number = splitCoords[i];
|
||||
if (isNaN(currSplitCoord)) continue;
|
||||
var minCoord:Number = currSplitCoord - threshold;
|
||||
var maxCoord:Number = currSplitCoord + threshold;
|
||||
var areaNegative:Number = area*(currSplitCoord - bb[axis]);
|
||||
var areaPositive:Number = area*(bb[int(axis + 3)] - currSplitCoord);
|
||||
var numNegative:int = 0;
|
||||
var numPositive:int = 0;
|
||||
var conflict:Boolean = false;
|
||||
// Проверяем объекты
|
||||
var numObjects:int = node.indices.length;
|
||||
for (var j:int = 0; j < numObjects; j++) {
|
||||
var boundBox:BoundBox = staticBoundBoxes[node.indices[j]];
|
||||
_bb[0] = boundBox.minX;
|
||||
_bb[1] = boundBox.minY;
|
||||
_bb[2] = boundBox.minZ;
|
||||
_bb[3] = boundBox.maxX;
|
||||
_bb[4] = boundBox.maxY;
|
||||
_bb[5] = boundBox.maxZ;
|
||||
if (_bb[axis + 3] <= maxCoord) {
|
||||
if (_bb[axis] < minCoord) numNegative++;
|
||||
} else {
|
||||
if (_bb[axis] >= minCoord) {
|
||||
numPositive++;
|
||||
} else {
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Если хороший сплит, сохраняем
|
||||
var cost:Number = areaNegative*numNegative + areaPositive*numPositive;
|
||||
if (!conflict && cost < splitCost) {
|
||||
splitAxis = axis;
|
||||
splitCost = cost;
|
||||
splitCoord = currSplitCoord;
|
||||
}
|
||||
for (j = i + 1; j < numSplitCoords; ++j) {
|
||||
if (splitCoords[j] >= currSplitCoord - threshold && splitCoords[j] <= currSplitCoord + threshold) splitCoords[j] = NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
140
0.0.9.2/src/alternativa/physics/collision/CollisionPrimitive.as
Normal file
140
0.0.9.2/src/alternativa/physics/collision/CollisionPrimitive.as
Normal file
@@ -0,0 +1,140 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
import alternativa.physics.Body;
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
import alternativa.math.Matrix4;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
/**
|
||||
* Базовый класс для примитивов, использующихся детектором столкновений.
|
||||
*/
|
||||
public class CollisionPrimitive {
|
||||
// Константы типов примитива
|
||||
public static const BOX:int = 1;
|
||||
public static const PLANE:int = 2;
|
||||
public static const SPHERE:int = 3;
|
||||
public static const RECT:int = 4;
|
||||
public static const TRIANGLE:int = 5;
|
||||
|
||||
// Тип примитива
|
||||
public var type:int;
|
||||
// Группы примитива. Каждая группа определяется установленным битом. Столкновения проверяются только для примитивов,
|
||||
// имеющих хотя бы одну общую группу.
|
||||
public var collisionGroup:int;
|
||||
// Предикат примитива, вызывающийся детектором при нахождении столкновения. В зависимости от возвращённого
|
||||
// предикатом результата столкновение либо регистрируется, либо игнорируется.
|
||||
// Nullable
|
||||
public var postCollisionPredicate:ICollisionPredicate;
|
||||
// Тело, владеющее примитивом. Поле сделано открытым для быстрого доступа на чтение. Установка значения должна выполняться вызовом метода setBody().
|
||||
// Nullable
|
||||
public var body:Body;
|
||||
// Трансформация примитива в системе координат тела, если оно указано. Не допускается масштабирование матрицы.
|
||||
public var localTransform:Matrix4;
|
||||
// Полная трансформация примитива. Не допускается масштабирование матрицы.
|
||||
public var transform:Matrix4 = new Matrix4();
|
||||
// AABB в мировой системе координат. Расчитывается системой вызовом функции calculateBoundBox().
|
||||
public var aabb:BoundBox = new BoundBox();
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр примитива.
|
||||
*
|
||||
* @param type тип примитива
|
||||
* @param collisionGroup группа примитива
|
||||
*/
|
||||
public function CollisionPrimitive(type:int, collisionGroup:int) {
|
||||
this.type = type;
|
||||
this.collisionGroup = collisionGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Устанавливает тело, владеющее примитивом.
|
||||
*
|
||||
* @param body тело, которое владеет примитивом
|
||||
* @param localTransform трансформация примитива в системе координат тела. Указание значения null равносильно
|
||||
* заданию единичной матрицы, однако в первом случае не будет дополнительного умножения матриц при вычислении полной трансформации примитива.
|
||||
*/
|
||||
public function setBody(body:Body, localTransform:Matrix4 = null):void {
|
||||
if (this.body == body) return;
|
||||
this.body = body;
|
||||
if (body != null) {
|
||||
if (localTransform != null) {
|
||||
if (this.localTransform == null) {
|
||||
this.localTransform = new Matrix4();
|
||||
}
|
||||
this.localTransform.copy(localTransform);
|
||||
} else {
|
||||
this.localTransform = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Рассчитывает AABB примитива. Наследники должны переопределять этот метод, реализуя в нём корректный рассчёт.
|
||||
*
|
||||
* @return ссылка на свой AABB
|
||||
*/
|
||||
public function calculateAABB():BoundBox {
|
||||
return aabb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет пересечение луча с примитивом.
|
||||
*
|
||||
* @param origin начальная точка луча в мировых координатах
|
||||
* @param vector направляющий вектор луча в мировых координатах. Вектор может быть любой отличной от нуля длины.
|
||||
* @param epsilon погрешность измерения расстояния. Величина, не превышающая по абсолютному значению указанную погрешность, считается равной нулю.
|
||||
* @param normal если пересечение существует, в этот параметр записывается нормаль к примитиву в точке пересечения
|
||||
* @return если пересечение существует, возвращается неотрицательное время точки пересечения, в противном случае возвращается -1.
|
||||
*/
|
||||
public function getRayIntersection(origin:Vector3, vector:Vector3, epsilon:Number, normal:Vector3):Number {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Клонирует примитив. Переопределять не рекомендуется. Вместо этого переопределяются методы createPrimitive() и copyFrom().
|
||||
*
|
||||
* @return клон примитива
|
||||
*/
|
||||
public function clone():CollisionPrimitive {
|
||||
var p:CollisionPrimitive = createPrimitive();
|
||||
return p.copyFrom(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирует параметры указанного примитива. Объекты копируются по значению.
|
||||
*
|
||||
* @param source примитив, чьи параметры копируются
|
||||
* @return this
|
||||
*/
|
||||
public function copyFrom(source:CollisionPrimitive):CollisionPrimitive {
|
||||
if (source == null) {
|
||||
throw new ArgumentError("Parameter source cannot be null");
|
||||
}
|
||||
type = source.type;
|
||||
transform.copy(source.transform);
|
||||
collisionGroup = source.collisionGroup;
|
||||
setBody(source.body, source.localTransform);
|
||||
aabb.copyFrom(source.aabb);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт строковое представление объекта.
|
||||
*
|
||||
* @return строковое представление объекта
|
||||
*/
|
||||
public function toString():String {
|
||||
return "[CollisionPrimitive type=" + type + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт новый экземпляр примитива соответствующего типа.
|
||||
*
|
||||
* @return новый экземпляр примитива
|
||||
*/
|
||||
protected function createPrimitive():CollisionPrimitive {
|
||||
return new CollisionPrimitive(type, collisionGroup);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.Body;
|
||||
|
||||
public interface IBodyCollisionPredicate {
|
||||
function considerBodies(body1:Body, body2:Body):Boolean;
|
||||
}
|
||||
}
|
||||
29
0.0.9.2/src/alternativa/physics/collision/ICollider.as
Normal file
29
0.0.9.2/src/alternativa/physics/collision/ICollider.as
Normal file
@@ -0,0 +1,29 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
import alternativa.physics.Contact;
|
||||
|
||||
/**
|
||||
* Интерфейс определителя столкновений между двумя примитивами.
|
||||
*/
|
||||
public interface ICollider {
|
||||
|
||||
/**
|
||||
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
|
||||
|
||||
/**
|
||||
* Выполняет быстрый тест на наличие пересечения двух примитивов.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.Contact;
|
||||
import alternativa.physics.collision.types.RayIntersection;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
/**
|
||||
* Интерфейс детектора столкновений.
|
||||
*/
|
||||
public interface ICollisionDetector {
|
||||
|
||||
/**
|
||||
* Получает все столкновения в текущей конфигурации физической геометрии.
|
||||
*
|
||||
* @param contacts список контактов, в кторые будет записана информация о столкновении
|
||||
* @return количество найденных столкновений
|
||||
*/
|
||||
function getAllContacts(contacts:Contact):Contact;
|
||||
|
||||
/**
|
||||
* Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
|
||||
* В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча пересечение.
|
||||
*
|
||||
* @param origin начальная точка луча в мировых координатах
|
||||
* @param direction направляющий вектор луча в мировых координатах. Длина вектора должна быть отлична от нуля.
|
||||
* @param collisionGroup идентификатор группы
|
||||
* @param maxTime параметр, задающий длину проверяемого сегмента. Единица соответствует одной длине направлящего вектора.
|
||||
* @param predicate предикат, применяемый к столкновениям
|
||||
* @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
|
||||
* переданной структуре не гарантируется.
|
||||
* @return true в случае наличия пересечения, иначе false
|
||||
*/
|
||||
function intersectRay(origin:Vector3, direction:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param origin
|
||||
* @param direction
|
||||
* @param collisionGroup
|
||||
* @param maxTime
|
||||
* @param predicate
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
function intersectRayWithStatic(origin:Vector3, direction:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @param contact
|
||||
* @return
|
||||
*/
|
||||
function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @return
|
||||
*/
|
||||
function testCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
public interface ICollisionPredicate {
|
||||
|
||||
function considerCollision(primitive:CollisionPrimitive):Boolean;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package alternativa.physics.collision {
|
||||
import alternativa.physics.Body;
|
||||
|
||||
public interface IRayCollisionPredicate {
|
||||
function considerBody(body:Body):Boolean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
package alternativa.physics.collision {
|
||||
|
||||
|
||||
import alternativa.physics.Body;
|
||||
import alternativa.physics.Contact;
|
||||
import alternativa.physics.ContactPoint;
|
||||
import alternativa.physics.altphysics;
|
||||
import alternativa.physics.collision.colliders.BoxBoxCollider;
|
||||
import alternativa.physics.collision.colliders.BoxRectCollider;
|
||||
import alternativa.physics.collision.colliders.BoxSphereCollider;
|
||||
import alternativa.physics.collision.colliders.BoxTriangleCollider;
|
||||
import alternativa.physics.collision.colliders.SphereSphereCollider;
|
||||
import alternativa.physics.collision.types.BoundBox;
|
||||
import alternativa.physics.collision.types.RayIntersection;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
use namespace altphysics;
|
||||
|
||||
/**
|
||||
* Детектор, хранящий статическую геометрию в kD-дереве и использующий дерево для ускорения тестов на пересечения.
|
||||
*/
|
||||
public class KdTreeCollisionDetector implements ICollisionDetector {
|
||||
|
||||
altphysics var tree:CollisionKdTree;
|
||||
altphysics var dynamicPrimitives:Vector.<CollisionPrimitive>;
|
||||
altphysics var dynamicPrimitivesNum:int;
|
||||
altphysics var threshold:Number = 0.0001;
|
||||
private var colliders:Object = {};
|
||||
|
||||
private var _time:MinMax = new MinMax();
|
||||
private var _n:Vector3 = new Vector3();
|
||||
private var _o:Vector3 = new Vector3();
|
||||
private var _dynamicIntersection:RayIntersection = new RayIntersection();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function KdTreeCollisionDetector() {
|
||||
tree = new CollisionKdTree();
|
||||
dynamicPrimitives = new Vector.<CollisionPrimitive>();
|
||||
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.BOX, new BoxBoxCollider());
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.SPHERE, new BoxSphereCollider());
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.RECT, new BoxRectCollider());
|
||||
addCollider(CollisionPrimitive.BOX, CollisionPrimitive.TRIANGLE, new BoxTriangleCollider());
|
||||
// addCollider(CollisionPrimitive.BOX, CollisionPrimitive.PLANE, new BoxPlaneCollider());
|
||||
|
||||
// addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.PLANE, new SpherePlaneCollider());
|
||||
addCollider(CollisionPrimitive.SPHERE, CollisionPrimitive.SPHERE, new SphereSphereCollider());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param primitive
|
||||
* @param isStatic
|
||||
* @return
|
||||
*/
|
||||
public function addPrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
|
||||
// if (isStatic) tree.addStaticPrimitive(primitive);
|
||||
// else dynamicPrimitives[dynamicPrimitivesNum++] = primitive;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param primitive
|
||||
* @param isStatic
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public function removePrimitive(primitive:CollisionPrimitive, isStatic:Boolean = true):Boolean {
|
||||
// if (isStatic) return tree.removeStaticPrimitive(primitive);
|
||||
// var idx:int = dynamicPrimitives.indexOf(primitive);
|
||||
// if (idx < 0) return false;
|
||||
// dynamicPrimitives.splice(idx, 1);
|
||||
// dynamicPrimitivesNum--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function init(collisionPrimitives:Vector.<CollisionPrimitive>):void {
|
||||
tree.createTree(collisionPrimitives);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param contacts
|
||||
* @return
|
||||
*/
|
||||
public function getAllContacts(contacts:Contact):Contact {
|
||||
// for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
|
||||
// var primitive:CollisionPrimitive = dynamicPrimitives[i];
|
||||
// primitive.calculateAABB();
|
||||
// if (primitive.body != null && primitive.body.frozen) continue;
|
||||
// var contact:Contact = getPrimitiveNodeCollisions(tree.rootNode, primitive, contacts);
|
||||
//
|
||||
// // Столкновения динамических примитивов между собой
|
||||
// // TODO: Попробовать различные оптимизации (сортировка по баундам, встраивание в дерево)
|
||||
// for (var j:int = i + 1; j < dynamicPrimitivesNum; j++) {
|
||||
// if (getContact(primitive, dynamicPrimitives[j], contacts[colNum])) colNum++;
|
||||
// }
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @param contact
|
||||
* @return
|
||||
*/
|
||||
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
if ((prim1.collisionGroup & prim2.collisionGroup) == 0) return false;
|
||||
if (prim1.body != null && prim1.body == prim2.body) return false;
|
||||
if (!prim1.aabb.intersects(prim2.aabb, 0.01)) return false;
|
||||
var collider:ICollider = colliders[prim1.type <= prim2.type ? (prim1.type << 16) | prim2.type : (prim2.type << 16) | prim1.type] as ICollider;
|
||||
if (collider != null && collider.getContact(prim1, prim2, contact)) {
|
||||
if (prim1.postCollisionPredicate != null && !prim1.postCollisionPredicate.considerCollision(prim2)) return false;
|
||||
if (prim2.postCollisionPredicate != null && !prim2.postCollisionPredicate.considerCollision(prim1)) return false;
|
||||
// Сохраняем ссылку на контакт для каждого тела
|
||||
// if (prim1.body != null) prim1.body.contacts[prim1.body.contactsNum++] = contact;
|
||||
// if (prim2.body != null) prim2.body.contacts[prim2.body.contactsNum++] = contact;
|
||||
// // Вычисляем максимальную глубину пересечения для контакта
|
||||
// contact.maxPenetration = ContactPoint(contact.points[0]).penetration;
|
||||
// var pen:Number;
|
||||
// for (var i:int = contact.pcount - 1; i >= 1; i--) {
|
||||
// if ((pen = (contact.points[i] as ContactPoint).penetration) > contact.maxPenetration) contact.maxPenetration = pen;
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @param contact
|
||||
* @return
|
||||
*/
|
||||
public function testCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
|
||||
if ((prim1.collisionGroup & prim2.collisionGroup) == 0) return false;
|
||||
if (prim1.body != null && prim1.body == prim2.body) return false;
|
||||
if (!prim1.aabb.intersects(prim2.aabb, 0.01)) return false;
|
||||
var collider:ICollider = colliders[prim1.type <= prim2.type ? (prim1.type << 16) | prim2.type : (prim2.type << 16) | prim1.type] as ICollider;
|
||||
if (collider != null && collider.haveCollision(prim1, prim2)) {
|
||||
if (prim1.postCollisionPredicate != null && !prim1.postCollisionPredicate.considerCollision(prim2)) return false;
|
||||
if (prim2.postCollisionPredicate != null && !prim2.postCollisionPredicate.considerCollision(prim1)) return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Тестирует луч на пересечение с физической геометрией. Подразумевается, что детектор содержит набор примитивов, для которых выполняется проверка.
|
||||
* В случае наличия нескольких пересечений, метод должен возвращать ближайшее к началу луча.
|
||||
*
|
||||
* @param origin
|
||||
* @param dir
|
||||
* @param collisionGroup идентификатор группы
|
||||
* @param maxTime параметр, задающий длину проверяемого сегмента
|
||||
* @param predicate
|
||||
* @param result переменная для записи информации о столкновении в случае положительного теста. В случае отрицательного результата сохранность начальных данных в
|
||||
* переданной структуре не гарантируется.
|
||||
* @return true в случае наличия пересечения, иначе false
|
||||
*/
|
||||
public function intersectRay(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
|
||||
var hasStaticIntersection:Boolean = intersectRayWithStatic(origin, dir, collisionGroup, maxTime, predicate, result);
|
||||
var hasDynamicIntersection:Boolean = intersectRayWithDynamic(origin, dir, collisionGroup, maxTime, predicate, _dynamicIntersection);
|
||||
|
||||
if (!(hasDynamicIntersection || hasStaticIntersection)) return false;
|
||||
|
||||
if (hasDynamicIntersection && hasStaticIntersection) {
|
||||
if (result.t > _dynamicIntersection.t) result.copy(_dynamicIntersection);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasStaticIntersection) return true;
|
||||
|
||||
result.copy(_dynamicIntersection);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param origin
|
||||
* @param dir
|
||||
* @param collisionGroup
|
||||
* @param maxTime
|
||||
* @param predicate
|
||||
* @param result
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public function intersectRayWithStatic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
|
||||
// Вычисление точки пересечения с корневм узлом
|
||||
if (!getRayBoundBoxIntersection(origin, dir, tree.rootNode.boundBox, _time)) return false;
|
||||
if (_time.max < 0 || _time.min > maxTime) return false;
|
||||
|
||||
if (_time.min <= 0) {
|
||||
_time.min = 0;
|
||||
_o.x = origin.x;
|
||||
_o.y = origin.y;
|
||||
_o.z = origin.z;
|
||||
} else {
|
||||
_o.x = origin.x + _time.min*dir.x;
|
||||
_o.y = origin.y + _time.min*dir.y;
|
||||
_o.z = origin.z + _time.min*dir.z;
|
||||
}
|
||||
if (_time.max > maxTime) _time.max = maxTime;
|
||||
|
||||
var hasIntersection:Boolean = testRayAgainstNode(tree.rootNode, origin, _o, dir, collisionGroup, _time.min, _time.max, predicate, result);
|
||||
return hasIntersection ? result.t <= maxTime : false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body
|
||||
* @param primitive
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
public function testBodyPrimitiveCollision(body:Body, primitive:CollisionPrimitive):Boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param type1
|
||||
* @param type2
|
||||
* @param collider
|
||||
*/
|
||||
private function addCollider(type1:int, type2:int, collider:ICollider):void {
|
||||
colliders[type1 <= type2 ? (type1 << 16) | type2 : (type2 << 16) | type1] = collider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет поиск столкновений динамического примитива с примитивами узла дерева.
|
||||
*
|
||||
* @param node
|
||||
* @param primitive
|
||||
* @param contacts
|
||||
* @param startIndex
|
||||
* @return
|
||||
*/
|
||||
private function getPrimitiveNodeCollisions(node:CollisionKdNode, primitive:CollisionPrimitive, contacts:Contact):Contact {
|
||||
// var colNum:int = 0;
|
||||
// if (node.indices != null) {
|
||||
// // Поиск столкновений со статическими примитивами узла
|
||||
// var primitives:Vector.<CollisionPrimitive> = tree.staticChildren;
|
||||
// var indices:Vector.<int> = node.indices;
|
||||
// for (var i:int = indices.length - 1; i >= 0; i--)
|
||||
// if (getContact(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;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static var _rayAABB:BoundBox = new BoundBox();
|
||||
/**
|
||||
* Тест пересечения луча с динамическими примитивами.
|
||||
*
|
||||
* @param origin
|
||||
* @param dir
|
||||
* @param collisionGroup
|
||||
* @param maxTime
|
||||
* @param predicate
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
private function intersectRayWithDynamic(origin:Vector3, dir:Vector3, collisionGroup:int, maxTime:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
|
||||
var xx:Number = origin.x + dir.x*maxTime;
|
||||
var yy:Number = origin.y + dir.y*maxTime;
|
||||
var zz:Number = origin.z + dir.z*maxTime;
|
||||
if (xx < origin.x) {
|
||||
_rayAABB.minX = xx;
|
||||
_rayAABB.maxX = origin.x;
|
||||
} else {
|
||||
_rayAABB.minX = origin.x;
|
||||
_rayAABB.maxX = xx;
|
||||
}
|
||||
if (yy < origin.y) {
|
||||
_rayAABB.minY = yy;
|
||||
_rayAABB.maxY = origin.y;
|
||||
} else {
|
||||
_rayAABB.minY = origin.y;
|
||||
_rayAABB.maxY = yy;
|
||||
}
|
||||
if (zz < origin.z) {
|
||||
_rayAABB.minZ = zz;
|
||||
_rayAABB.maxZ = origin.z;
|
||||
} else {
|
||||
_rayAABB.minZ = origin.z;
|
||||
_rayAABB.maxZ = zz;
|
||||
}
|
||||
|
||||
var minTime:Number = maxTime + 1;
|
||||
for (var i:int = 0; i < dynamicPrimitivesNum; i++) {
|
||||
var primitive:CollisionPrimitive = dynamicPrimitives[i];
|
||||
if ((primitive.collisionGroup & collisionGroup) == 0) continue;
|
||||
var paabb:BoundBox = primitive.aabb;
|
||||
if (_rayAABB.maxX < paabb.minX || _rayAABB.minX > paabb.maxX || _rayAABB.maxY < paabb.minY || _rayAABB.minY > paabb.maxY || _rayAABB.maxZ < paabb.minZ || _rayAABB.minZ > paabb.maxZ) continue;
|
||||
if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
|
||||
var t:Number = primitive.getRayIntersection(origin, dir, threshold, _n);
|
||||
if (t > 0 && t < minTime) {
|
||||
minTime = t;
|
||||
result.primitive = primitive;
|
||||
result.normal.x = _n.x;
|
||||
result.normal.y = _n.y;
|
||||
result.normal.z = _n.z;
|
||||
}
|
||||
}
|
||||
if (minTime > maxTime) return false;
|
||||
result.pos.x = origin.x + dir.x*minTime;
|
||||
result.pos.y = origin.y + dir.y*minTime;
|
||||
result.pos.z = origin.z + dir.z*minTime;
|
||||
result.t = minTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет точки пересечения луча с AABB.
|
||||
*
|
||||
* @param origin точка начала луча
|
||||
* @param dir направляющий вектор луча. Вектор может иметь любую отличную от нуля длину.
|
||||
* @param bb AABB, с которым пересекается луч
|
||||
* @param time возвращаемое значение. В эту переменную записывается минимальное и максимальное время пересечения
|
||||
* @return true в случае наличия хотя бы одного пересечения, иначе false
|
||||
*/
|
||||
private function getRayBoundBoxIntersection(origin:Vector3, dir:Vector3, bb:BoundBox, time:MinMax):Boolean {
|
||||
time.min = -1;
|
||||
time.max = 1e308;
|
||||
var t1:Number;
|
||||
var t2:Number;
|
||||
// Цикл по осям бокса
|
||||
for (var i:int = 0; i < 3; i++) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
if (dir.x < threshold && dir.x > -threshold) {
|
||||
if (origin.x < bb.minX || origin.x > bb.maxX) return false;
|
||||
else continue;
|
||||
}
|
||||
t1 = (bb.minX - origin.x)/dir.x;
|
||||
t2 = (bb.maxX - origin.x)/dir.x;
|
||||
break;
|
||||
case 1:
|
||||
if (dir.y < threshold && dir.y > -threshold) {
|
||||
if (origin.y < bb.minY || origin.y > bb.maxY) return false;
|
||||
else continue;
|
||||
}
|
||||
t1 = (bb.minY - origin.y)/dir.y;
|
||||
t2 = (bb.maxY - origin.y)/dir.y;
|
||||
break;
|
||||
case 2:
|
||||
if (dir.z < threshold && dir.z > -threshold) {
|
||||
if (origin.z < bb.minZ || origin.z > bb.maxZ) return false;
|
||||
else continue;
|
||||
}
|
||||
t1 = (bb.minZ - origin.z)/dir.z;
|
||||
t2 = (bb.maxZ - origin.z)/dir.z;
|
||||
break;
|
||||
}
|
||||
if (t1 < t2) {
|
||||
if (t1 > time.min) time.min = t1;
|
||||
if (t2 < time.max) time.max = t2;
|
||||
} else {
|
||||
if (t2 > time.min) time.min = t2;
|
||||
if (t1 < time.max) time.max = t1;
|
||||
}
|
||||
if (time.max < time.min) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @param origin
|
||||
* @param dir
|
||||
* @param collisionGroup
|
||||
* @param t1 время входа луча в узел
|
||||
* @param t2 время выхода луча из узла
|
||||
* @param intersection
|
||||
*/
|
||||
private function testRayAgainstNode(node:CollisionKdNode, origin:Vector3, localOrigin:Vector3, dir:Vector3, collisionGroup:int, t1:Number, t2:Number, predicate:IRayCollisionPredicate, result:RayIntersection):Boolean {
|
||||
// При наличии в узле объектов, проверяем пересечение с ними
|
||||
if (node.indices != null && getRayNodeIntersection(origin, dir, collisionGroup, tree.staticChildren, node.indices, predicate, result)) return true;
|
||||
// Выход из функции если это конечный узел
|
||||
if (node.axis == -1) return false;
|
||||
|
||||
// Определение времени пересечения луча и плоскости разделения узла
|
||||
var splitTime:Number;
|
||||
var currChildNode:CollisionKdNode;
|
||||
switch (node.axis) {
|
||||
case 0:
|
||||
if (dir.x > -threshold && dir.x < threshold) splitTime = t2 + 1;
|
||||
else splitTime = (node.coord - origin.x)/dir.x;
|
||||
currChildNode = localOrigin.x < node.coord ? node.negativeNode : node.positiveNode;
|
||||
break;
|
||||
case 1:
|
||||
if (dir.y > -threshold && dir.y < threshold) splitTime = t2 + 1;
|
||||
else splitTime = (node.coord - origin.y)/dir.y;
|
||||
currChildNode = localOrigin.y < node.coord ? node.negativeNode : node.positiveNode;
|
||||
break;
|
||||
case 2:
|
||||
if (dir.z > -threshold && dir.z < threshold) splitTime = t2 + 1;
|
||||
else splitTime = (node.coord - origin.z)/dir.z;
|
||||
currChildNode = localOrigin.z < node.coord ? node.negativeNode : node.positiveNode;
|
||||
break;
|
||||
}
|
||||
// Определение порядка проверки
|
||||
if (splitTime < t1 || splitTime > t2) {
|
||||
// Луч не переходит в соседний дочерний узел
|
||||
return testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, t2, predicate, result);
|
||||
} else {
|
||||
// Луч переходит из одного дочернего узла в другой
|
||||
var intersects:Boolean = testRayAgainstNode(currChildNode, origin, localOrigin, dir, collisionGroup, t1, splitTime, predicate, result);
|
||||
if (intersects) return true;
|
||||
_o.x = origin.x + splitTime*dir.x;
|
||||
_o.y = origin.y + splitTime*dir.y;
|
||||
_o.z = origin.z + splitTime*dir.z;
|
||||
return testRayAgainstNode(currChildNode == node.negativeNode ? node.positiveNode : node.negativeNode, origin, _o, dir, collisionGroup, splitTime, t2, predicate, result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param origin
|
||||
* @param dir
|
||||
* @param collisionGroup
|
||||
* @param primitives
|
||||
* @param indices
|
||||
* @param intersection
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
private function getRayNodeIntersection(origin:Vector3, dir:Vector3, collisionGroup:int, primitives:Vector.<CollisionPrimitive>, indices:Vector.<int>, predicate:IRayCollisionPredicate, intersection:RayIntersection):Boolean {
|
||||
var pnum:int = indices.length;
|
||||
var minTime:Number = 1e308;
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
var primitive:CollisionPrimitive = primitives[indices[i]];
|
||||
if ((primitive.collisionGroup & collisionGroup) == 0) continue;
|
||||
if (predicate != null && primitive.body != null && !predicate.considerBody(primitive.body)) continue;
|
||||
var t:Number = primitive.getRayIntersection(origin, dir, threshold, _n);
|
||||
if (t > 0 && t < minTime) {
|
||||
minTime = t;
|
||||
intersection.primitive = primitive;
|
||||
intersection.normal.x = _n.x;
|
||||
intersection.normal.y = _n.y;
|
||||
intersection.normal.z = _n.z;
|
||||
}
|
||||
}
|
||||
if (minTime == 1e308) return false;
|
||||
intersection.pos.x = origin.x + dir.x*minTime;
|
||||
intersection.pos.y = origin.y + dir.y*minTime;
|
||||
intersection.pos.z = origin.z + dir.z*minTime;
|
||||
intersection.t = minTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class MinMax {
|
||||
public var min:Number = 0;
|
||||
public var max:Number = 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 123
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/colliders
|
||||
END
|
||||
BoxSphereCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 144
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/colliders/BoxSphereCollider.as
|
||||
END
|
||||
BoxBoxCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 141
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/colliders/BoxBoxCollider.as
|
||||
END
|
||||
BoxRectCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 142
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/colliders/BoxRectCollider.as
|
||||
END
|
||||
SpherePlaneCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 146
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/colliders/SpherePlaneCollider.as
|
||||
END
|
||||
BoxCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 138
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/colliders/BoxCollider.as
|
||||
END
|
||||
BoxPlaneCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 143
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/colliders/BoxPlaneCollider.as
|
||||
END
|
||||
SphereSphereCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 147
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/colliders/SphereSphereCollider.as
|
||||
END
|
||||
BoxTriangleCollider.as
|
||||
K 25
|
||||
svn:wc:ra_dav:version-url
|
||||
V 146
|
||||
/!svn/ver/29411/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/colliders/BoxTriangleCollider.as
|
||||
END
|
||||
124
0.0.9.2/src/alternativa/physics/collision/colliders/.svn/entries
Normal file
124
0.0.9.2/src/alternativa/physics/collision/colliders/.svn/entries
Normal file
@@ -0,0 +1,124 @@
|
||||
8
|
||||
|
||||
dir
|
||||
46043
|
||||
http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/AlternativaPhysics/tags/0.0.9.2/src/alternativa/physics/collision/colliders
|
||||
http://svndev.alternativaplatform.com
|
||||
|
||||
|
||||
|
||||
2010-03-06T16:51:19.781174Z
|
||||
29404
|
||||
mike
|
||||
|
||||
|
||||
svn:special svn:externals svn:needs-lock
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
d9e2387a-1f3e-40e2-b57f-9df5970a2fa5
|
||||
|
||||
BoxSphereCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
25170bcb9a1eec2a0941f4b50a06a554
|
||||
2009-12-08T15:53:11.094448Z
|
||||
24506
|
||||
mike
|
||||
|
||||
BoxBoxCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
15da13b1383e9c1892edc205c8c055b0
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
BoxRectCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
4a35f94d3ef1af0a5b42e1a100580f6d
|
||||
2010-03-06T16:51:19.781174Z
|
||||
29404
|
||||
mike
|
||||
|
||||
SpherePlaneCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
67621ed47d7e90b72498acf2802c4e2d
|
||||
2009-11-24T08:44:15.941480Z
|
||||
23758
|
||||
mike
|
||||
|
||||
BoxCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
52e72ed30a24a3a43ce16a19930546b5
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
BoxPlaneCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
204c7b02a9a1913269c558658bad4262
|
||||
2010-02-12T12:34:25.192477Z
|
||||
28424
|
||||
mike
|
||||
|
||||
SphereSphereCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
301c7102f91a7f3ede83e35ca202db0b
|
||||
2009-11-24T08:44:15.941480Z
|
||||
23758
|
||||
mike
|
||||
|
||||
BoxTriangleCollider.as
|
||||
file
|
||||
|
||||
|
||||
|
||||
|
||||
2010-10-28T04:32:44.000000Z
|
||||
448502271cac4f09491ca128b4bb485a
|
||||
2010-03-06T16:51:19.781174Z
|
||||
29404
|
||||
mike
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
8
|
||||
@@ -0,0 +1,589 @@
|
||||
package alternativa.physics.collision.colliders {
|
||||
|
||||
|
||||
import alternativa.physics.Contact;
|
||||
import alternativa.physics.ContactPoint;
|
||||
import alternativa.physics.altphysics;
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
import alternativa.physics.collision.primitives.CollisionBox;
|
||||
import alternativa.math.Matrix4;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
use namespace altphysics;
|
||||
|
||||
/**
|
||||
* Расчитывает точки контакта двух боксов. Нормаль контакта направляется в сторону бокса с меньшим ID.
|
||||
*/
|
||||
public class BoxBoxCollider extends BoxCollider {
|
||||
|
||||
private var epsilon:Number = 0.001;
|
||||
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 bestAxisIndex:int;
|
||||
private var minOverlap:Number;
|
||||
private var points1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
private var points2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
private var tmpPoints:Vector.<ContactPoint> = new Vector.<ContactPoint>(8, true);
|
||||
private var pcount:int;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function BoxBoxCollider() {
|
||||
for (var i:int = 0; i < 8; i++) {
|
||||
tmpPoints[i] = new ContactPoint();
|
||||
points1[i] = new Vector3();
|
||||
points2[i] = new Vector3();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
override public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
if (!haveCollision(prim1, prim2)) return false;
|
||||
|
||||
var box1:CollisionBox;
|
||||
var box2:CollisionBox;
|
||||
if (prim1.body != null) {
|
||||
box1 = prim1 as CollisionBox;
|
||||
box2 = prim2 as CollisionBox;
|
||||
} else {
|
||||
box1 = prim2 as CollisionBox;
|
||||
box2 = prim1 as CollisionBox;
|
||||
}
|
||||
|
||||
if (bestAxisIndex < 6) {
|
||||
// Контакт грань-(грань|ребро|вершина)
|
||||
if (!findFaceContactPoints(box1, box2, vectorToBox1, bestAxisIndex, contact)) return false;
|
||||
} else {
|
||||
// Контакт ребро-ребро
|
||||
bestAxisIndex -= 6;
|
||||
findEdgesIntersection(box1, box2, vectorToBox1, int(bestAxisIndex/3), bestAxisIndex%3, contact);
|
||||
}
|
||||
contact.body1 = box1.body;
|
||||
contact.body2 = box2.body;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет быстрый тест на наличие пересечения двух примитивов.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
override public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
|
||||
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;
|
||||
|
||||
// Вектор из центра второго бокса в центр первого
|
||||
vectorToBox1.x = transform1.d - transform2.d;
|
||||
vectorToBox1.y = transform1.h - transform2.h;
|
||||
vectorToBox1.z = transform1.l - transform2.l;
|
||||
|
||||
// Проверка пересечения по основным осям
|
||||
axis10.x = transform1.a;
|
||||
axis10.y = transform1.e;
|
||||
axis10.z = transform1.i;
|
||||
if (!testMainAxis(box1, box2, axis10, 0, vectorToBox1)) return false;
|
||||
axis11.x = transform1.b;
|
||||
axis11.y = transform1.f;
|
||||
axis11.z = transform1.j;
|
||||
if (!testMainAxis(box1, box2, axis11, 1, vectorToBox1)) return false;
|
||||
axis12.x = transform1.c;
|
||||
axis12.y = transform1.g;
|
||||
axis12.z = transform1.k;
|
||||
if (!testMainAxis(box1, box2, axis12, 2, vectorToBox1)) return false;
|
||||
|
||||
axis20.x = transform2.a;
|
||||
axis20.y = transform2.e;
|
||||
axis20.z = transform2.i;
|
||||
if (!testMainAxis(box1, box2, axis20, 3, vectorToBox1)) return false;
|
||||
axis21.x = transform2.b;
|
||||
axis21.y = transform2.f;
|
||||
axis21.z = transform2.j;
|
||||
if (!testMainAxis(box1, box2, axis21, 4, vectorToBox1)) return false;
|
||||
axis22.x = transform2.c;
|
||||
axis22.y = transform2.g;
|
||||
axis22.z = transform2.k;
|
||||
if (!testMainAxis(box1, box2, axis22, 5, vectorToBox1)) return false;
|
||||
|
||||
// Проверка производных осей
|
||||
if (!testDerivedAxis(box1, box2, axis10, axis20, 6, vectorToBox1)) return false;
|
||||
if (!testDerivedAxis(box1, box2, axis10, axis21, 7, vectorToBox1)) return false;
|
||||
if (!testDerivedAxis(box1, box2, axis10, axis22, 8, vectorToBox1)) return false;
|
||||
|
||||
if (!testDerivedAxis(box1, box2, axis11, axis20, 9, vectorToBox1)) return false;
|
||||
if (!testDerivedAxis(box1, box2, axis11, axis21, 10, vectorToBox1)) return false;
|
||||
if (!testDerivedAxis(box1, box2, axis11, axis22, 11, vectorToBox1)) return false;
|
||||
|
||||
if (!testDerivedAxis(box1, box2, axis12, axis20, 12, vectorToBox1)) return false;
|
||||
if (!testDerivedAxis(box1, box2, axis12, axis21, 13, vectorToBox1)) return false;
|
||||
if (!testDerivedAxis(box1, box2, axis12, axis22, 14, vectorToBox1)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет поиск точек контакта грани одного бокса с гранью/ребром/вершиной другого.
|
||||
*
|
||||
* @param box1 первый бокс
|
||||
* @param box2 второй бокс
|
||||
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
|
||||
* @param faceAxisIdx индекс оси первого бокса, перпендикулярной грани, с которой произошло столкновение
|
||||
* @param contactInfo структура, в которую записывается информация о точках контакта
|
||||
*/
|
||||
private function findFaceContactPoints(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, faceAxisIdx:int, contact:Contact):Boolean {
|
||||
var swapNormal:Boolean = false;
|
||||
if (faceAxisIdx > 2) {
|
||||
// Столкновение с гранью второго бокса. Для дальнейших расчётов боксы меняются местами,
|
||||
// но нормаль контакта всё равно должна быть направлена в сторону первоначального box1
|
||||
var tmpBox:CollisionBox = box1;
|
||||
box1 = box2;
|
||||
box2 = tmpBox;
|
||||
vectorToBox1.x = -vectorToBox1.x;
|
||||
vectorToBox1.y = -vectorToBox1.y;
|
||||
vectorToBox1.z = -vectorToBox1.z;
|
||||
faceAxisIdx -= 3;
|
||||
swapNormal = true;
|
||||
}
|
||||
|
||||
var transform1:Matrix4 = box1.transform;
|
||||
var transform2:Matrix4 = box2.transform;
|
||||
|
||||
var colAxis:Vector3 = contact.normal;
|
||||
|
||||
transform1.getAxis(faceAxisIdx, colAxis);
|
||||
var negativeFace:Boolean = colAxis.x*vectorToBox1.x + colAxis.y*vectorToBox1.y + colAxis.z*vectorToBox1.z > 0;
|
||||
|
||||
var code:int = 1 << (faceAxisIdx << 1);
|
||||
if (negativeFace) {
|
||||
code <<= 1;
|
||||
}
|
||||
if ((code & box1.excludedFaces) != 0) return false;
|
||||
|
||||
if (!negativeFace) {
|
||||
colAxis.x = -colAxis.x;
|
||||
colAxis.y = -colAxis.y;
|
||||
colAxis.z = -colAxis.z;
|
||||
}
|
||||
// Ищем ось второго бокса, определяющую наиболее антипараллельную грань
|
||||
var incidentAxisIdx:int = 0;
|
||||
var incidentAxisDot:Number;
|
||||
var maxDot:Number = 0;
|
||||
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
|
||||
transform2.getAxis(axisIdx, axis);
|
||||
var dot:Number = axis.x*colAxis.x + axis.y*colAxis.y + axis.z*colAxis.z;
|
||||
var absDot:Number = dot < 0 ? -dot : dot;
|
||||
if (absDot > maxDot) {
|
||||
maxDot = absDot;
|
||||
incidentAxisDot = dot;
|
||||
incidentAxisIdx = axisIdx;
|
||||
}
|
||||
}
|
||||
// Получаем список вершин грани второго бокса, переводим их в систему координат первого бокса и выполняем обрезку
|
||||
// по грани первого бокса. Таким образом получается список потенциальных точек контакта.
|
||||
transform2.getAxis(incidentAxisIdx, axis);
|
||||
getFaceVertsByAxis(box2.hs, incidentAxisIdx, incidentAxisDot < 0, points1);
|
||||
|
||||
// TODO: Вычислить результирующую матрицу, затем преобразовать векторы за один заход
|
||||
transform2.transformVectorsN(points1, points2, 4);
|
||||
transform1.transformVectorsInverseN(points2, points1, 4);
|
||||
|
||||
var pnum:int = clip(box1.hs, faceAxisIdx);
|
||||
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
|
||||
var pen:Number;
|
||||
pcount = 0;
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
var v:Vector3 = points1[i];
|
||||
if ((pen = getPointBoxPenetration(box1.hs, v, faceAxisIdx, negativeFace)) > -epsilon) {
|
||||
var cp:ContactPoint = tmpPoints[pcount++];
|
||||
var cpPos:Vector3 = cp.pos;
|
||||
cpPos.x = transform1.a*v.x + transform1.b*v.y + transform1.c*v.z + transform1.d;
|
||||
cpPos.y = transform1.e*v.x + transform1.f*v.y + transform1.g*v.z + transform1.h;
|
||||
cpPos.z = transform1.i*v.x + transform1.j*v.y + transform1.k*v.z + transform1.l;
|
||||
var r:Vector3 = cp.r1;
|
||||
r.x = cpPos.x - transform1.d;
|
||||
r.y = cpPos.y - transform1.h;
|
||||
r.z = cpPos.z - transform1.l;
|
||||
|
||||
r = cp.r2;
|
||||
r.x = cpPos.x - transform2.d;
|
||||
r.y = cpPos.y - transform2.h;
|
||||
r.z = cpPos.z - transform2.l;
|
||||
|
||||
cp.penetration = pen;
|
||||
}
|
||||
}
|
||||
if (swapNormal) {
|
||||
colAxis.x = -colAxis.x;
|
||||
colAxis.y = -colAxis.y;
|
||||
colAxis.z = -colAxis.z;
|
||||
}
|
||||
|
||||
if (pcount > 4) {
|
||||
reducePoints();
|
||||
}
|
||||
for (i = 0; i < pcount; i++) {
|
||||
cp = contact.points[i];
|
||||
cp.copyFrom(tmpPoints[i]);
|
||||
}
|
||||
contact.pcount = pcount;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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[int(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;
|
||||
}
|
||||
var ii:int = minIdx == 0 ? (pcount - 1) : (minIdx - 1);
|
||||
cp1 = tmpPoints[ii];
|
||||
cp2 = tmpPoints[minIdx];
|
||||
p1 = cp1.pos;
|
||||
p2 = cp2.pos;
|
||||
p2.x = 0.5*(p1.x + p2.x);
|
||||
p2.y = 0.5*(p1.y + p2.y);
|
||||
p2.z = 0.5*(p1.z + p2.z);
|
||||
cp2.penetration = 0.5*(cp1.penetration + cp2.penetration);
|
||||
if (minIdx > 0) {
|
||||
for (i = minIdx; i < pcount; i++) {
|
||||
tmpPoints[int(i - 1)] = tmpPoints[i];
|
||||
}
|
||||
tmpPoints[int(pcount - 1)] = cp1;
|
||||
}
|
||||
pcount--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hs
|
||||
* @param p
|
||||
* @param axisIndex
|
||||
* @param reverse
|
||||
* @return
|
||||
*/
|
||||
private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
|
||||
switch (faceAxisIdx) {
|
||||
case 0:
|
||||
if (reverse) return p.x + hs.x;
|
||||
else return hs.x - p.x;
|
||||
case 1:
|
||||
if (reverse) return p.y + hs.y;
|
||||
else return hs.y - p.y;
|
||||
case 2:
|
||||
if (reverse) return p.z + hs.z;
|
||||
else return hs.z - p.z;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
|
||||
*
|
||||
* @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
|
||||
* @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
|
||||
* @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
|
||||
*/
|
||||
private function clip(hs:Vector3, faceAxisIdx:int):int {
|
||||
var pnum:int = 4;
|
||||
switch (faceAxisIdx) {
|
||||
case 0:
|
||||
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, points2, points1, epsilon);
|
||||
case 1:
|
||||
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
return clipHighX(hs.x, pnum, points2, points1, epsilon);
|
||||
case 2:
|
||||
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, points2, points1, epsilon);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет точку столкновения рёбер двух боксов.
|
||||
*
|
||||
* @param box1 первый бокс
|
||||
* @param box2 второй бокс
|
||||
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
|
||||
* @param axisIdx1 индекс направляющей оси ребра первого бокса
|
||||
* @param axisIdx2 индекс направляющей оси ребра второго бокса
|
||||
* @param contactInfo структура, в которую записывается информация о столкновении
|
||||
*/
|
||||
private function findEdgesIntersection(box1:CollisionBox, box2:CollisionBox, vectorToBox1:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):void {
|
||||
var transform1:Matrix4 = box1.transform;
|
||||
var transform2:Matrix4 = box2.transform;
|
||||
transform1.getAxis(axisIdx1, axis10);
|
||||
transform2.getAxis(axisIdx2, axis20);
|
||||
|
||||
var colAxis:Vector3 = contact.normal;
|
||||
|
||||
colAxis.x = axis10.y*axis20.z - axis10.z*axis20.y;
|
||||
colAxis.y = axis10.z*axis20.x - axis10.x*axis20.z;
|
||||
colAxis.z = axis10.x*axis20.y - axis10.y*axis20.x;
|
||||
var k:Number = 1/Math.sqrt(colAxis.x*colAxis.x + colAxis.y*colAxis.y + colAxis.z*colAxis.z);
|
||||
colAxis.x *= k;
|
||||
colAxis.y *= k;
|
||||
colAxis.z *= k;
|
||||
|
||||
// Разворот оси в сторону первого бокса
|
||||
if (colAxis.x*vectorToBox1.x + colAxis.y*vectorToBox1.y + colAxis.z*vectorToBox1.z < 0) {
|
||||
colAxis.x = -colAxis.x;
|
||||
colAxis.y = -colAxis.y;
|
||||
colAxis.z = -colAxis.z;
|
||||
}
|
||||
|
||||
// Находим среднюю точку на каждом из пересекающихся рёбер
|
||||
var halfLen1:Number;
|
||||
var halfLen2:Number;
|
||||
|
||||
var tx:Number = box1.hs.x;
|
||||
var ty:Number = box1.hs.y;
|
||||
var tz:Number = box1.hs.z;
|
||||
|
||||
var x2:Number = box2.hs.x;
|
||||
var y2:Number = box2.hs.y;
|
||||
var z2:Number = box2.hs.z;
|
||||
// x
|
||||
if (axisIdx1 == 0) {
|
||||
tx = 0;
|
||||
halfLen1 = box1.hs.x;
|
||||
} else {
|
||||
if (colAxis.x*transform1.a + colAxis.y*transform1.e + colAxis.z*transform1.i > 0) {
|
||||
tx = -tx;
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 0) {
|
||||
x2 = 0;
|
||||
halfLen2 = box2.hs.x;
|
||||
} else {
|
||||
if (colAxis.x*transform2.a + colAxis.y*transform2.e + colAxis.z*transform2.i < 0) {
|
||||
x2 = -x2;
|
||||
}
|
||||
}
|
||||
// y
|
||||
if (axisIdx1 == 1) {
|
||||
ty = 0;
|
||||
halfLen1 = box1.hs.y;
|
||||
} else {
|
||||
if (colAxis.x*transform1.b + colAxis.y*transform1.f + colAxis.z*transform1.j > 0) {
|
||||
ty = -ty;
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 1) {
|
||||
y2 = 0;
|
||||
halfLen2 = box2.hs.y;
|
||||
} else {
|
||||
if (colAxis.x*transform2.b + colAxis.y*transform2.f + colAxis.z*transform2.j < 0) {
|
||||
y2 = -y2;
|
||||
}
|
||||
}
|
||||
// z
|
||||
if (axisIdx1 == 2) {
|
||||
tz = 0;
|
||||
halfLen1 = box1.hs.z;
|
||||
} else {
|
||||
if (colAxis.x*transform1.c + colAxis.y*transform1.g + colAxis.z*transform1.k > 0) {
|
||||
tz = -tz;
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 2) {
|
||||
z2 = 0;
|
||||
halfLen2 = box2.hs.z;
|
||||
} else {
|
||||
if (colAxis.x*transform2.c + colAxis.y*transform2.g + colAxis.z*transform2.k < 0) {
|
||||
z2 = -z2;
|
||||
}
|
||||
}
|
||||
// Получаем глобальные координаты средних точек рёбер
|
||||
var x1:Number = transform1.a*tx + transform1.b*ty + transform1.c*tz + transform1.d;
|
||||
var y1:Number = transform1.e*tx + transform1.f*ty + transform1.g*tz + transform1.h;
|
||||
var z1:Number = transform1.i*tx + transform1.j*ty + transform1.k*tz + transform1.l;
|
||||
tx = x2;
|
||||
ty = y2;
|
||||
tz = z2;
|
||||
x2 = transform2.a*tx + transform2.b*ty + transform2.c*tz + transform2.d;
|
||||
y2 = transform2.e*tx + transform2.f*ty + transform2.g*tz + transform2.h;
|
||||
z2 = transform2.i*tx + transform2.j*ty + transform2.k*tz + transform2.l;
|
||||
|
||||
// Находим точку пересечения рёбер, решая систему уравнений
|
||||
k = axis10.x*axis20.x + axis10.y*axis20.y + axis10.z*axis20.z;
|
||||
var det:Number = k*k - 1;
|
||||
tx = x2 - x1;
|
||||
ty = y2 - y1;
|
||||
tz = z2 - z1;
|
||||
var c1:Number = axis10.x*tx + axis10.y*ty + axis10.z*tz;
|
||||
var c2:Number = axis20.x*tx + axis20.y*ty + axis20.z*tz;
|
||||
var t1:Number = (c2*k - c1)/det;
|
||||
var t2:Number = (c2 - c1*k)/det;
|
||||
// Запись данных о столкновении
|
||||
contact.pcount = 1;
|
||||
var cp:ContactPoint = contact.points[0];
|
||||
var cpPos:Vector3 = cp.pos;
|
||||
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
|
||||
cp.pos.x = 0.5*(x1 + axis10.x*t1 + x2 + axis20.x*t2);
|
||||
cp.pos.y = 0.5*(y1 + axis10.y*t1 + y2 + axis20.y*t2);
|
||||
cp.pos.z = 0.5*(z1 + axis10.z*t1 + z2 + axis20.z*t2);
|
||||
var r:Vector3 = cp.r1;
|
||||
r.x = cpPos.x - transform1.d;
|
||||
r.y = cpPos.y - transform1.h;
|
||||
r.z = cpPos.z - transform1.l;
|
||||
r = cp.r2;
|
||||
r.x = cpPos.x - transform2.d;
|
||||
r.y = cpPos.y - transform2.h;
|
||||
r.z = cpPos.z - transform2.l;
|
||||
cp.penetration = minOverlap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет пересечение боксов вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
|
||||
*
|
||||
* @param box1
|
||||
* @param box2
|
||||
* @param axis
|
||||
* @param axisIndex
|
||||
* @param toBox1
|
||||
* @param bestAxis
|
||||
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
|
||||
*/
|
||||
private function testMainAxis(box1:CollisionBox, box2:CollisionBox, axis:Vector3, axisIndex:int, toBox1:Vector3):Boolean {
|
||||
var overlap:Number = overlapOnAxis(box1, box2, axis, toBox1);
|
||||
if (overlap < -epsilon) return false;
|
||||
if (overlap + epsilon < minOverlap) {
|
||||
minOverlap = overlap;
|
||||
bestAxisIndex = axisIndex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param box1
|
||||
* @param box2
|
||||
* @param axis1
|
||||
* @param axis2
|
||||
* @param axisIndex
|
||||
* @param toBox1
|
||||
* @return
|
||||
*/
|
||||
private function testDerivedAxis(box1:CollisionBox, box2:CollisionBox, axis1:Vector3, axis2:Vector3, axisIndex:int, toBox1:Vector3):Boolean {
|
||||
// axis = axis1 cross axis2
|
||||
axis.x = axis1.y*axis2.z - axis1.z*axis2.y;
|
||||
axis.y = axis1.z*axis2.x - axis1.x*axis2.z;
|
||||
axis.z = axis1.x*axis2.y - axis1.y*axis2.x;
|
||||
var lenSqr:Number = axis.x*axis.x + axis.y*axis.y + axis.z*axis.z;
|
||||
if (lenSqr < 0.0001) return true;
|
||||
var k:Number = 1/Math.sqrt(lenSqr);
|
||||
axis.x *= k;
|
||||
axis.y *= k;
|
||||
axis.z *= k;
|
||||
var overlap:Number = overlapOnAxis(box1, box2, axis, toBox1);
|
||||
if (overlap < -epsilon) return false;
|
||||
if (overlap + epsilon < minOverlap) {
|
||||
minOverlap = overlap;
|
||||
bestAxisIndex = axisIndex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет глубину перекрытия двух боксов вдоль заданной оси.
|
||||
*
|
||||
* @param box1 первый бокс
|
||||
* @param box2 второй бокс
|
||||
* @param axis ось
|
||||
* @param 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.math.Vector3;
|
||||
|
||||
class CollisionPointTmp {
|
||||
|
||||
public var pos:Vector3 = new Vector3();
|
||||
public var penetration:Number;
|
||||
|
||||
public var feature1:int;
|
||||
public var feature2:int;
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
package alternativa.physics.collision.colliders {
|
||||
|
||||
|
||||
import alternativa.physics.Contact;
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
import alternativa.physics.collision.ICollider;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BoxCollider implements ICollider {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function BoxCollider() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет быстрый тест на наличие пересечения двух примитивов.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Формирует список вершин грани бокса, заданной нормальной к грани осью. Вершины перечисляются против часовой стрелки.
|
||||
*
|
||||
* @param box бокс, в котором ишутся вершины
|
||||
* @param axisIdx индекс нормальной оси
|
||||
* @param reverse если указано значение true, возвращаются вершины противоположной грани
|
||||
* @param result список, в который помещаются вершины
|
||||
*/
|
||||
protected function getFaceVertsByAxis(hs:Vector3, axisIdx:int, negativeFace:Boolean, result:Vector.<Vector3>):void {
|
||||
var v:Vector3;
|
||||
switch (axisIdx) {
|
||||
case 0:
|
||||
if (negativeFace) {
|
||||
v = result[0]; v.x = -hs.x; v.y = hs.y; v.z = -hs.z;
|
||||
v = result[1]; v.x = -hs.x; v.y = -hs.y; v.z = -hs.z;
|
||||
v = result[2]; v.x = -hs.x; v.y = -hs.y; v.z = hs.z;
|
||||
v = result[3]; v.x = -hs.x; v.y = hs.y; v.z = hs.z;
|
||||
} else {
|
||||
v = result[0]; v.x = hs.x; v.y = -hs.y; v.z = -hs.z;
|
||||
v = result[1]; v.x = hs.x; v.y = hs.y; v.z = -hs.z;
|
||||
v = result[2]; v.x = hs.x; v.y = hs.y; v.z = hs.z;
|
||||
v = result[3]; v.x = hs.x; v.y = -hs.y; v.z = hs.z;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (negativeFace) {
|
||||
v = result[0]; v.x = -hs.x; v.y = -hs.y; v.z = -hs.z;
|
||||
v = result[1]; v.x = hs.x; v.y = -hs.y; v.z = -hs.z;
|
||||
v = result[2]; v.x = hs.x; v.y = -hs.y; v.z = hs.z;
|
||||
v = result[3]; v.x = -hs.x; v.y = -hs.y; v.z = hs.z;
|
||||
} else {
|
||||
v = result[0]; v.x = hs.x; v.y = hs.y; v.z = -hs.z;
|
||||
v = result[1]; v.x = -hs.x; v.y = hs.y; v.z = -hs.z;
|
||||
v = result[2]; v.x = -hs.x; v.y = hs.y; v.z = hs.z;
|
||||
v = result[3]; v.x = hs.x; v.y = hs.y; v.z = hs.z;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (negativeFace) {
|
||||
v = result[0]; v.x = -hs.x; v.y = hs.y; v.z = -hs.z;
|
||||
v = result[1]; v.x = hs.x; v.y = hs.y; v.z = -hs.z;
|
||||
v = result[2]; v.x = hs.x; v.y = -hs.y; v.z = -hs.z;
|
||||
v = result[3]; v.x = -hs.x; v.y = -hs.y; v.z = -hs.z;
|
||||
} else {
|
||||
v = result[0]; v.x = -hs.x; v.y = -hs.y; v.z = hs.z;
|
||||
v = result[1]; v.x = hs.x; v.y = -hs.y; v.z = hs.z;
|
||||
v = result[2]; v.x = hs.x; v.y = hs.y; v.z = hs.z;
|
||||
v = result[3]; v.x = -hs.x; v.y = hs.y; v.z = hs.z;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
protected function clipLowX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
|
||||
var x1:Number = x - epsilon;
|
||||
var num:int = 0;
|
||||
var p1:Vector3 = points[int(pnum - 1)];
|
||||
var p2:Vector3;
|
||||
|
||||
var dx:Number;
|
||||
var dy:Number;
|
||||
var dz:Number;
|
||||
var t:Number;
|
||||
var v:Vector3;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.x > x1) {
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x;
|
||||
v.y = p1.y;
|
||||
v.z = p1.z;
|
||||
if (p2.x < x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
} else if (p2.x > x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
protected function clipHighX(x:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
|
||||
var x1:Number = x + epsilon;
|
||||
var num:int = 0;
|
||||
var p1:Vector3 = points[int(pnum - 1)];
|
||||
var p2:Vector3;
|
||||
|
||||
var dx:Number;
|
||||
var dy:Number;
|
||||
var dz:Number;
|
||||
var t:Number;
|
||||
var v:Vector3;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.x < x1) {
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x;
|
||||
v.y = p1.y;
|
||||
v.z = p1.z;
|
||||
if (p2.x > x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
} else if (p2.x < x1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (x - p1.x)/dx;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
protected function clipLowY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
|
||||
var y1:Number = y - epsilon;
|
||||
var num:int = 0;
|
||||
var p1:Vector3 = points[int(pnum - 1)];
|
||||
var p2:Vector3;
|
||||
|
||||
var dx:Number;
|
||||
var dy:Number;
|
||||
var dz:Number;
|
||||
var t:Number;
|
||||
var v:Vector3;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.y > y1) {
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x;
|
||||
v.y = p1.y;
|
||||
v.z = p1.z;
|
||||
if (p2.y < y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
} else if (p2.y > y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
protected function clipHighY(y:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
|
||||
var y1:Number = y + epsilon;
|
||||
var num:int = 0;
|
||||
var p1:Vector3 = points[int(pnum - 1)];
|
||||
var p2:Vector3;
|
||||
|
||||
var dx:Number;
|
||||
var dy:Number;
|
||||
var dz:Number;
|
||||
var t:Number;
|
||||
var v:Vector3;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.y < y1) {
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x;
|
||||
v.y = p1.y;
|
||||
v.z = p1.z;
|
||||
if (p2.y > y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
} else if (p2.y < y1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (y - p1.y)/dy;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
protected function clipLowZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
|
||||
var z1:Number = z - epsilon;
|
||||
var num:int = 0;
|
||||
var p1:Vector3 = points[int(pnum - 1)];
|
||||
var p2:Vector3;
|
||||
|
||||
var dx:Number;
|
||||
var dy:Number;
|
||||
var dz:Number;
|
||||
var t:Number;
|
||||
var v:Vector3;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.z > z1) {
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x;
|
||||
v.y = p1.y;
|
||||
v.z = p1.z;
|
||||
if (p2.z < z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
} else if (p2.z > z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param pnum
|
||||
* @param points
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
protected function clipHighZ(z:Number, pnum:int, points:Vector.<Vector3>, result:Vector.<Vector3>, epsilon:Number):int {
|
||||
var z1:Number = z + epsilon;
|
||||
var num:int = 0;
|
||||
var p1:Vector3 = points[int(pnum - 1)];
|
||||
var p2:Vector3;
|
||||
|
||||
var dx:Number;
|
||||
var dy:Number;
|
||||
var dz:Number;
|
||||
var t:Number;
|
||||
var v:Vector3;
|
||||
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
p2 = points[i];
|
||||
if (p1.z < z1) {
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x;
|
||||
v.y = p1.y;
|
||||
v.z = p1.z;
|
||||
if (p2.z > z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
} else if (p2.z < z1) {
|
||||
dx = p2.x - p1.x;
|
||||
dy = p2.y - p1.y;
|
||||
dz = p2.z - p1.z;
|
||||
t = (z - p1.z)/dz;
|
||||
v = result[num];
|
||||
num++;
|
||||
v.x = p1.x + t*dx;
|
||||
v.y = p1.y + t*dy;
|
||||
v.z = p1.z + t*dz;
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package alternativa.physics.collision.colliders {
|
||||
|
||||
|
||||
import alternativa.physics.Contact;
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
import alternativa.physics.collision.ICollider;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
public class BoxPlaneCollider implements ICollider {
|
||||
|
||||
private var verts1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
private var verts2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
private var normal:Vector3 = new Vector3();
|
||||
|
||||
public function BoxPlaneCollider() {
|
||||
for (var i:int = 0; i < 8; i++) {
|
||||
verts1[i] = new Vector3();
|
||||
verts2[i] = new Vector3();
|
||||
}
|
||||
}
|
||||
|
||||
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
// var box:RigidBox = body1 as RigidBox;
|
||||
// var plane:RigidPlane;
|
||||
// if (box == null) {
|
||||
// box = body2 as RigidBox;
|
||||
// plane = body1 as RigidPlane;
|
||||
// } else {
|
||||
// plane = body2 as RigidPlane;
|
||||
// }
|
||||
//
|
||||
// // Вычисляем глобальные координаты вершин бокса
|
||||
// var sx:Number = box.halfSize.x;
|
||||
// var sy:Number = box.halfSize.y;
|
||||
// var sz:Number = box.halfSize.z;
|
||||
// (verts1[0] as Vector3).reset(-sx, -sy, -sz);
|
||||
// (verts1[1] as Vector3).reset(sx, -sy, -sz);
|
||||
// (verts1[2] as Vector3).reset(sx, sy, -sz);
|
||||
// (verts1[3] as Vector3).reset(-sx, sy, -sz);
|
||||
// (verts1[4] as Vector3).reset(-sx, -sy, sz);
|
||||
// (verts1[5] as Vector3).reset(sx, -sy, sz);
|
||||
// (verts1[6] as Vector3).reset(sx, sy, sz);
|
||||
// (verts1[7] as Vector3).reset(-sx, sy, sz);
|
||||
//
|
||||
// box.transform.transformVectors(verts1, verts2);
|
||||
// // Вычисляем глобальные нормаль и смещение плоскости
|
||||
// plane.baseMatrix.transformVector(plane.normal, normal);
|
||||
// var offset:Number = plane.offset + normal.x*plane.transform.d + normal.y*plane.transform.h + normal.z*plane.transform.l;
|
||||
// // Проверяем наличие столкновений с каждой вершиной
|
||||
// collisionInfo.pcount = 0;
|
||||
// for (var i:int = 0; i < 8; i++) {
|
||||
// // Вершина добавляется в список точек столкновения, если лежит под плоскостью
|
||||
// var dist:Number = (verts2[i] as Vector3).dot(normal);
|
||||
// if (dist < offset) {
|
||||
// var cp:ContactPoint;
|
||||
// if (collisionInfo.pcount == collisionInfo.points.length) {
|
||||
// cp = new ContactPoint();
|
||||
// collisionInfo.points[collisionInfo.pcount] = cp;
|
||||
// } else {
|
||||
// cp = collisionInfo.points[collisionInfo.pcount];
|
||||
// }
|
||||
// cp.pos.copy(verts2[i]);
|
||||
// cp.r1.diff(cp.pos, box.state.pos);
|
||||
// cp.r2.diff(cp.pos, plane.state.pos);
|
||||
// cp.penetration = offset - dist;
|
||||
// collisionInfo.pcount++;
|
||||
// }
|
||||
// }
|
||||
// if (collisionInfo.pcount > 0) {
|
||||
// collisionInfo.body1 = box;
|
||||
// collisionInfo.body2 = plane;
|
||||
// collisionInfo.normal.copy(normal);
|
||||
// return true;
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @return
|
||||
*/
|
||||
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,575 @@
|
||||
package alternativa.physics.collision.colliders {
|
||||
import alternativa.physics.Contact;
|
||||
import alternativa.physics.ContactPoint;
|
||||
import alternativa.physics.altphysics;
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
import alternativa.physics.collision.primitives.CollisionBox;
|
||||
import alternativa.physics.collision.primitives.CollisionRect;
|
||||
import alternativa.math.Matrix4;
|
||||
import alternativa.math.Vector3;
|
||||
use namespace altphysics;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BoxRectCollider extends BoxCollider {
|
||||
|
||||
private var epsilon:Number = 0.001;
|
||||
|
||||
private var vectorToBox:Vector3 = new Vector3();
|
||||
private var axis:Vector3 = new Vector3();
|
||||
private var axis10:Vector3 = new Vector3();
|
||||
private var axis11:Vector3 = new Vector3();
|
||||
private var axis12:Vector3 = new Vector3();
|
||||
private var axis20:Vector3 = new Vector3();
|
||||
private var axis21:Vector3 = new Vector3();
|
||||
private var axis22:Vector3 = new Vector3();
|
||||
|
||||
private var bestAxisIndex:int;
|
||||
private var minOverlap:Number;
|
||||
|
||||
private var points1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
private var points2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function BoxRectCollider() {
|
||||
for (var i:int = 0; i < 8; i++) {
|
||||
points1[i] = new Vector3();
|
||||
points2[i] = new Vector3();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
override public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
if (!haveCollision(prim1, prim2)) return false;
|
||||
|
||||
var box:CollisionBox = prim1 as CollisionBox;
|
||||
var rect:CollisionRect;
|
||||
if (box == null) {
|
||||
box = prim2 as CollisionBox;
|
||||
rect = prim1 as CollisionRect;
|
||||
} else {
|
||||
rect = prim2 as CollisionRect;
|
||||
}
|
||||
|
||||
if (bestAxisIndex < 4) {
|
||||
// Контакт вдоль одной из основных осей
|
||||
if (!findFaceContactPoints(box, rect, vectorToBox, bestAxisIndex, contact)) return false;
|
||||
} else {
|
||||
// Контакт ребро-ребро
|
||||
bestAxisIndex -= 4;
|
||||
if (!findEdgesIntersection(box, rect, vectorToBox, int(bestAxisIndex/2), bestAxisIndex%2, contact)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
contact.body1 = box.body;
|
||||
contact.body2 = rect.body;
|
||||
|
||||
// Хак для танков, чтобы исключить утыкания танков в стыки статических примитивов
|
||||
if (rect.transform.k > 0.99999) {
|
||||
contact.normal.x = 0;
|
||||
contact.normal.y = 0;
|
||||
contact.normal.z = 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет быстрый тест на наличие пересечения двух примитивов.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
override public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
|
||||
minOverlap = 1e10;
|
||||
var box:CollisionBox = prim1 as CollisionBox;
|
||||
var rect:CollisionRect;
|
||||
if (box == null) {
|
||||
box = prim2 as CollisionBox;
|
||||
rect = prim1 as CollisionRect;
|
||||
} else {
|
||||
rect = prim2 as CollisionRect;
|
||||
}
|
||||
var boxTransform:Matrix4 = box.transform;
|
||||
var rectTransform:Matrix4 = rect.transform;
|
||||
|
||||
// Вектор из центра прямоугольника в центр бокса
|
||||
vectorToBox.x = boxTransform.d - rectTransform.d;
|
||||
vectorToBox.y = boxTransform.h - rectTransform.h;
|
||||
vectorToBox.z = boxTransform.l - rectTransform.l;
|
||||
|
||||
// Проверка пересечения по нормали прямоугольника
|
||||
rectTransform.getAxis(2, axis22);
|
||||
if (!testMainAxis(box, rect, axis22, 0, vectorToBox)) return false;
|
||||
|
||||
// Проверка пересечения по основным осям бокса
|
||||
boxTransform.getAxis(0, axis10);
|
||||
if (!testMainAxis(box, rect, axis10, 1, vectorToBox)) return false;
|
||||
boxTransform.getAxis(1, axis11);
|
||||
if (!testMainAxis(box, rect, axis11, 2, vectorToBox)) return false;
|
||||
boxTransform.getAxis(2, axis12);
|
||||
if (!testMainAxis(box, rect, axis12, 3, vectorToBox)) return false;
|
||||
|
||||
// Получаем направляющие рёбер прямоугольника
|
||||
rectTransform.getAxis(0, axis20);
|
||||
rectTransform.getAxis(1, axis21);
|
||||
|
||||
// Проверка производных осей
|
||||
if (!testDerivedAxis(box, rect, axis10, axis20, 4, vectorToBox)) return false;
|
||||
if (!testDerivedAxis(box, rect, axis10, axis21, 5, vectorToBox)) return false;
|
||||
|
||||
if (!testDerivedAxis(box, rect, axis11, axis20, 6, vectorToBox)) return false;
|
||||
if (!testDerivedAxis(box, rect, axis11, axis21, 7, vectorToBox)) return false;
|
||||
|
||||
if (!testDerivedAxis(box, rect, axis12, axis20, 8, vectorToBox)) return false;
|
||||
if (!testDerivedAxis(box, rect, axis12, axis21, 9, vectorToBox)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет поиск точек контакта бокса с прямоугольником.
|
||||
*
|
||||
* @param box бокс
|
||||
* @param rect прямоугольник
|
||||
* @param vectorToBox вектор, направленный из центра прямоугольника в центр бокса
|
||||
* @param faceAxisIdx индекс оси, идентифицирующей полскость столкновения (грань бокса или полскость прямоугольника)
|
||||
* @param colInfo структура, в которую записывается информация о точках контакта
|
||||
*/
|
||||
private function findFaceContactPoints(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, faceAxisIdx:int, contact:Contact):Boolean {
|
||||
var pnum:int;
|
||||
var i:int;
|
||||
var v:Vector3;
|
||||
var cp:ContactPoint;
|
||||
var boxTransform:Matrix4 = box.transform;
|
||||
var rectTransform:Matrix4 = rect.transform;
|
||||
var colAxis:Vector3 = contact.normal;
|
||||
|
||||
var negativeFace:Boolean;
|
||||
var code:int;
|
||||
|
||||
if (faceAxisIdx == 0) {
|
||||
// Столкновение с плоскостью прямоугольника
|
||||
|
||||
// Проверим положение бокса относительно плоскости прямоугольника
|
||||
colAxis.x = rectTransform.c;
|
||||
colAxis.y = rectTransform.g;
|
||||
colAxis.z = rectTransform.k;
|
||||
|
||||
// var offset:Number = colAxis.x*rectTransform.d + colAxis.y*rectTransform.h + colAxis.z*rectTransform.l;
|
||||
// if (bbPos.vDot(colAxis) < offset) return false;
|
||||
|
||||
// Ищем ось бокса, определяющую наиболее антипараллельную грань
|
||||
var incidentAxisIdx:int = 0;
|
||||
var incidentAxisDot:Number;
|
||||
var maxDot:Number = 0;
|
||||
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
|
||||
boxTransform.getAxis(axisIdx, axis);
|
||||
var dot:Number = axis.x*colAxis.x + axis.y*colAxis.y + axis.z*colAxis.z;
|
||||
var absDot:Number = dot < 0 ? -dot : dot;
|
||||
if (absDot > maxDot) {
|
||||
maxDot = absDot;
|
||||
incidentAxisIdx = axisIdx;
|
||||
incidentAxisDot = dot;
|
||||
}
|
||||
}
|
||||
negativeFace = incidentAxisDot > 0;
|
||||
|
||||
code = 1 << (incidentAxisIdx << 1);
|
||||
if (negativeFace) {
|
||||
code <<= 1;
|
||||
}
|
||||
if ((code & box.excludedFaces) != 0) return false;
|
||||
|
||||
|
||||
// Получаем список вершин грани бокса, переводим их в систему координат прямоугольника и выполняем обрезку
|
||||
// по прямоугольнику. Таким образом получается список потенциальных точек контакта.
|
||||
boxTransform.getAxis(incidentAxisIdx, axis);
|
||||
getFaceVertsByAxis(box.hs, incidentAxisIdx, negativeFace, points1);
|
||||
boxTransform.transformVectorsN(points1, points2, 4);
|
||||
rectTransform.transformVectorsInverseN(points2, points1, 4);
|
||||
pnum = clipByRect(rect.hs);
|
||||
// Проверяем каждую потенциальную точку на принадлежность нижней полуплоскости прямоугольника и добавляем такие точки в список контактов
|
||||
contact.pcount = 0;
|
||||
for (i = 0; i < pnum; i++) {
|
||||
v = points1[i];
|
||||
if (v.z < epsilon) {
|
||||
cp = contact.points[contact.pcount++];
|
||||
cp.penetration = -v.z;
|
||||
var cpPos:Vector3 = cp.pos;
|
||||
cpPos.x = rectTransform.a*v.x + rectTransform.b*v.y + rectTransform.c*v.z + rectTransform.d;
|
||||
cpPos.y = rectTransform.e*v.x + rectTransform.f*v.y + rectTransform.g*v.z + rectTransform.h;
|
||||
cpPos.z = rectTransform.i*v.x + rectTransform.j*v.y + rectTransform.k*v.z + rectTransform.l;
|
||||
v = cp.r1;
|
||||
v.x = cpPos.x - boxTransform.d;
|
||||
v.y = cpPos.y - boxTransform.h;
|
||||
v.z = cpPos.z - boxTransform.l;
|
||||
v = cp.r2;
|
||||
v.x = cpPos.x - rectTransform.d;
|
||||
v.y = cpPos.y - rectTransform.h;
|
||||
v.z = cpPos.z - rectTransform.l;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Столкновение с гранью бокса
|
||||
faceAxisIdx--;
|
||||
boxTransform.getAxis(faceAxisIdx, colAxis);
|
||||
negativeFace = colAxis.x*vectorToBox.x + colAxis.y*vectorToBox.y + colAxis.z*vectorToBox.z > 0;
|
||||
|
||||
code = 1 << (faceAxisIdx << 1);
|
||||
if (negativeFace) {
|
||||
code <<= 1;
|
||||
}
|
||||
if ((code & box.excludedFaces) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!negativeFace) {
|
||||
colAxis.x = -colAxis.x;
|
||||
colAxis.y = -colAxis.y;
|
||||
colAxis.z = -colAxis.z;
|
||||
}
|
||||
|
||||
if (rectTransform.c*colAxis.x + rectTransform.g*colAxis.y + rectTransform.k*colAxis.z < 0) return false;
|
||||
|
||||
getFaceVertsByAxis(rect.hs, 2, false, points1);
|
||||
rectTransform.transformVectorsN(points1, points2, 4);
|
||||
boxTransform.transformVectorsInverseN(points2, points1, 4);
|
||||
pnum = clipByBox(box.hs, faceAxisIdx);
|
||||
// Проверяем каждую потенциальную точку на принадлежность первому боксу и добавляем такие точки в список контактов
|
||||
var pen:Number;
|
||||
contact.pcount = 0;
|
||||
for (i = 0; i < pnum; i++) {
|
||||
v = points1[i];
|
||||
if ((pen = getPointBoxPenetration(box.hs, v, faceAxisIdx, negativeFace)) > -epsilon) {
|
||||
cp = contact.points[contact.pcount++];
|
||||
cp.penetration = pen;
|
||||
cpPos = cp.pos;
|
||||
cpPos.x = boxTransform.a*v.x + boxTransform.b*v.y + boxTransform.c*v.z + boxTransform.d;
|
||||
cpPos.y = boxTransform.e*v.x + boxTransform.f*v.y + boxTransform.g*v.z + boxTransform.h;
|
||||
cpPos.z = boxTransform.i*v.x + boxTransform.j*v.y + boxTransform.k*v.z + boxTransform.l;
|
||||
v = cp.r1;
|
||||
v.x = cpPos.x - boxTransform.d;
|
||||
v.y = cpPos.y - boxTransform.h;
|
||||
v.z = cpPos.z - boxTransform.l;
|
||||
v = cp.r2;
|
||||
v.x = cpPos.x - rectTransform.d;
|
||||
v.y = cpPos.y - rectTransform.h;
|
||||
v.z = cpPos.z - rectTransform.l;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hs
|
||||
* @param p
|
||||
* @param axisIndex
|
||||
* @param reverse
|
||||
* @return
|
||||
*/
|
||||
private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, reverse:Boolean):Number {
|
||||
switch (faceAxisIdx) {
|
||||
case 0:
|
||||
if (reverse) return p.x + hs.x;
|
||||
else return hs.x - p.x;
|
||||
case 1:
|
||||
if (reverse) return p.y + hs.y;
|
||||
else return hs.y - p.y;
|
||||
case 2:
|
||||
if (reverse) return p.z + hs.z;
|
||||
else return hs.z - p.z;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет обрезку грани, заданной списком вершин в поле объекта verts1. Результат сохраняется в этом же поле.
|
||||
*
|
||||
* @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
|
||||
* @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
|
||||
* @return количество вершин, получившихся в результате обрезки грани, заданной вершинами в поле verts1
|
||||
*/
|
||||
private function clipByBox(hs:Vector3, faceAxisIdx:int):int {
|
||||
var pnum:int = 4;
|
||||
switch (faceAxisIdx) {
|
||||
case 0:
|
||||
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, points2, points1, epsilon);
|
||||
case 1:
|
||||
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
return clipHighX(hs.x, pnum, points2, points1, epsilon);
|
||||
case 2:
|
||||
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, points2, points1, epsilon);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hs
|
||||
* @return
|
||||
*/
|
||||
private function clipByRect(hs:Vector3):int {
|
||||
var pnum:int = 4;
|
||||
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, points2, points1, epsilon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет точку столкновения рёбер двух боксов.
|
||||
*
|
||||
* @param box первый бокс
|
||||
* @param rect второй бокс
|
||||
* @param vectorToBox1 вектор, направленный из центра второго бокса в центр первого
|
||||
* @param axisIdx1 индекс направляющей оси ребра первого бокса
|
||||
* @param axisIdx2 индекс направляющей оси ребра второго бокса
|
||||
* @param colInfo структура, в которую записывается информация о столкновении
|
||||
*/
|
||||
private function findEdgesIntersection(box:CollisionBox, rect:CollisionRect, vectorToBox:Vector3, axisIdx1:int, axisIdx2:int, contact:Contact):Boolean {
|
||||
var boxTransform:Matrix4 = box.transform;
|
||||
var rectTransform:Matrix4 = rect.transform;
|
||||
|
||||
boxTransform.getAxis(axisIdx1, axis10);
|
||||
rectTransform.getAxis(axisIdx2, axis20);
|
||||
var colAxis:Vector3 = contact.normal;
|
||||
colAxis.x = axis10.y*axis20.z - axis10.z*axis20.y;
|
||||
colAxis.y = axis10.z*axis20.x - axis10.x*axis20.z;
|
||||
colAxis.z = axis10.x*axis20.y - axis10.y*axis20.x;
|
||||
var k:Number = 1/Math.sqrt(colAxis.x*colAxis.x + colAxis.y*colAxis.y + colAxis.z*colAxis.z);
|
||||
colAxis.x *= k;
|
||||
colAxis.y *= k;
|
||||
colAxis.z *= k;
|
||||
|
||||
// Разворот оси в сторону бокса
|
||||
if (colAxis.x*vectorToBox.x + colAxis.y*vectorToBox.y + colAxis.z*vectorToBox.z < 0) {
|
||||
colAxis.x = -colAxis.x;
|
||||
colAxis.y = -colAxis.y;
|
||||
colAxis.z = -colAxis.z;
|
||||
}
|
||||
|
||||
// Находим среднюю точку на каждом из пересекающихся рёбер
|
||||
var halfLen1:Number;
|
||||
var halfLen2:Number;
|
||||
var vx:Number = box.hs.x;
|
||||
var vy:Number = box.hs.y;
|
||||
var vz:Number = box.hs.z;
|
||||
var x2:Number = rect.hs.x;
|
||||
var y2:Number = rect.hs.y;
|
||||
var z2:Number = rect.hs.z;
|
||||
// x
|
||||
if (axisIdx1 == 0) {
|
||||
vx = 0;
|
||||
halfLen1 = box.hs.x;
|
||||
} else {
|
||||
if (boxTransform.a*colAxis.x + boxTransform.e*colAxis.y + boxTransform.i*colAxis.z > 0) {
|
||||
vx = -vx;
|
||||
if ((box.excludedFaces & 2) != 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((box.excludedFaces & 1) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 0) {
|
||||
x2 = 0;
|
||||
halfLen2 = rect.hs.x;
|
||||
} else {
|
||||
if (rectTransform.a*colAxis.x + rectTransform.e*colAxis.y + rectTransform.i*colAxis.z < 0) {
|
||||
x2 = -x2;
|
||||
}
|
||||
}
|
||||
// y
|
||||
if (axisIdx1 == 1) {
|
||||
vy = 0;
|
||||
halfLen1 = box.hs.y;
|
||||
} else {
|
||||
if (boxTransform.b*colAxis.x + boxTransform.f*colAxis.y + boxTransform.j*colAxis.z > 0) {
|
||||
vy = -vy;
|
||||
if ((box.excludedFaces & 8) != 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((box.excludedFaces & 4) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (axisIdx2 == 1) {
|
||||
y2 = 0;
|
||||
halfLen2 = rect.hs.y;
|
||||
} else {
|
||||
if (rectTransform.b*colAxis.x + rectTransform.f*colAxis.y + rectTransform.j*colAxis.z < 0) {
|
||||
y2 = -y2;
|
||||
}
|
||||
}
|
||||
// z
|
||||
if (axisIdx1 == 2) {
|
||||
vz = 0;
|
||||
halfLen1 = box.hs.z;
|
||||
} else {
|
||||
if (boxTransform.c*colAxis.x + boxTransform.g*colAxis.y + boxTransform.k*colAxis.z > 0) {
|
||||
vz = -vz;
|
||||
if ((box.excludedFaces & 32) != 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((box.excludedFaces & 16) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Получаем глобальные координаты средних точек рёбер
|
||||
|
||||
var x1:Number = boxTransform.a*vx + boxTransform.b*vy + boxTransform.c*vz + boxTransform.d;
|
||||
var y1:Number = boxTransform.e*vx + boxTransform.f*vy + boxTransform.g*vz + boxTransform.h;
|
||||
var z1:Number = boxTransform.i*vx + boxTransform.j*vy + boxTransform.k*vz + boxTransform.l;
|
||||
vx = x2;
|
||||
vy = y2;
|
||||
vz = z2;
|
||||
x2 = rectTransform.a*vx + rectTransform.b*vy + rectTransform.c*vz + rectTransform.d;
|
||||
y2 = rectTransform.e*vx + rectTransform.f*vy + rectTransform.g*vz + rectTransform.h;
|
||||
z2 = rectTransform.i*vx + rectTransform.j*vy + rectTransform.k*vz + rectTransform.l;
|
||||
// Находим точку пересечения рёбер, решая систему уравнений
|
||||
k = axis10.x*axis20.x + axis10.y*axis20.y + axis10.z*axis20.z;
|
||||
var det:Number = k*k - 1;
|
||||
vx = x2 - x1;
|
||||
vy = y2 - y1;
|
||||
vz = z2 - z1;
|
||||
var c1:Number = axis10.x*vx + axis10.y*vy + axis10.z*vz;
|
||||
var c2:Number = axis20.x*vx + axis20.y*vy + axis20.z*vz;
|
||||
var t1:Number = (c2*k - c1)/det;
|
||||
var t2:Number = (c2 - c1*k)/det;
|
||||
// Запись данных о столкновении
|
||||
contact.pcount = 1;
|
||||
var cp:ContactPoint = contact.points[0];
|
||||
cp.penetration = minOverlap;
|
||||
var cpPos:Vector3 = cp.pos;
|
||||
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
|
||||
cpPos.x = 0.5*(x1 + axis10.x*t1 + x2 + axis20.x*t2);
|
||||
cpPos.y = 0.5*(y1 + axis10.y*t1 + y2 + axis20.y*t2);
|
||||
cpPos.z = 0.5*(z1 + axis10.z*t1 + z2 + axis20.z*t2);
|
||||
var v:Vector3 = cp.r1;
|
||||
v.x = cpPos.x - boxTransform.d;
|
||||
v.y = cpPos.y - boxTransform.h;
|
||||
v.z = cpPos.z - boxTransform.l;
|
||||
v = cp.r2;
|
||||
v.x = cpPos.x - rectTransform.d;
|
||||
v.y = cpPos.y - rectTransform.h;
|
||||
v.z = cpPos.z - rectTransform.l;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет пересечение вдоль заданной оси. При наличии пересечения сохраняется глубина пересечения, если она минимальна.
|
||||
*
|
||||
* @param box
|
||||
* @param rect
|
||||
* @param axis
|
||||
* @param axisIndex
|
||||
* @param vectorToBox
|
||||
* @param bestAxis
|
||||
* @return true в случае, если проекции боксов на заданную ось пересекаются, иначе false
|
||||
*/
|
||||
private function testMainAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
|
||||
var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
|
||||
if (overlap < -epsilon) return false;
|
||||
if (overlap + epsilon < minOverlap) {
|
||||
minOverlap = overlap;
|
||||
bestAxisIndex = axisIndex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param box
|
||||
* @param rect
|
||||
* @param axis1
|
||||
* @param axis2
|
||||
* @param axisIndex
|
||||
* @param vectorToBox
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
private function testDerivedAxis(box:CollisionBox, rect:CollisionRect, axis1:Vector3, axis2:Vector3, axisIndex:int, vectorToBox:Vector3):Boolean {
|
||||
// axis = axis1 cross axis2
|
||||
axis.x = axis1.y*axis2.z - axis1.z*axis2.y;
|
||||
axis.y = axis1.z*axis2.x - axis1.x*axis2.z;
|
||||
axis.z = axis1.x*axis2.y - axis1.y*axis2.x;
|
||||
var lenSqr:Number = axis.x*axis.x + axis.y*axis.y + axis.z*axis.z;
|
||||
if (lenSqr < 0.0001) return true;
|
||||
var k:Number = 1/Math.sqrt(lenSqr);
|
||||
axis.x *= k;
|
||||
axis.y *= k;
|
||||
axis.z *= k;
|
||||
var overlap:Number = overlapOnAxis(box, rect, axis, vectorToBox);
|
||||
if (overlap < -epsilon) return false;
|
||||
if (overlap + epsilon < minOverlap) {
|
||||
minOverlap = overlap;
|
||||
bestAxisIndex = axisIndex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет глубину перекрытия вдоль заданной оси.
|
||||
*
|
||||
* @param box бокс
|
||||
* @param rect прямоугольник
|
||||
* @param axis ось
|
||||
* @param vectorToBox вектор, соединяющий центр прямоугольника с центром бокса
|
||||
* @return величина перекрытия вдоль оси
|
||||
*/
|
||||
public function overlapOnAxis(box:CollisionBox, rect:CollisionRect, axis:Vector3, vectorToBox:Vector3):Number {
|
||||
var m:Matrix4 = box.transform;
|
||||
var d:Number = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*box.hs.x;
|
||||
if (d < 0) d = -d;
|
||||
var projection:Number = d;
|
||||
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*box.hs.y;
|
||||
if (d < 0) d = -d;
|
||||
projection += d;
|
||||
d = (m.c*axis.x + m.g*axis.y + m.k*axis.z)*box.hs.z;
|
||||
if (d < 0) d = -d;
|
||||
projection += d;
|
||||
|
||||
m = rect.transform;
|
||||
d = (m.a*axis.x + m.e*axis.y + m.i*axis.z)*rect.hs.x;
|
||||
if (d < 0) d = -d;
|
||||
projection += d;
|
||||
d = (m.b*axis.x + m.f*axis.y + m.j*axis.z)*rect.hs.y;
|
||||
if (d < 0) d = -d;
|
||||
projection += d;
|
||||
|
||||
d = vectorToBox.x*axis.x + vectorToBox.y*axis.y + vectorToBox.z*axis.z;
|
||||
if (d < 0) d = -d;
|
||||
|
||||
return projection - d;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package alternativa.physics.collision.colliders {
|
||||
import alternativa.physics.Contact;
|
||||
import alternativa.physics.ContactPoint;
|
||||
import alternativa.physics.altphysics;
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
import alternativa.physics.collision.ICollider;
|
||||
import alternativa.physics.collision.primitives.CollisionBox;
|
||||
import alternativa.physics.collision.primitives.CollisionSphere;
|
||||
import alternativa.math.Vector3;
|
||||
use namespace altphysics;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BoxSphereCollider implements ICollider {
|
||||
|
||||
private var center:Vector3 = new Vector3();
|
||||
private var closestPt:Vector3 = new Vector3();
|
||||
|
||||
private var bPos:Vector3 = new Vector3();
|
||||
private var sPos:Vector3 = new Vector3();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function BoxSphereCollider() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param body1
|
||||
* @param body2
|
||||
* @param collisionInfo
|
||||
* @return
|
||||
*/
|
||||
public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
var sphere:CollisionSphere = prim1 as CollisionSphere;
|
||||
var box:CollisionBox;
|
||||
if (sphere == null) {
|
||||
sphere = prim2 as CollisionSphere;
|
||||
box = prim1 as CollisionBox;
|
||||
} else {
|
||||
box = prim2 as CollisionBox;
|
||||
}
|
||||
// Трансформируем центр сферы в систему координат бокса
|
||||
sphere.transform.getAxis(3, sPos);
|
||||
box.transform.getAxis(3, bPos);
|
||||
box.transform.transformVectorInverse(sPos, center);
|
||||
// Выполняем поиск разделяющей оси
|
||||
var hs:Vector3 = box.hs;
|
||||
var sx:Number = hs.x + sphere.r;
|
||||
var sy:Number = hs.y + sphere.r;
|
||||
var sz:Number = hs.z + sphere.r;
|
||||
if (center.x > sx || center.x < -sx
|
||||
|| center.y > sy || center.y < -sy
|
||||
|| center.z > sz || center.z < -sz) {
|
||||
return false;
|
||||
}
|
||||
// Находим ближайшую к сфере точку на боксе
|
||||
if (center.x > hs.x) {
|
||||
closestPt.x = hs.x;
|
||||
} else if (center.x < -hs.x) {
|
||||
closestPt.x = -hs.x;
|
||||
} else {
|
||||
closestPt.x = center.x;
|
||||
}
|
||||
|
||||
if (center.y > hs.y) {
|
||||
closestPt.y = hs.y;
|
||||
} else if (center.y < -hs.y) {
|
||||
closestPt.y = -hs.y;
|
||||
} else {
|
||||
closestPt.y = center.y;
|
||||
}
|
||||
|
||||
if (center.z > hs.z) {
|
||||
closestPt.z = hs.z;
|
||||
} else if (center.z < -hs.z) {
|
||||
closestPt.z = -hs.z;
|
||||
} else {
|
||||
closestPt.z = center.z;
|
||||
}
|
||||
|
||||
// TODO: Предусмотреть обработку случая, когда центр сферы внутри бокса
|
||||
|
||||
var distSqr:Number = center.vSubtract(closestPt).vLengthSqr();
|
||||
if (distSqr > sphere.r*sphere.r) {
|
||||
return false;
|
||||
}
|
||||
// Зафиксированно столкновение
|
||||
contact.body1 = sphere.body;
|
||||
contact.body2 = box.body;
|
||||
contact.normal.vCopy(closestPt).vTransformBy4(box.transform).vSubtract(sPos).vNormalize().vReverse();
|
||||
contact.pcount = 1;
|
||||
|
||||
var cp:ContactPoint = contact.points[0];
|
||||
cp.penetration = sphere.r - Math.sqrt(distSqr);
|
||||
cp.pos.vCopy(contact.normal).vScale(-sphere.r).vAdd(sPos);
|
||||
cp.r1.vDiff(cp.pos, sPos);
|
||||
cp.r2.vDiff(cp.pos, bPos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prim1
|
||||
* @param prim2
|
||||
* @return
|
||||
*/
|
||||
public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
|
||||
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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,725 @@
|
||||
package alternativa.physics.collision.colliders {
|
||||
|
||||
|
||||
import alternativa.physics.Contact;
|
||||
import alternativa.physics.ContactPoint;
|
||||
import alternativa.physics.collision.CollisionPrimitive;
|
||||
import alternativa.physics.collision.primitives.CollisionBox;
|
||||
import alternativa.physics.collision.primitives.CollisionTriangle;
|
||||
import alternativa.math.Matrix4;
|
||||
import alternativa.math.Vector3;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BoxTriangleCollider extends BoxCollider {
|
||||
|
||||
public var epsilon:Number = 0.001;
|
||||
|
||||
private var bestAxisIndex:int;
|
||||
private var minOverlap:Number;
|
||||
private var toBox:Vector3 = new Vector3();
|
||||
private var axis:Vector3 = new Vector3();
|
||||
private var colNormal:Vector3 = new Vector3();
|
||||
private var axis10:Vector3 = new Vector3();
|
||||
private var axis11:Vector3 = new Vector3();
|
||||
private var axis12:Vector3 = new Vector3();
|
||||
private var axis20:Vector3 = new Vector3();
|
||||
private var axis21:Vector3 = new Vector3();
|
||||
private var axis22:Vector3 = new Vector3();
|
||||
private var point1:Vector3 = new Vector3();
|
||||
private var point2:Vector3 = new Vector3();
|
||||
private var vector:Vector3 = new Vector3();
|
||||
private var points1:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
private var points2:Vector.<Vector3> = new Vector.<Vector3>(8, true);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function BoxTriangleCollider() {
|
||||
for (var i:int = 0; i < 8; i++) {
|
||||
points1[i] = new Vector3();
|
||||
points2[i] = new Vector3();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверяет наличие пересечения примитивов. Если пересечение существует, заполняется информация о контакте.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
override public function getContact(prim1:CollisionPrimitive, prim2:CollisionPrimitive, contact:Contact):Boolean {
|
||||
if (!haveCollision(prim1, prim2)) return false;
|
||||
|
||||
var tri:CollisionTriangle = prim1 as CollisionTriangle;
|
||||
var box:CollisionBox;
|
||||
if (tri == null) {
|
||||
box = CollisionBox(prim1);
|
||||
tri = CollisionTriangle(prim2);
|
||||
} else {
|
||||
box = CollisionBox(prim2);
|
||||
}
|
||||
|
||||
if (bestAxisIndex < 4) {
|
||||
// Контакт вдоль одной из основных осей
|
||||
if (!findFaceContactPoints(box, tri, toBox, bestAxisIndex, contact)) return false;
|
||||
} else {
|
||||
// Контакт ребро-ребро
|
||||
bestAxisIndex -= 4;
|
||||
if (!findEdgesIntersection(box, tri, toBox, bestAxisIndex%3, int(bestAxisIndex/3), contact)) return false;
|
||||
}
|
||||
|
||||
contact.body1 = box.body;
|
||||
contact.body2 = tri.body;
|
||||
|
||||
// Хак для танков, чтобы исключить утыкания танков в стыки статических примитивов
|
||||
if (tri.transform.k > 0.99999) {
|
||||
contact.normal.x = 0;
|
||||
contact.normal.y = 0;
|
||||
contact.normal.z = 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет быстрый тест на наличие пересечения двух примитивов.
|
||||
*
|
||||
* @param prim1 первый примитив
|
||||
* @param prim2 второй примитив
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
override public function haveCollision(prim1:CollisionPrimitive, prim2:CollisionPrimitive):Boolean {
|
||||
var tri:CollisionTriangle = prim1 as CollisionTriangle;
|
||||
var box:CollisionBox;
|
||||
if (tri == null) {
|
||||
box = CollisionBox(prim1);
|
||||
tri = CollisionTriangle(prim2);
|
||||
} else {
|
||||
box = CollisionBox(prim2);
|
||||
}
|
||||
|
||||
var boxTransform:Matrix4 = box.transform;
|
||||
var triTransform:Matrix4 = tri.transform;
|
||||
|
||||
toBox.x = boxTransform.d - triTransform.d;
|
||||
toBox.y = boxTransform.h - triTransform.h;
|
||||
toBox.z = boxTransform.l - triTransform.l;
|
||||
|
||||
minOverlap = 1e308;
|
||||
|
||||
// Сначала проверяется нормаль треугольника
|
||||
axis.x = triTransform.c;
|
||||
axis.y = triTransform.g;
|
||||
axis.z = triTransform.k;
|
||||
if (!testMainAxis(box, tri, axis, 0, toBox)) return false;
|
||||
|
||||
// Проверка основных осей бокса
|
||||
axis10.x = boxTransform.a;
|
||||
axis10.y = boxTransform.e;
|
||||
axis10.z = boxTransform.i;
|
||||
if (!testMainAxis(box, tri, axis10, 1, toBox)) return false;
|
||||
axis11.x = boxTransform.b;
|
||||
axis11.y = boxTransform.f;
|
||||
axis11.z = boxTransform.j;
|
||||
if (!testMainAxis(box, tri, axis11, 2, toBox)) return false;
|
||||
axis12.x = boxTransform.c;
|
||||
axis12.y = boxTransform.g;
|
||||
axis12.z = boxTransform.k;
|
||||
if (!testMainAxis(box, tri, axis12, 3, toBox)) return false;
|
||||
|
||||
// Проверка производных осей
|
||||
// TODO: заменить вычисления векторных произведений инлайнами
|
||||
var v:Vector3 = tri.e0;
|
||||
axis20.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z;
|
||||
axis20.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z;
|
||||
axis20.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z;
|
||||
if (!testDerivedAxis(box, tri, axis10, axis20, 4, toBox)) return false;
|
||||
if (!testDerivedAxis(box, tri, axis11, axis20, 5, toBox)) return false;
|
||||
if (!testDerivedAxis(box, tri, axis12, axis20, 6, toBox)) return false;
|
||||
|
||||
v = tri.e1;
|
||||
axis21.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z;
|
||||
axis21.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z;
|
||||
axis21.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z;
|
||||
if (!testDerivedAxis(box, tri, axis10, axis21, 7, toBox)) return false;
|
||||
if (!testDerivedAxis(box, tri, axis11, axis21, 8, toBox)) return false;
|
||||
if (!testDerivedAxis(box, tri, axis12, axis21, 9, toBox)) return false;
|
||||
|
||||
v = tri.e2;
|
||||
axis22.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z;
|
||||
axis22.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z;
|
||||
axis22.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z;
|
||||
if (!testDerivedAxis(box, tri, axis10, axis22, 10, toBox)) return false;
|
||||
if (!testDerivedAxis(box, tri, axis11, axis22, 11, toBox)) return false;
|
||||
if (!testDerivedAxis(box, tri, axis12, axis22, 12, toBox)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Тестирует пересечение примитивов вдоль заданной оси.
|
||||
*
|
||||
* @param box бокс
|
||||
* @param tri треугольник
|
||||
* @param axis ось
|
||||
* @param axisIndex индекс оси
|
||||
* @param toBox вектор, соединяющий центр треугольника с центром бокса
|
||||
* @return true, если примитивы имеют перекрытие проекций вдоль указанной оси, иначе false
|
||||
*/
|
||||
private function testMainAxis(box:CollisionBox, tri:CollisionTriangle, axis:Vector3, axisIndex:int, toBox:Vector3):Boolean {
|
||||
var overlap:Number = overlapOnAxis(box, tri, axis, toBox);
|
||||
if (overlap < -epsilon) return false;
|
||||
if (overlap + epsilon < minOverlap) {
|
||||
minOverlap = overlap;
|
||||
bestAxisIndex = axisIndex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param box
|
||||
* @param tri
|
||||
* @param axis1
|
||||
* @param axis2
|
||||
* @param axisIndex
|
||||
* @param toBox
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
private function testDerivedAxis(box:CollisionBox, tri:CollisionTriangle, axis1:Vector3, axis2:Vector3, axisIndex:int, toBox:Vector3):Boolean {
|
||||
// axis = axis1 cross axis2
|
||||
axis.x = axis1.y*axis2.z - axis1.z*axis2.y;
|
||||
axis.y = axis1.z*axis2.x - axis1.x*axis2.z;
|
||||
axis.z = axis1.x*axis2.y - axis1.y*axis2.x;
|
||||
var lenSqr:Number = axis.x*axis.x + axis.y*axis.y + axis.z*axis.z;
|
||||
if (lenSqr < 0.0001) return true;
|
||||
var k:Number = 1/Math.sqrt(lenSqr);
|
||||
axis.x *= k;
|
||||
axis.y *= k;
|
||||
axis.z *= k;
|
||||
var overlap:Number = overlapOnAxis(box, tri, axis, toBox);
|
||||
if (overlap < -epsilon) return false;
|
||||
if (overlap + epsilon < minOverlap) {
|
||||
minOverlap = overlap;
|
||||
bestAxisIndex = axisIndex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Рассчитывает величину перекрытия проекций бокса и треугольника на заданную ось.
|
||||
*
|
||||
* @param box бокс
|
||||
* @param tri треугольник
|
||||
* @param axis единичный направляющий вектор оси
|
||||
* @param toBox вектор, соединяющий центр треугольника с центром бокса
|
||||
* @return величина перекрытия. Положительное значение указывает на наличие перекрытия, нулевое или отрицательное значение указывает на отсутствие перекрытия.
|
||||
*/
|
||||
private function overlapOnAxis(box:CollisionBox, tri:CollisionTriangle, axis:Vector3, toBox:Vector3):Number {
|
||||
var t:Matrix4 = box.transform;
|
||||
var projection:Number = (t.a*axis.x + t.e*axis.y + t.i*axis.z)*box.hs.x;
|
||||
if (projection < 0) projection = -projection;
|
||||
var d:Number = (t.b*axis.x + t.f*axis.y + t.j*axis.z)*box.hs.y;
|
||||
projection += d < 0 ? -d : d;
|
||||
d = (t.c*axis.x + t.g*axis.y + t.k*axis.z)*box.hs.z;
|
||||
projection += d < 0 ? -d : d;
|
||||
|
||||
var vectorProjection:Number = toBox.x*axis.x + toBox.y*axis.y + toBox.z*axis.z;
|
||||
// Для оптимизации ось преобразуется в систему треугольника, а не его вершины в мировую систему
|
||||
t = tri.transform;
|
||||
var ax:Number = t.a*axis.x + t.e*axis.y + t.i*axis.z;
|
||||
var ay:Number = t.b*axis.x + t.f*axis.y + t.j*axis.z;
|
||||
var az:Number = t.c*axis.x + t.g*axis.y + t.k*axis.z;
|
||||
var max:Number = 0;
|
||||
if (vectorProjection < 0) {
|
||||
vectorProjection = -vectorProjection;
|
||||
d = tri.v0.x*ax + tri.v0.y*ay + tri.v0.z*az;
|
||||
if (d < max) max = d;
|
||||
d = tri.v1.x*ax + tri.v1.y*ay + tri.v1.z*az;
|
||||
if (d < max) max = d;
|
||||
d = tri.v2.x*ax + tri.v2.y*ay + tri.v2.z*az;
|
||||
if (d < max) max = d;
|
||||
max = -max;
|
||||
} else {
|
||||
d = tri.v0.x*ax + tri.v0.y*ay + tri.v0.z*az;
|
||||
if (d > max) max = d;
|
||||
d = tri.v1.x*ax + tri.v1.y*ay + tri.v1.z*az;
|
||||
if (d > max) max = d;
|
||||
d = tri.v2.x*ax + tri.v2.y*ay + tri.v2.z*az;
|
||||
if (d > max) max = d;
|
||||
}
|
||||
|
||||
return projection + max - vectorProjection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Определяет точки контакта бокса и треугольника.
|
||||
*
|
||||
* @param box бокс
|
||||
* @param tri треугольник
|
||||
* @param toBox вектор, соединяющий центр треугольника с центром бокса
|
||||
* @param faceAxisIndex индекс оси, вдоль которой перекрытие минимально
|
||||
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
private function findFaceContactPoints(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, faceAxisIndex:int, contact:Contact):Boolean {
|
||||
if (faceAxisIndex == 0) {
|
||||
// Столкновение с плоскостью треугольника
|
||||
return getBoxToTriContact(box, tri, toBox, contact);
|
||||
} else {
|
||||
// Столкновение с гранью бокса
|
||||
return getTriToBoxContact(box, tri, toBox, faceAxisIndex, contact);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Определяет точки контакта бокса с плоскостью треугольника.
|
||||
*
|
||||
* @param box бокс
|
||||
* @param tri треугольник
|
||||
* @param toBox вектор, соединяющий центр треугольника с центром бокса
|
||||
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
private function getBoxToTriContact(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, contact:Contact):Boolean {
|
||||
var boxTransform:Matrix4 = box.transform;
|
||||
var triTransform:Matrix4 = tri.transform;
|
||||
|
||||
colNormal.x = triTransform.c;
|
||||
colNormal.y = triTransform.g;
|
||||
colNormal.z = triTransform.k;
|
||||
|
||||
var over:Boolean = toBox.x*colNormal.x + toBox.y*colNormal.y + toBox.z*colNormal.z > 0;
|
||||
if (!over) {
|
||||
colNormal.x = -colNormal.x;
|
||||
colNormal.y = -colNormal.y;
|
||||
colNormal.z = -colNormal.z;
|
||||
}
|
||||
// Ищем ось бокса, определяющую наиболее антипараллельную грань
|
||||
var incFaceAxisIdx:int = 0;
|
||||
var incAxisDot:Number = 0;
|
||||
var maxDot:Number = 0;
|
||||
for (var axisIdx:int = 0; axisIdx < 3; axisIdx++) {
|
||||
boxTransform.getAxis(axisIdx, axis);
|
||||
var dot:Number = axis.x*colNormal.x + axis.y*colNormal.y + axis.z*colNormal.z;
|
||||
var absDot:Number = dot < 0 ? -dot : dot;
|
||||
if (absDot > maxDot) {
|
||||
maxDot = absDot;
|
||||
incAxisDot = dot;
|
||||
incFaceAxisIdx = axisIdx;
|
||||
}
|
||||
}
|
||||
// Обрезка грани
|
||||
var negativeFace:Boolean = incAxisDot > 0;
|
||||
|
||||
var code:int = 1 << (incFaceAxisIdx << 1);
|
||||
if (negativeFace) {
|
||||
code <<= 1;
|
||||
}
|
||||
if ((code & box.excludedFaces) != 0) return false;
|
||||
|
||||
getFaceVertsByAxis(box.hs, incFaceAxisIdx, negativeFace, points1);
|
||||
boxTransform.transformVectorsN(points1, points2, 4);
|
||||
triTransform.transformVectorsInverseN(points2, points1, 4);
|
||||
var pnum:int = clipByTriangle(tri);
|
||||
// Среди конечного списка точек определяются лежащие под плоскостью треугольника
|
||||
var cp:ContactPoint;
|
||||
contact.pcount = 0;
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
var v:Vector3 = points2[i];
|
||||
if ((over && v.z < 0) || (!over && v.z > 0)) {
|
||||
cp = contact.points[contact.pcount++];
|
||||
var cpPos:Vector3 = cp.pos;
|
||||
cpPos.x = triTransform.a*v.x + triTransform.b*v.y + triTransform.c*v.z + triTransform.d;
|
||||
cpPos.y = triTransform.e*v.x + triTransform.f*v.y + triTransform.g*v.z + triTransform.h;
|
||||
cpPos.z = triTransform.i*v.x + triTransform.j*v.y + triTransform.k*v.z + triTransform.l;
|
||||
var r:Vector3 = cp.r1;
|
||||
r.x = cpPos.x - boxTransform.d;
|
||||
r.y = cpPos.y - boxTransform.h;
|
||||
r.z = cpPos.z - boxTransform.l;
|
||||
r = cp.r2;
|
||||
r.x = cpPos.x - triTransform.d;
|
||||
r.y = cpPos.y - triTransform.h;
|
||||
r.z = cpPos.z - triTransform.l;
|
||||
cp.penetration = over ? -v.z : v.z;
|
||||
}
|
||||
}
|
||||
contact.normal.x = colNormal.x;
|
||||
contact.normal.y = colNormal.y;
|
||||
contact.normal.z = colNormal.z;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Оперделяет точки контакта треугольника с гранью бокса.
|
||||
*
|
||||
* @param box бокс
|
||||
* @param tri треугольник
|
||||
* @param toBox вектор, соединяющий центр треугольника с центром бокса
|
||||
* @param faceAxisIndex индекс оси, вдоль которой перекрытие минимально
|
||||
* @param contact переменная, в которую записывается информация о контакте, если пересечение существует
|
||||
* @return true, если пересечение существует, иначе false
|
||||
*/
|
||||
private function getTriToBoxContact(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, faceAxisIdx:int, contact:Contact):Boolean {
|
||||
faceAxisIdx--;
|
||||
|
||||
var boxTransform:Matrix4 = box.transform;
|
||||
var triTransform:Matrix4 = tri.transform;
|
||||
|
||||
boxTransform.getAxis(faceAxisIdx, colNormal);
|
||||
var negativeFace:Boolean = toBox.x*colNormal.x + toBox.y*colNormal.y + toBox.z*colNormal.z > 0;
|
||||
|
||||
var code:int = 1 << (faceAxisIdx << 1);
|
||||
if (negativeFace) {
|
||||
code <<= 1;
|
||||
}
|
||||
if ((code & box.excludedFaces) != 0) return false;
|
||||
|
||||
if (!negativeFace) {
|
||||
colNormal.x = -colNormal.x;
|
||||
colNormal.y = -colNormal.y;
|
||||
colNormal.z = -colNormal.z;
|
||||
}
|
||||
var v:Vector3 = points1[0];
|
||||
v.x = tri.v0.x;
|
||||
v.y = tri.v0.y;
|
||||
v.z = tri.v0.z;
|
||||
|
||||
v = points1[1];
|
||||
v.x = tri.v1.x;
|
||||
v.y = tri.v1.y;
|
||||
v.z = tri.v1.z;
|
||||
|
||||
v = points1[2];
|
||||
v.x = tri.v2.x;
|
||||
v.y = tri.v2.y;
|
||||
v.z = tri.v2.z;
|
||||
|
||||
triTransform.transformVectorsN(points1, points2, 3);
|
||||
boxTransform.transformVectorsInverseN(points2, points1, 3);
|
||||
|
||||
var pnum:int = clipByBox(box.hs, faceAxisIdx);
|
||||
// Проверяем каждую потенциальную точку на принадлежность боксу и добавляем такие точки в список контактов
|
||||
var penetration:Number;
|
||||
contact.pcount = 0;
|
||||
for (var i:int = 0; i < pnum; i++) {
|
||||
v = points1[i];
|
||||
penetration = getPointBoxPenetration(box.hs, v, faceAxisIdx, negativeFace);
|
||||
if (penetration > -epsilon) {
|
||||
var cp:ContactPoint = contact.points[contact.pcount++];
|
||||
var cpPos:Vector3 = cp.pos;
|
||||
cpPos.x = boxTransform.a*v.x + boxTransform.b*v.y + boxTransform.c*v.z + boxTransform.d;
|
||||
cpPos.y = boxTransform.e*v.x + boxTransform.f*v.y + boxTransform.g*v.z + boxTransform.h;
|
||||
cpPos.z = boxTransform.i*v.x + boxTransform.j*v.y + boxTransform.k*v.z + boxTransform.l;
|
||||
var r:Vector3 = cp.r1;
|
||||
r.x = cpPos.x - boxTransform.d;
|
||||
r.y = cpPos.y - boxTransform.h;
|
||||
r.z = cpPos.z - boxTransform.l;
|
||||
r = cp.r2;
|
||||
r.x = cpPos.x - triTransform.d;
|
||||
r.y = cpPos.y - triTransform.h;
|
||||
r.z = cpPos.z - triTransform.l;
|
||||
cp.penetration = penetration;
|
||||
}
|
||||
}
|
||||
contact.normal.x = colNormal.x;
|
||||
contact.normal.y = colNormal.y;
|
||||
contact.normal.z = colNormal.z;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Вычисляет величину проникновения точки в бокс.
|
||||
*
|
||||
* @param hs вектор половинных размеров бокса
|
||||
* @param p точка в системе координат бокса
|
||||
* @param axisIndex индекс оси
|
||||
* @param negativeFace если true, проверяется нижняя грань
|
||||
* @return величина проникновения точки в бокс
|
||||
*/
|
||||
private function getPointBoxPenetration(hs:Vector3, p:Vector3, faceAxisIdx:int, negativeFace:Boolean):Number {
|
||||
switch (faceAxisIdx) {
|
||||
case 0:
|
||||
if (negativeFace) return p.x + hs.x;
|
||||
else return hs.x - p.x;
|
||||
case 1:
|
||||
if (negativeFace) return p.y + hs.y;
|
||||
else return hs.y - p.y;
|
||||
case 2:
|
||||
if (negativeFace) return p.z + hs.z;
|
||||
else return hs.z - p.z;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выполняет обрезку грани, вершины которой заданы в списке points1. Результат сохраняется в этом же списке.
|
||||
*
|
||||
* @param hs вектор половинных размеров бокса, гранью которого обрезается грань второго бокса
|
||||
* @param faceAxisIdx индекс нормальной оси грани, по которой выполняется обрезка
|
||||
* @return количество вершин, получившихся в результате обрезки грани
|
||||
*/
|
||||
private function clipByBox(hs:Vector3, faceAxisIdx:int):int {
|
||||
var pnum:int = 3;
|
||||
switch (faceAxisIdx) {
|
||||
case 0:
|
||||
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, points2, points1, epsilon);
|
||||
case 1:
|
||||
if ((pnum = clipLowZ(-hs.z, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipHighZ(hs.z, pnum, points2, points1, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
return clipHighX(hs.x, pnum, points2, points1, epsilon);
|
||||
case 2:
|
||||
if ((pnum = clipLowX(-hs.x, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipHighX(hs.x, pnum, points2, points1, epsilon)) == 0) return 0;
|
||||
if ((pnum = clipLowY(-hs.y, pnum, points1, points2, epsilon)) == 0) return 0;
|
||||
return clipHighY(hs.y, pnum, points2, points1, epsilon);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрезает грань прямоугольника сторонами треугольника. Обрезка выполняется в системе координат и в проекции на
|
||||
* плоскость треугольника. Входные вершины грани в количестве четырёх штук должны находиться в списке points1.
|
||||
* Конечные вершины сохраняются в списке points2.
|
||||
*
|
||||
* @param tri треугольник
|
||||
* @return количество врешин в конечном списке
|
||||
*/
|
||||
private function clipByTriangle(tri:CollisionTriangle):int {
|
||||
var vnum:int = 4;
|
||||
vnum = clipByLine(tri.v0, tri.e0, points1, vnum, points2);
|
||||
if (vnum == 0) return 0;
|
||||
vnum = clipByLine(tri.v1, tri.e1, points2, vnum, points1);
|
||||
if (vnum == 0) return 0;
|
||||
return clipByLine(tri.v2, tri.e2, points1, vnum, points2);;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обрезает полигон указанной прямой.
|
||||
*
|
||||
* @param linePoint точка на прямой
|
||||
* @param lineDir единичный направляющий вектор прямой
|
||||
* @param verticesIn список вершин исходного полигона
|
||||
* @param vnum количество вершин исходного полигона
|
||||
* @param verticesOut список, куда будут записаны вершины конечного полигона
|
||||
* @return количество вершин конечного полигона
|
||||
*/
|
||||
private function clipByLine(linePoint:Vector3, lineDir:Vector3, verticesIn:Vector.<Vector3>, vnum:int, verticesOut:Vector.<Vector3>):int {
|
||||
var nx:Number = -lineDir.y;
|
||||
var ny:Number = lineDir.x;
|
||||
var offset:Number = linePoint.x*nx + linePoint.y*ny;
|
||||
var v1:Vector3 = verticesIn[int(vnum - 1)];
|
||||
var offset1:Number = v1.x*nx + v1.y*ny;
|
||||
var t:Number;
|
||||
var v:Vector3;
|
||||
var num:int = 0;
|
||||
for (var i:int = 0; i < vnum; i++) {
|
||||
var v2:Vector3 = verticesIn[i];
|
||||
var offset2:Number = v2.x*nx + v2.y*ny;
|
||||
if (offset1 < offset) {
|
||||
// Первая точка ребра во внешней полуплоскости
|
||||
if (offset2 > offset) {
|
||||
// Вторая точка ребра во внутренней полуплоскости, в конечный список добавляется точка на пересечении
|
||||
t = (offset - offset1)/(offset2 - offset1);
|
||||
v = verticesOut[num];
|
||||
v.x = v1.x + t*(v2.x - v1.x);
|
||||
v.y = v1.y + t*(v2.y - v1.y);
|
||||
v.z = v1.z + t*(v2.z - v1.z);
|
||||
num++;
|
||||
}
|
||||
} else {
|
||||
// Первая точка ребра во внутренней полуплоскости. Добавляем её в конечный список.
|
||||
v = verticesOut[num];
|
||||
v.x = v1.x;
|
||||
v.y = v1.y;
|
||||
v.z = v1.z;
|
||||
num++;
|
||||
if (offset2 < offset) {
|
||||
// Вторая точка ребра во внешней полуплоскости, в конечный список добавляется точка на пересечении
|
||||
t = (offset - offset1)/(offset2 - offset1);
|
||||
v = verticesOut[num];
|
||||
v.x = v1.x + t*(v2.x - v1.x);
|
||||
v.y = v1.y + t*(v2.y - v1.y);
|
||||
v.z = v1.z + t*(v2.z - v1.z);
|
||||
num++;
|
||||
}
|
||||
}
|
||||
v1 = v2;
|
||||
offset1 = offset2;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param box
|
||||
* @param tri
|
||||
* @param toBox
|
||||
* @param boxAxisIdx
|
||||
* @param triAxisIdx
|
||||
* @param contact
|
||||
*/
|
||||
private function findEdgesIntersection(box:CollisionBox, tri:CollisionTriangle, toBox:Vector3, boxAxisIdx:int, triAxisIdx:int, contact:Contact):Boolean {
|
||||
// Определение точки и направляющего вектора ребра треугольника
|
||||
var tmpx1:Number;
|
||||
var tmpy1:Number;
|
||||
var tmpz1:Number;
|
||||
var tmpx2:Number;
|
||||
var tmpy2:Number;
|
||||
var tmpz2:Number;
|
||||
switch (triAxisIdx) {
|
||||
case 0:
|
||||
tmpx1 = tri.e0.x;
|
||||
tmpy1 = tri.e0.y;
|
||||
tmpz1 = tri.e0.z;
|
||||
tmpx2 = tri.v0.x;
|
||||
tmpy2 = tri.v0.y;
|
||||
tmpz2 = tri.v0.z;
|
||||
break;
|
||||
case 1:
|
||||
tmpx1 = tri.e1.x;
|
||||
tmpy1 = tri.e1.y;
|
||||
tmpz1 = tri.e1.z;
|
||||
tmpx2 = tri.v1.x;
|
||||
tmpy2 = tri.v1.y;
|
||||
tmpz2 = tri.v1.z;
|
||||
break;
|
||||
case 2:
|
||||
tmpx1 = tri.e2.x;
|
||||
tmpy1 = tri.e2.y;
|
||||
tmpz1 = tri.e2.z;
|
||||
tmpx2 = tri.v2.x;
|
||||
tmpy2 = tri.v2.y;
|
||||
tmpz2 = tri.v2.z;
|
||||
break;
|
||||
}
|
||||
var triTransform:Matrix4 = tri.transform;
|
||||
axis20.x = triTransform.a*tmpx1 + triTransform.b*tmpy1 + triTransform.c*tmpz1;
|
||||
axis20.y = triTransform.e*tmpx1 + triTransform.f*tmpy1 + triTransform.g*tmpz1;
|
||||
axis20.z = triTransform.i*tmpx1 + triTransform.j*tmpy1 + triTransform.k*tmpz1;
|
||||
var x2:Number = triTransform.a*tmpx2 + triTransform.b*tmpy2 + triTransform.c*tmpz2 + triTransform.d;
|
||||
var y2:Number = triTransform.e*tmpx2 + triTransform.f*tmpy2 + triTransform.g*tmpz2 + triTransform.h;
|
||||
var z2:Number = triTransform.i*tmpx2 + triTransform.j*tmpy2 + triTransform.k*tmpz2 + triTransform.l;
|
||||
|
||||
// Определение нормали контакта, точки и направляющего вектора ребра бокса
|
||||
var boxTransform:Matrix4 = box.transform;
|
||||
boxTransform.getAxis(boxAxisIdx, axis10);
|
||||
|
||||
// Нормаль контакта
|
||||
var v:Vector3 = contact.normal;
|
||||
v.x = axis10.y*axis20.z - axis10.z*axis20.y;
|
||||
v.y = axis10.z*axis20.x - axis10.x*axis20.z;
|
||||
v.z = axis10.x*axis20.y - axis10.y*axis20.x;
|
||||
k = 1/Math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
|
||||
v.x *= k;
|
||||
v.y *= k;
|
||||
v.z *= k;
|
||||
// Разворот нормали в сторону бокса
|
||||
if (v.x*toBox.x + v.y*toBox.y + v.z*toBox.z < 0) {
|
||||
v.x = -v.x;
|
||||
v.y = -v.y;
|
||||
v.z = -v.z;
|
||||
}
|
||||
|
||||
var boxHalfLen:Number;
|
||||
tmpx1 = box.hs.x;
|
||||
tmpy1 = box.hs.y;
|
||||
tmpz1 = box.hs.z;
|
||||
// X
|
||||
if (boxAxisIdx == 0) {
|
||||
tmpx1 = 0;
|
||||
boxHalfLen = box.hs.x;
|
||||
} else {
|
||||
if (boxTransform.a*v.x + boxTransform.e*v.y + boxTransform.i*v.z > 0) {
|
||||
tmpx1 = -tmpx1;
|
||||
if ((box.excludedFaces & 2) != 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((box.excludedFaces & 1) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Y
|
||||
if (boxAxisIdx == 1) {
|
||||
tmpy1 = 0;
|
||||
boxHalfLen = box.hs.y;
|
||||
} else {
|
||||
if (boxTransform.b*v.x + boxTransform.f*v.y + boxTransform.j*v.z > 0) {
|
||||
tmpy1 = -tmpy1;
|
||||
if ((box.excludedFaces & 8) != 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((box.excludedFaces & 4) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Z
|
||||
if (boxAxisIdx == 2) {
|
||||
tmpz1 = 0;
|
||||
boxHalfLen = box.hs.z;
|
||||
} else {
|
||||
if (boxTransform.c*v.x + boxTransform.g*v.y + boxTransform.k*v.z > 0) {
|
||||
tmpz1 = -tmpz1;
|
||||
if ((box.excludedFaces & 32) != 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if ((box.excludedFaces & 16) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
var x1:Number = boxTransform.a*tmpx1 + boxTransform.b*tmpy1 + boxTransform.c*tmpz1 + boxTransform.d;
|
||||
var y1:Number = boxTransform.e*tmpx1 + boxTransform.f*tmpy1 + boxTransform.g*tmpz1 + boxTransform.h;
|
||||
var z1:Number = boxTransform.i*tmpx1 + boxTransform.j*tmpy1 + boxTransform.k*tmpz1 + boxTransform.l;
|
||||
|
||||
// Находим точку пересечения рёбер, решая систему уравнений
|
||||
// axis10 - направляющий вектор ребра бокса
|
||||
// x1, y1, z1 - средняя точка ребра бокса
|
||||
// axis20 - направляющий вектор ребра треугольника
|
||||
// x2, y2, z2 - начальная точка ребра треугольника
|
||||
var k:Number = axis10.x*axis20.x + axis10.y*axis20.y + axis10.z*axis20.z;
|
||||
var det:Number = k*k - 1;
|
||||
var vx:Number = x2 - x1;
|
||||
var vy:Number = y2 - y1;
|
||||
var vz:Number = z2 - z1;
|
||||
var c1:Number = axis10.x*vx + axis10.y*vy + axis10.z*vz;
|
||||
var c2:Number = axis20.x*vx + axis20.y*vy + axis20.z*vz;
|
||||
var t1:Number = (c2*k - c1)/det;
|
||||
var t2:Number = (c2 - c1*k)/det;
|
||||
|
||||
// Запись данных о контакте
|
||||
contact.pcount = 1;
|
||||
var cp:ContactPoint = contact.points[0];
|
||||
cp.penetration = minOverlap;
|
||||
v = cp.pos;
|
||||
// Точка столкновения вычисляется как среднее между ближайшими точками на рёбрах
|
||||
v.x = 0.5*(x1 + axis10.x*t1 + x2 + axis20.x*t2);
|
||||
v.y = 0.5*(y1 + axis10.y*t1 + y2 + axis20.y*t2);
|
||||
v.z = 0.5*(z1 + axis10.z*t1 + z2 + axis20.z*t2);
|
||||
var r:Vector3 = cp.r1;
|
||||
r.x = v.x - boxTransform.d;
|
||||
r.y = v.y - boxTransform.h;
|
||||
r.z = v.z - boxTransform.l;
|
||||
r = cp.r2;
|
||||
r.x = v.x - triTransform.d;
|
||||
r.y = v.y - triTransform.h;
|
||||
r.z = v.z - triTransform.l;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user