mirror of
				https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
				synced 2025-10-30 00:49:12 -07:00 
			
		
		
		
	Remove .svn folders
This commit is contained in:
		| @@ -1,17 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 80 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/.settings | ||||
| END | ||||
| org.eclipse.core.resources.prefs | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 113 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/.settings/org.eclipse.core.resources.prefs | ||||
| END | ||||
| org.eclipse.ltk.core.refactoring.prefs | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 119 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/.settings/org.eclipse.ltk.core.refactoring.prefs | ||||
| END | ||||
| @@ -1,52 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/.settings | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| org.eclipse.core.resources.prefs | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 63644c8a8f9fe441148cb750eb3dc2f3 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| org.eclipse.ltk.core.refactoring.prefs | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 85719ffc1d818e46b40e2f90aad31e8c | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
| @@ -1,3 +0,0 @@ | ||||
| #Thu Feb 14 09:12:30 YEKT 2008 | ||||
| eclipse.preferences.version=1 | ||||
| encoding/<project>=UTF-8 | ||||
| @@ -1,3 +0,0 @@ | ||||
| #Tue Nov 13 17:53:36 YEKT 2007 | ||||
| eclipse.preferences.version=1 | ||||
| org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false | ||||
| @@ -1,29 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 70 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1 | ||||
| END | ||||
| .flexLibProperties | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 89 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/.flexLibProperties | ||||
| END | ||||
| .project | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 79 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/.project | ||||
| END | ||||
| manifest.xml | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 83 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/manifest.xml | ||||
| END | ||||
| .actionScriptProperties | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 94 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/.actionScriptProperties | ||||
| END | ||||
| @@ -1,13 +0,0 @@ | ||||
| K 10 | ||||
| svn:ignore | ||||
| V 46 | ||||
| help | ||||
| Alternativa3D API Documentation.launch | ||||
|  | ||||
| K 13 | ||||
| svn:mergeinfo | ||||
| V 187 | ||||
| /platform/clients/fp10/libraries/Alternativa3D/trunk:306-495 | ||||
| /platform/clients/fp9/libraries/Alternativa3D/branches/5.4:304-463 | ||||
| /platform/clients/fp9/libraries/Alternativa3D/trunk:304-494 | ||||
| END | ||||
| @@ -1,82 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1 | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-09-08T10:19:31.619199Z | ||||
| 497 | ||||
| mike | ||||
| has-props | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| .flexLibProperties | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 92b1825c4919e233344817ef71ffff54 | ||||
| 2008-08-29T11:42:07.353094Z | ||||
| 305 | ||||
| wolf | ||||
|  | ||||
| .project | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 5cfe073356c81d48a97889a21f8ec909 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| manifest.xml | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 2e2b1aa9d128d192e5f544209f610433 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| alternativa | ||||
| dir | ||||
|  | ||||
| .actionScriptProperties | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| a4a624fd1b8a5127de717a90589b5141 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| .settings | ||||
| dir | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
| @@ -1,36 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <actionScriptProperties mainApplicationPath="Engine3DLibrary.as" version="3"> | ||||
|   <compiler additionalCompilerArguments="-compute-digest=false" copyDependentFiles="false" enableModuleDebug="false" flexSDK="Flex 4" generateAccessible="false" htmlExpressInstall="true" htmlGenerate="false" htmlHistoryManagement="false" htmlPlayerVersion="10.0.0" htmlPlayerVersionCheck="true" outputFolderPath="" strict="true" useApolloConfig="false" verifyDigests="true" warn="true"> | ||||
|     <compilerSourcePath/> | ||||
|     <libraryPath defaultLinkType="1"> | ||||
|       <libraryPathEntry kind="4" path=""> | ||||
|         <excludedEntries> | ||||
|           <libraryPathEntry kind="1" linkType="1" path="${PROJECT_FRAMEWORKS}/locale/{locale}"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/text_model.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/qtp.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/player/{targetPlayerMajorVersion}" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/framework.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/text_importExport.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/flex4.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="2" path="${PROJECT_FRAMEWORKS}/libs/utilities.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/text_edit.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/flex.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/rpc.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/datavisualization.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_dmv.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_flashflexkit.swc" useDefaultLinkType="false"/> | ||||
|           <libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_agent.swc" useDefaultLinkType="false"/> | ||||
|         </excludedEntries> | ||||
|       </libraryPathEntry> | ||||
|       <libraryPathEntry kind="3" linkType="2" path="/AlternativaTypes/AlternativaTypes.swc" useDefaultLinkType="false"/> | ||||
|       <libraryPathEntry kind="3" linkType="2" path="/AlternativaUtils/AlternativaUtils.swc" useDefaultLinkType="false"/> | ||||
|     </libraryPath> | ||||
|     <sourceAttachmentPath/> | ||||
|   </compiler> | ||||
|   <applications> | ||||
|     <application path="Engine3DLibrary.as"/> | ||||
|   </applications> | ||||
|   <modules/> | ||||
|   <buildCSSFiles/> | ||||
| </actionScriptProperties> | ||||
| @@ -1,63 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <flexLibProperties version="1"> | ||||
|   <includeClasses> | ||||
|     <classEntry path="alternativa.Alternativa3D"/> | ||||
|     <classEntry path="alternativa.engine3d.alternativa3d"/> | ||||
|     <classEntry path="alternativa.engine3d.controllers.CameraController"/> | ||||
|     <classEntry path="alternativa.engine3d.controllers.FlyController"/> | ||||
|     <classEntry path="alternativa.engine3d.controllers.ObjectController"/> | ||||
|     <classEntry path="alternativa.engine3d.controllers.WalkController"/> | ||||
|     <classEntry path="alternativa.engine3d.core.BSPNode"/> | ||||
|     <classEntry path="alternativa.engine3d.core.Camera3D"/> | ||||
|     <classEntry path="alternativa.engine3d.core.Face"/> | ||||
|     <classEntry path="alternativa.engine3d.core.Mesh"/> | ||||
|     <classEntry path="alternativa.engine3d.core.Object3D"/> | ||||
|     <classEntry path="alternativa.engine3d.core.Operation"/> | ||||
|     <classEntry path="alternativa.engine3d.core.PolyPrimitive"/> | ||||
|     <classEntry path="alternativa.engine3d.core.Scene3D"/> | ||||
|     <classEntry path="alternativa.engine3d.core.Surface"/> | ||||
|     <classEntry path="alternativa.engine3d.core.Vertex"/> | ||||
|     <classEntry path="alternativa.engine3d.display.Skin"/> | ||||
|     <classEntry path="alternativa.engine3d.display.View"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.Engine3DError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.FaceExistsError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.FaceNeedMoreVerticesError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.FaceNotFoundError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.InvalidIDError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.Object3DHierarchyError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.Object3DNotFoundError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.ObjectExistsError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.ObjectNotFoundError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.SurfaceExistsError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.SurfaceNotFoundError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.VertexExistsError"/> | ||||
|     <classEntry path="alternativa.engine3d.errors.VertexNotFoundError"/> | ||||
|     <classEntry path="alternativa.engine3d.loaders.Loader3DS"/> | ||||
|     <classEntry path="alternativa.engine3d.loaders.LoaderMTL"/> | ||||
|     <classEntry path="alternativa.engine3d.loaders.LoaderOBJ"/> | ||||
|     <classEntry path="alternativa.engine3d.loaders.MTLTextureMapInfo"/> | ||||
|     <classEntry path="alternativa.engine3d.loaders.MaterialInfo"/> | ||||
|     <classEntry path="alternativa.engine3d.materials.DrawPoint"/> | ||||
|     <classEntry path="alternativa.engine3d.materials.FillMaterial"/> | ||||
|     <classEntry path="alternativa.engine3d.materials.Material"/> | ||||
|     <classEntry path="alternativa.engine3d.materials.SurfaceMaterial"/> | ||||
|     <classEntry path="alternativa.engine3d.materials.TextureMaterial"/> | ||||
|     <classEntry path="alternativa.engine3d.materials.TextureMaterialPrecision"/> | ||||
|     <classEntry path="alternativa.engine3d.materials.WireMaterial"/> | ||||
|     <classEntry path="alternativa.engine3d.physics.Collision"/> | ||||
|     <classEntry path="alternativa.engine3d.physics.CollisionPlane"/> | ||||
|     <classEntry path="alternativa.engine3d.physics.CollisionSetMode"/> | ||||
|     <classEntry path="alternativa.engine3d.physics.EllipsoidCollider"/> | ||||
|     <classEntry path="alternativa.engine3d.primitives.Box"/> | ||||
|     <classEntry path="alternativa.engine3d.primitives.Cone"/> | ||||
|     <classEntry path="alternativa.engine3d.primitives.GeoPlane"/> | ||||
|     <classEntry path="alternativa.engine3d.primitives.GeoSphere"/> | ||||
|     <classEntry path="alternativa.engine3d.primitives.Plane"/> | ||||
|     <classEntry path="alternativa.engine3d.primitives.Sphere"/> | ||||
|     <classEntry path="alternativa.utils.MeshUtils"/> | ||||
|   </includeClasses> | ||||
|   <includeResources/> | ||||
|   <namespaceManifests> | ||||
|     <namespaceManifestEntry manifest="manifest.xml" namespace="http://alternativaplatform.com"/> | ||||
|   </namespaceManifests> | ||||
| </flexLibProperties> | ||||
| @@ -1,18 +0,0 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <projectDescription> | ||||
| 	<name>Alternativa3D</name> | ||||
| 	<comment></comment> | ||||
| 	<projects> | ||||
| 	</projects> | ||||
| 	<buildSpec> | ||||
| 		<buildCommand> | ||||
| 			<name>com.adobe.flexbuilder.project.flexbuilder</name> | ||||
| 			<arguments> | ||||
| 			</arguments> | ||||
| 		</buildCommand> | ||||
| 	</buildSpec> | ||||
| 	<natures> | ||||
| 		<nature>com.adobe.flexbuilder.project.flexlibnature</nature> | ||||
| 		<nature>com.adobe.flexbuilder.project.actionscriptnature</nature> | ||||
| 	</natures> | ||||
| </projectDescription> | ||||
| @@ -1,4 +0,0 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <componentPackage> | ||||
| 	<component id="Alternativa3D" class="alternativa.Alternativa3D"/> | ||||
| </componentPackage> | ||||
| @@ -1,11 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 82 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa | ||||
| END | ||||
| Alternativa3D.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 99 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/Alternativa3D.as | ||||
| END | ||||
| @@ -1,46 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-09-08T10:18:35.971715Z | ||||
| 496 | ||||
| mike | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| utils | ||||
| dir | ||||
|  | ||||
| Alternativa3D.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 18109d1146c9ae7270dff914e704698e | ||||
| 2008-09-08T08:01:08.116493Z | ||||
| 478 | ||||
| mike | ||||
|  | ||||
| engine3d | ||||
| dir | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
| @@ -1,14 +0,0 @@ | ||||
| package alternativa { | ||||
|  | ||||
| 	/** | ||||
| 	 * Класс содержит информацию о версии библиотеки. | ||||
| 	 * Также используется для интеграции библиотеки в среду разработки Adobe Flash. | ||||
| 	 */ | ||||
| 	public class Alternativa3D { | ||||
|  | ||||
| 		/** | ||||
| 		 * Версия библиотеки в формате: поколение.feature-версия.fix-версия | ||||
| 		 */ | ||||
| 		public static const version:String = "5.4.1"; | ||||
| 	} | ||||
| } | ||||
| @@ -1,11 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 91 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d | ||||
| END | ||||
| alternativa3d.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 108 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/alternativa3d.as | ||||
| END | ||||
| @@ -1,64 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-09-08T10:18:35.971715Z | ||||
| 496 | ||||
| mike | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| materials | ||||
| dir | ||||
|  | ||||
| physics | ||||
| dir | ||||
|  | ||||
| alternativa3d.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 64183f832985e252cc4bc98977484bc9 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| display | ||||
| dir | ||||
|  | ||||
| controllers | ||||
| dir | ||||
|  | ||||
| core | ||||
| dir | ||||
|  | ||||
| loaders | ||||
| dir | ||||
|  | ||||
| primitives | ||||
| dir | ||||
|  | ||||
| errors | ||||
| dir | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
| @@ -1,3 +0,0 @@ | ||||
| package alternativa.engine3d { | ||||
| 	public namespace alternativa3d = "http://alternativaplatform.com/en/alternativa3d"; | ||||
| } | ||||
| @@ -1,29 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 103 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers | ||||
| END | ||||
| FlyController.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 120 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers/FlyController.as | ||||
| END | ||||
| WalkController.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 121 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers/WalkController.as | ||||
| END | ||||
| ObjectController.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 123 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers/ObjectController.as | ||||
| END | ||||
| CameraController.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 123 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers/CameraController.as | ||||
| END | ||||
| @@ -1,76 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/controllers | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| FlyController.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 819162f5f890b890eca280272d1c4dd8 | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| WalkController.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 67cb497ffc9a536d65065dacb16a99be | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| ObjectController.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 20fdfabe14732c8fb81c041f142da4a4 | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| CameraController.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| cc1c7ecdc58c115dcdab53d2ea0ef1d9 | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
| @@ -1,889 +0,0 @@ | ||||
| package alternativa.engine3d.controllers { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.engine3d.physics.EllipsoidCollider; | ||||
| 	import alternativa.types.Map; | ||||
| 	import alternativa.types.Matrix3D; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.types.Set; | ||||
| 	import alternativa.utils.KeyboardUtils; | ||||
| 	import alternativa.utils.MathUtils; | ||||
| 	 | ||||
| 	import flash.display.DisplayObject; | ||||
| 	import flash.events.KeyboardEvent; | ||||
| 	import flash.events.MouseEvent; | ||||
| 	import flash.geom.Point; | ||||
| 	import flash.utils.getTimer; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
|  | ||||
| 	/** | ||||
| 	 * Контроллер камеры. Контроллер обеспечивает управление движением и поворотами камеры с использованием | ||||
| 	 * клавиатуры и мыши, а также предоставляет простую проверку столкновений камеры с объектами сцены. | ||||
| 	 */	 | ||||
| 	public class CameraController	{ | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения вперёд. | ||||
| 		 */ | ||||
| 		public static const ACTION_FORWARD:String = "ACTION_FORWARD"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения назад. | ||||
| 		 */ | ||||
| 		public static const ACTION_BACK:String = "ACTION_BACK"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения влево. | ||||
| 		 */ | ||||
| 		public static const ACTION_LEFT:String = "ACTION_LEFT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения вправо. | ||||
| 		 */ | ||||
| 		public static const ACTION_RIGHT:String = "ACTION_RIGHT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения вверх. | ||||
| 		 */ | ||||
| 		public static const ACTION_UP:String = "ACTION_UP"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения вниз. | ||||
| 		 */ | ||||
| 		public static const ACTION_DOWN:String = "ACTION_DOWN"; | ||||
| //		public static const ACTION_ROLL_LEFT:String = "ACTION_ROLL_LEFT"; | ||||
| //		public static const ACTION_ROLL_RIGHT:String = "ACTION_ROLL_RIGHT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш увеличения угла тангажа. | ||||
| 		 */ | ||||
| 		public static const ACTION_PITCH_UP:String = "ACTION_PITCH_UP"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш уменьшения угла тангажа. | ||||
| 		 */ | ||||
| 		public static const ACTION_PITCH_DOWN:String = "ACTION_PITCH_DOWN"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш уменьшения угла рысканья (поворота налево). | ||||
| 		 */ | ||||
| 		public static const ACTION_YAW_LEFT:String = "ACTION_YAW_LEFT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш увеличения угла рысканья (поворота направо). | ||||
| 		 */ | ||||
| 		public static const ACTION_YAW_RIGHT:String = "ACTION_YAW_RIGHT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш увеличения скорости. | ||||
| 		 */ | ||||
| 		public static const ACTION_ACCELERATE:String = "ACTION_ACCELERATE"; | ||||
|  | ||||
| 		// флаги действий | ||||
| 		private var _forward:Boolean; | ||||
| 		private var _back:Boolean; | ||||
| 		private var _left:Boolean; | ||||
| 		private var _right:Boolean; | ||||
| 		private var _up:Boolean; | ||||
| 		private var _down:Boolean; | ||||
| 		private var _pitchUp:Boolean; | ||||
| 		private var _pitchDown:Boolean; | ||||
| 		private var _yawLeft:Boolean; | ||||
| 		private var _yawRight:Boolean; | ||||
| 		private var _accelerate:Boolean; | ||||
| 		 | ||||
| 		private var _moveLocal:Boolean = true; | ||||
|  | ||||
| 		// Флаг включения управления камерой | ||||
| 		private var _controlsEnabled:Boolean = false; | ||||
|  | ||||
| 		// Значение таймера в начале прошлого кадра | ||||
| 		private var lastFrameTime:uint; | ||||
| 		 | ||||
| 		// Чувствительность обзора мышью. Коэффициент умножения базовых коэффициентов поворотов. | ||||
| 		private var _mouseSensitivity:Number = 1; | ||||
| 		// Коэффициент поворота камеры по тангажу. Значение угла в радианах на один пиксель перемещения мыши по вертикали. | ||||
| 		private var _mousePitch:Number = Math.PI / 360; | ||||
| 		// Результирующий коэффициент поворота камеры мышью по тангажу | ||||
| 		private var _mousePitchCoeff:Number = _mouseSensitivity * _mousePitch; | ||||
| 		// Коэффициент поворота камеры по рысканью. Значение угла в радианах на один пиксель перемещения мыши по горизонтали. | ||||
| 		private var _mouseYaw:Number = Math.PI / 360; | ||||
| 		// Результирующий коэффициент поворота камеры мышью по расканью | ||||
| 		private var _mouseYawCoeff:Number = _mouseSensitivity * _mouseYaw; | ||||
| 		 | ||||
| 		// Вспомогательные переменные для обзора мышью | ||||
| 		private var mouseLookActive:Boolean; | ||||
| 		private var startDragCoords:Point = new Point(); | ||||
| 		private var currentDragCoords:Point = new Point(); | ||||
| 		private var prevDragCoords:Point = new Point(); | ||||
| 		private var startRotX:Number; | ||||
| 		private var startRotZ:Number; | ||||
|  | ||||
| 		// Скорость изменения тангажа в радианах за секунду при управлении с клавиатуры | ||||
| 		private var _pitchSpeed:Number = 1; | ||||
| 		// Скорость изменения рысканья в радианах за секунду при управлении с клавиатуры | ||||
| 		private var _yawSpeed:Number = 1; | ||||
| 		// Скорость изменения крена в радианах за секунду при управлении с клавиатуры | ||||
| //		public var bankSpeed:Number = 2; | ||||
| //		private var bankMatrix:Matrix3D = new Matrix3D(); | ||||
|  | ||||
| 		// Скорость поступательного движения в единицах за секунду | ||||
| 		private var _speed:Number = 100; | ||||
| 		// Коэффициент увеличения скорости при соответствующей нажатой клавише | ||||
| 		private var _speedMultiplier:Number = 2; | ||||
| 		 | ||||
| 		private var velocity:Point3D = new Point3D(); | ||||
| 		private var destination:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		private var _fovStep:Number = Math.PI / 180; | ||||
| 		private var _zoomMultiplier:Number = 0.1; | ||||
| 		 | ||||
| 		// Привязка клавиш к действиям | ||||
| 		private var keyBindings:Map = new Map(); | ||||
| 		// Привязка действий к обработчикам | ||||
| 		private var actionBindings:Map = new Map(); | ||||
| 		 | ||||
| 		// Источник событий клавиатуры и мыши | ||||
| 		private var _eventsSource:DisplayObject; | ||||
| 		// Управляемая камера | ||||
| 		private var _camera:Camera3D;  | ||||
|  | ||||
| 		// Класс реализации определния столкновений | ||||
| 		private var _collider:EllipsoidCollider; | ||||
| 		// Флаг необходимости проверки столкновений | ||||
| 		private var _checkCollisions:Boolean; | ||||
| 		// Радиус сферы для определения столкновений | ||||
| 		private var _collisionRadius:Number = 0; | ||||
| 		// Набор исключаемых из проверки столкновений объектов | ||||
| 		private var _collisionIgnoreSet:Set = new Set(true); | ||||
| 		// Флаг движения | ||||
| 		private var _isMoving:Boolean; | ||||
| 		 | ||||
| 		private var _onStartMoving:Function; | ||||
| 		private var _onStopMoving:Function; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра контроллера. | ||||
| 		 *  | ||||
| 		 * @param eventsSourceObject объект, используемый для получения событий мыши и клавиатуры | ||||
| 		 */ | ||||
| 		public function CameraController(eventsSourceObject:DisplayObject) { | ||||
| 			if (eventsSourceObject == null) { | ||||
| 				throw new ArgumentError("CameraController: eventsSource is null"); | ||||
| 			} | ||||
| 			_eventsSource = eventsSourceObject; | ||||
| 			 | ||||
| 			actionBindings[ACTION_FORWARD] = forward; | ||||
| 			actionBindings[ACTION_BACK] = back; | ||||
| 			actionBindings[ACTION_LEFT] = left; | ||||
| 			actionBindings[ACTION_RIGHT] = right; | ||||
| 			actionBindings[ACTION_UP] = up; | ||||
| 			actionBindings[ACTION_DOWN] = down; | ||||
| 			actionBindings[ACTION_PITCH_UP] = pitchUp; | ||||
| 			actionBindings[ACTION_PITCH_DOWN] = pitchDown; | ||||
| 			actionBindings[ACTION_YAW_LEFT] = yawLeft; | ||||
| 			actionBindings[ACTION_YAW_RIGHT] = yawRight; | ||||
| 			actionBindings[ACTION_ACCELERATE] = accelerate; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Установка привязки клавиш по умолчанию. Данный метод очищает все существующие привязки клавиш и устанавливает следующие: | ||||
| 		 * <table border="1" style="border-collapse: collapse"> | ||||
| 		 * <tr><th>Клавиша</th><th>Действие</th></tr> | ||||
| 		 * <tr><td>W</td><td>ACTION_FORWARD</td></tr> | ||||
| 		 * <tr><td>S</td><td>ACTION_BACK</td></tr> | ||||
| 		 * <tr><td>A</td><td>ACTION_LEFT</td></tr> | ||||
| 		 * <tr><td>D</td><td>ACTION_RIGHT</td></tr> | ||||
| 		 * <tr><td>SPACE</td><td>ACTION_UP</td></tr> | ||||
| 		 * <tr><td>CONTROL</td><td>ACTION_DOWN</td></tr> | ||||
| 		 * <tr><td>SHIFT</td><td>ACTION_ACCELERATE</td></tr> | ||||
| 		 * <tr><td>UP</td><td>ACTION_PITCH_UP</td></tr> | ||||
| 		 * <tr><td>DOWN</td><td>ACTION_PITCH_DOWN</td></tr> | ||||
| 		 * <tr><td>LEFT</td><td>ACTION_YAW_LEFT</td></tr> | ||||
| 		 * <tr><td>RIGHT</td><td>ACTION_YAW_RIGHT</td></tr> | ||||
| 		 * </table> | ||||
| 		 */ | ||||
| 		public function setDefaultBindings():void { | ||||
| 			unbindAll(); | ||||
| 			bindKey(KeyboardUtils.W, ACTION_FORWARD); | ||||
| 			bindKey(KeyboardUtils.S, ACTION_BACK); | ||||
| 			bindKey(KeyboardUtils.A, ACTION_LEFT); | ||||
| 			bindKey(KeyboardUtils.D, ACTION_RIGHT); | ||||
| 			bindKey(KeyboardUtils.SPACE, ACTION_UP); | ||||
| 			bindKey(KeyboardUtils.CONTROL, ACTION_DOWN); | ||||
| 			bindKey(KeyboardUtils.UP, ACTION_PITCH_UP); | ||||
| 			bindKey(KeyboardUtils.DOWN, ACTION_PITCH_DOWN); | ||||
| 			bindKey(KeyboardUtils.LEFT, ACTION_YAW_LEFT); | ||||
| 			bindKey(KeyboardUtils.RIGHT, ACTION_YAW_RIGHT); | ||||
| 			bindKey(KeyboardUtils.SHIFT, ACTION_ACCELERATE); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Направление камеры на точку. | ||||
| 		 *  | ||||
| 		 * @param point координаты точки направления камеры | ||||
| 		 */ | ||||
| 		public function lookAt(point:Point3D):void { | ||||
| 			if (_camera == null) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var dx:Number = point.x - _camera.x; | ||||
| 			var dy:Number = point.y - _camera.y; | ||||
| 			var dz:Number = point.z - _camera.z; | ||||
| 			_camera.rotationZ = -Math.atan2(dx, dy); | ||||
| 			_camera.rotationX = Math.atan2(dz, Math.sqrt(dx * dx + dy * dy)) - MathUtils.DEG90; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Callback-функция, вызываемая при начале движения камеры. | ||||
| 		 */ | ||||
| 		public function get onStartMoving():Function { | ||||
| 			return _onStartMoving; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set onStartMoving(value:Function):void { | ||||
| 			_onStartMoving = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Callback-функция, вызываемая при прекращении движения камеры. | ||||
| 		 */ | ||||
| 		public function get onStopMoving():Function { | ||||
| 			return _onStopMoving; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set onStopMoving(value:Function):void { | ||||
| 			_onStopMoving = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Набор объектов, исключаемых из проверки столкновений. | ||||
| 		 */		 | ||||
| 		public function get collisionIgnoreSet():Set { | ||||
| 			return _collisionIgnoreSet; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Источник событий клавиатуры и мыши. | ||||
| 		 */ | ||||
| 		public function get eventsSource():DisplayObject { | ||||
| 			return _eventsSource; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set eventsSource(value:DisplayObject):void { | ||||
| 			if (_eventsSource != value) { | ||||
| 				if (value == null) { | ||||
| 					throw new ArgumentError("CameraController: eventsSource is null"); | ||||
| 				} | ||||
| 				if (_controlsEnabled) { | ||||
| 					unregisterEventsListeners(); | ||||
| 				} | ||||
| 				_eventsSource = value; | ||||
| 				if (_controlsEnabled) { | ||||
| 					registerEventListeners(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Ассоциированная камера. | ||||
| 		 */ | ||||
| 		public function get camera():Camera3D { | ||||
| 			return _camera; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set camera(value:Camera3D):void { | ||||
| 			if (_camera != value) { | ||||
| 				_camera = value; | ||||
| 				if (value == null) { | ||||
| 					controlsEnabled = false; | ||||
| 				} else { | ||||
| 					createCollider(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Режим движения камеры. Если значение равно <code>true</code>, то перемещения камеры происходят относительно | ||||
| 		 * локальной системы координат, иначе относительно глобальной. | ||||
| 		 *  | ||||
| 		 * @default true  | ||||
| 		 */ | ||||
| 		public function get moveLocal():Boolean { | ||||
| 			return _moveLocal; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set moveLocal(value:Boolean):void { | ||||
| 			_moveLocal = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение режима проверки столкновений. | ||||
| 		 */ | ||||
| 		public function get checkCollisions():Boolean { | ||||
| 			return _checkCollisions; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set checkCollisions(value:Boolean):void { | ||||
| 			_checkCollisions = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Радиус сферы для определения столкновений. | ||||
| 		 *  | ||||
| 		 * @default 0 | ||||
| 		 */ | ||||
| 		public function get collisionRadius():Number { | ||||
| 			return _collisionRadius; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set collisionRadius(value:Number):void { | ||||
| 			_collisionRadius = value; | ||||
| 			if (_collider != null) { | ||||
| 				_collider.radiusX = _collisionRadius; | ||||
| 				_collider.radiusY = _collisionRadius; | ||||
| 				_collider.radiusZ = _collisionRadius; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Привязка клавиши к действию. | ||||
| 		 *  | ||||
| 		 * @param keyCode код клавиши | ||||
| 		 * @param action наименование действия | ||||
| 		 */ | ||||
| 		public function bindKey(keyCode:uint, action:String):void { | ||||
| 			var method:Function = actionBindings[action]; | ||||
| 			if (method != null) { | ||||
| 				keyBindings[keyCode] = method; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Очистка привязки клавиши. | ||||
| 		 *  | ||||
| 		 * @param keyCode код клавиши | ||||
| 		 */ | ||||
| 		public function unbindKey(keyCode:uint):void { | ||||
| 			keyBindings.remove(keyCode); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Очистка привязки всех клавиш. | ||||
| 		 */ | ||||
| 		public function unbindAll():void { | ||||
| 			keyBindings.clear(); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения камеры вперёд. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function forward(value:Boolean):void { | ||||
| 			_forward = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения камеры назад. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function back(value:Boolean):void { | ||||
| 			_back = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация движения камеры влево. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function left(value:Boolean):void { | ||||
| 			_left = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения камеры вправо. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function right(value:Boolean):void { | ||||
| 			_right = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения камеры вверх. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function up(value:Boolean):void { | ||||
| 			_up = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения камеры вниз. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function down(value:Boolean):void { | ||||
| 			_down = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота камеры вверх. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function pitchUp(value:Boolean):void { | ||||
| 			_pitchUp = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота камеры вниз. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function pitchDown(value:Boolean):void { | ||||
| 			_pitchDown = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота камеры влево. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function yawLeft(value:Boolean):void { | ||||
| 			_yawLeft = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота камеры вправо. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function yawRight(value:Boolean):void { | ||||
| 			_yawRight = value; | ||||
| 		} | ||||
| 		 | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация режима увеличенной скорости. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для включения ускорения, <code>false</code> для выключения | ||||
| 		 */ | ||||
| 		public function accelerate(value:Boolean):void { | ||||
| 			_accelerate = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		private function createCollider():void { | ||||
| 			_collider = new EllipsoidCollider(_camera.scene, _collisionRadius); | ||||
| 			_collider.offsetThreshold = 0.01; | ||||
| 			_collider.collisionSet = _collisionIgnoreSet;				 | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Чувствительность мыши — коэффициент умножения <code>mousePitch</code> и <code>mouseYaw</code>. | ||||
| 		 *  | ||||
| 		 * @default 1 | ||||
| 		 *  | ||||
| 		 * @see #mousePitch() | ||||
| 		 * @see #mouseYaw() | ||||
| 		 */		 | ||||
| 		public function get mouseSensitivity():Number { | ||||
| 			return _mouseSensitivity; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set mouseSensitivity(sensitivity:Number):void { | ||||
| 			_mouseSensitivity = sensitivity; | ||||
| 			_mousePitchCoeff = _mouseSensitivity * _mousePitch; | ||||
| 			_mouseYawCoeff = _mouseSensitivity * _mouseYaw; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Скорость изменения угла тангажа при управлении мышью (радианы на пиксель). | ||||
| 		 *  | ||||
| 		 * @default Math.PI / 360 | ||||
| 		 */		 | ||||
| 		public function get mousePitch():Number { | ||||
| 			return _mousePitch; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set mousePitch(pitch:Number):void { | ||||
| 			_mousePitch = pitch; | ||||
| 			_mousePitchCoeff = _mouseSensitivity * _mousePitch; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Скорость изменения угла рысканья при управлении мышью (радианы на пиксель). | ||||
| 		 *  | ||||
| 		 * @default Math.PI / 360 | ||||
| 		 */		 | ||||
| 		public function get mouseYaw():Number { | ||||
| 			return _mouseYaw; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set mouseYaw(yaw:Number):void { | ||||
| 			_mouseYaw = yaw; | ||||
| 			_mouseYawCoeff = _mouseSensitivity * _mouseYaw; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Угловая скорость по тангажу при управлении с клавиатуры (радианы в секунду). | ||||
| 		 *  | ||||
| 		 * @default 1 | ||||
| 		 */		 | ||||
| 		public function get pitchSpeed():Number { | ||||
| 			return _pitchSpeed; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set pitchSpeed(spd:Number):void { | ||||
| 			_pitchSpeed = spd; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Угловая скорость по рысканью при управлении с клавиатуры (радианы в секунду). | ||||
| 		 *  | ||||
| 		 * @default 1 | ||||
| 		 */		 | ||||
| 		public function get yawSpeed():Number { | ||||
| 			return _yawSpeed; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set yawSpeed(spd:Number):void { | ||||
| 			_yawSpeed = spd; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Скорость поступательного движения (единицы в секунду). | ||||
| 		 */		 | ||||
| 		public function get speed():Number { | ||||
| 			return _speed; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set speed(spd:Number):void { | ||||
| 			_speed = spd; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Коэффициент увеличения скорости при активном действии <code>ACTION_ACCELERATE</code>. | ||||
| 		 *  | ||||
| 		 * @default 2 | ||||
| 		 */ | ||||
| 		public function get speedMultiplier():Number { | ||||
| 			return _speedMultiplier; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set speedMultiplier(value:Number):void { | ||||
| 			_speedMultiplier = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активность управления камеры. | ||||
| 		 *  | ||||
| 		 * @default false | ||||
| 		 */ | ||||
| 		public function get controlsEnabled():Boolean { | ||||
| 			return _controlsEnabled; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set controlsEnabled(value:Boolean):void { | ||||
| 			if (_camera == null || _controlsEnabled == value) return; | ||||
| 			if (value) { | ||||
| 				lastFrameTime = getTimer(); | ||||
| 				registerEventListeners(); | ||||
| 			} | ||||
| 			else { | ||||
| 				unregisterEventsListeners(); | ||||
| 			} | ||||
| 			_controlsEnabled = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Базовый шаг изменения угла зрения в радианах. Реальный шаг получаеся умножением этого значения на величину | ||||
| 		 * <code>MouseEvent.delta</code>. | ||||
| 		 *  | ||||
| 		 * @default Math.PI / 180  | ||||
| 		 */ | ||||
| 		public function get fovStep():Number { | ||||
| 			return _fovStep; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set fovStep(value:Number):void { | ||||
| 			_fovStep = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Множитель при изменении коэффициента увеличения. Закон изменения коэффициента увеличения описывается формулой:<br> | ||||
| 		 * <code>zoom (1 + MouseEvent.delta zoomMultiplier)</code>. | ||||
| 		 *  | ||||
| 		 * @default 0.1  | ||||
| 		 */ | ||||
| 		public function get zoomMultiplier():Number { | ||||
| 			return _zoomMultiplier; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set zoomMultiplier(value:Number):void { | ||||
| 			_zoomMultiplier = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		private function registerEventListeners():void { | ||||
| 			_eventsSource.addEventListener(KeyboardEvent.KEY_DOWN, onKey); | ||||
| 			_eventsSource.addEventListener(KeyboardEvent.KEY_UP, onKey); | ||||
| 			_eventsSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); | ||||
| 			_eventsSource.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		private function unregisterEventsListeners():void { | ||||
| 			_eventsSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKey); | ||||
| 			_eventsSource.removeEventListener(KeyboardEvent.KEY_UP, onKey); | ||||
| 			_eventsSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); | ||||
| 			_eventsSource.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp); | ||||
| 			_eventsSource.removeEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param e | ||||
| 		 */ | ||||
| 		private function onKey(e:KeyboardEvent):void { | ||||
| 			var method:Function = keyBindings[e.keyCode]; | ||||
| 			if (method != null) { | ||||
| 				method.call(this, e.type == KeyboardEvent.KEY_DOWN); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param e | ||||
| 		 */		 | ||||
| 		private function onMouseDown(e:MouseEvent):void { | ||||
| 			mouseLookActive = true;  | ||||
| 			currentDragCoords.x = startDragCoords.x = _eventsSource.stage.mouseX; | ||||
| 			currentDragCoords.y = startDragCoords.y = _eventsSource.stage.mouseY; | ||||
| 			startRotX = _camera.rotationX; | ||||
| 			startRotZ = _camera.rotationZ; | ||||
| 			_eventsSource.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param e | ||||
| 		 */		 | ||||
| 		private function onMouseUp(e:MouseEvent):void { | ||||
| 			mouseLookActive = false; | ||||
| 			_eventsSource.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param e | ||||
| 		 */		 | ||||
| 		private function onMouseWheel(e:MouseEvent):void { | ||||
| 			if (_camera.orthographic) { | ||||
| 				_camera.zoom = _camera.zoom * (1 + e.delta * _zoomMultiplier); | ||||
| 			} else { | ||||
| 				_camera.fov -= _fovStep * e.delta; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Обработка управляющих воздействий. | ||||
| 		 * Метод должен вызываться каждый кадр перед вызовом <code>Scene3D.calculate()</code>. | ||||
| 		 *  | ||||
| 		 * @see alternativa.engine3d.core.Scene3D#calculate() | ||||
| 		 */		 | ||||
| 		public function processInput(): void { | ||||
| 			if (!_controlsEnabled || _camera == null) return; | ||||
| 			 | ||||
| 			// Время в секундах от начала предыдущего кадра | ||||
| 			var frameTime:Number = getTimer() - lastFrameTime; | ||||
| 			lastFrameTime += frameTime; | ||||
| 			frameTime /= 1000; | ||||
| 			 | ||||
| 			// Обработка mouselook | ||||
| 			if (mouseLookActive) { | ||||
| 				prevDragCoords.x = currentDragCoords.x; | ||||
| 				prevDragCoords.y = currentDragCoords.y; | ||||
| 				currentDragCoords.x = _eventsSource.stage.mouseX; | ||||
| 				currentDragCoords.y = _eventsSource.stage.mouseY; | ||||
| 				if (!prevDragCoords.equals(currentDragCoords)) { | ||||
| 					_camera.rotationZ = startRotZ + (startDragCoords.x - currentDragCoords.x) * _mouseYawCoeff; | ||||
| 					var rotX:Number = startRotX + (startDragCoords.y - currentDragCoords.y) * _mousePitchCoeff; | ||||
| 					_camera.rotationX = (rotX > 0) ? 0 : (rotX < -MathUtils.DEG180) ? -MathUtils.DEG180 : rotX; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Поворот относительно вертикальной оси (рысканье, yaw) | ||||
| 			if (_yawLeft) { | ||||
| 				_camera.rotationZ += _yawSpeed * frameTime; | ||||
| 			} else if (_yawRight) { | ||||
| 				_camera.rotationZ -= _yawSpeed * frameTime; | ||||
| 			} | ||||
| 			 | ||||
| 			// Поворот относительно поперечной оси (тангаж, pitch) | ||||
| 			if (_pitchUp) { | ||||
| 				rotX = _camera.rotationX + _pitchSpeed * frameTime; | ||||
| 				_camera.rotationX = (rotX > 0) ? 0 : (rotX < -MathUtils.DEG180) ? -MathUtils.DEG180 : rotX; | ||||
| 			} else if (_pitchDown) { | ||||
| 				rotX = _camera.rotationX - _pitchSpeed * frameTime; | ||||
| 				_camera.rotationX = (rotX > 0) ? 0 : (rotX < -MathUtils.DEG180) ? -MathUtils.DEG180 : rotX; | ||||
| 			} | ||||
|  | ||||
| 			// TODO: Поворот относительно продольной оси (крен, roll) | ||||
| 			 | ||||
| 			var frameDistance:Number = _speed * frameTime; | ||||
| 			if (_accelerate) { | ||||
| 				frameDistance *= _speedMultiplier; | ||||
| 			} | ||||
| 			velocity.x = 0; | ||||
| 			velocity.y = 0; | ||||
| 			velocity.z = 0; | ||||
| 			var transformation:Matrix3D = _camera.transformation; | ||||
| 			 | ||||
| 			if (_moveLocal) { | ||||
| 				// Режим относительных пермещений | ||||
| 				// Движение вперед-назад | ||||
| 				if (_forward) { | ||||
| 					velocity.x += frameDistance * transformation.c; | ||||
| 					velocity.y += frameDistance * transformation.g; | ||||
| 					velocity.z += frameDistance * transformation.k; | ||||
| 				} else if (_back) { | ||||
| 					velocity.x -= frameDistance * transformation.c; | ||||
| 					velocity.y -= frameDistance * transformation.g; | ||||
| 					velocity.z -= frameDistance * transformation.k; | ||||
| 				} | ||||
| 				// Движение влево-вправо | ||||
| 				if (_left) { | ||||
| 					velocity.x -= frameDistance * transformation.a; | ||||
| 					velocity.y -= frameDistance * transformation.e; | ||||
| 					velocity.z -= frameDistance * transformation.i; | ||||
| 				} else if (_right) { | ||||
| 					velocity.x += frameDistance * transformation.a; | ||||
| 					velocity.y += frameDistance * transformation.e; | ||||
| 					velocity.z += frameDistance * transformation.i; | ||||
| 				} | ||||
| 				// Движение вверх-вниз | ||||
| 				if (_up) { | ||||
| 					velocity.x -= frameDistance * transformation.b; | ||||
| 					velocity.y -= frameDistance * transformation.f; | ||||
| 					velocity.z -= frameDistance * transformation.j; | ||||
| 				} else if (_down) { | ||||
| 					velocity.x += frameDistance * transformation.b; | ||||
| 					velocity.y += frameDistance * transformation.f; | ||||
| 					velocity.z += frameDistance * transformation.j; | ||||
| 				} | ||||
| 			} | ||||
| 			else { | ||||
| 				// Режим глобальных перемещений | ||||
| 				var cosZ:Number = Math.cos(_camera.rotationZ); | ||||
| 				var sinZ:Number = Math.sin(_camera.rotationZ); | ||||
| 				// Движение вперед-назад | ||||
| 				if (_forward) { | ||||
| 					velocity.x -= frameDistance * sinZ; | ||||
| 					velocity.y += frameDistance * cosZ; | ||||
| 				} else if (_back) { | ||||
| 					velocity.x += frameDistance * sinZ; | ||||
| 					velocity.y -= frameDistance * cosZ; | ||||
| 				} | ||||
| 				// Движение влево-вправо | ||||
| 				if (_left) { | ||||
| 					velocity.x -= frameDistance * cosZ; | ||||
| 					velocity.y -= frameDistance * sinZ; | ||||
| 				} else if (_right) { | ||||
| 					velocity.x += frameDistance * cosZ; | ||||
| 					velocity.y += frameDistance * sinZ; | ||||
| 				} | ||||
| 				// Движение вверх-вниз | ||||
| 				if (_up) { | ||||
| 					velocity.z += frameDistance; | ||||
| 				} else if (_down) { | ||||
| 					velocity.z -= frameDistance; | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Коррекция модуля вектора скорости | ||||
| 			if (velocity.x != 0 || velocity.y != 0 || velocity.z != 0) { | ||||
| 				velocity.length = frameDistance; | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверка столкновений | ||||
| 			if (_checkCollisions) { | ||||
| 				_collider.calculateDestination(_camera.coords, velocity, destination); | ||||
| 				_camera.x = destination.x; | ||||
| 				_camera.y = destination.y; | ||||
| 				_camera.z = destination.z; | ||||
| 			} else { | ||||
| 				_camera.x += velocity.x; | ||||
| 				_camera.y += velocity.y; | ||||
| 				_camera.z += velocity.z; | ||||
| 			} | ||||
|  | ||||
| 			// Обработка начала/окончания движения | ||||
| 			if (_camera.changeRotationOrScaleOperation.queued || _camera.changeCoordsOperation.queued) { | ||||
| 				if (!_isMoving) { | ||||
| 					_isMoving = true; | ||||
| 					if (_onStartMoving != null) { | ||||
| 						_onStartMoving.call(this); | ||||
| 					}  | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (_isMoving) { | ||||
| 					_isMoving = false; | ||||
| 					if (_onStopMoving != null) { | ||||
| 						_onStopMoving.call(this); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,450 +0,0 @@ | ||||
| package alternativa.engine3d.controllers { | ||||
| 	 | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.types.Matrix3D; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.utils.KeyboardUtils; | ||||
| 	import alternativa.utils.MathUtils; | ||||
| 	 | ||||
| 	import flash.display.DisplayObject; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
|  | ||||
| 	/** | ||||
| 	 * Контроллер, реализующий управление, подобное управлению летательным аппаратом для объекта, находящегося в системе | ||||
| 	 * координат корневого объекта сцены. Повороты выполняются вокруг локальных осей объекта, собственные ускорения | ||||
| 	 * действуют вдоль локальных осей. | ||||
| 	 *  | ||||
| 	 * <p>Соответствия локальных осей для объектов, не являющихся камерой: | ||||
| 	 * <table border="1" style="border-collapse: collapse"> | ||||
| 	 * <tr> | ||||
| 	 * <th>Ось</th><th>Направление</th><th>Поворот</th> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>X</td><td>Вправо</td><td>Тангаж</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>Y</td><td>Вперёд</td><td>Крен</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>Z</td><td>Вверх</td><td>Рысканье</td> | ||||
| 	 * </tr> | ||||
| 	 * </table> | ||||
| 	 *   | ||||
| 	 * <p>Соответствия локальных осей для объектов, являющихся камерой: | ||||
| 	 * <table border="1" style="border-collapse: collapse"> | ||||
| 	 * <tr> | ||||
| 	 * <th>Ось</th><th>Направление</th><th>Поворот</th> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>X</td><td>Вправо</td><td>Тангаж</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>Y</td><td>Вниз</td><td>Рысканье</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 * <td>Z</td><td>Вперёд</td><td>Крен</td> | ||||
| 	 * </tr> | ||||
| 	 * </table> | ||||
| 	 *  | ||||
| 	 * Поворот мышью реализован следующим образом: в момент активации режима поворота (нажата левая кнопка мыши или | ||||
| 	 * соответствующая кнопка на клавиатуре) текущее положение курсора становится точкой, относительно которой определяются | ||||
| 	 * дальнейшие отклонения. Отклонение курсора по вертикали в пикселях, умноженное на коэффициент чувствительности мыши | ||||
| 	 * по вертикали даёт угловую скорость по тангажу. Отклонение курсора по горизонтали в пикселях, умноженное на коэффициент | ||||
| 	 * чувствительности мыши по горизонтали даёт угловую скорость по крену. | ||||
| 	 */ | ||||
| 	public class FlyController extends ObjectController { | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш поворота по крену влево. | ||||
| 		 */ | ||||
| 		public static const ACTION_ROLL_LEFT:String = "ACTION_ROLL_LEFT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш поворота по крену вправо. | ||||
| 		 */ | ||||
| 		public static const ACTION_ROLL_RIGHT:String = "ACTION_ROLL_RIGHT"; | ||||
| 		 | ||||
| 		private var _rollLeft:Boolean; | ||||
| 		private var _rollRight:Boolean; | ||||
| 		 | ||||
| 		private var _rollSpeed:Number = 1; | ||||
| 		 | ||||
| 		private var rotations:Point3D; | ||||
| 		private var rollMatrix:Matrix3D = new Matrix3D(); | ||||
| 		private var transformation:Matrix3D = new Matrix3D(); | ||||
| 		private var axis:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		private var velocity:Point3D = new Point3D(); | ||||
| 		private var displacement:Point3D = new Point3D(); | ||||
| 		private var destination:Point3D = new Point3D(); | ||||
| 		private var deltaVelocity:Point3D = new Point3D(); | ||||
| 		private var accelerationVector:Point3D = new Point3D(); | ||||
| 		private var currentTransform:Matrix3D = new Matrix3D(); | ||||
| 		 | ||||
| 		private var _currentSpeed:Number = 1; | ||||
| 		/** | ||||
| 		 * Текущие координаты мышиного курсора в режиме mouse look. | ||||
| 		 */ | ||||
| 		private var currentMouseCoords:Point3D = new Point3D(); | ||||
|  | ||||
| 		/** | ||||
| 		 * Модуль вектора ускорния, получаемого от команд движения.  | ||||
| 		 */ | ||||
| 		public var acceleration:Number = 1000; | ||||
| 		/** | ||||
| 		 * Модуль вектора замедляющего ускорения. | ||||
| 		 */ | ||||
| 		public var deceleration:Number = 50; | ||||
| 		/** | ||||
| 		 * Погрешность определения скорости. Скорость приравнивается к нулю, если её модуль не превышает заданного значения. | ||||
| 		 */ | ||||
| 		public var speedThreshold:Number = 1; | ||||
| 		/** | ||||
| 		 * Переключение инерционного режима. В инерционном режиме отсутствует замедляющее ускорение, в результате чего вектор | ||||
| 		 * скорости объекта остаётся постоянным, если нет управляющих воздействий. При выключенном инерционном режиме к объекту | ||||
| 		 * прикладывается замедляющее ускорение.  | ||||
| 		 */ | ||||
| 		public var inertialMode:Boolean; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		public function FlyController(eventsSourceObject:DisplayObject) { | ||||
| 			super(eventsSourceObject); | ||||
|  | ||||
| 			actionBindings[ACTION_ROLL_LEFT] = rollLeft; | ||||
| 			actionBindings[ACTION_ROLL_RIGHT] = rollRight; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Текущая скорость движения. | ||||
| 		 */ | ||||
| 		public function get currentSpeed():Number { | ||||
| 			return _currentSpeed; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация вращения по крену влево. | ||||
| 		 */ | ||||
| 		public function rollLeft(value:Boolean):void { | ||||
| 			_rollLeft = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация вращения по крену вправо. | ||||
| 		 */ | ||||
| 		public function rollRight(value:Boolean):void { | ||||
| 			_rollRight = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Установка привязки клавиш по умолчанию. Данный метод очищает все существующие привязки клавиш и устанавливает следующие: | ||||
| 		 * <table border="1" style="border-collapse: collapse"> | ||||
| 		 * <tr><th>Клавиша</th><th>Действие</th></tr> | ||||
| 		 * <tr><td>W</td><td>ACTION_FORWARD</td></tr> | ||||
| 		 * <tr><td>S</td><td>ACTION_BACK</td></tr> | ||||
| 		 * <tr><td>A</td><td>ACTION_LEFT</td></tr> | ||||
| 		 * <tr><td>D</td><td>ACTION_RIGHT</td></tr> | ||||
| 		 * <tr><td>SPACE</td><td>ACTION_UP</td></tr> | ||||
| 		 * <tr><td>CONTROL</td><td>ACTION_DOWN</td></tr> | ||||
| 		 * <tr><td>UP</td><td>ACTION_PITCH_UP</td></tr> | ||||
| 		 * <tr><td>DOWN</td><td>ACTION_PITCH_DOWN</td></tr> | ||||
| 		 * <tr><td>LEFT</td><td>ACTION_ROLL_LEFT</td></tr> | ||||
| 		 * <tr><td>RIGHT</td><td>ACTION_ROLL_RIGHT</td></tr> | ||||
| 		 * <tr><td>Q</td><td>ACTION_YAW_LEFT</td></tr> | ||||
| 		 * <tr><td>E</td><td>ACTION_YAW_RIGHT</td></tr> | ||||
| 		 * <tr><td>M</td><td>ACTION_MOUSE_LOOK</td></tr> | ||||
| 		 * </table> | ||||
| 		 */		 | ||||
| 		override public function setDefaultBindings():void { | ||||
| 			unbindAll(); | ||||
| 			bindKey(KeyboardUtils.W, ACTION_FORWARD); | ||||
| 			bindKey(KeyboardUtils.S, ACTION_BACK); | ||||
| 			bindKey(KeyboardUtils.A, ACTION_LEFT); | ||||
| 			bindKey(KeyboardUtils.D, ACTION_RIGHT); | ||||
| 			bindKey(KeyboardUtils.SPACE, ACTION_UP); | ||||
| 			bindKey(KeyboardUtils.CONTROL, ACTION_DOWN); | ||||
| 			bindKey(KeyboardUtils.UP, ACTION_PITCH_UP); | ||||
| 			bindKey(KeyboardUtils.DOWN, ACTION_PITCH_DOWN); | ||||
| 			bindKey(KeyboardUtils.LEFT, ACTION_ROLL_LEFT); | ||||
| 			bindKey(KeyboardUtils.RIGHT, ACTION_ROLL_RIGHT); | ||||
| 			bindKey(KeyboardUtils.Q, ACTION_YAW_LEFT); | ||||
| 			bindKey(KeyboardUtils.E, ACTION_YAW_RIGHT); | ||||
| 			bindKey(KeyboardUtils.M, ACTION_MOUSE_LOOK); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет поворот объекта относительно локальных осей в соответствии с имеющимися воздействиями. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 */ | ||||
| 		override protected function rotateObject(frameTime:Number):void { | ||||
| 			var transformation:Matrix3D = _object.transformation; | ||||
| 			if (_mouseLookActive) { | ||||
| 				currentMouseCoords.x = _eventsSource.stage.mouseX; | ||||
| 				currentMouseCoords.y = _eventsSource.stage.mouseY; | ||||
| 				if (!currentMouseCoords.equals(startMouseCoords)) { | ||||
| 					var deltaYaw:Number = (currentMouseCoords.x - startMouseCoords.x) * _mouseCoefficientX; | ||||
| 					if (_object is Camera3D) { | ||||
| 						axis.x = transformation.c; | ||||
| 						axis.y = transformation.g; | ||||
| 						axis.z = transformation.k; | ||||
| 					} else { | ||||
| 						axis.x = transformation.b; | ||||
| 						axis.y = transformation.f; | ||||
| 						axis.z = transformation.j; | ||||
| 					} | ||||
| 					 | ||||
| 					rotateObjectAroundAxis(axis, deltaYaw * frameTime); | ||||
|  | ||||
| 					currentTransform.toTransform(0, 0, 0, _object.rotationX, _object.rotationY, _object.rotationZ, 1, 1, 1); | ||||
| 					var deltaPitch:Number = (startMouseCoords.y - currentMouseCoords.y) * _mouseCoefficientY; | ||||
| 					axis.x = currentTransform.a; | ||||
| 					axis.y = currentTransform.e; | ||||
| 					axis.z = currentTransform.i; | ||||
|  | ||||
| 					rotateObjectAroundAxis(axis, deltaPitch * frameTime); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Поворот относительно продольной оси (крен, roll) | ||||
| 			if (_rollLeft) { | ||||
| 				if (_object is Camera3D) { | ||||
| 					axis.x = transformation.c; | ||||
| 					axis.y = transformation.g; | ||||
| 					axis.z = transformation.k; | ||||
| 				} else { | ||||
| 					axis.x = transformation.b; | ||||
| 					axis.y = transformation.f; | ||||
| 					axis.z = transformation.j; | ||||
| 				} | ||||
| 				rotateObjectAroundAxis(axis, -_rollSpeed * frameTime); | ||||
| 			} else if (_rollRight) { | ||||
| 				if (_object is Camera3D) { | ||||
| 					axis.x = transformation.c; | ||||
| 					axis.y = transformation.g; | ||||
| 					axis.z = transformation.k; | ||||
| 				} else { | ||||
| 					axis.x = transformation.b; | ||||
| 					axis.y = transformation.f; | ||||
| 					axis.z = transformation.j; | ||||
| 				} | ||||
| 				rotateObjectAroundAxis(axis, _rollSpeed * frameTime); | ||||
| 			} | ||||
| 			 | ||||
| 			// Поворот относительно поперечной оси (тангаж, pitch) | ||||
| 			if (_pitchUp) { | ||||
| 				axis.x = transformation.a; | ||||
| 				axis.y = transformation.e; | ||||
| 				axis.z = transformation.i; | ||||
| 				rotateObjectAroundAxis(axis, _pitchSpeed * frameTime); | ||||
| 			} else if (_pitchDown) { | ||||
| 				axis.x = transformation.a; | ||||
| 				axis.y = transformation.e; | ||||
| 				axis.z = transformation.i; | ||||
| 				rotateObjectAroundAxis(axis, -_pitchSpeed * frameTime); | ||||
| 			} | ||||
|  | ||||
| 			// Поворот относительно вертикальной оси (рысканье, yaw) | ||||
| 			if (_yawRight) { | ||||
| 				if (_object is Camera3D) { | ||||
| 					axis.x = transformation.b; | ||||
| 					axis.y = transformation.f; | ||||
| 					axis.z = transformation.j; | ||||
| 					rotateObjectAroundAxis(axis, _yawSpeed * frameTime); | ||||
| 				} else { | ||||
| 					axis.x = transformation.c; | ||||
| 					axis.y = transformation.g; | ||||
| 					axis.z = transformation.k; | ||||
| 					rotateObjectAroundAxis(axis, -_yawSpeed * frameTime); | ||||
| 				} | ||||
| 			} else if (_yawLeft) { | ||||
| 				if (_object is Camera3D) { | ||||
| 					axis.x = transformation.b; | ||||
| 					axis.y = transformation.f; | ||||
| 					axis.z = transformation.j; | ||||
| 					rotateObjectAroundAxis(axis, -_yawSpeed * frameTime); | ||||
| 				} else { | ||||
| 					axis.x = transformation.c; | ||||
| 					axis.y = transformation.g; | ||||
| 					axis.z = transformation.k; | ||||
| 					rotateObjectAroundAxis(axis, _yawSpeed * frameTime); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод вычисляет вектор потенциального смещения эллипсоида. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 * @param displacement в эту переменную записывается вычисленное потенциальное смещение объекта | ||||
| 		 */ | ||||
| 		override protected function getDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 			// Движение вперед-назад | ||||
| 			accelerationVector.x = 0; | ||||
| 			accelerationVector.y = 0; | ||||
| 			accelerationVector.z = 0; | ||||
| 			if (_forward) { | ||||
| 				accelerationVector.y = 1; | ||||
| 			} else if (_back) { | ||||
| 				accelerationVector.y = -1; | ||||
| 			} | ||||
| 			// Движение влево-вправо | ||||
| 			if (_right) { | ||||
| 				accelerationVector.x = 1; | ||||
| 			} else if (_left) { | ||||
| 				accelerationVector.x = -1; | ||||
| 			} | ||||
| 			// Движение ввверх-вниз | ||||
| 			if (_up) { | ||||
| 				accelerationVector.z = 1; | ||||
| 			} else if (_down) { | ||||
| 				accelerationVector.z = -1; | ||||
| 			} | ||||
| 			 | ||||
| 			var speedLoss:Number; | ||||
| 			var len:Number; | ||||
| 			 | ||||
| 			if (accelerationVector.x != 0 || accelerationVector.y != 0 || accelerationVector.z != 0) { | ||||
| 				// Управление активно | ||||
| 				if (_object is Camera3D) { | ||||
| 					var tmp:Number = accelerationVector.z; | ||||
| 					accelerationVector.z = accelerationVector.y; | ||||
| 					accelerationVector.y = -tmp; | ||||
| 				} | ||||
| 				accelerationVector.normalize(); | ||||
| 				accelerationVector.x *= acceleration; | ||||
| 				accelerationVector.y *= acceleration; | ||||
| 				accelerationVector.z *= acceleration; | ||||
| 				currentTransform.toTransform(0, 0, 0, _object.rotationX, _object.rotationY, _object.rotationZ, 1, 1, 1); | ||||
| 				accelerationVector.transform(currentTransform); | ||||
| 				deltaVelocity.x = accelerationVector.x; | ||||
| 				deltaVelocity.y = accelerationVector.y; | ||||
| 				deltaVelocity.z = accelerationVector.z; | ||||
| 				deltaVelocity.x *= frameTime; | ||||
| 				deltaVelocity.y *= frameTime; | ||||
| 				deltaVelocity.z *= frameTime; | ||||
|  | ||||
| 				if (!inertialMode) { | ||||
| 					speedLoss = deceleration * frameTime; | ||||
| 					var dot:Number = Point3D.dot(velocity, accelerationVector); | ||||
| 					if (dot > 0) { | ||||
| 						len = accelerationVector.length; | ||||
| 						var x:Number = accelerationVector.x / len; | ||||
| 						var y:Number = accelerationVector.y / len; | ||||
| 						var z:Number = accelerationVector.z / len; | ||||
| 						len = dot / len; | ||||
| 						x = velocity.x - len * x; | ||||
| 						y = velocity.y - len * y; | ||||
| 						z = velocity.z - len * z; | ||||
| 						len = Math.sqrt(x*x + y*y + z*z); | ||||
| 						if (len > speedLoss) { | ||||
| 							x *= speedLoss / len; | ||||
| 							y *= speedLoss / len; | ||||
| 							z *= speedLoss / len; | ||||
| 						} | ||||
| 						velocity.x -= x; | ||||
| 						velocity.y -= y; | ||||
| 						velocity.z -= z; | ||||
| 					} else { | ||||
| 						len = velocity.length; | ||||
| 						velocity.length = (len > speedLoss) ? (len - speedLoss) : 0; | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				velocity.x += deltaVelocity.x; | ||||
| 				velocity.y += deltaVelocity.y; | ||||
| 				velocity.z += deltaVelocity.z; | ||||
|  | ||||
| 				if (velocity.length > _speed) { | ||||
| 					velocity.length = _speed; | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Управление неактивно | ||||
| 				if (!inertialMode) { | ||||
| 					speedLoss = deceleration * frameTime; | ||||
| 					len = velocity.length; | ||||
| 					velocity.length = (len > speedLoss) ? (len - speedLoss) : 0; | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			displacement.x = velocity.x * frameTime; | ||||
| 			displacement.y = velocity.y * frameTime; | ||||
| 			displacement.z = velocity.z * frameTime; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод применяет потенциальный вектор смещения к эллипсоиду с учётом столкновений с геометрией сцены, если включён | ||||
| 		 * соотвествующий режим. | ||||
| 		 *    | ||||
| 		 * @param frameTime время кадра в секундах | ||||
| 		 * @param displacement векотр потенциального смещения эллипсоида | ||||
| 		 */ | ||||
| 		override protected function applyDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 			if (checkCollisions) { | ||||
| 				_collider.calculateDestination(_coords, displacement, destination); | ||||
|  | ||||
| 				displacement.x = destination.x - _coords.x; | ||||
| 				displacement.y = destination.y - _coords.y; | ||||
| 				displacement.z = destination.z - _coords.z; | ||||
| 			} else { | ||||
| 				destination.x = _coords.x + displacement.x; | ||||
| 				destination.y = _coords.y + displacement.y; | ||||
| 				destination.z = _coords.z + displacement.z; | ||||
| 			} | ||||
|  | ||||
| 			velocity.x = displacement.x / frameTime; | ||||
| 			velocity.y = displacement.y / frameTime; | ||||
| 			velocity.z = displacement.z / frameTime; | ||||
|  | ||||
| 			_coords.x = destination.x; | ||||
| 			_coords.y = destination.y; | ||||
| 			_coords.z = destination.z; | ||||
| 			setObjectCoords(); | ||||
| 			 | ||||
| 			var len:Number = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z); | ||||
| 			if (len < speedThreshold) { | ||||
| 				velocity.x = 0; | ||||
| 				velocity.y = 0; | ||||
| 				velocity.z = 0; | ||||
| 				_currentSpeed = 0; | ||||
| 			} else { | ||||
| 				_currentSpeed = len; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Поворот объекта вокруг заданной оси. | ||||
| 		 *  | ||||
| 		 * @param axis | ||||
| 		 * @param angle | ||||
| 		 */		 | ||||
| 		private function rotateObjectAroundAxis(axis:Point3D, angle:Number):void { | ||||
| 			transformation.toTransform(0, 0, 0, _object.rotationX, _object.rotationY, _object.rotationZ, 1, 1, 1); | ||||
| 			rollMatrix.fromAxisAngle(axis, angle); | ||||
| 			rollMatrix.inverseCombine(transformation); | ||||
| 			rotations = rollMatrix.getRotations(rotations); | ||||
| 			_object.rotationX = rotations.x; | ||||
| 			_object.rotationY = rotations.y; | ||||
| 			_object.rotationZ = rotations.z; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Направление объекта на точку. В результате работы метода локальная ось объекта, соответствующая направлению "вперёд" | ||||
| 		 * будет направлена на указанную точку, а угол поворота вокруг этой оси будет равен нулю. | ||||
| 		 *  | ||||
| 		 * @param point координаты точки, на которую должен быть направлен объект | ||||
| 		 */ | ||||
| 		public function lookAt(point:Point3D):void { | ||||
| 			if (_object == null) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var dx:Number = point.x - _object.x; | ||||
| 			var dy:Number = point.y - _object.y; | ||||
| 			var dz:Number = point.z - _object.z; | ||||
| 			_object.rotationX = Math.atan2(dz, Math.sqrt(dx * dx + dy * dy)) - (_object is Camera3D ? MathUtils.DEG90 : 0); | ||||
| 			_object.rotationY = 0; | ||||
| 			_object.rotationZ = -Math.atan2(dx, dy); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,839 +0,0 @@ | ||||
| package alternativa.engine3d.controllers { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.engine3d.physics.EllipsoidCollider; | ||||
| 	import alternativa.types.Map; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.utils.MathUtils; | ||||
| 	import alternativa.utils.ObjectUtils; | ||||
| 	 | ||||
| 	import flash.display.DisplayObject; | ||||
| 	import flash.events.KeyboardEvent; | ||||
| 	import flash.events.MouseEvent; | ||||
| 	import flash.utils.getTimer; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Базовый контроллер для изменения ориентации и положения объекта в сцене с помощью клавиатуры и мыши. В классе | ||||
| 	 * реализована поддержка назначения обработчиков клавиатурных команд, а также обработчики для основных команд | ||||
| 	 * перемещения. | ||||
| 	 */ | ||||
| 	public class ObjectController { | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения вперёд. | ||||
| 		 */ | ||||
| 		public static const ACTION_FORWARD:String = "ACTION_FORWARD"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения назад. | ||||
| 		 */ | ||||
| 		public static const ACTION_BACK:String = "ACTION_BACK"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения влево. | ||||
| 		 */ | ||||
| 		public static const ACTION_LEFT:String = "ACTION_LEFT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения вправо. | ||||
| 		 */ | ||||
| 		public static const ACTION_RIGHT:String = "ACTION_RIGHT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения вверх. | ||||
| 		 */ | ||||
| 		public static const ACTION_UP:String = "ACTION_UP"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш движения вниз. | ||||
| 		 */ | ||||
| 		public static const ACTION_DOWN:String = "ACTION_DOWN"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш поворота вверх. | ||||
| 		 */ | ||||
| 		public static const ACTION_PITCH_UP:String = "ACTION_PITCH_UP"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш поворота вниз. | ||||
| 		 */ | ||||
| 		public static const ACTION_PITCH_DOWN:String = "ACTION_PITCH_DOWN"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш поворота налево. | ||||
| 		 */ | ||||
| 		public static const ACTION_YAW_LEFT:String = "ACTION_YAW_LEFT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш поворота направо. | ||||
| 		 */ | ||||
| 		public static const ACTION_YAW_RIGHT:String = "ACTION_YAW_RIGHT"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш увеличения скорости. | ||||
| 		 */ | ||||
| 		public static const ACTION_ACCELERATE:String = "ACTION_ACCELERATE"; | ||||
| 		/** | ||||
| 		 * Имя действия для привязки клавиш активации обзора мышью. | ||||
| 		 */ | ||||
| 		public static const ACTION_MOUSE_LOOK:String = "ACTION_MOUSE_LOOK"; | ||||
|  | ||||
| 		/** | ||||
| 		 * Флаг движения вперёд. | ||||
| 		 */ | ||||
| 		protected var _forward:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг движения назад. | ||||
| 		 */ | ||||
| 		protected var _back:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг движения влево. | ||||
| 		 */ | ||||
| 		protected var _left:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг движения вправо. | ||||
| 		 */ | ||||
| 		protected var _right:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг движения вверх. | ||||
| 		 */ | ||||
| 		protected var _up:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг движения вниз. | ||||
| 		 */ | ||||
| 		protected var _down:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг поворота относительно оси X в положительном направлении (взгляд вверх). | ||||
| 		 */ | ||||
| 		protected var _pitchUp:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг поворота относительно оси X в отрицательном направлении (взгляд вниз). | ||||
| 		 */ | ||||
| 		protected var _pitchDown:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг поворота относительно оси Z в положительном направлении (взгляд налево). | ||||
| 		 */ | ||||
| 		protected var _yawLeft:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг активности поворота относительно оси Z в отрицательном направлении (взгляд направо). | ||||
| 		 */ | ||||
| 		protected var _yawRight:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг активности режима ускорения. | ||||
| 		 */ | ||||
| 		protected var _accelerate:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг активности режима поворотов мышью.  | ||||
| 		 */ | ||||
| 		protected var _mouseLookActive:Boolean; | ||||
| 		/** | ||||
| 		 * Начальные координаты мышиного курсора в режиме mouse look. | ||||
| 		 */ | ||||
| 		protected var startMouseCoords:Point3D = new Point3D(); | ||||
| 		/** | ||||
| 		 * Флаг активности контроллера. | ||||
| 		 */ | ||||
| 		protected var _enabled:Boolean = true; | ||||
| 		/** | ||||
| 		 * Источник событий клавиатуры и мыши | ||||
| 		 */ | ||||
| 		protected var _eventsSource:DisplayObject; | ||||
| 		/** | ||||
| 		 * Ассоциативный массив, связывающий коды клавиатурных клавиш с именами команд. | ||||
| 		 */ | ||||
| 		protected var keyBindings:Map = new Map(); | ||||
| 		/** | ||||
| 		 * Ассоциативный массив, связывающий имена команд с реализующими их функциями. Функции должны иметь вид | ||||
| 		 * function(value:Boolean):void. Значение параметра <code>value</code> указывает, нажата или отпущена соответсвующая команде | ||||
| 		 * клавиша. | ||||
| 		 */ | ||||
| 		protected var actionBindings:Map = new Map(); | ||||
| 		/** | ||||
| 		 * Флаг активности клавиатуры. | ||||
| 		 */ | ||||
| 		protected var _keyboardEnabled:Boolean; | ||||
| 		/** | ||||
| 		 * Флаг активности мыши. | ||||
| 		 */ | ||||
| 		protected var _mouseEnabled:Boolean; | ||||
| 		/** | ||||
| 		 * Общая чувствительность мыши. Коэффициент умножения чувствительности по вертикали и горизонтали. | ||||
| 		 */ | ||||
| 		protected var _mouseSensitivity:Number = 1; | ||||
| 		/** | ||||
| 		 * Коэффициент чувствительности мыши по вертикали. Значение угла в радианах на один пиксель перемещения мыши по вертикали. | ||||
| 		 */ | ||||
| 		protected var _mouseSensitivityY:Number = Math.PI / 360; | ||||
| 		/** | ||||
| 		 * Коэффициент чувствительности мыши по горизонтали. Значение угла в радианах на один пиксель перемещения мыши по горизонтали. | ||||
| 		 */ | ||||
| 		protected var _mouseSensitivityX:Number = Math.PI / 360; | ||||
| 		/** | ||||
| 		 * Результирующий коэффициент чувствительности мыши по вертикали. Значение угла в радианах на один пиксель перемещения мыши по вертикали. | ||||
| 		 */  | ||||
| 		protected var _mouseCoefficientY:Number = _mouseSensitivity * _mouseSensitivityY; | ||||
| 		/** | ||||
| 		 * Результирующий коэффициент чувствительности мыши по горизонтали. Значение угла в радианах на один пиксель перемещения мыши по горизонтали. | ||||
| 		 */ | ||||
| 		protected var _mouseCoefficientX:Number = _mouseSensitivity * _mouseSensitivityX; | ||||
| 		/** | ||||
| 		 * Угловая скорость поворота вокруг поперечной оси в радианах за секунду. | ||||
| 		 */ | ||||
| 		protected var _pitchSpeed:Number = 1; | ||||
| 		/** | ||||
| 		 * Угловая скорость поворота вокруг вертикальной оси в радианах за секунду. | ||||
| 		 */ | ||||
| 		protected var _yawSpeed:Number = 1; | ||||
| 		/** | ||||
| 		 * Скорость поступательного движения в единицах за секунду. | ||||
| 		 */ | ||||
| 		protected var _speed:Number = 100; | ||||
| 		/** | ||||
| 		 * Коэффициент увеличения скорости при соответствующей активной команде. | ||||
| 		 */ | ||||
| 		protected var _speedMultiplier:Number = 2; | ||||
| 		/** | ||||
| 		 * Управляемый объект. | ||||
| 		 */ | ||||
| 		protected var _object:Object3D; | ||||
| 		/** | ||||
| 		 * Время в секундах, прошедшее с последнего вызова метода processInput (обычно с последнего кадра). | ||||
| 		 */ | ||||
| 		protected var lastFrameTime:uint; | ||||
| 		/** | ||||
| 		 * Текущие координаты контроллера. | ||||
| 		 */ | ||||
| 		protected var _coords:Point3D = new Point3D(); | ||||
| 		/** | ||||
| 		 * Индикатор движения объекта (перемещения или поворота) в текущем кадре. | ||||
| 		 */ | ||||
| 		protected var _isMoving:Boolean; | ||||
| 		/** | ||||
| 		 * Объект для определения столкновений. | ||||
| 		 */ | ||||
| 		protected var _collider:EllipsoidCollider = new EllipsoidCollider(); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение и выключение режима проверки столкновений.  | ||||
| 		 */ | ||||
| 		public var checkCollisions:Boolean; | ||||
| 		/** | ||||
| 		 * Функция вида <code>function():void</code>, вызываемая при начале движения объекта. Под движением | ||||
| 		 * понимается изменение координат или ориентации. | ||||
| 		 */ | ||||
| 		public var onStartMoving:Function; | ||||
| 		/** | ||||
| 		 * Функция вида <code>function():void</code>, вызываемая при прекращении движения объекта. Под движением | ||||
| 		 * понимается изменение координат или ориентации. | ||||
| 		 */ | ||||
| 		public var onStopMoving:Function; | ||||
| 		 | ||||
| 		// Вектор смещения | ||||
| 		private var _displacement:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создаёт новый экземпляр контролллера. | ||||
| 		 *  | ||||
| 		 * @param eventsSourceObject источник событий клавиатуры и мыши | ||||
| 		 */		 | ||||
| 		public function ObjectController(eventsSourceObject:DisplayObject) { | ||||
| 			if (eventsSourceObject == null) { | ||||
| 				throw new ArgumentError(ObjectUtils.getClassName(this) + ": eventsSourceObject is null"); | ||||
| 			} | ||||
| 			_eventsSource = eventsSourceObject; | ||||
|  | ||||
| 			actionBindings[ACTION_FORWARD] = moveForward; | ||||
| 			actionBindings[ACTION_BACK] = moveBack; | ||||
| 			actionBindings[ACTION_LEFT] = moveLeft; | ||||
| 			actionBindings[ACTION_RIGHT] = moveRight; | ||||
| 			actionBindings[ACTION_UP] = moveUp; | ||||
| 			actionBindings[ACTION_DOWN] = moveDown; | ||||
| 			actionBindings[ACTION_PITCH_UP] = pitchUp; | ||||
| 			actionBindings[ACTION_PITCH_DOWN] = pitchDown; | ||||
| 			actionBindings[ACTION_YAW_LEFT] = yawLeft; | ||||
| 			actionBindings[ACTION_YAW_RIGHT] = yawRight; | ||||
| 			actionBindings[ACTION_ACCELERATE] = accelerate; | ||||
| 			actionBindings[ACTION_MOUSE_LOOK] = setMouseLook; | ||||
| 			 | ||||
| 			keyboardEnabled = true; | ||||
| 			mouseEnabled = true; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение и выключение контроллера. Выключенный контроллер пропускает выполнение метода <code>processInput()</code>. | ||||
| 		 *  | ||||
| 		 * @default true | ||||
| 		 *  | ||||
| 		 * @see #processInput() | ||||
| 		 */ | ||||
| 		public function get enabled():Boolean { | ||||
| 			return _enabled; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set enabled(value:Boolean):void { | ||||
| 			_enabled = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Координаты контроллера. Координаты совпадают с координатами центра эллипсоида, используемого для определения | ||||
| 		 * столкновений. Координаты управляемого объекта могут не совпадать с координатами контроллера. | ||||
| 		 *  | ||||
| 		 * @see #setObjectCoords() | ||||
| 		 */ | ||||
| 		public function get coords():Point3D { | ||||
| 			return _coords.clone(); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set coords(value:Point3D):void { | ||||
| 			_coords.copy(value); | ||||
| 			setObjectCoords(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Чтение координат контроллера в заданную переменную. | ||||
| 		 *  | ||||
| 		 * @param point переменная, в которую записываются координаты контроллера | ||||
| 		 */ | ||||
| 		public function readCoords(point:Point3D):void { | ||||
| 			point.copy(_coords); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Управляемый объект. | ||||
| 		 */ | ||||
| 		public function get object():Object3D { | ||||
| 			return _object; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * При установке объекта устанавливается сцена для коллайдера. | ||||
| 		 */ | ||||
| 		public function set object(value:Object3D):void { | ||||
| 			_object = value; | ||||
| 			_collider.scene = _object == null ? null : _object.scene; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Объект, реализующий проверку столкновений для эллипсоида. | ||||
| 		 */ | ||||
| 		public function get collider():EllipsoidCollider { | ||||
| 			return _collider; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация движения вперёд. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveForward(value:Boolean):void { | ||||
| 			_forward = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения назад. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveBack(value:Boolean):void { | ||||
| 			_back = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация движения влево. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveLeft(value:Boolean):void { | ||||
| 			_left = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения вправо. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveRight(value:Boolean):void { | ||||
| 			_right = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения вверх. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveUp(value:Boolean):void { | ||||
| 			_up = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация движения вниз. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function moveDown(value:Boolean):void { | ||||
| 			_down = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота вверх. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function pitchUp(value:Boolean):void { | ||||
| 			_pitchUp = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота вниз. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function pitchDown(value:Boolean):void { | ||||
| 			_pitchDown = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота влево. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function yawLeft(value:Boolean):void { | ||||
| 			_yawLeft = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Активация поворота вправо. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для начала движения, <code>false</code> для окончания | ||||
| 		 */ | ||||
| 		public function yawRight(value:Boolean):void { | ||||
| 			_yawRight = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация режима увеличенной скорости. | ||||
| 		 *  | ||||
| 		 * @param value <code>true</code> для включения ускорения, <code>false</code> для выключения | ||||
| 		 */ | ||||
| 		public function accelerate(value:Boolean):void { | ||||
| 			_accelerate = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Угловая скорость поворота вокруг поперечной оси (радианы в секунду). | ||||
| 		 *  | ||||
| 		 * @default 1 | ||||
| 		 */		 | ||||
| 		public function get pitchSpeed():Number { | ||||
| 			return _pitchSpeed; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set pitchSpeed(spd:Number):void { | ||||
| 			_pitchSpeed = spd; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Угловая скорость поворота вокруг вертикальной оси (радианы в секунду). | ||||
| 		 *  | ||||
| 		 * @default 1 | ||||
| 		 */		 | ||||
| 		public function get yawSpeed():Number { | ||||
| 			return _yawSpeed; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set yawSpeed(spd:Number):void { | ||||
| 			_yawSpeed = spd; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Скорость движения в единицах за секунду. При установке отрицательного значения берётся модуль. | ||||
| 		 *  | ||||
| 		 * @default 100 | ||||
| 		 */ | ||||
| 		public function get speed():Number { | ||||
| 			return _speed; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set speed(value:Number):void { | ||||
| 			_speed = value < 0 ? -value : value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Коэффициент увеличения скорости при активном действии <code>ACTION_ACCELERATE</code>. | ||||
| 		 *  | ||||
| 		 * @default 2 | ||||
| 		 */ | ||||
| 		public function get speedMultiplier():Number { | ||||
| 			return _speedMultiplier; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set speedMultiplier(value:Number):void { | ||||
| 			_speedMultiplier = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Чувствительность мыши — коэффициент умножения <code>mouseSensitivityX</code> и <code>mouseSensitivityY</code>. | ||||
| 		 *  | ||||
| 		 * @default 1 | ||||
| 		 *  | ||||
| 		 * @see #mouseSensitivityY() | ||||
| 		 * @see #mouseSensitivityX() | ||||
| 		 */		 | ||||
| 		public function get mouseSensitivity():Number { | ||||
| 			return _mouseSensitivity; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set mouseSensitivity(sensitivity:Number):void { | ||||
| 			_mouseSensitivity = sensitivity; | ||||
| 			_mouseCoefficientY = _mouseSensitivity * _mouseSensitivityY; | ||||
| 			_mouseCoefficientX = _mouseSensitivity * _mouseSensitivityX; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Чувтсвительность мыши по вертикали. | ||||
| 		 *  | ||||
| 		 * @default Math.PI / 360 | ||||
| 		 *  | ||||
| 		 * @see #mouseSensitivity() | ||||
| 		 * @see #mouseSensitivityX() | ||||
| 		 */		 | ||||
| 		public function get mouseSensitivityY():Number { | ||||
| 			return _mouseSensitivityY; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set mouseSensitivityY(value:Number):void { | ||||
| 			_mouseSensitivityY = value; | ||||
| 			_mouseCoefficientY = _mouseSensitivity * _mouseSensitivityY; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Чувтсвительность мыши по горизонтали. | ||||
| 		 *  | ||||
| 		 * @default Math.PI / 360 | ||||
| 		 *  | ||||
| 		 * @see #mouseSensitivity() | ||||
| 		 * @see #mouseSensitivityY() | ||||
| 		 */		 | ||||
| 		public function get mouseSensitivityX():Number { | ||||
| 			return _mouseSensitivityX; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set mouseSensitivityX(value:Number):void { | ||||
| 			_mouseSensitivityX = value; | ||||
| 			_mouseCoefficientX = _mouseSensitivity * _mouseSensitivityX; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение/выключение режима вращения объекта мышью. При включении режима вполняется метод <code>startMouseLook()</code>, | ||||
| 		 * при выключении — <code>stoptMouseLook()</code>. | ||||
| 		 *  | ||||
| 		 * @see #startMouseLook() | ||||
| 		 * @see #stopMouseLook() | ||||
| 		 */ | ||||
| 		public function setMouseLook(value:Boolean):void { | ||||
| 			if (_mouseLookActive != value) { | ||||
| 				_mouseLookActive = value; | ||||
| 				if (_mouseLookActive) { | ||||
| 					startMouseLook(); | ||||
| 				} else { | ||||
| 					stopMouseLook(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет необходимые действия при включении режима вращения объекта мышью. | ||||
| 		 * Реализация по умолчанию записывает начальные глобальные координаты курсора мыши в переменную <code>startMouseCoords</code>. | ||||
| 		 *  | ||||
| 		 * @see #startMouseCoords | ||||
| 		 * @see #setMouseLook() | ||||
| 		 * @see #stopMouseLook() | ||||
| 		 */		 | ||||
| 		protected function startMouseLook():void { | ||||
| 			startMouseCoords.x = _eventsSource.stage.mouseX; | ||||
| 			startMouseCoords.y = _eventsSource.stage.mouseY; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Метод выполняет необходимые действия при выключении вращения объекта мышью. Реализация по умолчанию не делает | ||||
| 		 * ничего. | ||||
| 		 *  | ||||
| 		 * @see #setMouseLook() | ||||
| 		 * @see #startMouseLook() | ||||
| 		 */		 | ||||
| 		protected function stopMouseLook():void { | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет обработку всех воздействий на объект. Если объект не установлен или свойство <code>enabled</code> | ||||
| 		 * равно <code>false</code>, метод не выполняется. | ||||
| 		 * <p> | ||||
| 		 * Алгоритм работы метода следующий: | ||||
| 		 * <ul> | ||||
| 		 * <li> Вычисляется время в секундах, прошедшее с последнего вызова метода (с последнего кадра). Это время считается | ||||
| 		 * 	длительностью текущего кадра; | ||||
| 		 * <li> Вызывается метод rotateObject(), который изменяет ориентацию объекта в соответствии с воздействиями; | ||||
| 		 * <li> Вызывается метод getDisplacement(), который вычисляет потенциальное перемещение объекта; | ||||
| 		 * <li> Вызывается метод applyDisplacement(), которому передаётся вектор перемещения, полученный на предыдущем шаге. | ||||
| 		 * Задачей метода является применение заданного вектора перемещения; | ||||
| 		 * <li> При необходимости вызываются обработчики начала и прекращения движения управляемого объекта;  | ||||
| 		 * </ul> | ||||
| 		 */ | ||||
| 		public function processInput():void { | ||||
| 			if (!_enabled || _object == null) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var frameTime:Number = getTimer() - lastFrameTime; | ||||
| 			lastFrameTime += frameTime; | ||||
| 			if (frameTime > 100) { | ||||
| 				frameTime = 100; | ||||
| 			} | ||||
| 			frameTime /= 1000; | ||||
| 			 | ||||
| 			rotateObject(frameTime); | ||||
| 			getDisplacement(frameTime, _displacement); | ||||
| 			applyDisplacement(frameTime, _displacement); | ||||
| 			 | ||||
| 			// Обработка начала/окончания движения | ||||
| 			if (_object.changeRotationOrScaleOperation.queued || _object.changeCoordsOperation.queued) { | ||||
| 				if (!_isMoving) { | ||||
| 					_isMoving = true; | ||||
| 					if (onStartMoving != null) { | ||||
| 						onStartMoving.call(this); | ||||
| 					}  | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (_isMoving) { | ||||
| 					_isMoving = false; | ||||
| 					if (onStopMoving != null) { | ||||
| 						onStopMoving.call(this); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет поворот объекта в соответствии с имеющимися воздействиями. Реализация по умолчанию не делает ничего. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 */ | ||||
| 		protected function rotateObject(frameTime:Number):void { | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод вычисляет потенциальное смещение объекта за кадр. Реализация по умолчанию не делает ничего. | ||||
| 		 *   | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 * @param displacement в эту переменную записывается вычисленное потенциальное смещение объекта | ||||
| 		 */ | ||||
| 		protected function getDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод применяет потенциальное смещение объекта. Реализация по умолчанию не делает ничего. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 * @param displacement смещение объекта, которое нужно обработать | ||||
| 		 */ | ||||
| 		protected function applyDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Метод выполняет привязку клавиши к действию. Одной клавише может быть назначено только одно действие. | ||||
| 		 *  | ||||
| 		 * @param keyCode код клавиши | ||||
| 		 * @param action наименование действия | ||||
| 		 *  | ||||
| 		 * @see #unbindKey() | ||||
| 		 * @see #unbindAll() | ||||
| 		 */ | ||||
| 		public function bindKey(keyCode:uint, action:String):void { | ||||
| 			var method:Function = actionBindings[action]; | ||||
| 			if (method != null) { | ||||
| 				keyBindings[keyCode] = method; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Очистка привязки клавиши. | ||||
| 		 *  | ||||
| 		 * @param keyCode код клавиши | ||||
| 		 *  | ||||
| 		 * @see #bindKey() | ||||
| 		 * @see #unbindAll() | ||||
| 		 */ | ||||
| 		public function unbindKey(keyCode:uint):void { | ||||
| 			keyBindings.remove(keyCode); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Очистка привязки всех клавиш. | ||||
| 		 *  | ||||
| 		 * @see #bindKey() | ||||
| 		 * @see #unbindKey() | ||||
| 		 */ | ||||
| 		public function unbindAll():void { | ||||
| 			keyBindings.clear(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод устанавливает привязки клавиш по умолчанию. Реализация по умолчанию не делает ничего. | ||||
| 		 *  | ||||
| 		 * @see #bindKey() | ||||
| 		 * @see #unbindKey() | ||||
| 		 * @see #unbindAll() | ||||
| 		 */ | ||||
| 		public function setDefaultBindings():void { | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение и выключение обработки клавиатурных событий. При включении выполняется метод <code>registerKeyboardListeners</code>, | ||||
| 		 * при выключении — <code>unregisterKeyboardListeners</code>. | ||||
| 		 *  | ||||
| 		 * @see #registerKeyboardListeners() | ||||
| 		 * @see #unregisterKeyboardListeners() | ||||
| 		 */ | ||||
| 		public function get keyboardEnabled():Boolean { | ||||
| 			return _keyboardEnabled; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set keyboardEnabled(value:Boolean):void { | ||||
| 			if (_keyboardEnabled != value) { | ||||
| 				_keyboardEnabled = value; | ||||
| 				if (_keyboardEnabled) { | ||||
| 					registerKeyboardListeners(); | ||||
| 				} else { | ||||
| 					unregisterKeyboardListeners(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Запуск обработчиков клавиатурных команд. | ||||
| 		 */ | ||||
| 		private function onKeyboardEvent(e:KeyboardEvent):void { | ||||
| 			var method:Function = keyBindings[e.keyCode]; | ||||
| 			if (method != null) { | ||||
| 				method.call(this, e.type == KeyboardEvent.KEY_DOWN); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Регистрация необходимых обработчиков при включении обработки клавиатурных событий. | ||||
| 		 *  | ||||
| 		 * @see #unregisterKeyboardListeners() | ||||
| 		 */ | ||||
| 		protected function registerKeyboardListeners():void { | ||||
| 			_eventsSource.addEventListener(KeyboardEvent.KEY_DOWN, onKeyboardEvent); | ||||
| 			_eventsSource.addEventListener(KeyboardEvent.KEY_UP, onKeyboardEvent); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Удаление обработчиков при выключении обработки клавиатурных событий. | ||||
| 		 *  | ||||
| 		 * @see #registerKeyboardListeners() | ||||
| 		 */ | ||||
| 		protected function unregisterKeyboardListeners():void { | ||||
| 			_eventsSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyboardEvent); | ||||
| 			_eventsSource.removeEventListener(KeyboardEvent.KEY_UP, onKeyboardEvent); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение и выключение обработки мышиных событий. При включении выполняется метод <code>registerMouseListeners</code>, | ||||
| 		 * при выключении — <code>unregisterMouseListeners</code>. | ||||
| 		 *  | ||||
| 		 * @see #registerMouseListeners() | ||||
| 		 * @see #unregisterMouseListeners() | ||||
| 		 */ | ||||
| 		public function get mouseEnabled():Boolean { | ||||
| 			return _mouseEnabled; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set mouseEnabled(value:Boolean):void { | ||||
| 			if (_mouseEnabled != value) { | ||||
| 				_mouseEnabled = value; | ||||
| 				if (_mouseEnabled) { | ||||
| 					registerMouseListeners(); | ||||
| 				} else { | ||||
| 					unregisterMouseListeners(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Регистрация необходимых обработчиков при включении обработки мышиных событий.  | ||||
| 		 *  | ||||
| 		 * @see #unregisterMouseListeners() | ||||
| 		 */		 | ||||
| 		protected function registerMouseListeners():void { | ||||
| 			_eventsSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Удаление используемых обработчиков при выключении обработки мышиных событий. | ||||
| 		 *  | ||||
| 		 * @see #registerMouseListeners() | ||||
| 		 */		 | ||||
| 		protected function unregisterMouseListeners():void { | ||||
| 			_eventsSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); | ||||
| 			_eventsSource.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Активация mouselook | ||||
| 		 */		 | ||||
| 		private function onMouseDown(e:MouseEvent):void { | ||||
| 			setMouseLook(true); | ||||
| 			_eventsSource.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Отключение mouselook | ||||
| 		 */		 | ||||
| 		private function onMouseUp(e:MouseEvent):void { | ||||
| 			setMouseLook(false); | ||||
| 			_eventsSource.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Установка координат управляемого объекта в зависимости от текущих координат контроллера. | ||||
| 		 */ | ||||
| 		protected function setObjectCoords():void { | ||||
| 			_object.coords = _coords; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Индикатор режима увеличенной скорости. | ||||
| 		 */ | ||||
| 		public function get accelerated():Boolean { | ||||
| 			return _accelerate; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,508 +0,0 @@ | ||||
| package alternativa.engine3d.controllers { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.engine3d.physics.Collision; | ||||
| 	import alternativa.types.Matrix3D; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.utils.KeyboardUtils; | ||||
| 	import alternativa.utils.MathUtils; | ||||
| 	 | ||||
| 	import flash.display.DisplayObject; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Контроллер, реализующий управление движением объекта, находящегося в системе координат корневого объекта сцены.  | ||||
| 	 *  | ||||
| 	 * <p>Контроллер предоставляет два режима движения: режим ходьбы с учётом силы тяжести и режим полёта, в котором сила | ||||
| 	 * тяжести не учитывается. В обоих режимах может быть включена проверка столкновений с объектами сцены. Если проверка | ||||
| 	 * столкновений отключена, то в режиме ходьбы сила тяжести также игнорируется и дополнительно появляется возможность | ||||
| 	 * движения по вертикали. | ||||
| 	 *  | ||||
| 	 * <p>Для всех объектов, за исключением <code>Camera3D</code>, направлением "вперёд" считается направление его оси | ||||
| 	 * <code>Y</code>, направлением "вверх" — направление оси <code>Z</code>. Для объектов класса | ||||
| 	 * <code>Camera3D</code> направление "вперёд" совпадает с направлением локальной оси <code>Z</code>, а направление | ||||
| 	 * "вверх" противоположно направлению локальной оси <code>Y</code>. | ||||
| 	 *  | ||||
| 	 * <p>Вне зависимости от того, включена проверка столкновений или нет, координаты при перемещении расчитываются для | ||||
| 	 * эллипсоида, параметры которого устанавливаются через свойство <code>collider</code>. Координаты управляемого | ||||
| 	 * объекта вычисляются исходя из положения центра эллипсоида и положения объекта на вертикальной оси эллипсоида, | ||||
| 	 * задаваемого параметром <code>objectZPosition</code>. | ||||
| 	 *  | ||||
| 	 * <p>Команда <code>ACTION_UP</code> в режиме ходьбы при ненулевой гравитации вызывает прыжок, в остальных случаях | ||||
| 	 * происходит движение вверх. | ||||
| 	 */	 | ||||
| 	public class WalkController extends ObjectController { | ||||
| 		/** | ||||
| 		 * Величина ускорения свободного падения. При положительном значении сила тяжести направлена против оси Z, | ||||
| 		 * при отрицательном — по оси Z. | ||||
| 		 */ | ||||
| 		public var gravity:Number = 0; | ||||
| 		/** | ||||
| 		 * Вертикальная скорость прыжка. | ||||
| 		 */ | ||||
| 		public var jumpSpeed:Number = 0; | ||||
| 		/** | ||||
| 		 * Объект, на котором стоит эллипсоид при ненулевой гравитации. | ||||
| 		 */ | ||||
| 		private var _groundMesh:Mesh; | ||||
| 		/** | ||||
| 		 * Погрешность определения скорости. В режиме полёта или в режиме ходьбы при нахождении на поверхности | ||||
| 		 * скорость приравнивается к нулю, если её модуль не превышает заданного значения. | ||||
| 		 */ | ||||
| 		public var speedThreshold:Number = 1; | ||||
|  | ||||
| 		// Коэффициент эффективности управления перемещением при нахождении в воздухе в режиме ходьбы и нулевой гравитации. | ||||
| 		private var _airControlCoefficient:Number = 1; | ||||
|  | ||||
| 		private var _currentSpeed:Number = 0; | ||||
| 		 | ||||
| 		private var minGroundCos:Number = Math.cos(MathUtils.toRadian(70)); | ||||
| 		 | ||||
| 		private var destination:Point3D = new Point3D(); | ||||
| 		private var collision:Collision = new Collision(); | ||||
| 		 | ||||
| 		private var _objectZPosition:Number = 0.5; | ||||
| 		private var _flyMode:Boolean; | ||||
| 		private	var _onGround:Boolean; | ||||
| 		 | ||||
| 		private var velocity:Point3D = new Point3D(); | ||||
| 		private var tmpVelocity:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		private var controlsActive:Boolean; | ||||
| 		 | ||||
| 		private var inJump:Boolean; | ||||
| 		private var startRotX:Number; | ||||
| 		private var startRotZ:Number; | ||||
|  | ||||
| 		// Координаты мышиного курсора в режиме mouse look в предыдущем кадре. | ||||
| 		private var prevMouseCoords:Point3D = new Point3D(); | ||||
| 		// Текущие координаты мышиного курсора в режиме mouse look. | ||||
| 		private var currentMouseCoords:Point3D = new Point3D(); | ||||
|  | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		public function WalkController(eventSourceObject:DisplayObject) { | ||||
| 			super(eventSourceObject); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Объект, на котором стоит эллипсоид при ненулевой гравитации. | ||||
| 		 */ | ||||
| 		public function get groundMesh():Mesh { | ||||
| 			return _groundMesh; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Направление объекта на точку. В результате работы метода локальная ось объекта, соответствующая направлению "вперёд" | ||||
| 		 * будет направлена на указанную точку, а угол поворота вокруг этой оси будет равен нулю. | ||||
| 		 *  | ||||
| 		 * @param point координаты точки, на которую должен быть направлен объект | ||||
| 		 */ | ||||
| 		public function lookAt(point:Point3D):void { | ||||
| 			if (_object == null) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var dx:Number = point.x - _object.x; | ||||
| 			var dy:Number = point.y - _object.y; | ||||
| 			var dz:Number = point.z - _object.z; | ||||
| 			_object.rotationX = Math.atan2(dz, Math.sqrt(dx * dx + dy * dy)) - (_object is Camera3D ? MathUtils.DEG90 : 0); | ||||
| 			_object.rotationY = 0; | ||||
| 			_object.rotationZ = -Math.atan2(dx, dy); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Коэффициент эффективности управления перемещением в режиме ходьбы при нахождении в воздухе и ненулевой гравитации. | ||||
| 		 * Значение 0 обозначает полное отсутствие контроля, значение 1 указывает, что управление так же эффективно, как при | ||||
| 		 * нахождении на поверхности. | ||||
| 		 *  | ||||
| 		 * @default 1 | ||||
| 		 */ | ||||
| 		public function get airControlCoefficient():Number { | ||||
| 			return _airControlCoefficient; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set airControlCoefficient(value:Number):void { | ||||
| 			_airControlCoefficient = value > 0 ? value : -value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Максимальный угол наклона поверхности в радианах, на которой возможен прыжок и на которой объект стоит на месте | ||||
| 		 * в отсутствие управляющих воздействий. Если угол наклона поверхности превышает заданное значение, свойство | ||||
| 		 * <code>onGround</code> будет иметь значение <code>false</code>. | ||||
| 		 *  | ||||
| 		 * @see #onGround | ||||
| 		 */ | ||||
| 		public function get maxGroundAngle():Number { | ||||
| 			return Math.acos(minGroundCos);  | ||||
| 		} | ||||
| 			 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set maxGroundAngle(value:Number):void { | ||||
| 			minGroundCos = Math.cos(value); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Положение управляемого объекта на оси Z эллипсоида. Значение 0 указывает положение в нижней точке эллипсоида, | ||||
| 		 * значение 1 -- положение в верхней точке эллипсоида. | ||||
| 		 */		 | ||||
| 		public function get objectZPosition():Number { | ||||
| 			return _objectZPosition; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set objectZPosition(value:Number):void { | ||||
| 			_objectZPosition = value; | ||||
| 			setObjectCoords(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включене и выключение режима полёта. | ||||
| 		 *  | ||||
| 		 * @default false | ||||
| 		 */		 | ||||
| 		public function get flyMode():Boolean { | ||||
| 			return _flyMode; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set flyMode(value:Boolean):void { | ||||
| 			_flyMode = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Индикатор положения эллипсоида на поверхности в режиме ходьбы. Считается, что эллипсоид находится на поверхности, | ||||
| 		 * если угол наклона поверхности под ним не превышает заданного свойством <code>maxGroundAngle</code> значения. | ||||
| 		 *  | ||||
| 		 * @see #maxGroundAngle  | ||||
| 		 */ | ||||
| 		public function get onGround():Boolean { | ||||
| 			return _onGround; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Модуль текущей скорости. | ||||
| 		 */ | ||||
| 		public function get currentSpeed():Number { | ||||
| 			return _currentSpeed; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Установка привязки клавиш по умолчанию. Данный метод очищает все существующие привязки клавиш и устанавливает следующие: | ||||
| 		 * <table border="1" style="border-collapse: collapse"> | ||||
| 		 * <tr><th>Клавиша</th><th>Действие</th></tr> | ||||
| 		 * <tr><td>W</td><td>ACTION_FORWARD</td></tr> | ||||
| 		 * <tr><td>S</td><td>ACTION_BACK</td></tr> | ||||
| 		 * <tr><td>A</td><td>ACTION_LEFT</td></tr> | ||||
| 		 * <tr><td>D</td><td>ACTION_RIGHT</td></tr> | ||||
| 		 * <tr><td>SPACE</td><td>ACTION_UP</td></tr> | ||||
| 		 * <tr><td>CONTROL</td><td>ACTION_DOWN</td></tr> | ||||
| 		 * <tr><td>SHIFT</td><td>ACTION_ACCELERATE</td></tr> | ||||
| 		 * <tr><td>UP</td><td>ACTION_PITCH_UP</td></tr> | ||||
| 		 * <tr><td>DOWN</td><td>ACTION_PITCH_DOWN</td></tr> | ||||
| 		 * <tr><td>LEFT</td><td>ACTION_YAW_LEFT</td></tr> | ||||
| 		 * <tr><td>RIGHT</td><td>ACTION_YAW_RIGHT</td></tr> | ||||
| 		 * <tr><td>M</td><td>ACTION_MOUSE_LOOK</td></tr> | ||||
| 		 * </table> | ||||
| 		 */ | ||||
| 		override public function setDefaultBindings():void { | ||||
| 			unbindAll(); | ||||
| 			bindKey(KeyboardUtils.W, ACTION_FORWARD); | ||||
| 			bindKey(KeyboardUtils.S, ACTION_BACK); | ||||
| 			bindKey(KeyboardUtils.A, ACTION_LEFT); | ||||
| 			bindKey(KeyboardUtils.D, ACTION_RIGHT); | ||||
| 			bindKey(KeyboardUtils.SPACE, ACTION_UP); | ||||
| 			bindKey(KeyboardUtils.CONTROL, ACTION_DOWN); | ||||
| 			bindKey(KeyboardUtils.UP, ACTION_PITCH_UP); | ||||
| 			bindKey(KeyboardUtils.DOWN, ACTION_PITCH_DOWN); | ||||
| 			bindKey(KeyboardUtils.LEFT, ACTION_YAW_LEFT); | ||||
| 			bindKey(KeyboardUtils.RIGHT, ACTION_YAW_RIGHT); | ||||
| 			bindKey(KeyboardUtils.SHIFT, ACTION_ACCELERATE); | ||||
| 			bindKey(KeyboardUtils.M, ACTION_MOUSE_LOOK); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет поворот объекта в соответствии с имеющимися воздействиями. Взгляд вверх и вниз ограничен | ||||
| 		 * отклонением в 90 градусов от горизонтали. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 */ | ||||
| 		override protected function rotateObject(frameTime:Number):void { | ||||
| 			// Mouse look | ||||
| 			var rotX:Number; | ||||
| 			if (_mouseLookActive) { | ||||
| 				prevMouseCoords.copy(currentMouseCoords); | ||||
| 				currentMouseCoords.x = _eventsSource.stage.mouseX; | ||||
| 				currentMouseCoords.y = _eventsSource.stage.mouseY; | ||||
| 				if (!prevMouseCoords.equals(currentMouseCoords)) { | ||||
| 					_object.rotationZ = startRotZ + (startMouseCoords.x - currentMouseCoords.x) * _mouseCoefficientX; | ||||
| 					rotX = startRotX + (startMouseCoords.y - currentMouseCoords.y) * _mouseCoefficientY; | ||||
| 					if (_object is Camera3D) { | ||||
| 						// Коррекция поворота для камеры | ||||
| 						_object.rotationX = (rotX > MathUtils.DEG90) ? 0 : (rotX < -MathUtils.DEG90) ? -Math.PI : rotX - MathUtils.DEG90; | ||||
| 					} else { | ||||
| 						_object.rotationX = (rotX > MathUtils.DEG90) ? MathUtils.DEG90 : (rotX < -MathUtils.DEG90) ? -MathUtils.DEG90 : rotX; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Повороты влево-вправо | ||||
| 			if (_yawLeft) { | ||||
| 				_object.rotationZ += _yawSpeed * frameTime; | ||||
| 			} else if (_yawRight) { | ||||
| 				_object.rotationZ -= _yawSpeed * frameTime; | ||||
| 			} | ||||
| 			// Взгляд вверх-вниз | ||||
| 			rotX = NaN; | ||||
| 			if (_pitchUp) { | ||||
| 				rotX = _object.rotationX + _pitchSpeed * frameTime; | ||||
| 			} else if (_pitchDown) { | ||||
| 				rotX = _object.rotationX - _pitchSpeed * frameTime; | ||||
| 			} | ||||
| 			if (!isNaN(rotX)) { | ||||
| 				if (_object is Camera3D) { | ||||
| 					// Коррекция поворота для камеры | ||||
| 					_object.rotationX = (rotX > 0) ? 0 : (rotX < -Math.PI) ? -Math.PI : rotX; | ||||
| 				} else { | ||||
| 					_object.rotationX = (rotX > MathUtils.DEG90) ? MathUtils.DEG90 : (rotX < -MathUtils.DEG90) ? -MathUtils.DEG90 : rotX; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод вычисляет вектор потенциального смещения эллипсоида, учитывая режим перемещения, команды перемещения и силу тяжести. | ||||
| 		 *  | ||||
| 		 * @param frameTime длительность текущего кадра в секундах  | ||||
| 		 * @param displacement в эту переменную записывается вычисленное потенциальное смещение объекта | ||||
| 		 */ | ||||
| 		override protected function getDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 			var cos:Number = 0; | ||||
| 			if (checkCollisions && !_flyMode) { | ||||
| 				// Проверка наличия под ногами поверхности | ||||
| 				displacement.x = 0; | ||||
| 				displacement.y = 0; | ||||
| 				displacement.z = - 0.5 * gravity * frameTime * frameTime; | ||||
| 				if (_collider.getCollision(_coords, displacement, collision)) { | ||||
| 					cos = collision.normal.z; | ||||
| 					_groundMesh = collision.face._mesh; | ||||
| 				} else { | ||||
| 					_groundMesh = null; | ||||
| 				} | ||||
| 			} | ||||
| 			_onGround = cos > minGroundCos; | ||||
| 			 | ||||
| 			if (_onGround && inJump) { | ||||
| 				inJump = false; | ||||
| 			} | ||||
| 			 | ||||
| 			var len:Number; | ||||
| 			var x:Number; | ||||
| 			var y:Number; | ||||
| 			var z:Number; | ||||
|  | ||||
| 			// При наличии управляющих воздействий расчитывается приращение скорости  | ||||
| 			controlsActive = _forward || _back || _right || _left || _up || _down; | ||||
| 			if (controlsActive) { | ||||
| 				if (_flyMode) { | ||||
| 					tmpVelocity.x = 0; | ||||
| 					tmpVelocity.y = 0; | ||||
| 					tmpVelocity.z = 0; | ||||
| 					// Режим полёта, ускорения направлены вдоль локальных осей | ||||
| 					// Ускорение вперёд-назад | ||||
| 					if (_forward) { | ||||
| 						tmpVelocity.y = 1; | ||||
| 					} else if (_back) { | ||||
| 						tmpVelocity.y = -1; | ||||
| 					} | ||||
| 					// Ускорение влево-вправо | ||||
| 					if (_right) { | ||||
| 						tmpVelocity.x = 1; | ||||
| 					} else if (_left) { | ||||
| 						tmpVelocity.x = -1; | ||||
| 					} | ||||
| 					// Ускорение вверх-вниз | ||||
| 					if (_up) { | ||||
| 						tmpVelocity.z = 1; | ||||
| 					} else if (_down) { | ||||
| 						tmpVelocity.z = -1; | ||||
| 					} | ||||
| 					var matrix:Matrix3D = _object.transformation; | ||||
| 					x = tmpVelocity.x; | ||||
| 					if (_object is Camera3D) { | ||||
| 						y = -tmpVelocity.z; | ||||
| 						z = tmpVelocity.y; | ||||
| 					} else { | ||||
| 						y = tmpVelocity.y; | ||||
| 						z = tmpVelocity.z; | ||||
| 					} | ||||
| 					// Поворот вектора из локальной системы координат объекта в глобальную | ||||
| 					velocity.x += (x * matrix.a + y * matrix.b + z * matrix.c) * _speed; | ||||
| 					velocity.y += (x * matrix.e + y * matrix.f + z * matrix.g) * _speed; | ||||
| 					velocity.z += (x * matrix.i + y * matrix.j + z * matrix.k) * _speed; | ||||
| 				} else { | ||||
| 					 | ||||
| 					// Режим хождения, ускорения вперёд-назад-влево-вправо лежат в глобальной плоскости XY, вверх-вниз направлены вдоль глобальной оси Z | ||||
| 					var heading:Number = _object.rotationZ; | ||||
| 					var headingCos:Number = Math.cos(heading); | ||||
| 					var headingSin:Number = Math.sin(heading); | ||||
| 					 | ||||
| 					var spd:Number = _speed; | ||||
| 					if (gravity != 0 && !_onGround) { | ||||
| 						spd *= _airControlCoefficient; | ||||
| 					} | ||||
| 					 | ||||
| 					// Вперёд-назад | ||||
| 					if (_forward) { | ||||
| 						velocity.x -= spd * headingSin; | ||||
| 						velocity.y += spd * headingCos; | ||||
| 					} else if (_back) { | ||||
| 						velocity.x += spd * headingSin; | ||||
| 						velocity.y -= spd * headingCos; | ||||
| 					} | ||||
| 					// Влево-вправо | ||||
| 					if (_right) { | ||||
| 						velocity.x += spd * headingCos; | ||||
| 						velocity.y += spd * headingSin; | ||||
| 					} else if (_left) { | ||||
| 						velocity.x -= spd * headingCos; | ||||
| 						velocity.y -= spd * headingSin; | ||||
| 					} | ||||
| 					if (gravity == 0) { | ||||
| 						// Ускорение вверх-вниз | ||||
| 						if (_up) { | ||||
| 							velocity.z += _speed; | ||||
| 						} else if (_down) { | ||||
| 							velocity.z -= _speed; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Управление неактивно, замедление движения | ||||
| 				len = 1 / Math.pow(3, frameTime * 10); | ||||
| 				if (_flyMode || gravity == 0) { | ||||
| 					velocity.x *= len; | ||||
| 					velocity.y *= len; | ||||
| 					velocity.z *= len; | ||||
| 				} else { | ||||
| 					if (_onGround) { | ||||
| 						velocity.x *= len; | ||||
| 						velocity.y *= len; | ||||
| 						if (velocity.z < 0) { | ||||
| 							velocity.z *= len; | ||||
| 						} | ||||
| 					} else { | ||||
| 						if (cos > 0 && velocity.z > 0) { | ||||
| 							velocity.z = 0; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			// Прыжок | ||||
| 			if (_onGround && _up && !inJump) { | ||||
| 				velocity.z = jumpSpeed; | ||||
| 				inJump = true; | ||||
| 				_onGround = false; | ||||
| 				cos = 0; | ||||
| 			} | ||||
| 			// В режиме ходьбы добавляется ускорение свободного падения, если находимся не на ровной поверхности | ||||
| 			if (!(_flyMode || _onGround)) { | ||||
| 				velocity.z -= gravity * frameTime; | ||||
| 			} | ||||
| 			 | ||||
| 			// Ограничение скорости | ||||
| 			var max:Number = _accelerate ? _speed * _speedMultiplier : _speed; | ||||
| 			if (_flyMode || gravity == 0) { | ||||
| 				len = velocity.length; | ||||
| 				if (len > max) { | ||||
| 					velocity.length = max; | ||||
| 				} | ||||
| 			} else { | ||||
| 				len = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y); | ||||
| 				if (len > max) { | ||||
| 					velocity.x *= max / len; | ||||
| 					velocity.y *= max / len; | ||||
| 				} | ||||
| 				if (cos > 0 && velocity.z > 0) { | ||||
| 					velocity.z = 0; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Cмещение за кадр | ||||
| 			displacement.x = velocity.x * frameTime; | ||||
| 			displacement.y = velocity.y * frameTime; | ||||
| 			displacement.z = velocity.z * frameTime; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод применяет потенциальный вектор смещения к эллипсоиду с учётом столкновений с геометрией сцены, если включён | ||||
| 		 * соотвествующий режим. | ||||
| 		 *    | ||||
| 		 * @param frameTime время кадра в секундах | ||||
| 		 * @param displacement векотр потенциального смещения эллипсоида | ||||
| 		 */ | ||||
| 		override protected function applyDisplacement(frameTime:Number, displacement:Point3D):void { | ||||
| 			if (checkCollisions) { | ||||
| 				_collider.calculateDestination(_coords, displacement, destination); | ||||
|  | ||||
| 				displacement.x = destination.x - _coords.x; | ||||
| 				displacement.y = destination.y - _coords.y; | ||||
| 				displacement.z = destination.z - _coords.z; | ||||
| 			} else { | ||||
| 				destination.x = _coords.x + displacement.x; | ||||
| 				destination.y = _coords.y + displacement.y; | ||||
| 				destination.z = _coords.z + displacement.z; | ||||
| 			} | ||||
|  | ||||
| 			velocity.x = displacement.x / frameTime; | ||||
| 			velocity.y = displacement.y / frameTime; | ||||
| 			velocity.z = displacement.z / frameTime; | ||||
|  | ||||
| 			_coords.x = destination.x; | ||||
| 			_coords.y = destination.y; | ||||
| 			_coords.z = destination.z; | ||||
| 			setObjectCoords(); | ||||
| 			 | ||||
| 			var len:Number = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z); | ||||
| 			if (len < speedThreshold) { | ||||
| 				velocity.x = 0; | ||||
| 				velocity.y = 0; | ||||
| 				velocity.z = 0; | ||||
| 				_currentSpeed = 0; | ||||
| 			} else { | ||||
| 				_currentSpeed = len; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод устанавливает координаты управляемого объекта в зависимости от параметра <code>objectZPosition</code>. | ||||
| 		 *  | ||||
| 		 * @see #objectZPosition | ||||
| 		 */ | ||||
| 		override protected function setObjectCoords():void { | ||||
| 			_object.x = _coords.x; | ||||
| 			_object.y = _coords.y; | ||||
| 			_object.z = _coords.z + (2 * _objectZPosition - 1) * _collider.radiusZ; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет необходимые действия при включении вращения объекта мышью. | ||||
| 		 */		 | ||||
| 		override protected function startMouseLook():void { | ||||
| 			super.startMouseLook(); | ||||
| 			startRotX = _object is Camera3D ? _object.rotationX + MathUtils.DEG90 : _object.rotationX; | ||||
| 			startRotZ = _object.rotationZ; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,65 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 96 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core | ||||
| END | ||||
| Object3D.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 108 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core/Object3D.as | ||||
| END | ||||
| Vertex.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 106 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core/Vertex.as | ||||
| END | ||||
| Face.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 104 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core/Face.as | ||||
| END | ||||
| Camera3D.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 108 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core/Camera3D.as | ||||
| END | ||||
| Operation.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 109 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core/Operation.as | ||||
| END | ||||
| Scene3D.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 107 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core/Scene3D.as | ||||
| END | ||||
| Surface.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 107 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core/Surface.as | ||||
| END | ||||
| BSPNode.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 107 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core/BSPNode.as | ||||
| END | ||||
| Mesh.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 104 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core/Mesh.as | ||||
| END | ||||
| PolyPrimitive.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 113 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core/PolyPrimitive.as | ||||
| END | ||||
| @@ -1,148 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/core | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-09-08T10:18:35.971715Z | ||||
| 496 | ||||
| mike | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| Object3D.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| b073db9ca4e4c92a2dd85f27d9bc4984 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Vertex.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 45061bec8e94d8b1a0e676cc97d1cd98 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Face.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 69f10c80b9cfa8708935d58574478212 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Camera3D.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| dd7767ec455535922dbb40119335493e | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Operation.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 417bf52e75ce5e357a143f017420d7f8 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Scene3D.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| c046f1821c29117d8a0512737aa59ba0 | ||||
| 2008-09-08T10:18:35.971715Z | ||||
| 496 | ||||
| mike | ||||
|  | ||||
| Surface.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 32a190736e7772887ee56ff4fda61749 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| BSPNode.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| e252b1e950354fdb596a11c374363487 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Mesh.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 7ca25e823b271a1d575f79379f2e42e8 | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| PolyPrimitive.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 9ff789ed50e7857a8987830bfdaa1f9f | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
| @@ -1,65 +0,0 @@ | ||||
| package alternativa.engine3d.core { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.types.Set; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * @private | ||||
| 	 */ | ||||
| 	public final class BSPNode { | ||||
| 		 | ||||
| 		// Родительская нода | ||||
| 		alternativa3d var parent:BSPNode; | ||||
| 		 | ||||
| 		// Дочерние ветки | ||||
| 		alternativa3d var front:BSPNode;  | ||||
| 		alternativa3d var back:BSPNode; | ||||
| 		 | ||||
| 		// Нормаль плоскости ноды | ||||
| 		alternativa3d var normal:Point3D = new Point3D(); | ||||
|  | ||||
| 		// Смещение плоскости примитива | ||||
| 		alternativa3d var offset:Number; | ||||
| 		 | ||||
| 		// Минимальная мобильность ноды | ||||
| 		alternativa3d var mobility:int = int.MAX_VALUE; | ||||
| 		 | ||||
| 		// Набор примитивов в ноде | ||||
| 		alternativa3d var primitive:PolyPrimitive; | ||||
| 		alternativa3d var backPrimitives:Set; | ||||
| 		alternativa3d var frontPrimitives:Set; | ||||
| 		 | ||||
| 		// Хранилище неиспользуемых нод | ||||
| 		static private var collector:Array = new Array(); | ||||
| 		 | ||||
| 		// Создать ноду на основе примитива | ||||
| 		static alternativa3d function createBSPNode(primitive:PolyPrimitive):BSPNode { | ||||
| 			// Достаём ноду из коллектора | ||||
| 			var node:BSPNode = collector.pop(); | ||||
| 			// Если коллектор пуст, создаём новую ноду | ||||
| 			if (node == null) { | ||||
| 				node = new BSPNode(); | ||||
| 			} | ||||
|  | ||||
| 			// Добавляем примитив в ноду | ||||
| 			node.primitive = primitive; | ||||
| 			// Сохраняем ноду | ||||
| 			primitive.node = node; | ||||
| 			// Сохраняем плоскость | ||||
| 			node.normal.copy(primitive.face.globalNormal); | ||||
| 			node.offset = primitive.face.globalOffset; | ||||
| 			// Сохраняем мобильность | ||||
| 			node.mobility = primitive.mobility; | ||||
| 			return node; | ||||
| 		} | ||||
| 		 | ||||
| 		// Удалить ноду, все ссылки должны быть почищены | ||||
| 		static alternativa3d function destroyBSPNode(node:BSPNode):void { | ||||
| 			//trace(node.back, node.front, node.parent, node.primitive, node.backPrimitives, node.frontPrimitives); | ||||
| 			collector.push(node); | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| } | ||||
| @@ -1,902 +0,0 @@ | ||||
| package alternativa.engine3d.core { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.display.Skin; | ||||
| 	import alternativa.engine3d.display.View; | ||||
| 	import alternativa.engine3d.materials.DrawPoint; | ||||
| 	import alternativa.engine3d.materials.SurfaceMaterial; | ||||
| 	import alternativa.types.Matrix3D; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.types.Set; | ||||
| 	 | ||||
| 	import flash.geom.Matrix; | ||||
| 	import flash.geom.Point; | ||||
| 	import alternativa.utils.MathUtils; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Камера для отображения 3D-сцены на экране. | ||||
| 	 *  | ||||
| 	 * <p> Направление камеры совпадает с её локальной осью Z, поэтому только что созданная камера смотрит вверх в системе | ||||
| 	 * координат родителя. | ||||
| 	 *  | ||||
| 	 * <p> Для отображения видимой через камеру части сцены на экран, к камере должна быть подключёна область вывода — | ||||
| 	 * экземпляр класса <code>alternativa.engine3d.display.View</code>.  | ||||
| 	 *  | ||||
| 	 * @see alternativa.engine3d.display.View | ||||
| 	 */ | ||||
| 	public class Camera3D extends Object3D { | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт матрицы пространства камеры | ||||
| 		 */ | ||||
| 		alternativa3d var calculateMatrixOperation:Operation = new Operation("calculateMatrix", this, calculateMatrix, Operation.CAMERA_CALCULATE_MATRIX); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт плоскостей отсечения | ||||
| 		 */		 | ||||
| 		alternativa3d var calculatePlanesOperation:Operation = new Operation("calculatePlanes", this, calculatePlanes, Operation.CAMERA_CALCULATE_PLANES); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Отрисовка | ||||
| 		 */		 | ||||
| 		alternativa3d var renderOperation:Operation = new Operation("render", this, render, Operation.CAMERA_RENDER); | ||||
|  | ||||
| 		// Инкремент количества объектов | ||||
| 		private static var counter:uint = 0; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Поле зрения | ||||
| 		 */ | ||||
| 		alternativa3d var _fov:Number = Math.PI/2; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Фокусное расстояние | ||||
| 		 */ | ||||
| 		alternativa3d var focalLength:Number; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Перспективное искажение | ||||
| 		 */ | ||||
| 		alternativa3d var focalDistortion:Number; | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Флаги рассчитанности UV-матриц | ||||
| 		 */ | ||||
| 		alternativa3d var uvMatricesCalculated:Set = new Set(true); | ||||
| 		 | ||||
| 		// Всмомогательные точки для расчёта UV-матриц | ||||
| 		private var textureA:Point3D = new Point3D(); | ||||
| 		private var textureB:Point3D = new Point3D(); | ||||
| 		private var textureC:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Вид из камеры | ||||
| 		 */ | ||||
| 		alternativa3d var _view:View; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Режим отрисовки | ||||
| 		 */ | ||||
| 		alternativa3d var _orthographic:Boolean = false; | ||||
| 		private var fullDraw:Boolean; | ||||
| 		 | ||||
| 		// Масштаб | ||||
| 		private var _zoom:Number = 1; | ||||
| 		 | ||||
| 		// Синус половинчатого угла обзора камеры | ||||
| 		private var viewAngle:Number; | ||||
| 		 | ||||
| 		// Направление камеры | ||||
| 		private var direction:Point3D = new Point3D(0, 0, 1); | ||||
| 		 | ||||
| 		// Обратная трансформация камеры | ||||
| 		private var cameraMatrix:Matrix3D = new Matrix3D(); | ||||
|  | ||||
| 		// Скины | ||||
| 		private var firstSkin:Skin; | ||||
| 		private var prevSkin:Skin; | ||||
| 		private var currentSkin:Skin; | ||||
| 		 | ||||
| 		// Плоскости отсечения | ||||
| 		private var leftPlane:Point3D = new Point3D(); | ||||
| 		private var rightPlane:Point3D = new Point3D(); | ||||
| 		private var topPlane:Point3D = new Point3D(); | ||||
| 		private var bottomPlane:Point3D = new Point3D(); | ||||
| 		private var leftOffset:Number; | ||||
| 		private var rightOffset:Number; | ||||
| 		private var topOffset:Number; | ||||
| 		private var bottomOffset:Number; | ||||
| 		 | ||||
| 		// Вспомогательные массивы точек для отрисовки | ||||
| 		private var points1:Array = new Array(); | ||||
| 		private var points2:Array = new Array(); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание нового экземпляра камеры. | ||||
| 		 *  | ||||
| 		 * @param name имя экземпляра | ||||
| 		 */ | ||||
| 		public function Camera3D(name:String = null) { | ||||
| 			super(name); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		private function calculateMatrix():void { | ||||
| 			// Расчёт матрицы пространства камеры | ||||
| 			cameraMatrix.copy(transformation); | ||||
| 			cameraMatrix.invert(); | ||||
| 			if (_orthographic) { | ||||
| 				cameraMatrix.scale(_zoom, _zoom, _zoom); | ||||
| 			} | ||||
| 			// Направление камеры | ||||
| 			direction.x = transformation.c; | ||||
| 			direction.y = transformation.g; | ||||
| 			direction.z = transformation.k; | ||||
| 			direction.normalize(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт плоскостей отсечения | ||||
| 		 */ | ||||
| 		private function calculatePlanes():void { | ||||
| 			var halfWidth:Number = _view._width*0.5; | ||||
| 			var halfHeight:Number = _view._height*0.5; | ||||
| 			 | ||||
| 			var aw:Number = transformation.a*halfWidth; | ||||
| 			var ew:Number = transformation.e*halfWidth; | ||||
| 			var iw:Number = transformation.i*halfWidth; | ||||
| 			var bh:Number = transformation.b*halfHeight; | ||||
| 			var fh:Number = transformation.f*halfHeight; | ||||
| 			var jh:Number = transformation.j*halfHeight; | ||||
| 			if (_orthographic) { | ||||
| 				// Расчёт плоскостей отсечения в изометрии | ||||
| 				aw /= _zoom; | ||||
| 				ew /= _zoom; | ||||
| 				iw /= _zoom; | ||||
| 				bh /= _zoom; | ||||
| 				fh /= _zoom; | ||||
| 				jh /= _zoom; | ||||
| 				 | ||||
| 				// Левая плоскость | ||||
| 				leftPlane.x = transformation.f*transformation.k - transformation.j*transformation.g; | ||||
| 				leftPlane.y = transformation.j*transformation.c - transformation.b*transformation.k; | ||||
| 				leftPlane.z = transformation.b*transformation.g - transformation.f*transformation.c; | ||||
| 				leftOffset = (transformation.d - aw)*leftPlane.x + (transformation.h - ew)*leftPlane.y + (transformation.l - iw)*leftPlane.z; | ||||
| 				 | ||||
| 				// Правая плоскость | ||||
| 				rightPlane.x = -leftPlane.x; | ||||
| 				rightPlane.y = -leftPlane.y; | ||||
| 				rightPlane.z = -leftPlane.z; | ||||
| 				rightOffset = (transformation.d + aw)*rightPlane.x + (transformation.h + ew)*rightPlane.y + (transformation.l + iw)*rightPlane.z; | ||||
| 				 | ||||
| 				// Верхняя плоскость | ||||
| 				topPlane.x = transformation.g*transformation.i - transformation.k*transformation.e; | ||||
| 				topPlane.y = transformation.k*transformation.a - transformation.c*transformation.i; | ||||
| 				topPlane.z = transformation.c*transformation.e - transformation.g*transformation.a; | ||||
| 				topOffset = (transformation.d - bh)*topPlane.x + (transformation.h - fh)*topPlane.y + (transformation.l - jh)*topPlane.z; | ||||
| 				 | ||||
| 				// Нижняя плоскость | ||||
| 				bottomPlane.x = -topPlane.x; | ||||
| 				bottomPlane.y = -topPlane.y; | ||||
| 				bottomPlane.z = -topPlane.z; | ||||
| 				bottomOffset = (transformation.d + bh)*bottomPlane.x + (transformation.h + fh)*bottomPlane.y + (transformation.l + jh)*bottomPlane.z; | ||||
| 			} else { | ||||
| 				// Вычисляем расстояние фокуса | ||||
| 				focalLength = Math.sqrt(_view._width*_view._width + _view._height*_view._height)*0.5/Math.tan(0.5*_fov); | ||||
| 				// Вычисляем минимальное (однопиксельное) искажение перспективной коррекции | ||||
| 				focalDistortion = 1/(focalLength*focalLength); | ||||
| 				 | ||||
| 				// Расчёт плоскостей отсечения в перспективе | ||||
| 				var cl:Number = transformation.c*focalLength; | ||||
| 				var gl:Number = transformation.g*focalLength; | ||||
| 				var kl:Number = transformation.k*focalLength; | ||||
| 				 | ||||
| 				// Угловые вектора пирамиды видимости | ||||
| 				var leftTopX:Number = -aw - bh + cl; | ||||
| 				var leftTopY:Number = -ew - fh + gl; | ||||
| 				var leftTopZ:Number = -iw - jh + kl; | ||||
| 				var rightTopX:Number = aw - bh + cl; | ||||
| 				var rightTopY:Number = ew - fh + gl; | ||||
| 				var rightTopZ:Number = iw - jh + kl; | ||||
| 				var leftBottomX:Number = -aw + bh + cl; | ||||
| 				var leftBottomY:Number = -ew + fh + gl; | ||||
| 				var leftBottomZ:Number = -iw + jh + kl; | ||||
| 				var rightBottomX:Number = aw + bh + cl; | ||||
| 				var rightBottomY:Number = ew + fh + gl; | ||||
| 				var rightBottomZ:Number = iw + jh + kl; | ||||
| 				 | ||||
| 				// Левая плоскость | ||||
| 				leftPlane.x = leftBottomY*leftTopZ - leftBottomZ*leftTopY; | ||||
| 				leftPlane.y = leftBottomZ*leftTopX - leftBottomX*leftTopZ; | ||||
| 				leftPlane.z = leftBottomX*leftTopY - leftBottomY*leftTopX; | ||||
| 				leftOffset = transformation.d*leftPlane.x + transformation.h*leftPlane.y + transformation.l*leftPlane.z; | ||||
|  | ||||
| 				// Правая плоскость | ||||
| 				rightPlane.x = rightTopY*rightBottomZ - rightTopZ*rightBottomY; | ||||
| 				rightPlane.y = rightTopZ*rightBottomX - rightTopX*rightBottomZ; | ||||
| 				rightPlane.z = rightTopX*rightBottomY - rightTopY*rightBottomX; | ||||
| 				rightOffset = transformation.d*rightPlane.x + transformation.h*rightPlane.y + transformation.l*rightPlane.z; | ||||
|  | ||||
| 				// Верхняя плоскость | ||||
| 				topPlane.x = leftTopY*rightTopZ - leftTopZ*rightTopY; | ||||
| 				topPlane.y = leftTopZ*rightTopX - leftTopX*rightTopZ; | ||||
| 				topPlane.z = leftTopX*rightTopY - leftTopY*rightTopX; | ||||
| 				topOffset = transformation.d*topPlane.x + transformation.h*topPlane.y + transformation.l*topPlane.z; | ||||
|  | ||||
| 				// Нижняя плоскость | ||||
| 				bottomPlane.x = rightBottomY*leftBottomZ - rightBottomZ*leftBottomY; | ||||
| 				bottomPlane.y = rightBottomZ*leftBottomX - rightBottomX*leftBottomZ; | ||||
| 				bottomPlane.z = rightBottomX*leftBottomY - rightBottomY*leftBottomX; | ||||
| 				bottomOffset = transformation.d*bottomPlane.x + transformation.h*bottomPlane.y + transformation.l*bottomPlane.z; | ||||
| 				 | ||||
| 				 | ||||
| 				// Расчёт угла конуса | ||||
| 				var length:Number = Math.sqrt(leftTopX*leftTopX + leftTopY*leftTopY + leftTopZ*leftTopZ); | ||||
| 				leftTopX /= length; | ||||
| 				leftTopY /= length; | ||||
| 				leftTopZ /= length; | ||||
| 				length = Math.sqrt(rightTopX*rightTopX + rightTopY*rightTopY + rightTopZ*rightTopZ); | ||||
| 				rightTopX /= length; | ||||
| 				rightTopY /= length; | ||||
| 				rightTopZ /= length; | ||||
| 				length = Math.sqrt(leftBottomX*leftBottomX + leftBottomY*leftBottomY + leftBottomZ*leftBottomZ); | ||||
| 				leftBottomX /= length; | ||||
| 				leftBottomY /= length; | ||||
| 				leftBottomZ /= length; | ||||
| 				length = Math.sqrt(rightBottomX*rightBottomX + rightBottomY*rightBottomY + rightBottomZ*rightBottomZ); | ||||
| 				rightBottomX /= length; | ||||
| 				rightBottomY /= length; | ||||
| 				rightBottomZ /= length; | ||||
|  | ||||
| 				viewAngle = leftTopX*direction.x + leftTopY*direction.y + leftTopZ*direction.z; | ||||
| 				var dot:Number = rightTopX*direction.x + rightTopY*direction.y + rightTopZ*direction.z; | ||||
| 				viewAngle = (dot < viewAngle) ? dot : viewAngle; | ||||
| 				dot = leftBottomX*direction.x + leftBottomY*direction.y + leftBottomZ*direction.z; | ||||
| 				viewAngle = (dot < viewAngle) ? dot : viewAngle; | ||||
| 				dot = rightBottomX*direction.x + rightBottomY*direction.y + rightBottomZ*direction.z; | ||||
| 				viewAngle = (dot < viewAngle) ? dot : viewAngle; | ||||
| 				 | ||||
| 				viewAngle = Math.sin(Math.acos(viewAngle)); | ||||
| 			} | ||||
| 		} | ||||
| 			 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		private function render():void { | ||||
| 			// Режим отрисовки | ||||
| 			fullDraw = (calculateMatrixOperation.queued || calculatePlanesOperation.queued); | ||||
|  | ||||
| 			// Очистка рассчитанных текстурных матриц | ||||
| 			uvMatricesCalculated.clear(); | ||||
| 			 | ||||
| 			// Отрисовка | ||||
| 			prevSkin = null; | ||||
| 			currentSkin = firstSkin; | ||||
| 			renderBSPNode(_scene.bsp); | ||||
|  | ||||
| 			// Удаление ненужных скинов | ||||
| 			while (currentSkin != null) { | ||||
|  				removeCurrentSkin(); | ||||
| 	 		} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		private function renderBSPNode(node:BSPNode):void { | ||||
| 			if (node != null) { | ||||
| 				var primitive:*; | ||||
| 				var normal:Point3D = node.normal; | ||||
| 				var cameraAngle:Number = direction.x*normal.x + direction.y*normal.y + direction.z*normal.z; | ||||
| 				var cameraOffset:Number; | ||||
| 				if (!_orthographic) { | ||||
| 					 cameraOffset = globalCoords.x*normal.x + globalCoords.y*normal.y + globalCoords.z*normal.z - node.offset; | ||||
| 				} | ||||
| 				if (node.primitive != null) { | ||||
| 					// В ноде только базовый примитив | ||||
| 					if (_orthographic ? (cameraAngle < 0) : (cameraOffset > 0)) { | ||||
| 						// Камера спереди ноды | ||||
| 						if (_orthographic || cameraAngle < viewAngle) { | ||||
| 							renderBSPNode(node.back); | ||||
| 							drawSkin(node.primitive); | ||||
| 						} | ||||
| 						renderBSPNode(node.front); | ||||
| 					} else { | ||||
| 						// Камера сзади ноды | ||||
| 						if (_orthographic || cameraAngle > -viewAngle) { | ||||
| 							renderBSPNode(node.front); | ||||
| 						} | ||||
| 						renderBSPNode(node.back); | ||||
| 					} | ||||
| 				} else { | ||||
| 					// В ноде несколько примитивов | ||||
| 					if (_orthographic ? (cameraAngle < 0) : (cameraOffset > 0)) { | ||||
| 						// Камера спереди ноды | ||||
| 						if (_orthographic || cameraAngle < viewAngle) { | ||||
| 							renderBSPNode(node.back); | ||||
| 							for (primitive in node.frontPrimitives) { | ||||
| 								drawSkin(primitive); | ||||
| 							} | ||||
| 						} | ||||
| 						renderBSPNode(node.front); | ||||
| 					} else { | ||||
| 						// Камера сзади ноды | ||||
| 						if (_orthographic || cameraAngle > -viewAngle) { | ||||
| 							renderBSPNode(node.front); | ||||
| 							for (primitive in node.backPrimitives) { | ||||
| 								drawSkin(primitive); | ||||
| 							} | ||||
| 						} | ||||
| 						renderBSPNode(node.back); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Отрисовка скина примитива | ||||
| 		 */ | ||||
|  		private function drawSkin(primitive:PolyPrimitive):void { | ||||
|  			if (!fullDraw && currentSkin != null && currentSkin.primitive == primitive && !_scene.changedPrimitives[primitive]) { | ||||
| 	 			// Пропуск скина | ||||
| 				prevSkin = currentSkin; | ||||
| 				currentSkin = currentSkin.nextSkin; | ||||
| 			} else { | ||||
| 	 			// Проверка поверхности  | ||||
| 	 			var surface:Surface = primitive.face._surface; | ||||
| 	 			if (surface == null) { | ||||
| 	 				return; | ||||
| 	 			} | ||||
| 	 			// Проверка материала | ||||
| 	 			var material:SurfaceMaterial = surface._material; | ||||
|  				if (material == null || !material.canDraw(primitive)) { | ||||
|  					return; | ||||
|  				} | ||||
|  				// Отсечение выходящих за окно просмотра частей | ||||
|  				var i:uint; | ||||
|  				var length:uint = primitive.num; | ||||
|  				var primitivePoint:Point3D; | ||||
|  				var primitiveUV:Point; | ||||
|  				var point:DrawPoint; | ||||
|  				var useUV:Boolean = !_orthographic && material.useUV && primitive.face.uvMatrixBase;  | ||||
|  				if (useUV) { | ||||
|  					// Формируем список точек и UV-координат полигона | ||||
| 					for (i = 0; i < length; i++) { | ||||
| 						primitivePoint = primitive.points[i]; | ||||
| 						primitiveUV = primitive.uvs[i]; | ||||
| 						point = points1[i]; | ||||
| 						if (point == null) { | ||||
| 							points1[i] = new DrawPoint(primitivePoint.x, primitivePoint.y, primitivePoint.z, primitiveUV.x, primitiveUV.y); | ||||
| 						} else { | ||||
| 							point.x = primitivePoint.x; | ||||
| 							point.y = primitivePoint.y; | ||||
| 							point.z = primitivePoint.z; | ||||
| 							point.u = primitiveUV.x; | ||||
| 							point.v = primitiveUV.y; | ||||
| 						} | ||||
| 	 				} | ||||
|  				} else { | ||||
| 	 				// Формируем список точек полигона | ||||
| 					for (i = 0; i < length; i++) {	 | ||||
| 						primitivePoint = primitive.points[i]; | ||||
| 						point = points1[i]; | ||||
| 						if (point == null) { | ||||
| 							points1[i] = new DrawPoint(primitivePoint.x, primitivePoint.y, primitivePoint.z); | ||||
| 						} else { | ||||
| 							point.x = primitivePoint.x; | ||||
| 							point.y = primitivePoint.y; | ||||
| 							point.z = primitivePoint.z; | ||||
| 						} | ||||
| 	 				} | ||||
| 	 			} | ||||
|  				// Отсечение по левой стороне | ||||
|  				length = clip(length, points1, points2, leftPlane, leftOffset, useUV); | ||||
|  				if (length < 3) { | ||||
|  					return; | ||||
|  				} | ||||
|  				// Отсечение по правой стороне | ||||
|  				length = clip(length, points2, points1, rightPlane, rightOffset, useUV); | ||||
|  				if (length < 3) { | ||||
|  					return; | ||||
|  				} | ||||
|  				// Отсечение по верхней стороне | ||||
|  				length = clip(length, points1, points2, topPlane, topOffset, useUV); | ||||
|  				if (length < 3) { | ||||
|  					return; | ||||
|  				} | ||||
|  				// Отсечение по нижней стороне | ||||
|  				length = clip(length, points2, points1, bottomPlane, bottomOffset, useUV); | ||||
|  				if (length < 3) { | ||||
|  					return; | ||||
|  				} | ||||
| 	 					 | ||||
|  				if (fullDraw || _scene.changedPrimitives[primitive]) { | ||||
|  | ||||
| 					// Если конец списка скинов | ||||
|  					if (currentSkin == null) { | ||||
| 						// Добавляем скин в конец  | ||||
|  						addCurrentSkin(); | ||||
|  					} else { | ||||
|  						if (fullDraw || _scene.changedPrimitives[currentSkin.primitive]) { | ||||
| 							// Очистка скина | ||||
| 							currentSkin.material.clear(currentSkin); | ||||
| 	 					} else { | ||||
| 							// Вставка скина перед текущим | ||||
| 	 						insertCurrentSkin(); | ||||
| 	 					} | ||||
|  					} | ||||
|  					 | ||||
|  					// Переводим координаты в систему камеры | ||||
|  					var x:Number; | ||||
| 	 				var y:Number; | ||||
| 	 				var z:Number; | ||||
|  					for (i = 0; i < length; i++) { | ||||
|  						point = points1[i]; | ||||
|  						x = point.x; | ||||
|  						y = point.y; | ||||
|  						z = point.z; | ||||
|  						point.x = cameraMatrix.a*x + cameraMatrix.b*y + cameraMatrix.c*z + cameraMatrix.d; | ||||
| 						point.y = cameraMatrix.e*x + cameraMatrix.f*y + cameraMatrix.g*z + cameraMatrix.h; | ||||
| 						point.z = cameraMatrix.i*x + cameraMatrix.j*y + cameraMatrix.k*z + cameraMatrix.l; | ||||
|  					} | ||||
|  					 | ||||
| 					// Назначаем скину примитив и материал | ||||
| 					currentSkin.primitive = primitive; | ||||
| 					currentSkin.material = material; | ||||
| 					material.draw(this, currentSkin, length, points1); | ||||
| 					 | ||||
| 		 			// Переключаемся на следующий скин | ||||
| 		 			prevSkin = currentSkin; | ||||
| 		 			currentSkin = currentSkin.nextSkin; | ||||
|  					 | ||||
|  				} else { | ||||
|  					 | ||||
| 					// Удаление ненужных скинов | ||||
| 					while (currentSkin != null && _scene.changedPrimitives[currentSkin.primitive]) { | ||||
| 		 				removeCurrentSkin(); | ||||
| 		 			} | ||||
| 	 | ||||
| 		 			// Переключение на следующий скин | ||||
| 		 			if (currentSkin != null) { | ||||
| 			 			prevSkin = currentSkin; | ||||
| 		 				currentSkin = currentSkin.nextSkin; | ||||
| 		 			} | ||||
|  					 | ||||
|  				} | ||||
|  			} | ||||
|  		} | ||||
|  		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Отсечение полигона плоскостью. | ||||
| 		 */ | ||||
| 		private function clip(length:uint, points1:Array, points2:Array, plane:Point3D, offset:Number, calculateUV:Boolean):uint { | ||||
| 			var i:uint; | ||||
| 			var k:Number; | ||||
| 			var index:uint = 0; | ||||
| 			var point:DrawPoint; | ||||
| 			var point1:DrawPoint; | ||||
| 			var point2:DrawPoint; | ||||
| 			var offset1:Number; | ||||
| 			var offset2:Number; | ||||
| 			 | ||||
| 			point1 = points1[length - 1]; | ||||
| 			offset1 = plane.x*point1.x + plane.y*point1.y + plane.z*point1.z - offset; | ||||
| 			 | ||||
| 			if (calculateUV) { | ||||
|  | ||||
| 				for (i = 0; i < length; i++) { | ||||
| 	 | ||||
| 					point2 = points1[i]; | ||||
| 					offset2 = plane.x*point2.x + plane.y*point2.y + plane.z*point2.z - offset; | ||||
| 					 | ||||
| 					if (offset2 > 0) { | ||||
| 						if (offset1 <= 0) { | ||||
| 							k = offset2/(offset2 - offset1); | ||||
| 							point = points2[index]; | ||||
| 							if (point == null) { | ||||
| 								point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k, point2.u - (point2.u - point1.u)*k, point2.v - (point2.v - point1.v)*k); | ||||
| 								points2[index] = point; | ||||
| 							} else { | ||||
| 								point.x = point2.x - (point2.x - point1.x)*k; | ||||
| 								point.y = point2.y - (point2.y - point1.y)*k; | ||||
| 								point.z = point2.z - (point2.z - point1.z)*k; | ||||
| 								point.u = point2.u - (point2.u - point1.u)*k; | ||||
| 								point.v = point2.v - (point2.v - point1.v)*k; | ||||
| 							} | ||||
| 							index++; | ||||
| 						} | ||||
| 						point = points2[index]; | ||||
| 						if (point == null) { | ||||
| 							point = new DrawPoint(point2.x, point2.y, point2.z, point2.u, point2.v); | ||||
| 							points2[index] = point; | ||||
| 						} else { | ||||
| 							point.x = point2.x; | ||||
| 							point.y = point2.y; | ||||
| 							point.z = point2.z; | ||||
| 							point.u = point2.u; | ||||
| 							point.v = point2.v; | ||||
| 						} | ||||
| 						index++; | ||||
| 					} else { | ||||
| 						if (offset1 > 0) { | ||||
| 							k = offset2/(offset2 - offset1); | ||||
| 							point = points2[index]; | ||||
| 							if (point == null) { | ||||
| 								point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k, point2.u - (point2.u - point1.u)*k, point2.v - (point2.v - point1.v)*k); | ||||
| 								points2[index] = point; | ||||
| 							} else { | ||||
| 								point.x = point2.x - (point2.x - point1.x)*k; | ||||
| 								point.y = point2.y - (point2.y - point1.y)*k; | ||||
| 								point.z = point2.z - (point2.z - point1.z)*k; | ||||
| 								point.u = point2.u - (point2.u - point1.u)*k; | ||||
| 								point.v = point2.v - (point2.v - point1.v)*k; | ||||
| 							} | ||||
| 							index++; | ||||
| 						} | ||||
| 					} | ||||
| 					offset1 = offset2; | ||||
| 					point1 = point2; | ||||
| 				} | ||||
| 				 | ||||
| 			} else { | ||||
| 	 | ||||
| 				for (i = 0; i < length; i++) { | ||||
| 	 | ||||
| 					point2 = points1[i]; | ||||
| 					offset2 = plane.x*point2.x + plane.y*point2.y + plane.z*point2.z - offset; | ||||
| 					 | ||||
| 					if (offset2 > 0) { | ||||
| 						if (offset1 <= 0) { | ||||
| 							k = offset2/(offset2 - offset1); | ||||
| 							point = points2[index]; | ||||
| 							if (point == null) { | ||||
| 								point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k); | ||||
| 								points2[index] = point; | ||||
| 							} else { | ||||
| 								point.x = point2.x - (point2.x - point1.x)*k; | ||||
| 								point.y = point2.y - (point2.y - point1.y)*k; | ||||
| 								point.z = point2.z - (point2.z - point1.z)*k; | ||||
| 							} | ||||
| 							index++; | ||||
| 						} | ||||
| 						point = points2[index]; | ||||
| 						if (point == null) { | ||||
| 							point = new DrawPoint(point2.x, point2.y, point2.z); | ||||
| 							points2[index] = point; | ||||
| 						} else { | ||||
| 							point.x = point2.x; | ||||
| 							point.y = point2.y; | ||||
| 							point.z = point2.z; | ||||
| 						} | ||||
| 						index++; | ||||
| 					} else { | ||||
| 						if (offset1 > 0) { | ||||
| 							k = offset2/(offset2 - offset1); | ||||
| 							point = points2[index]; | ||||
| 							if (point == null) { | ||||
| 								point = new DrawPoint(point2.x - (point2.x - point1.x)*k, point2.y - (point2.y - point1.y)*k, point2.z - (point2.z - point1.z)*k); | ||||
| 								points2[index] = point; | ||||
| 							} else { | ||||
| 								point.x = point2.x - (point2.x - point1.x)*k; | ||||
| 								point.y = point2.y - (point2.y - point1.y)*k; | ||||
| 								point.z = point2.z - (point2.z - point1.z)*k; | ||||
| 							} | ||||
| 							index++; | ||||
| 						} | ||||
| 					} | ||||
| 					offset1 = offset2; | ||||
| 					point1 = point2; | ||||
| 				} | ||||
| 			} | ||||
| 			return index; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Добавление текущего скина. | ||||
| 		 */ | ||||
| 		private function addCurrentSkin():void { | ||||
|  			currentSkin = Skin.createSkin(); | ||||
|  			_view.canvas.addChild(currentSkin); | ||||
|  			if (prevSkin == null) { | ||||
|  				firstSkin = currentSkin; | ||||
|  			} else { | ||||
|  				prevSkin.nextSkin = currentSkin; | ||||
|  			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Вставляем под текущий скин. | ||||
| 		 */ | ||||
| 		private function insertCurrentSkin():void { | ||||
| 			var skin:Skin = Skin.createSkin(); | ||||
|  			_view.canvas.addChildAt(skin, _view.canvas.getChildIndex(currentSkin)); | ||||
|  			skin.nextSkin = currentSkin; | ||||
|  			if (prevSkin == null) { | ||||
|  				firstSkin = skin; | ||||
|  			} else { | ||||
|  				prevSkin.nextSkin = skin; | ||||
|  			} | ||||
|  			currentSkin = skin; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаляет текущий скин. | ||||
| 		 */ | ||||
| 		private function removeCurrentSkin():void { | ||||
| 			// Сохраняем следующий | ||||
| 			var next:Skin = currentSkin.nextSkin; | ||||
| 			// Удаляем из канваса | ||||
| 			_view.canvas.removeChild(currentSkin); | ||||
| 			// Очистка скина | ||||
| 			if (currentSkin.material != null) { | ||||
| 				currentSkin.material.clear(currentSkin); | ||||
| 			} | ||||
| 			// Зачищаем ссылки | ||||
| 			currentSkin.nextSkin = null; | ||||
| 			currentSkin.primitive = null; | ||||
| 			currentSkin.material = null; | ||||
| 			// Удаляем | ||||
| 			Skin.destroySkin(currentSkin); | ||||
| 			// Следующий устанавливаем текущим | ||||
| 			currentSkin = next; | ||||
| 			// Устанавливаем связь от предыдущего скина | ||||
| 			if (prevSkin == null) { | ||||
| 		 		firstSkin = currentSkin; | ||||
| 		 	} else { | ||||
| 		 		prevSkin.nextSkin = currentSkin; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		alternativa3d function calculateUVMatrix(face:Face, width:uint, height:uint):void { | ||||
|  | ||||
| 			// Расчёт точек базового примитива в координатах камеры | ||||
| 			var point:Point3D = face.primitive.points[0]; | ||||
| 			textureA.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z; | ||||
| 			textureA.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z; | ||||
| 			point = face.primitive.points[1]; | ||||
| 			textureB.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z; | ||||
| 			textureB.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z; | ||||
| 			point = face.primitive.points[2]; | ||||
| 			textureC.x = cameraMatrix.a*point.x + cameraMatrix.b*point.y + cameraMatrix.c*point.z; | ||||
| 			textureC.y = cameraMatrix.e*point.x + cameraMatrix.f*point.y + cameraMatrix.g*point.z; | ||||
| 			 | ||||
| 			// Находим AB и AC | ||||
| 			var abx:Number = textureB.x - textureA.x; | ||||
| 			var aby:Number = textureB.y - textureA.y; | ||||
| 			var acx:Number = textureC.x - textureA.x; | ||||
| 			var acy:Number = textureC.y - textureA.y; | ||||
|  | ||||
| 			// Расчёт текстурной матрицы | ||||
| 			var uvMatrixBase:Matrix = face.uvMatrixBase; | ||||
| 			var uvMatrix:Matrix = face.uvMatrix; | ||||
| 			uvMatrix.a = (uvMatrixBase.a*abx + uvMatrixBase.b*acx)/width; | ||||
| 			uvMatrix.b = (uvMatrixBase.a*aby + uvMatrixBase.b*acy)/width; | ||||
| 			uvMatrix.c = -(uvMatrixBase.c*abx + uvMatrixBase.d*acx)/height; | ||||
| 			uvMatrix.d = -(uvMatrixBase.c*aby + uvMatrixBase.d*acy)/height; | ||||
| 			uvMatrix.tx = (uvMatrixBase.tx + uvMatrixBase.c)*abx + (uvMatrixBase.ty + uvMatrixBase.d)*acx + textureA.x + cameraMatrix.d; | ||||
| 			uvMatrix.ty = (uvMatrixBase.tx + uvMatrixBase.c)*aby + (uvMatrixBase.ty + uvMatrixBase.d)*acy + textureA.y + cameraMatrix.h; | ||||
| 			 | ||||
| 			// Помечаем, как рассчитанную | ||||
| 			uvMatricesCalculated[face] = true; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Поле вывода, в котором происходит отрисовка камеры. | ||||
| 		 */ | ||||
| 		public function get view():View { | ||||
| 			return _view; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set view(value:View):void { | ||||
| 			if (value != _view) { | ||||
| 				if (_view != null) { | ||||
| 					_view.camera = null; | ||||
| 				} | ||||
| 				if (value != null) { | ||||
| 					value.camera = this; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Включение режима аксонометрической проекции. | ||||
| 		 *  | ||||
| 		 * @default false | ||||
| 		 */		 | ||||
| 		public function get orthographic():Boolean { | ||||
| 			return _orthographic; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set orthographic(value:Boolean):void { | ||||
| 			if (_orthographic != value) { | ||||
| 				// Отправляем сигнал об изменении типа камеры | ||||
| 				addOperationToScene(calculateMatrixOperation); | ||||
| 				// Сохраняем новое значение | ||||
| 				_orthographic = value; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Угол поля зрения в радианах в режиме перспективной проекции. При изменении FOV изменяется фокусное расстояние | ||||
| 		 * камеры по формуле <code>f = d/tan(fov/2)</code>, где <code>d</code> является половиной диагонали поля вывода. | ||||
| 		 * Угол зрения ограничен диапазоном 0-180 градусов. | ||||
| 		 */ | ||||
| 		public function get fov():Number { | ||||
| 			return _fov; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set fov(value:Number):void { | ||||
| 			value = (value < 0) ? 0 : ((value > (Math.PI - 0.0001)) ? (Math.PI - 0.0001) : value); | ||||
| 			if (_fov != value) { | ||||
| 				// Если перспектива | ||||
| 				if (!_orthographic) { | ||||
| 					// Отправляем сигнал об изменении плоскостей отсечения | ||||
| 					addOperationToScene(calculatePlanesOperation); | ||||
| 				} | ||||
| 				// Сохраняем новое значение | ||||
| 				_fov = value; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Коэффициент увеличения изображения в режиме аксонометрической проекции. | ||||
| 		 */ | ||||
| 		public function get zoom():Number { | ||||
| 			return _zoom; | ||||
| 		}		 | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set zoom(value:Number):void { | ||||
| 			value = (value < 0) ? 0 : value; | ||||
| 			if (_zoom != value) { | ||||
| 				// Если изометрия | ||||
| 				if (_orthographic) { | ||||
| 					// Отправляем сигнал об изменении zoom | ||||
| 					addOperationToScene(calculateMatrixOperation); | ||||
| 				} | ||||
| 				// Сохраняем новое значение | ||||
| 				_zoom = value; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		override protected function addToScene(scene:Scene3D):void { | ||||
| 			super.addToScene(scene); | ||||
| 			if (_view != null) { | ||||
| 				// Отправляем операцию расчёта плоскостей отсечения | ||||
| 				scene.addOperation(calculatePlanesOperation); | ||||
| 				// Подписываемся на сигналы сцены | ||||
| 				scene.changePrimitivesOperation.addSequel(renderOperation); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		override protected function removeFromScene(scene:Scene3D):void { | ||||
| 			super.removeFromScene(scene); | ||||
| 			 | ||||
| 			// Удаляем все операции из очереди | ||||
| 			scene.removeOperation(calculateMatrixOperation); | ||||
| 			scene.removeOperation(calculatePlanesOperation); | ||||
| 			scene.removeOperation(renderOperation); | ||||
| 			 | ||||
| 			if (_view != null) { | ||||
| 				// Отписываемся от сигналов сцены | ||||
| 				scene.changePrimitivesOperation.removeSequel(renderOperation); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		alternativa3d function addToView(view:View):void { | ||||
| 			// Сохраняем первый скин | ||||
| 			firstSkin = (view.canvas.numChildren > 0) ? Skin(view.canvas.getChildAt(0)) : null; | ||||
| 			 | ||||
| 			// Подписка на свои операции | ||||
|  | ||||
| 			// При изменении камеры пересчёт матрицы | ||||
| 			calculateTransformationOperation.addSequel(calculateMatrixOperation); | ||||
| 			// При изменении матрицы или FOV пересчёт плоскостей отсечения | ||||
| 			calculateMatrixOperation.addSequel(calculatePlanesOperation); | ||||
| 			// При изменении плоскостей перерисовка | ||||
| 			calculatePlanesOperation.addSequel(renderOperation); | ||||
|  | ||||
| 			if (_scene != null) { | ||||
| 				// Отправляем сигнал перерисовки | ||||
| 				_scene.addOperation(calculateMatrixOperation); | ||||
| 				// Подписываемся на сигналы сцены | ||||
| 				_scene.changePrimitivesOperation.addSequel(renderOperation); | ||||
| 			} | ||||
| 			 | ||||
| 			// Сохраняем вид | ||||
| 			_view = view; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		alternativa3d function removeFromView(view:View):void { | ||||
| 			// Сброс ссылки на первый скин | ||||
| 			firstSkin = null; | ||||
| 			 | ||||
| 			// Отписка от своих операций | ||||
|  | ||||
| 			// При изменении камеры пересчёт матрицы | ||||
| 			calculateTransformationOperation.removeSequel(calculateMatrixOperation); | ||||
| 			// При изменении матрицы или FOV пересчёт плоскостей отсечения | ||||
| 			calculateMatrixOperation.removeSequel(calculatePlanesOperation); | ||||
| 			// При изменении плоскостей перерисовка | ||||
| 			calculatePlanesOperation.removeSequel(renderOperation); | ||||
| 			 | ||||
| 			if (_scene != null) { | ||||
| 				// Удаляем все операции из очереди | ||||
| 				_scene.removeOperation(calculateMatrixOperation); | ||||
| 				_scene.removeOperation(calculatePlanesOperation); | ||||
| 				_scene.removeOperation(renderOperation); | ||||
| 				// Отписываемся от сигналов сцены | ||||
| 				_scene.changePrimitivesOperation.removeSequel(renderOperation); | ||||
| 			} | ||||
| 			// Удаляем ссылку на вид | ||||
| 			_view = null; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		override protected function defaultName():String { | ||||
| 			return "camera" + ++counter; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		protected override function createEmptyObject():Object3D { | ||||
| 			return new Camera3D(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		protected override function clonePropertiesFrom(source:Object3D):void { | ||||
| 			super.clonePropertiesFrom(source); | ||||
| 			 | ||||
| 			var src:Camera3D = Camera3D(source); | ||||
| 			orthographic = src._orthographic; | ||||
| 			zoom = src._zoom; | ||||
| 			fov = src._fov; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,921 +0,0 @@ | ||||
| package alternativa.engine3d.core { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.types.Set; | ||||
|  | ||||
| 	import flash.geom.Matrix; | ||||
| 	import flash.geom.Point; | ||||
|  | ||||
| 	use namespace alternativa3d; | ||||
|  | ||||
| 	/** | ||||
| 	 * Грань, образованная тремя или более вершинами. Грани являются составными частями полигональных объектов. Каждая грань | ||||
| 	 * содержит информацию об объекте и поверхности, которым она принадлежит. Для обеспечения возможности наложения | ||||
| 	 * текстуры на грань, первым трём её вершинам могут быть заданы UV-координаты, на основании которых расчитывается | ||||
| 	 * матрица трансформации текстуры. | ||||
| 	 */ | ||||
| 	final public class Face { | ||||
| 		// Операции | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт глобальной нормали плоскости грани. | ||||
| 		 */		 | ||||
| 		alternativa3d var calculateNormalOperation:Operation = new Operation("calculateNormal", this, calculateNormal, Operation.FACE_CALCULATE_NORMAL); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт UV-координат (выполняется до трансформации, чтобы UV корректно разбились при построении BSP). | ||||
| 		 */		  | ||||
| 		alternativa3d var calculateUVOperation:Operation = new Operation("calculateUV", this, calculateUV, Operation.FACE_CALCULATE_UV); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Обновление примитива в сцене. | ||||
| 		 */		  | ||||
| 		alternativa3d var updatePrimitiveOperation:Operation = new Operation("updatePrimitive", this, updatePrimitive, Operation.FACE_UPDATE_PRIMITIVE); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Обновление материала. | ||||
| 		 */		  | ||||
| 		alternativa3d var updateMaterialOperation:Operation = new Operation("updateMaterial", this, updateMaterial, Operation.FACE_UPDATE_MATERIAL); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт UV для фрагментов (выполняется после трансформации, если её не было). | ||||
| 		 */ | ||||
| 		alternativa3d var calculateFragmentsUVOperation:Operation = new Operation("calculateFragmentsUV", this, calculateFragmentsUV, Operation.FACE_CALCULATE_FRAGMENTS_UV); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Меш | ||||
| 		 */ | ||||
| 		alternativa3d var _mesh:Mesh; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Поверхность | ||||
| 		 */		 | ||||
| 		alternativa3d var _surface:Surface; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Вершины грани | ||||
| 		 */ | ||||
| 		alternativa3d var _vertices:Array; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Количество вершин | ||||
| 		 */ | ||||
| 		alternativa3d var _verticesCount:uint; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Примитив | ||||
| 		 */ | ||||
| 		alternativa3d var primitive:PolyPrimitive; | ||||
|  | ||||
| 		// UV-координаты | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		alternativa3d var _aUV:Point; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		alternativa3d var _bUV:Point; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		alternativa3d var _cUV:Point; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Коэффициенты базовой UV-матрицы | ||||
| 		 */ | ||||
| 		alternativa3d var uvMatrixBase:Matrix; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * UV Матрица перевода текстурных координат в изометрическую камеру.  | ||||
| 		 */ | ||||
| 		alternativa3d var uvMatrix:Matrix; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Нормаль плоскости | ||||
| 		 */ | ||||
| 		alternativa3d var globalNormal:Point3D = new Point3D(); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Смещение плоскости | ||||
| 		 */		 | ||||
| 		alternativa3d var globalOffset:Number; | ||||
|  | ||||
| 		/** | ||||
| 		 * Создание экземпляра грани. | ||||
| 		 *  | ||||
| 		 * @param vertices массив объектов типа <code>alternativa.engine3d.core.Vertex</code>, задающий вершины грани в | ||||
| 		 * порядке обхода лицевой стороны грани против часовой стрелки. | ||||
| 		 *  | ||||
| 		 * @see Vertex | ||||
| 		 */				 | ||||
| 		public function Face(vertices:Array) { | ||||
| 			// Сохраняем вершины | ||||
| 			_vertices = vertices; | ||||
| 			_verticesCount = vertices.length; | ||||
|  | ||||
| 			// Создаём оригинальный примитив | ||||
| 			primitive = PolyPrimitive.createPolyPrimitive(); | ||||
| 			primitive.face = this; | ||||
| 			primitive.num = _verticesCount; | ||||
| 			 | ||||
| 			// Обрабатываем вершины | ||||
| 			for (var i:uint = 0; i < _verticesCount; i++) { | ||||
| 				var vertex:Vertex = vertices[i]; | ||||
| 				// Добавляем координаты вершины в примитив | ||||
| 				primitive.points.push(vertex.globalCoords); | ||||
| 				// Добавляем пустые UV-координаты в примитив | ||||
| 				primitive.uvs.push(null); | ||||
| 				// Добавляем вершину в грань | ||||
| 				vertex.addToFace(this); | ||||
| 			} | ||||
|  | ||||
| 			// Расчёт нормали | ||||
| 			calculateNormalOperation.addSequel(updatePrimitiveOperation); | ||||
| 			 | ||||
| 			// Расчёт UV грани инициирует расчёт UV фрагментов и перерисовку | ||||
| 			calculateUVOperation.addSequel(calculateFragmentsUVOperation); | ||||
| 			calculateUVOperation.addSequel(updateMaterialOperation); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт нормали в глобальных координатах | ||||
| 		 */ | ||||
| 		private function calculateNormal():void { | ||||
| 			// Вектор AB | ||||
| 			var vertex:Vertex = _vertices[0]; | ||||
| 			var av:Point3D = vertex.globalCoords; | ||||
| 			vertex = _vertices[1]; | ||||
| 			var bv:Point3D = vertex.globalCoords; | ||||
| 			var abx:Number = bv.x - av.x; | ||||
| 			var aby:Number = bv.y - av.y; | ||||
| 			var abz:Number = bv.z - av.z; | ||||
| 			// Вектор AC | ||||
| 			vertex = _vertices[2]; | ||||
| 			var cv:Point3D = vertex.globalCoords; | ||||
| 			var acx:Number = cv.x - av.x; | ||||
| 			var acy:Number = cv.y - av.y; | ||||
| 			var acz:Number = cv.z - av.z; | ||||
| 			// Перпендикуляр к плоскости | ||||
| 			globalNormal.x = acz*aby - acy*abz; | ||||
| 			globalNormal.y = acx*abz - acz*abx; | ||||
| 			globalNormal.z = acy*abx - acx*aby; | ||||
| 			// Нормализация перпендикуляра | ||||
| 			globalNormal.normalize(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчитывает глобальное смещение плоскости грани. | ||||
| 		 * Помечает конечные примитивы на удаление, а базовый на добавление в сцене.   | ||||
| 		 */ | ||||
| 		private function updatePrimitive():void { | ||||
| 			// Расчёт смещения | ||||
| 			var vertex:Vertex = _vertices[0]; | ||||
| 			globalOffset = vertex.globalCoords.x*globalNormal.x + vertex.globalCoords.y*globalNormal.y + vertex.globalCoords.z*globalNormal.z; | ||||
|  | ||||
| 			removePrimitive(primitive); | ||||
| 			primitive.mobility = _mesh.inheritedMobility; | ||||
| 			_mesh._scene.addPrimitives.push(primitive); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Рекурсивно проходит по фрагментам примитива и отправляет конечные фрагменты на удаление из сцены  | ||||
| 		 */ | ||||
| 		private function removePrimitive(primitive:PolyPrimitive):void { | ||||
| 			if (primitive.backFragment != null) { | ||||
| 				removePrimitive(primitive.backFragment); | ||||
| 				removePrimitive(primitive.frontFragment); | ||||
| 				primitive.backFragment = null; | ||||
| 				primitive.frontFragment = null; | ||||
| 				if (primitive != this.primitive) { | ||||
| 					primitive.parent = null; | ||||
| 					primitive.sibling = null; | ||||
| 					PolyPrimitive.destroyPolyPrimitive(primitive); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Если примитив в BSP-дереве | ||||
| 				if (primitive.node != null) { | ||||
| 					// Удаление примитива | ||||
| 					_mesh._scene.removeBSPPrimitive(primitive); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Пометка на перерисовку фрагментов грани. | ||||
| 		 */ | ||||
| 		private function updateMaterial():void { | ||||
| 			if (!updatePrimitiveOperation.queued) { | ||||
| 				changePrimitive(primitive); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Рекурсивно проходит по фрагментам примитива и отправляет конечные фрагменты на перерисовку | ||||
| 		 */ | ||||
| 		private function changePrimitive(primitive:PolyPrimitive):void { | ||||
| 			if (primitive.backFragment != null) { | ||||
| 				changePrimitive(primitive.backFragment); | ||||
| 				changePrimitive(primitive.frontFragment); | ||||
| 			} else { | ||||
| 				_mesh._scene.changedPrimitives[primitive] = true; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт UV-матрицы на основании первых трёх UV-координат. | ||||
| 		 * Расчёт UV-координат для оставшихся точек.  | ||||
| 		 */ | ||||
| 		private function calculateUV():void { | ||||
| 			var i:uint; | ||||
| 			// Расчёт UV-матрицы | ||||
| 			if (_aUV != null && _bUV != null && _cUV != null) { | ||||
| 				var abu:Number = _bUV.x - _aUV.x; | ||||
| 				var abv:Number = _bUV.y - _aUV.y; | ||||
| 				var acu:Number = _cUV.x - _aUV.x; | ||||
| 				var acv:Number = _cUV.y - _aUV.y; | ||||
| 				var det:Number = abu*acv - abv*acu; | ||||
| 				if (det != 0) { | ||||
| 					if (uvMatrixBase == null) { | ||||
| 						uvMatrixBase = new Matrix(); | ||||
| 						uvMatrix = new Matrix(); | ||||
| 					} | ||||
| 					uvMatrixBase.a = acv/det; | ||||
| 					uvMatrixBase.b = -abv/det; | ||||
| 					uvMatrixBase.c = -acu/det; | ||||
| 					uvMatrixBase.d = abu/det; | ||||
| 					uvMatrixBase.tx = -(uvMatrixBase.a*_aUV.x + uvMatrixBase.c*_aUV.y); | ||||
| 					uvMatrixBase.ty = -(uvMatrixBase.b*_aUV.x + uvMatrixBase.d*_aUV.y); | ||||
| 					 | ||||
| 					// Заполняем UV в базовом примитиве | ||||
| 					primitive.uvs[0] = _aUV; | ||||
| 					primitive.uvs[1] = _bUV; | ||||
| 					primitive.uvs[2] = _cUV; | ||||
| 					 | ||||
| 					// Расчёт недостающих UV | ||||
| 					if (_verticesCount > 3) { | ||||
| 						var a:Point3D = primitive.points[0]; | ||||
| 						var b:Point3D = primitive.points[1]; | ||||
| 						var c:Point3D = primitive.points[2]; | ||||
|  | ||||
| 						var ab1:Number; | ||||
| 						var ab2:Number; | ||||
| 						var ac1:Number; | ||||
| 						var ac2:Number; | ||||
| 						var ad1:Number; | ||||
| 						var ad2:Number; | ||||
| 						var abk:Number; | ||||
| 						var ack:Number; | ||||
| 						 | ||||
| 						var uv:Point; | ||||
| 						var point:Point3D; | ||||
| 						 | ||||
| 						// Выбор наиболее подходящих осей для расчёта | ||||
| 						if (((globalNormal.x < 0) ? -globalNormal.x : globalNormal.x) > ((globalNormal.y < 0) ? -globalNormal.y : globalNormal.y)) { | ||||
| 							if (((globalNormal.x < 0) ? -globalNormal.x : globalNormal.x) > ((globalNormal.z < 0) ? -globalNormal.z : globalNormal.z)) { | ||||
| 								// Ось X | ||||
| 								ab1 = b.y - a.y; | ||||
| 								ab2 = b.z - a.z; | ||||
| 								ac1 = c.y - a.y; | ||||
| 								ac2 = c.z - a.z; | ||||
| 								det = ab1*ac2 - ac1*ab2; | ||||
| 								for (i = 3; i < _verticesCount; i++) { | ||||
| 									point = primitive.points[i]; | ||||
| 									ad1 = point.y - a.y; | ||||
| 									ad2 = point.z - a.z; | ||||
| 									abk = (ad1*ac2 - ac1*ad2)/det; | ||||
| 									ack = (ab1*ad2 - ad1*ab2)/det; | ||||
| 									uv = primitive.uvs[i]; | ||||
| 									if (uv == null) { | ||||
| 										uv = new Point(); | ||||
| 										primitive.uvs[i] = uv; | ||||
| 									} | ||||
| 									uv.x = _aUV.x + abu*abk + acu*ack; | ||||
| 									uv.y = _aUV.y + abv*abk + acv*ack; | ||||
| 								} | ||||
| 							} else { | ||||
| 								// Ось Z | ||||
| 								ab1 = b.x - a.x; | ||||
| 								ab2 = b.y - a.y; | ||||
| 								ac1 = c.x - a.x; | ||||
| 								ac2 = c.y - a.y; | ||||
| 								det = ab1*ac2 - ac1*ab2; | ||||
| 								for (i = 3; i < _verticesCount; i++) { | ||||
| 									point = primitive.points[i]; | ||||
| 									ad1 = point.x - a.x; | ||||
| 									ad2 = point.y - a.y; | ||||
| 									abk = (ad1*ac2 - ac1*ad2)/det; | ||||
| 									ack = (ab1*ad2 - ad1*ab2)/det; | ||||
| 									uv = primitive.uvs[i]; | ||||
| 									if (uv == null) { | ||||
| 										uv = new Point(); | ||||
| 										primitive.uvs[i] = uv; | ||||
| 									} | ||||
| 									uv.x = _aUV.x + abu*abk + acu*ack; | ||||
| 									uv.y = _aUV.y + abv*abk + acv*ack; | ||||
| 								} | ||||
| 							} | ||||
| 						} else { | ||||
| 							if (((globalNormal.y < 0) ? -globalNormal.y : globalNormal.y) > ((globalNormal.z < 0) ? -globalNormal.z : globalNormal.z)) { | ||||
| 								// Ось Y | ||||
| 								ab1 = b.x - a.x; | ||||
| 								ab2 = b.z - a.z; | ||||
| 								ac1 = c.x - a.x; | ||||
| 								ac2 = c.z - a.z; | ||||
| 								det = ab1*ac2 - ac1*ab2; | ||||
| 								for (i = 3; i < _verticesCount; i++) { | ||||
| 									point = primitive.points[i]; | ||||
| 									ad1 = point.x - a.x; | ||||
| 									ad2 = point.z - a.z; | ||||
| 									abk = (ad1*ac2 - ac1*ad2)/det; | ||||
| 									ack = (ab1*ad2 - ad1*ab2)/det; | ||||
| 									uv = primitive.uvs[i]; | ||||
| 									if (uv == null) { | ||||
| 										uv = new Point(); | ||||
| 										primitive.uvs[i] = uv; | ||||
| 									} | ||||
| 									uv.x = _aUV.x + abu*abk + acu*ack; | ||||
| 									uv.y = _aUV.y + abv*abk + acv*ack; | ||||
| 								} | ||||
| 							} else { | ||||
| 								// Ось Z | ||||
| 								ab1 = b.x - a.x; | ||||
| 								ab2 = b.y - a.y; | ||||
| 								ac1 = c.x - a.x; | ||||
| 								ac2 = c.y - a.y; | ||||
| 								det = ab1*ac2 - ac1*ab2; | ||||
| 								for (i = 3; i < _verticesCount; i++) { | ||||
| 									point = primitive.points[i]; | ||||
| 									ad1 = point.x - a.x; | ||||
| 									ad2 = point.y - a.y; | ||||
| 									abk = (ad1*ac2 - ac1*ad2)/det; | ||||
| 									ack = (ab1*ad2 - ad1*ab2)/det; | ||||
| 									uv = primitive.uvs[i]; | ||||
| 									if (uv == null) { | ||||
| 										uv = new Point(); | ||||
| 										primitive.uvs[i] = uv; | ||||
| 									} | ||||
| 									uv.x = _aUV.x + abu*abk + acu*ack; | ||||
| 									uv.y = _aUV.y + abv*abk + acv*ack; | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} else { | ||||
| 					// Удаляем UV-матрицу | ||||
| 					uvMatrixBase = null; | ||||
| 					uvMatrix = null; | ||||
| 					// Удаляем UV-координаты из базового примитива | ||||
| 					for (i = 0; i < _verticesCount; i++) { | ||||
| 						primitive.uvs[i] = null; | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Удаляем UV-матрицу | ||||
| 				uvMatrixBase = null; | ||||
| 				uvMatrix = null; | ||||
| 				// Удаляем UV-координаты из базового примитива | ||||
| 				for (i = 0; i < _verticesCount; i++) { | ||||
| 					primitive.uvs[i] = null; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private  | ||||
| 		 * Расчёт UV-координат для фрагментов примитива, если не было трансформации | ||||
| 		 */ | ||||
| 		private function calculateFragmentsUV():void { | ||||
| 			// Если в этом цикле не было трансформации  | ||||
| 			if (!updatePrimitiveOperation.queued) { | ||||
| 				if (uvMatrixBase != null) { | ||||
| 					// Рассчитываем UV в примитиве  | ||||
| 					calculatePrimitiveUV(primitive); | ||||
| 				} else { | ||||
| 					// Удаляем UV в примитиве | ||||
| 					removePrimitiveUV(primitive); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт UV для точек базового примитива. | ||||
| 		 *  | ||||
| 		 * @param primitive | ||||
| 		 */ | ||||
| 		private function calculatePrimitiveUV(primitive:PolyPrimitive):void { | ||||
| 			if (primitive.backFragment	!= null) { | ||||
| 				var points:Array = primitive.points; | ||||
| 				var backPoints:Array = primitive.backFragment.points; | ||||
| 				var frontPoints:Array = primitive.frontFragment.points; | ||||
| 				var uvs:Array = primitive.uvs; | ||||
| 				var backUVs:Array = primitive.backFragment.uvs; | ||||
| 				var frontUVs:Array = primitive.frontFragment.uvs; | ||||
| 				var index1:uint = 0; | ||||
| 				var index2:uint = 0; | ||||
| 				var point:Point3D; | ||||
| 				var uv:Point; | ||||
| 				var uv1:Point; | ||||
| 				var uv2:Point; | ||||
| 				var t:Number; | ||||
| 				var firstSplit:Boolean = true; | ||||
| 				for (var i:uint = 0; i < primitive.num; i++) { | ||||
| 					var split:Boolean = true; | ||||
| 					point = points[i]; | ||||
| 					if (point == frontPoints[index2]) { | ||||
| 						if (frontUVs[index2] == null) { | ||||
| 							frontUVs[index2] = uvs[i]; | ||||
| 						} | ||||
| 						split = false; | ||||
| 						index2++; | ||||
| 					} | ||||
| 					if (point == backPoints[index1]) { | ||||
| 						if (backUVs[index1] == null) { | ||||
| 							backUVs[index1] = uvs[i]; | ||||
| 						} | ||||
| 						split = false; | ||||
| 						index1++; | ||||
| 					} | ||||
| 					 | ||||
|  					if (split) { | ||||
| 						uv1 = uvs[(i == 0) ? (primitive.num - 1) : (i - 1)]; | ||||
| 						uv2 = uvs[i]; | ||||
| 						t = (firstSplit) ? primitive.splitTime1 : primitive.splitTime2; | ||||
| 						uv = frontUVs[index2]; | ||||
| 						if (uv == null) { | ||||
| 							uv = new Point(uv1.x + (uv2.x - uv1.x)*t, uv1.y + (uv2.y - uv1.y)*t); | ||||
| 							frontUVs[index2] = uv; | ||||
| 							backUVs[index1] = uv; | ||||
| 						} else { | ||||
| 							uv.x = uv1.x + (uv2.x - uv1.x)*t; | ||||
| 							uv.y = uv1.y + (uv2.y - uv1.y)*t; | ||||
| 						} | ||||
| 						firstSplit = false; | ||||
| 						index2++; | ||||
| 						index1++; | ||||
| 						if (point == frontPoints[index2]) { | ||||
| 							if (frontUVs[index2] == null) { | ||||
| 								frontUVs[index2] = uvs[i]; | ||||
| 							} | ||||
| 							index2++; | ||||
| 						} | ||||
| 						if (point == backPoints[index1]) { | ||||
| 							if (backUVs[index1] == null) { | ||||
| 								backUVs[index1] = uvs[i]; | ||||
| 							} | ||||
| 							index1++; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				// Проверяем рассечение последнего ребра | ||||
| 				if (index2 < primitive.frontFragment.num) { | ||||
| 					uv1 = uvs[primitive.num - 1]; | ||||
| 					uv2 = uvs[0]; | ||||
| 					t = (firstSplit) ? primitive.splitTime1 : primitive.splitTime2; | ||||
| 					uv = frontUVs[index2]; | ||||
| 					if (uv == null) { | ||||
| 						uv = new Point(uv1.x + (uv2.x - uv1.x)*t, uv1.y + (uv2.y - uv1.y)*t); | ||||
| 						frontUVs[index2] = uv; | ||||
| 						backUVs[index1] = uv; | ||||
| 					} else { | ||||
| 						uv.x = uv1.x + (uv2.x - uv1.x)*t; | ||||
| 						uv.y = uv1.y + (uv2.y - uv1.y)*t; | ||||
| 					} | ||||
| 				} | ||||
| 				 | ||||
| 				calculatePrimitiveUV(primitive.backFragment); | ||||
| 				calculatePrimitiveUV(primitive.frontFragment); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление UV в примитиве и его фрагментах | ||||
| 		 * @param primitive | ||||
| 		 */ | ||||
| 		private function removePrimitiveUV(primitive:PolyPrimitive):void { | ||||
| 			// Очищаем список UV | ||||
| 			for (var i:uint = 0; i < primitive.num; i++) { | ||||
| 				primitive.uvs[i] = null; | ||||
| 			} | ||||
| 			// Если есть фрагменты, удаляем UV в них | ||||
| 			if (primitive.backFragment != null) { | ||||
| 				removePrimitiveUV(primitive.backFragment); | ||||
| 				removePrimitiveUV(primitive.frontFragment); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Массив вершин грани, представленных объектами класса <code>alternativa.engine3d.core.Vertex</code>. | ||||
| 		 *  | ||||
| 		 * @see Vertex | ||||
| 		 */ | ||||
| 		public function get vertices():Array { | ||||
| 			return new Array().concat(_vertices); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Количество вершин грани.  | ||||
| 		 */ | ||||
| 		public function get verticesCount():uint { | ||||
| 			return _verticesCount; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Полигональный объект, которому принадлежит грань. | ||||
| 		 */ | ||||
| 		public function get mesh():Mesh { | ||||
| 			return _mesh; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Поверхность, которой принадлежит грань. | ||||
| 		 */ | ||||
| 		public function get surface():Surface { | ||||
| 			return _surface; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Идентификатор грани в полигональном объекте. В случае, если грань не принадлежит ни одному объекту, идентификатор | ||||
| 		 * имеет значение <code>null</code>. | ||||
| 		 */ | ||||
| 		public function get id():Object { | ||||
| 			return (_mesh != null) ? _mesh.getFaceId(this) : null; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * UV-координаты, соответствующие первой вершине грани. | ||||
| 		 */ | ||||
| 		public function get aUV():Point { | ||||
| 			return (_aUV != null) ? _aUV.clone() : null; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * UV-координаты, соответствующие второй вершине грани. | ||||
| 		 */ | ||||
| 		public function get bUV():Point { | ||||
| 			return (_bUV != null) ? _bUV.clone() : null; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * UV-координаты, соответствующие третьей вершине грани. | ||||
| 		 */ | ||||
| 		public function get cUV():Point { | ||||
| 			return (_cUV != null) ? _cUV.clone() : null; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set aUV(value:Point):void { | ||||
| 			if (_aUV != null) { | ||||
| 				if (value != null) { | ||||
| 					if (!_aUV.equals(value)) {   | ||||
| 						_aUV.x = value.x; | ||||
| 						_aUV.y = value.y; | ||||
| 						if (_mesh != null) { | ||||
| 							_mesh.addOperationToScene(calculateUVOperation); | ||||
| 						} | ||||
| 					} | ||||
| 				} else { | ||||
| 					_aUV = null; | ||||
| 					if (_mesh != null) { | ||||
| 						_mesh.addOperationToScene(calculateUVOperation); | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (value != null) { | ||||
| 					_aUV = value.clone(); | ||||
| 					if (_mesh != null) { | ||||
| 						_mesh.addOperationToScene(calculateUVOperation); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set bUV(value:Point):void { | ||||
| 			if (_bUV != null) { | ||||
| 				if (value != null) { | ||||
| 					if (!_bUV.equals(value)) {   | ||||
| 						_bUV.x = value.x; | ||||
| 						_bUV.y = value.y; | ||||
| 						if (_mesh != null) { | ||||
| 							_mesh.addOperationToScene(calculateUVOperation); | ||||
| 						} | ||||
| 					} | ||||
| 				} else { | ||||
| 					_bUV = null; | ||||
| 					if (_mesh != null) { | ||||
| 						_mesh.addOperationToScene(calculateUVOperation); | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (value != null) { | ||||
| 					_bUV = value.clone(); | ||||
| 					if (_mesh != null) { | ||||
| 						_mesh.addOperationToScene(calculateUVOperation); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set cUV(value:Point):void { | ||||
| 			if (_cUV != null) { | ||||
| 				if (value != null) { | ||||
| 					if (!_cUV.equals(value)) {   | ||||
| 						_cUV.x = value.x; | ||||
| 						_cUV.y = value.y; | ||||
| 						if (_mesh != null) { | ||||
| 							_mesh.addOperationToScene(calculateUVOperation); | ||||
| 						} | ||||
| 					} | ||||
| 				} else { | ||||
| 					_cUV = null; | ||||
| 					if (_mesh != null) { | ||||
| 						_mesh.addOperationToScene(calculateUVOperation); | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (value != null) { | ||||
| 					_cUV = value.clone(); | ||||
| 					if (_mesh != null) { | ||||
| 						_mesh.addOperationToScene(calculateUVOperation); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Нормаль в локальной системе координат. | ||||
| 		 */ | ||||
| 		public function get normal():Point3D { | ||||
| 			var res:Point3D = new Point3D(); | ||||
| 			var vertex:Vertex = _vertices[0]; | ||||
| 			var av:Point3D = vertex.coords; | ||||
| 			vertex = _vertices[1]; | ||||
| 			var bv:Point3D = vertex.coords; | ||||
| 			var abx:Number = bv.x - av.x; | ||||
| 			var aby:Number = bv.y - av.y; | ||||
| 			var abz:Number = bv.z - av.z; | ||||
| 			vertex = _vertices[2]; | ||||
| 			var cv:Point3D = vertex.coords; | ||||
| 			var acx:Number = cv.x - av.x; | ||||
| 			var acy:Number = cv.y - av.y; | ||||
| 			var acz:Number = cv.z - av.z; | ||||
| 			res.x = acz*aby - acy*abz; | ||||
| 			res.y = acx*abz - acz*abx; | ||||
| 			res.z = acy*abx - acx*aby; | ||||
| 			if (res.x != 0 || res.y != 0 || res.z != 0) { | ||||
| 				var k:Number = Math.sqrt(res.x*res.x + res.y*res.y + res.z*res.z); | ||||
| 				res.x /= k; | ||||
| 				res.y /= k; | ||||
| 				res.z /= k; | ||||
| 			} | ||||
| 			return res; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Расчёт UV-координат для произвольной точки в системе координат объекта, которому принадлежит грань. | ||||
| 		 *  | ||||
| 		 * @param point точка в плоскости грани, для которой производится расчёт UV-координат | ||||
| 		 * @return UV-координаты заданной точки | ||||
| 		 */		 | ||||
| 		public function getUV(point:Point3D):Point { | ||||
| 			return getUVFast(point, normal); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт UV-координат для произвольной точки в локальной системе координат без расчёта  | ||||
| 		 * локальной нормали грани. Используется для оптимизации. | ||||
| 		 *  | ||||
| 		 * @param point точка в плоскости грани, для которой производится расчёт UV-координат | ||||
| 		 * @param normal нормаль плоскости грани в локальной системе координат | ||||
| 		 * @return UV-координаты заданной точки | ||||
| 		 */ | ||||
| 		alternativa3d function getUVFast(point:Point3D, normal:Point3D):Point { | ||||
| 			if (_aUV == null || _bUV == null || _cUV == null) { | ||||
| 				return null; | ||||
| 			} | ||||
|  | ||||
| 			// Выбор наиболее длинной оси нормали | ||||
| 			var dir:uint;  | ||||
| 			if (((normal.x < 0) ? -normal.x : normal.x) > ((normal.y < 0) ? -normal.y : normal.y)) { | ||||
| 				if (((normal.x < 0) ? -normal.x : normal.x) > ((normal.z < 0) ? -normal.z : normal.z)) { | ||||
| 					dir = 0; | ||||
| 				} else { | ||||
| 					dir = 2; | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (((normal.y < 0) ? -normal.y : normal.y) > ((normal.z < 0) ? -normal.z : normal.z)) { | ||||
| 					dir = 1; | ||||
| 				} else { | ||||
| 					dir = 2; | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Расчёт соотношения по векторам AB и AC | ||||
| 			var v:Vertex = _vertices[0]; | ||||
| 			var a:Point3D = v._coords; | ||||
| 			v = _vertices[1]; | ||||
| 			var b:Point3D = v._coords; | ||||
| 			v = _vertices[2]; | ||||
| 			var c:Point3D = v._coords; | ||||
| 						 | ||||
| 			var ab1:Number = (dir == 0) ? (b.y - a.y) : (b.x - a.x); | ||||
| 			var ab2:Number = (dir == 2) ? (b.y - a.y) : (b.z - a.z); | ||||
| 			var ac1:Number = (dir == 0) ? (c.y - a.y) : (c.x - a.x); | ||||
| 			var ac2:Number = (dir == 2) ? (c.y - a.y) : (c.z - a.z); | ||||
| 			var det:Number = ab1*ac2 - ac1*ab2; | ||||
| 				 | ||||
| 			var ad1:Number = (dir == 0) ? (point.y - a.y) : (point.x - a.x); | ||||
| 			var ad2:Number = (dir == 2) ? (point.y - a.y) : (point.z - a.z); | ||||
| 			var abk:Number = (ad1*ac2 - ac1*ad2)/det; | ||||
| 			var ack:Number = (ab1*ad2 - ad1*ab2)/det; | ||||
| 			 | ||||
| 			// Интерполяция по UV первых точек | ||||
| 			var abu:Number = _bUV.x - _aUV.x; | ||||
| 			var abv:Number = _bUV.y - _aUV.y; | ||||
| 			var acu:Number = _cUV.x - _aUV.x; | ||||
| 			var acv:Number = _cUV.y - _aUV.y; | ||||
| 							 | ||||
| 			return new Point(_aUV.x + abu*abk + acu*ack, _aUV.y + abv*abk + acv*ack); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Множество граней, имеющих общие рёбра с текущей гранью. | ||||
| 		 */ | ||||
| 		public function get edgeJoinedFaces():Set { | ||||
| 			var res:Set = new Set(true); | ||||
| 			// Перебираем точки грани | ||||
| 			for (var i:uint = 0; i < _verticesCount; i++) { | ||||
| 				var a:Vertex = _vertices[i]; | ||||
| 				var b:Vertex = _vertices[(i < _verticesCount - 1) ? (i + 1) : 0]; | ||||
| 				 | ||||
| 				// Перебираем грани текущей точки | ||||
| 				for (var key:* in a._faces) { | ||||
| 					var face:Face = key; | ||||
| 					// Если это другая грань и у неё также есть следующая точка | ||||
| 					if (face != this && face._vertices.indexOf(b) >= 0) { | ||||
| 						// Значит у граней общее ребро | ||||
| 						res[face] = true; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			return res; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление всех вершин из грани. | ||||
| 		 * Очистка базового примитива.  | ||||
| 		 */ | ||||
| 		alternativa3d function removeVertices():void { | ||||
| 			// Удалить вершины | ||||
| 			for (var i:uint = 0; i < _verticesCount; i++) { | ||||
| 				// Удаляем из списка | ||||
| 				var vertex:Vertex = _vertices.pop(); | ||||
| 				// Удаляем координаты вершины из примитива | ||||
| 				primitive.points.pop(); | ||||
| 				// Удаляем вершину из грани | ||||
| 				vertex.removeFromFace(this); | ||||
| 			} | ||||
| 			// Обнуляем количество вершин | ||||
| 			_verticesCount = 0; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Добавление грани на сцену  | ||||
| 		 * @param scene | ||||
| 		 */ | ||||
| 		alternativa3d function addToScene(scene:Scene3D):void { | ||||
| 			// При добавлении на сцену рассчитываем плоскость и UV | ||||
| 			scene.addOperation(calculateNormalOperation); | ||||
| 			scene.addOperation(calculateUVOperation); | ||||
| 			 | ||||
| 			// Подписываем сцену на операции | ||||
| 			updatePrimitiveOperation.addSequel(scene.calculateBSPOperation); | ||||
| 			updateMaterialOperation.addSequel(scene.changePrimitivesOperation); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление грани из сцены | ||||
| 		 * @param scene | ||||
| 		 */ | ||||
| 		alternativa3d function removeFromScene(scene:Scene3D):void { | ||||
| 			// Удаляем все операции из очереди | ||||
| 			scene.removeOperation(calculateUVOperation); | ||||
| 			scene.removeOperation(calculateFragmentsUVOperation); | ||||
| 			scene.removeOperation(calculateNormalOperation); | ||||
| 			scene.removeOperation(updatePrimitiveOperation); | ||||
| 			scene.removeOperation(updateMaterialOperation); | ||||
|  | ||||
| 			// Удаляем примитивы из сцены | ||||
| 			removePrimitive(primitive); | ||||
|  | ||||
| 			// Посылаем операцию сцены на расчёт BSP | ||||
| 			scene.addOperation(scene.calculateBSPOperation); | ||||
| 					 | ||||
| 			// Отписываем сцену от операций | ||||
| 			updatePrimitiveOperation.removeSequel(scene.calculateBSPOperation); | ||||
| 			updateMaterialOperation.removeSequel(scene.changePrimitivesOperation); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Добавление грани в меш | ||||
| 		 * @param mesh | ||||
| 		 */ | ||||
| 		alternativa3d function addToMesh(mesh:Mesh):void { | ||||
| 			// Подписка на операции меша | ||||
| 			mesh.changeCoordsOperation.addSequel(updatePrimitiveOperation); | ||||
| 			mesh.changeRotationOrScaleOperation.addSequel(calculateNormalOperation); | ||||
| 			mesh.calculateMobilityOperation.addSequel(updatePrimitiveOperation); | ||||
| 			// Сохранить меш | ||||
| 			_mesh = mesh; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление грани из меша | ||||
| 		 * @param mesh | ||||
| 		 */ | ||||
| 		alternativa3d function removeFromMesh(mesh:Mesh):void { | ||||
| 			// Отписка от операций меша | ||||
| 			mesh.changeCoordsOperation.removeSequel(updatePrimitiveOperation); | ||||
| 			mesh.changeRotationOrScaleOperation.removeSequel(calculateNormalOperation); | ||||
| 			mesh.calculateMobilityOperation.removeSequel(updatePrimitiveOperation); | ||||
| 			// Удалить ссылку на меш | ||||
| 			_mesh = null; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Добавление к поверхности | ||||
| 		 *  | ||||
| 		 * @param surface | ||||
| 		 */		 | ||||
| 		alternativa3d function addToSurface(surface:Surface):void { | ||||
| 			// Подписка поверхности на операции | ||||
| 			surface.changeMaterialOperation.addSequel(updateMaterialOperation); | ||||
| 			// Если при смене поверхности изменился материал | ||||
| 			if (_mesh != null && (_surface != null && _surface._material != surface._material || _surface == null && surface._material != null)) { | ||||
| 				// Отправляем сигнал смены материала | ||||
| 				_mesh.addOperationToScene(updateMaterialOperation); | ||||
| 			} | ||||
| 			// Сохранить поверхность | ||||
| 			_surface = surface; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление из поверхности | ||||
| 		 *  | ||||
| 		 * @param surface | ||||
| 		 */		 | ||||
| 		alternativa3d function removeFromSurface(surface:Surface):void { | ||||
| 			// Отписка поверхности от операций | ||||
| 			surface.changeMaterialOperation.removeSequel(updateMaterialOperation); | ||||
| 			// Если был материал | ||||
| 			if (surface._material != null) { | ||||
| 				// Отправляем сигнал смены материала | ||||
| 				_mesh.addOperationToScene(updateMaterialOperation); | ||||
| 			} | ||||
| 			// Удалить ссылку на поверхность | ||||
| 			_surface = null; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Строковое представление объекта. | ||||
| 		 *  | ||||
| 		 * @return строковое представление объекта | ||||
| 		 */ | ||||
| 		public function toString():String { | ||||
| 			var res:String = "[Face ID:" + id + ((_verticesCount > 0) ? " vertices:" : ""); | ||||
| 			for (var i:uint = 0; i < _verticesCount; i++) { | ||||
| 				var vertex:Vertex = _vertices[i]; | ||||
| 				res += vertex.id + ((i < _verticesCount - 1) ? ", " : ""); | ||||
| 			} | ||||
| 			res += "]"; | ||||
| 			return res; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,985 +0,0 @@ | ||||
| package alternativa.engine3d.core { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.errors.FaceExistsError; | ||||
| 	import alternativa.engine3d.errors.FaceNeedMoreVerticesError; | ||||
| 	import alternativa.engine3d.errors.FaceNotFoundError; | ||||
| 	import alternativa.engine3d.errors.InvalidIDError; | ||||
| 	import alternativa.engine3d.errors.SurfaceExistsError; | ||||
| 	import alternativa.engine3d.errors.SurfaceNotFoundError; | ||||
| 	import alternativa.engine3d.errors.VertexExistsError; | ||||
| 	import alternativa.engine3d.errors.VertexNotFoundError; | ||||
| 	import alternativa.engine3d.materials.SurfaceMaterial; | ||||
| 	import alternativa.types.Map; | ||||
| 	import alternativa.utils.ObjectUtils; | ||||
| 	 | ||||
| 	import flash.geom.Point; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Полигональный объект — базовый класс для трёхмерных объектов, состоящих из граней-полигонов. Объект | ||||
| 	 * содержит в себе наборы вершин, граней и поверхностей. | ||||
| 	 */ | ||||
| 	public class Mesh extends Object3D { | ||||
| 		 | ||||
| 		// Инкремент количества объектов | ||||
| 		private static var counter:uint = 0; | ||||
| 		 | ||||
| 		// Инкременты для идентификаторов вершин, граней и поверхностей | ||||
| 		private var vertexIDCounter:uint = 0; | ||||
| 		private var faceIDCounter:uint = 0; | ||||
| 		private var surfaceIDCounter:uint = 0; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Список вершин | ||||
| 		 */ | ||||
| 		alternativa3d var _vertices:Map = new Map(); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Список граней | ||||
| 		 */ | ||||
| 		alternativa3d var _faces:Map = new Map(); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Список поверхностей | ||||
| 		 */ | ||||
| 		alternativa3d var _surfaces:Map = new Map(); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра полигонального объекта. | ||||
| 		 *  | ||||
| 		 * @param name имя экземпляра | ||||
| 		 */ | ||||
| 		public function Mesh(name:String = null) { | ||||
| 			super(name); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Добавление новой вершины к объекту. | ||||
| 		 *   | ||||
| 		 * @param x координата X в локальной системе координат объекта   | ||||
| 		 * @param y координата Y в локальной системе координат объекта | ||||
| 		 * @param z координата Z в локальной системе координат объекта | ||||
| 		 * @param id идентификатор вершины. Если указано значение <code>null</code>, идентификатор будет | ||||
| 		 * сформирован автоматически. | ||||
| 		 *  | ||||
| 		 * @return экземпляр добавленной вершины | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.VertexExistsError объект уже содержит вершину с указанным идентификатором | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 */ | ||||
| 		public function createVertex(x:Number = 0, y:Number = 0, z:Number = 0, id:Object = null):Vertex { | ||||
| 			// Проверяем ID | ||||
| 			if (id != null) { | ||||
| 				// Если уже есть вершина с таким ID | ||||
| 				if (_vertices[id] != undefined) { | ||||
| 					if (_vertices[id] is Vertex) { | ||||
| 						throw new VertexExistsError(id, this); | ||||
| 					} else { | ||||
| 						throw new InvalidIDError(id, this); | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Ищем первый свободный | ||||
| 				while (_vertices[vertexIDCounter] != undefined) { | ||||
| 					vertexIDCounter++; | ||||
| 				} | ||||
| 				id = vertexIDCounter; | ||||
| 			} | ||||
| 			 | ||||
| 			// Создаём вершину | ||||
| 			var v:Vertex = new Vertex(x, y, z); | ||||
| 			 | ||||
| 			// Добавляем вершину на сцену | ||||
| 			if (_scene != null) { | ||||
| 				v.addToScene(_scene); | ||||
| 			} | ||||
| 			 | ||||
| 			// Добавляем вершину в меш | ||||
| 			v.addToMesh(this); | ||||
| 			_vertices[id] = v; | ||||
| 			 | ||||
| 			return v; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Удаление вершины из объекта. При удалении вершины из объекта также удаляются все грани, которым принадлежит данная вершина. | ||||
| 		 *   | ||||
| 		 * @param vertex экземпляр класса <code>alternativa.engine3d.core.Vertex</code> или идентификатор удаляемой вершины | ||||
| 		 *   | ||||
| 		 * @return экземпляр удалённой вершины | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит указанную вершину | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 */ | ||||
| 		public function removeVertex(vertex:Object):Vertex { | ||||
| 			var byLink:Boolean = vertex is Vertex; | ||||
| 			 | ||||
| 			// Проверяем на null | ||||
| 			if (vertex == null) { | ||||
| 				throw new VertexNotFoundError(null, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем наличие вершины в меше | ||||
| 			if (byLink) { | ||||
| 				// Если удаляем по ссылке | ||||
| 				if (Vertex(vertex)._mesh != this) { | ||||
| 					// Если вершина не в меше | ||||
| 					throw new VertexNotFoundError(vertex, this); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Если удаляем по ID | ||||
| 				if (_vertices[vertex] == undefined) { | ||||
| 					// Если нет вершины с таким ID | ||||
| 					throw new VertexNotFoundError(vertex, this); | ||||
| 				} else if (!(_vertices[vertex] is Vertex)) { | ||||
| 					// По этому id не вершина | ||||
| 					throw new InvalidIDError(vertex, this); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Находим вершину и её ID | ||||
| 			var v:Vertex = byLink ? Vertex(vertex) : _vertices[vertex]; | ||||
| 			var id:Object = byLink ? getVertexId(Vertex(vertex)) : vertex; | ||||
| 			 | ||||
| 			// Удаляем вершину из сцены | ||||
| 			if (_scene != null) { | ||||
| 				v.removeFromScene(_scene); | ||||
| 			} | ||||
| 			 | ||||
| 			// Удаляем вершину из меша | ||||
| 			v.removeFromMesh(this); | ||||
| 			delete _vertices[id]; | ||||
| 			 | ||||
| 			return v; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Добавление грани к объекту. В результате выполнения метода в объекте появляется новая грань, не привязанная | ||||
| 		 * ни к одной поверхности. | ||||
| 		 *    | ||||
| 		 * @param vertices массив вершин грани, указанных в порядке обхода лицевой стороны грани против часовой | ||||
| 		 * стрелки. Каждый элемент массива может быть либо экземпляром класса <code>alternativa.engine3d.core.Vertex</code>, | ||||
| 		 * либо идентификатором в наборе вершин объекта. В обоих случаях объект должен содержать указанную вершину. | ||||
| 		 * @param id идентификатор грани. Если указано значение <code>null</code>, идентификатор будет | ||||
| 		 * сформирован автоматически. | ||||
| 		 *  | ||||
| 		 * @return экземпляр добавленной грани  | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.FaceNeedMoreVerticesError в качестве массива вершин был передан | ||||
| 		 * <code>null</code>, либо количество вершин в массиве меньше трёх | ||||
| 		 * @throws alternativa.engine3d.errors.FaceExistsError объект уже содержит грань с заданным идентификатором | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 * @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит какую-либо вершину из входного массива | ||||
| 		 *  | ||||
| 		 * @see Vertex | ||||
| 		 */  | ||||
| 		public function createFace(vertices:Array, id:Object = null):Face { | ||||
|  | ||||
| 			// Проверяем на null | ||||
| 			if (vertices == null) { | ||||
| 				throw new FaceNeedMoreVerticesError(this); | ||||
| 			}  | ||||
| 			 | ||||
| 			// Проверяем ID | ||||
| 			if (id != null) { | ||||
| 				// Если уже есть грань с таким ID | ||||
| 				if (_faces[id] != undefined) { | ||||
| 					if (_faces[id] is Face) { | ||||
| 						throw new FaceExistsError(id, this); | ||||
| 					} else { | ||||
| 						throw new InvalidIDError(id, this); | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Ищем первый свободный ID | ||||
| 				while (_faces[faceIDCounter] != undefined) { | ||||
| 					faceIDCounter++; | ||||
| 				} | ||||
| 				id = faceIDCounter; | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем количество точек | ||||
| 			var length:uint = vertices.length; | ||||
| 			if (length < 3) { | ||||
| 				throw new FaceNeedMoreVerticesError(this, length); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем и формируем список вершин | ||||
| 			var v:Array = new Array(); | ||||
| 			var vertex:Vertex; | ||||
| 			for (var i:uint = 0; i < length; i++) { | ||||
| 				if (vertices[i] is Vertex) { | ||||
| 					// Если работаем со ссылками | ||||
| 					vertex = vertices[i]; | ||||
| 					if (vertex._mesh != this) { | ||||
| 						// Если вершина не в меше | ||||
| 						throw new VertexNotFoundError(vertices[i], this); | ||||
| 					} | ||||
| 				} else { | ||||
| 					// Если работаем с ID | ||||
| 					if (_vertices[vertices[i]] == null) { | ||||
| 						// Если нет вершины с таким ID | ||||
| 						throw new VertexNotFoundError(vertices[i], this); | ||||
| 					} else if (!(_vertices[vertices[i]] is Vertex)) { | ||||
| 						// Если id зарезервировано | ||||
| 						throw new InvalidIDError(vertices[i],this); | ||||
| 					} | ||||
| 					vertex = _vertices[vertices[i]]; | ||||
| 				} | ||||
| 				v.push(vertex); | ||||
| 			} | ||||
|  | ||||
| 			// Создаём грань | ||||
| 			var f:Face = new Face(v); | ||||
| 			 | ||||
| 			// Добавляем грань на сцену | ||||
| 			if (_scene != null) { | ||||
| 				f.addToScene(_scene); | ||||
| 			} | ||||
| 			 | ||||
| 			// Добавляем грань в меш | ||||
| 			f.addToMesh(this); | ||||
| 			_faces[id] = f; | ||||
| 			 | ||||
| 			return f; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Удаление грани из объекта. Грань также удаляется из поверхности объекта, которой она принадлежит. | ||||
| 		 *   | ||||
| 		 * @param экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор удаляемой грани | ||||
| 		 *  | ||||
| 		 * @return экземпляр удалённой грани | ||||
| 		 *   | ||||
| 		 * @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит указанную грань | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 */ | ||||
| 		public function removeFace(face:Object):Face { | ||||
| 			var byLink:Boolean = face is Face; | ||||
| 			 | ||||
| 			// Проверяем на null | ||||
| 			if (face == null) { | ||||
| 				throw new FaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем наличие грани в меше | ||||
| 			if (byLink) { | ||||
| 				// Если удаляем по ссылке | ||||
| 				if (Face(face)._mesh != this) { | ||||
| 					// Если грань не в меше | ||||
| 					throw new FaceNotFoundError(face, this); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Если удаляем по ID | ||||
| 				if (_faces[face] == undefined) { | ||||
| 					// Если нет грани с таким ID | ||||
| 					throw new FaceNotFoundError(face, this); | ||||
| 				} else if (!(_faces[face] is Face)) { | ||||
| 					throw new InvalidIDError(face, this); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Находим грань и её ID | ||||
| 			var f:Face = byLink ? Face(face) : _faces[face] ; | ||||
| 			var id:Object = byLink ? getFaceId(Face(face)) : face; | ||||
| 			 | ||||
| 			// Удаляем грань из сцены | ||||
| 			if (_scene != null) { | ||||
| 				f.removeFromScene(_scene); | ||||
| 			} | ||||
| 			 | ||||
| 			// Удаляем вершины из грани | ||||
| 			f.removeVertices(); | ||||
| 			 | ||||
| 			// Удаляем грань из поверхности | ||||
| 			if (f._surface != null) { | ||||
| 				f._surface._faces.remove(f); | ||||
| 				f.removeFromSurface(f._surface); | ||||
| 			} | ||||
| 			 | ||||
| 			// Удаляем грань из меша | ||||
| 			f.removeFromMesh(this); | ||||
| 			delete _faces[id]; | ||||
| 			 | ||||
| 			return f; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Добавление новой поверхности к объекту. | ||||
| 		 *     | ||||
| 		 * @param faces набор граней, составляющих поверхность. Каждый элемент массива должен быть либо экземпляром класса | ||||
| 		 * <code>alternativa.engine3d.core.Face</code>, либо идентификатором грани. В обоих случаях объект должен содержать | ||||
| 		 * указанную грань. Если значение параметра равно <code>null</code>, то будет создана пустая поверхность. Если | ||||
| 		 * какая-либо грань содержится в другой поверхности, она будет перенесена в новую поверхность.  | ||||
| 		 * @param id идентификатор новой поверхности. Если указано значение <code>null</code>, идентификатор будет | ||||
| 		 * сформирован автоматически. | ||||
| 		 *  | ||||
| 		 * @return экземпляр добавленной поверхности | ||||
| 		 *    | ||||
| 		 * @throws alternativa.engine3d.errors.SurfaceExistsError объект уже содержит поверхность с заданным идентификатором | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 *  | ||||
| 		 * @see Face | ||||
| 		 */ | ||||
| 		public function createSurface(faces:Array = null, id:Object = null):Surface { | ||||
| 			// Проверяем ID | ||||
| 			if (id != null) { | ||||
| 				// Если уже есть поверхность с таким ID | ||||
| 				if (_surfaces[id] != undefined) { | ||||
| 					if (_surfaces[id] is Surface) { | ||||
| 						throw new SurfaceExistsError(id, this); | ||||
| 					} else { | ||||
| 						throw new InvalidIDError(id, this); | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Ищем первый свободный ID | ||||
| 				while (_surfaces[surfaceIDCounter] != undefined) { | ||||
| 					surfaceIDCounter++; | ||||
| 				} | ||||
| 				id = surfaceIDCounter; | ||||
| 			} | ||||
| 			 | ||||
| 			// Создаём поверхность | ||||
| 			var s:Surface = new Surface(); | ||||
| 			 | ||||
| 			// Добавляем поверхность на сцену | ||||
| 			if (_scene != null) { | ||||
| 				s.addToScene(_scene); | ||||
| 			} | ||||
| 			 | ||||
| 			// Добавляем поверхность в меш | ||||
| 			s.addToMesh(this); | ||||
| 			_surfaces[id] = s; | ||||
|  | ||||
| 			// Добавляем грани, если есть | ||||
| 			if (faces != null) { | ||||
| 				var length:uint = faces.length; | ||||
| 				for (var i:uint = 0; i < length; i++) { | ||||
| 					s.addFace(faces[i]); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			return s; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Удаление поверхности объекта. Из удаляемой поверхности также удаляются все содержащиеся в ней грани. | ||||
| 		 *   | ||||
| 		 * @param surface экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор удаляемой поверхности | ||||
| 		 *   | ||||
| 		 * @return экземпляр удалённой поверхности | ||||
| 		 *   | ||||
| 		 * @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанную поверхность  | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора  | ||||
| 		 */ | ||||
| 		public function removeSurface(surface:Object):Surface { | ||||
| 			var byLink:Boolean = surface is Surface; | ||||
| 			 | ||||
| 			// Проверяем на null | ||||
| 			if (surface == null) { | ||||
| 				throw new SurfaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем наличие поверхности в меше | ||||
| 			if (byLink) { | ||||
| 				// Если удаляем по ссылке | ||||
| 				if (Surface(surface)._mesh != this) { | ||||
| 					// Если поверхность не в меше | ||||
| 					throw new SurfaceNotFoundError(surface, this); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Если удаляем по ID | ||||
| 				if (_surfaces[surface] == undefined) { | ||||
| 					// Если нет поверхности с таким ID | ||||
| 					throw new SurfaceNotFoundError(surface, this); | ||||
| 				} else if (!(_surfaces[surface] is Surface)) { | ||||
| 						throw new InvalidIDError(surface, this); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Находим поверхность и её ID | ||||
| 			var s:Surface = byLink ? Surface(surface) : _surfaces[surface]; | ||||
| 			var id:Object = byLink ? getSurfaceId(Surface(surface)) : surface; | ||||
| 			 | ||||
| 			// Удаляем поверхность из сцены | ||||
| 			if (_scene != null) { | ||||
| 				s.removeFromScene(_scene); | ||||
| 			} | ||||
| 			 | ||||
| 			// Удаляем грани из поверхности | ||||
| 			s.removeFaces(); | ||||
| 			 | ||||
| 			// Удаляем поверхность из меша | ||||
| 			s.removeFromMesh(this); | ||||
| 			delete _surfaces[id]; | ||||
| 			 | ||||
| 			return s; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Добавление всех граней объекта в указанную поверхность. | ||||
| 		 * | ||||
| 		 * @param surface экземпляр класса <code>alternativa.engine3d.core.Surface</code> или идентификатор поверхности, в | ||||
| 		 * которую добавляются грани. Если задан идентификатор, и объект не содержит поверхность с таким идентификатором, | ||||
| 		 * будет создана новая поверхность. | ||||
| 		 *  | ||||
| 		 * @param removeSurfaces удалять или нет пустые поверхности после переноса граней  | ||||
| 		 *  | ||||
| 		 * @return экземпляр поверхности, в которую перенесены грани | ||||
| 		 *   | ||||
| 		 * @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанный экземпляр поверхности | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 */ | ||||
| 		public function moveAllFacesToSurface(surface:Object = null, removeSurfaces:Boolean = false):Surface { | ||||
| 			var returnSurface:Surface; | ||||
| 			var returnSurfaceId:Object; | ||||
| 			if (surface is Surface) { | ||||
| 				// Работаем с экземпляром Surface | ||||
| 				if (surface._mesh == this) { | ||||
| 					returnSurface = Surface(surface); | ||||
| 				} else { | ||||
| 					throw new SurfaceNotFoundError(surface, this); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Работаем с идентификатором | ||||
| 				if (_surfaces[surface] == undefined) { | ||||
| 					// Поверхности еще нет | ||||
| 					returnSurface = createSurface(null, surface); | ||||
| 					returnSurfaceId = surface; | ||||
| 				} else { | ||||
| 					if (_surfaces[surface] is Surface) { | ||||
| 						returnSurface = _surfaces[surface]; | ||||
| 					} else {  | ||||
| 						// _surfaces[surface] по идентификатору возвращает не Surface | ||||
| 						throw new InvalidIDError(surface, this); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			// Перемещаем все грани | ||||
| 			for each (var face:Face in _faces) { | ||||
| 				if (face._surface != returnSurface) { | ||||
| 					returnSurface.addFace(face); | ||||
| 				} | ||||
| 			} | ||||
| 			if (removeSurfaces) { | ||||
| 				// Удаляем старые, теперь вручную - меньше проверок, но рискованно | ||||
|  				if (returnSurfaceId == null) { | ||||
|  					returnSurfaceId = getSurfaceId(returnSurface); | ||||
|  				} | ||||
|  				var newSurfaces:Map = new Map(); | ||||
|  				newSurfaces[returnSurfaceId] = returnSurface; | ||||
|  				delete _surfaces[returnSurfaceId]; | ||||
|  				// Удаляем оставшиеся | ||||
|  				for (var currentSurfaceId:* in _surfaces) { | ||||
| 					 // Удаляем поверхность из сцены | ||||
| 					 var currentSurface:Surface = _surfaces[currentSurfaceId]; | ||||
| 					 if (_scene != null) { | ||||
| 						currentSurface.removeFromScene(_scene); | ||||
| 					} | ||||
| 					// Удаляем поверхность из меша | ||||
| 					currentSurface.removeFromMesh(this); | ||||
| 					delete _surfaces[currentSurfaceId]; | ||||
| 				} | ||||
| 				// Новый список граней | ||||
| 				_surfaces = newSurfaces; | ||||
| 			} | ||||
| 			return returnSurface; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Установка материала для указанной поверхности. | ||||
| 		 *   | ||||
| 		 * @param material материал, назначаемый поверхности. Один экземпляр SurfaceMaterial можно назначить только одной поверхности. | ||||
| 		 * @param surface экземпляр класса <code>alternativa.engine3d.core.Surface</code> или идентификатор поверхности | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанную поверхность  | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 *  | ||||
| 		 * @see Surface  | ||||
| 		 */ | ||||
| 		public function setMaterialToSurface(material:SurfaceMaterial, surface:Object):void { | ||||
| 			var byLink:Boolean = surface is Surface; | ||||
|  | ||||
| 			// Проверяем на null | ||||
| 			if (surface == null) { | ||||
| 				throw new SurfaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем наличие поверхности в меше | ||||
| 			if (byLink) { | ||||
| 				// Если назначаем по ссылке | ||||
| 				if (Surface(surface)._mesh != this) { | ||||
| 					// Если поверхность не в меше | ||||
| 					throw new SurfaceNotFoundError(surface, this); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Если назначаем по ID | ||||
| 				if (_surfaces[surface] == undefined) { | ||||
| 					// Если нет поверхности с таким ID | ||||
| 					throw new SurfaceNotFoundError(surface, this); | ||||
| 				} else if (!(_surfaces[surface] is Surface)) { | ||||
| 					throw new InvalidIDError(surface, this); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Находим поверхность | ||||
| 			var s:Surface = byLink ? Surface(surface) : _surfaces[surface]; | ||||
| 			 | ||||
| 			// Назначаем материал | ||||
| 			s.material = material; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Установка материала для всех поверхностей объекта. Для каждой поверхности устанавливается копия материала. | ||||
| 		 * При передаче <code>null</code> в качестве параметра происходит сброс материалов у всех поверхностей. | ||||
| 		 *  | ||||
| 		 * @param material устанавливаемый материал | ||||
| 		 */		 | ||||
| 		public function cloneMaterialToAllSurfaces(material:SurfaceMaterial):void { | ||||
| 			for each (var surface:Surface in _surfaces) { | ||||
| 				surface.material = (material != null) ? SurfaceMaterial(material.clone()) : null; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Установка UV-координат для указанной грани объекта. Матрица преобразования UV-координат расчитывается по | ||||
| 		 * UV-координатам первых трёх вершин грани, поэтому для корректного текстурирования эти вершины должны образовывать | ||||
| 		 * невырожденный треугольник в UV-пространстве. | ||||
| 		 * | ||||
| 		 * @param aUV UV-координаты, соответствующие первой вершине грани | ||||
| 		 * @param bUV UV-координаты, соответствующие второй вершине грани | ||||
| 		 * @param cUV UV-координаты, соответствующие третьей вершине грани | ||||
| 		 * @param face экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор грани | ||||
| 		 *   | ||||
| 		 * @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит указанную грань | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 *  | ||||
| 		 * @see Face | ||||
| 		 */ | ||||
| 		public function setUVsToFace(aUV:Point, bUV:Point, cUV:Point, face:Object):void { | ||||
| 			var byLink:Boolean = face is Face; | ||||
|  | ||||
| 			// Проверяем на null | ||||
| 			if (face == null) { | ||||
| 				throw new FaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем наличие грани в меше | ||||
| 			if (byLink) { | ||||
| 				// Если назначаем по ссылке | ||||
| 				if (Face(face)._mesh != this) { | ||||
| 					// Если грань не в меше | ||||
| 					throw new FaceNotFoundError(face, this); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Если назначаем по ID | ||||
| 				if (_faces[face] == undefined) { | ||||
| 					// Если нет грани с таким ID | ||||
| 					throw new FaceNotFoundError(face, this); | ||||
| 				} else if (!(_faces[face] is Face)) { | ||||
| 					throw new InvalidIDError(face, this); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Находим грань | ||||
| 			var f:Face = byLink ? Face(face) : _faces[face]; | ||||
| 			 | ||||
| 			// Назначаем UV-координаты | ||||
| 			f.aUV = aUV; | ||||
| 			f.bUV = bUV; | ||||
| 			f.cUV = cUV; | ||||
| 		}   | ||||
|  | ||||
| 		/** | ||||
| 		 * Набор вершин объекта. Ключами ассоциативного массива являются идентификаторы вершин, значениями - экземпляры вершин. | ||||
| 		 */ | ||||
| 		public function get vertices():Map { | ||||
| 			return _vertices.clone(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Набор граней объекта. Ключами ассоциативного массива являются идентификаторы граней, значениями - экземпляры граней. | ||||
| 		 */ | ||||
| 		public function get faces():Map { | ||||
| 			return _faces.clone(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Набор поверхностей объекта. Ключами ассоциативного массива являются идентификаторы поверхностей, значениями - экземпляры поверхностей. | ||||
| 		 */		 | ||||
| 		public function get surfaces():Map { | ||||
| 			return _surfaces.clone(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Получение вершины объекта по её идентификатору. | ||||
| 		 *   | ||||
| 		 * @param id идентификатор вершины | ||||
| 		 *  | ||||
| 		 * @return экземпляр вершины с указанным идентификатором | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит вершину с указанным идентификатором | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 */ | ||||
| 		public function getVertexById(id:Object):Vertex { | ||||
| 			if (id == null) { | ||||
| 				throw new VertexNotFoundError(null, this); | ||||
| 			} | ||||
| 			if (_vertices[id] == undefined) { | ||||
| 				// Если нет вершины с таким ID | ||||
| 				throw new VertexNotFoundError(id, this); | ||||
| 			} else { | ||||
| 				if (_vertices[id] is Vertex) { | ||||
| 					return _vertices[id]; | ||||
| 				} else { | ||||
| 					throw new InvalidIDError(id, this); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Получение идентификатора вершины объекта.  | ||||
| 		 * | ||||
| 		 * @param экземпляр вершины | ||||
| 		 * | ||||
| 		 * @return идентификатор указанной вершины | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.VertexNotFoundError объект не содержит указанную вершину | ||||
| 		 */ | ||||
| 		public function getVertexId(vertex:Vertex):Object { | ||||
| 			if (vertex == null) { | ||||
| 				throw new VertexNotFoundError(null, this); | ||||
| 			} | ||||
| 			if (vertex._mesh != this) { | ||||
| 				// Если вершина не в меше | ||||
| 				throw new VertexNotFoundError(vertex, this); | ||||
| 			} | ||||
| 			for (var i:Object in _vertices) { | ||||
| 				if (_vertices[i] == vertex) { | ||||
| 					return i; | ||||
| 				} | ||||
| 			} | ||||
| 			throw new VertexNotFoundError(vertex, this); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Проверка наличия вершины в объекте. | ||||
| 		 *  | ||||
| 		 * @param vertex экземпляр класса <code>alternativa.engine3d.core.Vertex</code> или идентификатор вершины | ||||
| 		 *  | ||||
| 		 * @return <code>true</code>, если объект содержит указанную вершину, иначе <code>false</code>   | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.VertexNotFoundError в качестве vertex был передан null | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 *  | ||||
| 		 * @see Vertex | ||||
| 		 */ | ||||
| 		public function hasVertex(vertex:Object):Boolean { | ||||
| 			if (vertex == null) { | ||||
| 				throw new VertexNotFoundError(null, this); | ||||
| 			} | ||||
| 			if (vertex is Vertex) { | ||||
| 				// Проверка вершины | ||||
| 				return vertex._mesh == this; | ||||
| 			} else { | ||||
| 				// Проверка ID вершины | ||||
| 				if (_vertices[vertex] != undefined) { | ||||
| 					// По этому ID есть объект | ||||
| 					if (_vertices[vertex] is Vertex) { | ||||
| 						// Объект является вершиной | ||||
| 						return true; | ||||
| 					} else { | ||||
| 						// ID некорректный | ||||
| 						throw new InvalidIDError(vertex, this); | ||||
| 					} | ||||
| 				} else { | ||||
| 					return false; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Получение грани объекта по ее идентификатору. | ||||
| 		 *   | ||||
| 		 * @param id идентификатор грани | ||||
| 		 *  | ||||
| 		 * @return экземпляр грани с указанным идентификатором | ||||
| 		 * | ||||
| 		 * @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит грань с указанным идентификатором | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 */ | ||||
| 		public function getFaceById(id:Object):Face { | ||||
| 			if (id == null) { | ||||
| 				throw new FaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			if (_faces[id] == undefined) { | ||||
| 				// Если нет грани с таким ID | ||||
| 				throw new FaceNotFoundError(id, this); | ||||
| 			} else { | ||||
| 				if (_faces[id] is Face) { | ||||
| 					return _faces[id]; | ||||
| 				} else {  | ||||
| 					throw new InvalidIDError(id, this); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Получение идентификатора грани объекта. | ||||
| 		 *   | ||||
| 		 * @param face экземпляр грани | ||||
| 		 *  | ||||
| 		 * @return идентификатор указанной грани  | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.FaceNotFoundError объект не содержит указанную грань | ||||
| 		 */ | ||||
| 		public function getFaceId(face:Face):Object { | ||||
| 			if (face == null) { | ||||
| 				throw new FaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			if (face._mesh != this) { | ||||
| 				// Если грань не в меше | ||||
| 				throw new FaceNotFoundError(face, this); | ||||
| 			} | ||||
| 			for (var i:Object in _faces) { | ||||
| 				if (_faces[i] == face) { | ||||
| 					return i; | ||||
| 				} | ||||
| 			} | ||||
| 			throw new FaceNotFoundError(face, this); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Проверка наличия грани в объекте. | ||||
| 		 *  | ||||
| 		 * @param face экземпляр класса <code>Face</code> или идентификатор грани | ||||
| 		 *  | ||||
| 		 * @return <code>true</code>, если объект содержит указанную грань, иначе <code>false</code>  | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.FaceNotFoundError в качестве face был указан null | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 */ | ||||
| 		public function hasFace(face:Object):Boolean { | ||||
| 			if (face == null) { | ||||
| 				throw new FaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			if (face is Face) { | ||||
| 				// Проверка грани | ||||
| 				return face._mesh == this; | ||||
| 			} else { | ||||
| 				// Проверка ID грани | ||||
| 				if (_faces[face] != undefined) { | ||||
| 					// По этому ID есть объект | ||||
| 					if (_faces[face] is Face) { | ||||
| 						// Объект является гранью | ||||
| 						return true; | ||||
| 					} else { | ||||
| 						// ID некорректный | ||||
| 						throw new InvalidIDError(face, this); | ||||
| 					} | ||||
| 				} else { | ||||
| 					return false; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Получение поверхности объекта по ее идентификатору | ||||
| 		 *   | ||||
| 		 * @param id идентификатор поверхности | ||||
| 		 *  | ||||
| 		 * @return экземпляр поверхности с указанным идентификатором | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит поверхность с указанным идентификатором | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора  | ||||
| 		 */ | ||||
| 		public function getSurfaceById(id:Object):Surface { | ||||
| 			if (id == null) { | ||||
| 				throw new SurfaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			if (_surfaces[id] == undefined) { | ||||
| 				// Если нет поверхности с таким ID | ||||
| 				throw new SurfaceNotFoundError(id, this); | ||||
| 			} else { | ||||
| 				if (_surfaces[id] is Surface) { | ||||
| 					return _surfaces[id]; | ||||
| 				} else { | ||||
| 					throw new InvalidIDError(id, this); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Получение идентификатора поверхности объекта. | ||||
| 		 *   | ||||
| 		 * @param surface экземпляр поверхности | ||||
| 		 *  | ||||
| 		 * @return идентификатор указанной поверхности | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.SurfaceNotFoundError объект не содержит указанную поверхность  | ||||
| 		 */ | ||||
| 		public function getSurfaceId(surface:Surface):Object { | ||||
| 			if (surface == null) { | ||||
| 				throw new SurfaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			if (surface._mesh != this) { | ||||
| 				// Если поверхность не в меше | ||||
| 				throw new SurfaceNotFoundError(surface, this); | ||||
| 			} | ||||
| 			for (var i:Object in _surfaces) { | ||||
| 				if (_surfaces[i] == surface) { | ||||
| 					return i; | ||||
| 				} | ||||
| 			} | ||||
| 			return null; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Проверка наличия поверхности в объекте. | ||||
| 		 *   | ||||
| 		 * @param surface экземпляр класса <code>Surface</code> или идентификатор поверхности | ||||
| 		 *   | ||||
| 		 * @return <code>true</true>, если объект содержит указанную поверхность, иначе <code>false</code> | ||||
| 		 *   | ||||
| 		 * @throws alternativa.engine3d.errors.SurfaceNotFoundError в качестве surface был передан null  | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора  | ||||
|  		 */ | ||||
| 		public function hasSurface(surface:Object):Boolean { | ||||
| 			if (surface == null) { | ||||
| 				throw new SurfaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			if (surface is Surface) { | ||||
| 				// Проверка поверхности | ||||
| 				return surface._mesh == this; | ||||
| 			} else { | ||||
| 				// Проверка ID поверхности | ||||
| 				if (_surfaces[surface] != undefined) { | ||||
| 					// По этому ID есть объект | ||||
| 					if (_surfaces[surface] is Surface) { | ||||
| 						// Объект является поверхностью | ||||
| 						return true; | ||||
| 					} else { | ||||
| 						// ID некорректный | ||||
| 						throw new InvalidIDError(surface, this); | ||||
| 					} | ||||
| 				} else { | ||||
| 					return false; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @inheritDoc | ||||
| 		 */		 | ||||
| 		override alternativa3d function setScene(value:Scene3D):void { | ||||
| 			if (_scene != value) { | ||||
| 				var vertex:Vertex; | ||||
| 				var face:Face; | ||||
| 				var surface:Surface; | ||||
| 				if (value != null) { | ||||
| 					// Добавить вершины на сцену | ||||
| 					for each (vertex in _vertices) { | ||||
| 						vertex.addToScene(value); | ||||
| 					} | ||||
| 					// Добавить грани на сцену | ||||
| 					for each (face in _faces) { | ||||
| 						face.addToScene(value); | ||||
| 					} | ||||
| 					// Добавить поверхности на сцену | ||||
| 					for each (surface in _surfaces) { | ||||
| 						surface.addToScene(value); | ||||
| 					} | ||||
| 				} else { | ||||
| 					// Удалить вершины из сцены | ||||
| 					for each (vertex in _vertices) { | ||||
| 						vertex.removeFromScene(_scene); | ||||
| 					} | ||||
| 					// Удалить грани из сцены | ||||
| 					for each (face in _faces) { | ||||
| 						face.removeFromScene(_scene); | ||||
| 					} | ||||
| 					// Удалить поверхности из сцены | ||||
| 					for each (surface in _surfaces) { | ||||
| 						surface.removeFromScene(_scene); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			super.setScene(value); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		override protected function defaultName():String { | ||||
| 			return "mesh" + ++counter; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		override public function toString():String { | ||||
| 			return "[" + ObjectUtils.getClassName(this) + " " + _name + " vertices: " + _vertices.length + " faces: " + _faces.length + "]"; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */		 | ||||
| 		protected override function createEmptyObject():Object3D { | ||||
| 			return new Mesh(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		protected override function clonePropertiesFrom(source:Object3D):void { | ||||
| 			super.clonePropertiesFrom(source); | ||||
| 			 | ||||
| 			var src:Mesh = Mesh(source); | ||||
|  | ||||
| 			var id:*; | ||||
| 			var len:int; | ||||
| 			var i:int; | ||||
| 			// Копирование вершин | ||||
| 			var vertexMap:Map = new Map(true); | ||||
| 			for (id in src._vertices) { | ||||
| 				var sourceVertex:Vertex = src._vertices[id]; | ||||
| 				vertexMap[sourceVertex] = createVertex(sourceVertex.x, sourceVertex.y, sourceVertex.z, id); | ||||
| 			} | ||||
| 			 | ||||
| 			// Копирование граней | ||||
| 			var faceMap:Map = new Map(true); | ||||
| 			for (id in src._faces) { | ||||
| 				var sourceFace:Face = src._faces[id]; | ||||
| 				len = sourceFace._vertices.length; | ||||
| 				var faceVertices:Array = new Array(len); | ||||
| 				for (i = 0; i < len; i++) { | ||||
| 					faceVertices[i] = vertexMap[sourceFace._vertices[i]]; | ||||
| 				} | ||||
| 				var newFace:Face = createFace(faceVertices, id); | ||||
| 				newFace.aUV = sourceFace._aUV; | ||||
| 				newFace.bUV = sourceFace._bUV; | ||||
| 				newFace.cUV = sourceFace._cUV; | ||||
| 				faceMap[sourceFace] = newFace; | ||||
| 			} | ||||
| 			 | ||||
| 			// Копирование поверхностей | ||||
| 			for (id in src._surfaces) { | ||||
| 				var sourceSurface:Surface = src._surfaces[id]; | ||||
| 				var surfaceFaces:Array = sourceSurface._faces.toArray(); | ||||
| 				len = surfaceFaces.length; | ||||
| 				for (i = 0; i < len; i++) { | ||||
| 					surfaceFaces[i] = faceMap[surfaceFaces[i]]; | ||||
| 				} | ||||
| 				var surface:Surface = createSurface(surfaceFaces, id); | ||||
| 				var sourceMaterial:SurfaceMaterial = sourceSurface.material; | ||||
| 				if (sourceMaterial != null) { | ||||
| 					surface.material = SurfaceMaterial(sourceMaterial.clone()); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,708 +0,0 @@ | ||||
| package alternativa.engine3d.core { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.errors.Object3DHierarchyError; | ||||
| 	import alternativa.engine3d.errors.Object3DNotFoundError; | ||||
| 	import alternativa.types.Matrix3D; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.types.Set; | ||||
| 	import alternativa.utils.ObjectUtils; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Базовый класс для объектов, находящихся в сцене. Класс реализует иерархию объектов сцены, а также содержит сведения | ||||
| 	 * о трансформации объекта как единого целого. | ||||
| 	 *  | ||||
| 	 * <p> Масштабирование, ориентация и положение объекта задаются в родительской системе координат. Результирующая | ||||
| 	 * локальная трансформация является композицией операций масштабирования, поворотов объекта относительно осей | ||||
| 	 * <code>X</code>, <code>Y</code>, <code>Z</code> и параллельного переноса центра объекта из начала координат. | ||||
| 	 * Операции применяются в порядке их перечисления. | ||||
| 	 *  | ||||
| 	 * <p> Глобальная трансформация (в системе координат корневого объекта сцены) является композицией трансформаций | ||||
| 	 * самого объекта и всех его предков по иерархии объектов сцены. | ||||
| 	 */ | ||||
| 	public class Object3D { | ||||
| 		// Операции | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Поворот или масштабирование | ||||
| 		 */		 | ||||
| 		alternativa3d var changeRotationOrScaleOperation:Operation = new Operation("changeRotationOrScale", this); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Перемещение | ||||
| 		 */		 | ||||
| 		alternativa3d var changeCoordsOperation:Operation = new Operation("changeCoords", this); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт матрицы трансформации | ||||
| 		 */		 | ||||
| 		alternativa3d var calculateTransformationOperation:Operation = new Operation("calculateTransformation", this, calculateTransformation, Operation.OBJECT_CALCULATE_TRANSFORMATION);   | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Изменение уровеня мобильности | ||||
| 		 */		 | ||||
| 		alternativa3d var calculateMobilityOperation:Operation = new Operation("calculateMobility", this, calculateMobility, Operation.OBJECT_CALCULATE_MOBILITY); | ||||
| 		 | ||||
| 		// Инкремент количества объектов | ||||
| 		private static var counter:uint = 0; | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Наименование | ||||
| 		 */		 | ||||
| 		alternativa3d var _name:String; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Сцена | ||||
| 		 */		 | ||||
| 		alternativa3d var _scene:Scene3D; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Родительский объект | ||||
| 		 */		 | ||||
| 		alternativa3d var _parent:Object3D; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Дочерние объекты | ||||
| 		 */		 | ||||
| 		alternativa3d var _children:Set = new Set(); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Уровень мобильности | ||||
| 		 */		 | ||||
| 		alternativa3d var _mobility:int = 0; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		alternativa3d var inheritedMobility:int; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Координаты объекта относительно родителя | ||||
| 		 */		 | ||||
| 		alternativa3d var _coords:Point3D = new Point3D(); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Поворот объекта по оси X относительно родителя. Угол измеряется в радианах. | ||||
| 		 */		 | ||||
| 		alternativa3d var _rotationX:Number = 0; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Поворот объекта по оси Y относительно родителя. Угол измеряется в радианах. | ||||
| 		 */		 | ||||
| 		alternativa3d var _rotationY:Number = 0; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Поворот объекта по оси Z относительно родителя. Угол измеряется в радианах. | ||||
| 		 */		 | ||||
| 		alternativa3d var _rotationZ:Number = 0; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Мастшаб объекта по оси X относительно родителя | ||||
| 		 */		 | ||||
| 		alternativa3d var _scaleX:Number = 1; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Мастшаб объекта по оси Y относительно родителя | ||||
| 		 */		 | ||||
| 		alternativa3d var _scaleY:Number = 1; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Мастшаб объекта по оси Z относительно родителя | ||||
| 		 */		 | ||||
| 		alternativa3d var _scaleZ:Number = 1; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Полная матрица трансформации, переводящая координаты из локальной системы координат объекта в систему координат сцены | ||||
| 		 */		 | ||||
| 		alternativa3d var transformation:Matrix3D = new Matrix3D(); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Координаты в сцене | ||||
| 		 */		 | ||||
| 		alternativa3d var globalCoords:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param name имя экземпляра | ||||
| 		 */ | ||||
| 		public function Object3D(name:String = null) { | ||||
| 			// Имя по-умолчанию | ||||
| 			_name = (name != null) ? name : defaultName(); | ||||
| 			 | ||||
| 			// Последствия операций | ||||
| 			changeRotationOrScaleOperation.addSequel(calculateTransformationOperation); | ||||
| 			changeCoordsOperation.addSequel(calculateTransformationOperation); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт трансформации | ||||
| 		 */ | ||||
| 		private function calculateTransformation():void { | ||||
| 			if (changeRotationOrScaleOperation.queued) { | ||||
| 				// Если полная трансформация | ||||
| 				transformation.toTransform(_coords.x, _coords.y, _coords.z, _rotationX, _rotationY, _rotationZ, _scaleX, _scaleY, _scaleZ); | ||||
| 				if (_parent != null) { | ||||
| 					transformation.combine(_parent.transformation); | ||||
| 				} | ||||
| 				// Сохраняем глобальные координаты объекта | ||||
| 				globalCoords.x = transformation.d; | ||||
| 				globalCoords.y = transformation.h; | ||||
| 				globalCoords.z = transformation.l; | ||||
| 			} else { | ||||
| 				// Если только перемещение | ||||
| 				globalCoords.copy(_coords); | ||||
| 				if (_parent != null) { | ||||
| 					globalCoords.transform(_parent.transformation); | ||||
| 				}  | ||||
| 				transformation.offset(globalCoords.x, globalCoords.y, globalCoords.z); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт общей мобильности | ||||
| 		 */ | ||||
| 		private function calculateMobility():void { | ||||
| 			inheritedMobility = ((_parent != null) ? _parent.inheritedMobility : 0) + _mobility; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Добавление дочернего объекта. Добавляемый объект удаляется из списка детей предыдущего родителя. | ||||
| 		 * Новой сценой дочернего объекта становится сцена родителя. | ||||
| 		 *  | ||||
| 		 * @param child добавляемый объект | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.Object3DHierarchyError нарушение иерархии объектов сцены | ||||
| 		 */	 | ||||
| 		public function addChild(child:Object3D):void { | ||||
| 			 | ||||
| 			// Проверка на null | ||||
| 			if (child == null) { | ||||
| 				throw new Object3DHierarchyError(null, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверка на наличие | ||||
| 			if (child._parent == this) { | ||||
| 				return; | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверка на добавление к самому себе | ||||
| 			if (child == this) { | ||||
| 				throw new Object3DHierarchyError(this, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверка на добавление родительского объекта | ||||
| 			if (child._scene == _scene) { | ||||
| 				// Если объект был в той же сцене, либо оба не были в сцене | ||||
| 				var parentObject:Object3D = _parent; | ||||
| 				while (parentObject != null) { | ||||
| 					if (child == parentObject) { | ||||
| 						throw new Object3DHierarchyError(child, this); | ||||
| 						return; | ||||
| 					} | ||||
| 					parentObject = parentObject._parent; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Если объект был в другом объекте | ||||
| 			if (child._parent != null) { | ||||
| 				// Удалить его оттуда | ||||
| 				child._parent._children.remove(child); | ||||
| 			} else { | ||||
| 				// Если объект был корневым в сцене | ||||
| 				if (child._scene != null && child._scene._root == child) { | ||||
| 					child._scene.root = null; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Добавляем в список | ||||
| 			_children.add(child); | ||||
| 			// Указываем себя как родителя | ||||
| 			child.setParent(this); | ||||
| 			// Устанавливаем уровни | ||||
| 			child.setLevel((calculateTransformationOperation.priority & 0xFFFFFF) + 1); | ||||
| 			// Указываем сцену | ||||
| 			child.setScene(_scene); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Удаление дочернего объекта. | ||||
| 		 *  | ||||
| 		 * @param child удаляемый дочерний объект | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.Object3DNotFoundError указанный объект не содержится в списке детей текущего объекта | ||||
| 		 */ | ||||
| 		public function removeChild(child:Object3D):void { | ||||
| 			// Проверка на null | ||||
| 			if (child == null) { | ||||
| 				throw new Object3DNotFoundError(null, this); | ||||
| 			} | ||||
| 			// Проверка на наличие | ||||
| 			if (child._parent != this) { | ||||
| 				throw new Object3DNotFoundError(child, this); | ||||
| 			} | ||||
| 			// Убираем из списка | ||||
| 			_children.remove(child); | ||||
| 			// Удаляем ссылку на родителя | ||||
| 			child.setParent(null); | ||||
| 			// Удаляем ссылку на сцену | ||||
| 			child.setScene(null); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Установка родительского объекта. | ||||
| 		 *  | ||||
| 		 * @param value родительский объект | ||||
| 		 */ | ||||
| 		alternativa3d function setParent(value:Object3D):void { | ||||
| 			// Отписываемся от сигналов старого родителя | ||||
| 			if (_parent != null) { | ||||
| 				removeParentSequels(); | ||||
| 			} | ||||
| 			// Сохранить родителя | ||||
| 			_parent = value; | ||||
| 			// Если устанавливаем родителя | ||||
| 			if (value != null) { | ||||
| 				// Подписка на сигналы родителя | ||||
| 				addParentSequels(); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Установка новой сцены для объекта. | ||||
| 		 *  | ||||
| 		 * @param value сцена | ||||
| 		 */ | ||||
| 		alternativa3d function setScene(value:Scene3D):void { | ||||
| 			if (_scene != value) { | ||||
| 				// Если была сцена | ||||
| 				if (_scene != null) { | ||||
| 					// Удалиться из сцены | ||||
| 					removeFromScene(_scene); | ||||
| 				} | ||||
| 				// Если новая сцена | ||||
| 				if (value != null) { | ||||
| 					// Добавиться на сцену | ||||
| 					addToScene(value); | ||||
| 				} | ||||
| 				// Сохранить сцену | ||||
| 				_scene = value; | ||||
| 			} else { | ||||
| 				// Посылаем операцию трансформации | ||||
| 				addOperationToScene(changeRotationOrScaleOperation); | ||||
| 				// Посылаем операцию пересчёта мобильности | ||||
| 				addOperationToScene(calculateMobilityOperation); | ||||
| 			} | ||||
| 			// Установить эту сцену у дочерних объектов | ||||
| 			for (var key:* in _children) { | ||||
| 				var object:Object3D = key; | ||||
| 				object.setScene(_scene); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Установка уровня операции трансформации. | ||||
| 		 *   | ||||
| 		 * @param value уровень операции трансформации | ||||
| 		 */ | ||||
| 		alternativa3d function setLevel(value:uint):void { | ||||
| 			// Установить уровень операции трансформации и расчёта мобильности | ||||
| 			calculateTransformationOperation.priority = (calculateTransformationOperation.priority & 0xFF000000) | value; | ||||
| 			calculateMobilityOperation.priority = (calculateMobilityOperation.priority & 0xFF000000) | value; | ||||
| 			// Установить уровни у дочерних объектов | ||||
| 			for (var key:* in _children) { | ||||
| 				var object:Object3D = key; | ||||
| 				object.setLevel(value + 1); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Подписка на сигналы родителя. | ||||
| 		 */ | ||||
| 		private function addParentSequels():void { | ||||
| 			_parent.changeCoordsOperation.addSequel(changeCoordsOperation); | ||||
| 			_parent.changeRotationOrScaleOperation.addSequel(changeRotationOrScaleOperation); | ||||
| 			_parent.calculateMobilityOperation.addSequel(calculateMobilityOperation); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление подписки на сигналы родителя. | ||||
| 		 */ | ||||
| 		private function removeParentSequels():void { | ||||
| 			_parent.changeCoordsOperation.removeSequel(changeCoordsOperation); | ||||
| 			_parent.changeRotationOrScaleOperation.removeSequel(changeRotationOrScaleOperation); | ||||
| 			_parent.calculateMobilityOperation.removeSequel(calculateMobilityOperation); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод вызывается при добавлении объекта на сцену. Наследники могут переопределять метод для выполнения | ||||
| 		 * специфических действий. | ||||
| 		 *  | ||||
| 		 * @param scene сцена, в которую добавляется объект | ||||
| 		 */		 | ||||
| 		protected function addToScene(scene:Scene3D):void { | ||||
| 			// При добавлении на сцену полная трансформация и расчёт мобильности | ||||
| 			scene.addOperation(changeRotationOrScaleOperation); | ||||
| 			scene.addOperation(calculateMobilityOperation); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Метод вызывается при удалении объекта со сцены. Наследники могут переопределять метод для выполнения | ||||
| 		 * специфических действий. | ||||
| 		 *  | ||||
| 		 * @param scene сцена, из которой удаляется объект | ||||
| 		 */		 | ||||
| 		protected function removeFromScene(scene:Scene3D):void { | ||||
| 			// Удаляем все операции из очереди | ||||
| 			scene.removeOperation(changeRotationOrScaleOperation); | ||||
| 			scene.removeOperation(changeCoordsOperation); | ||||
| 			scene.removeOperation(calculateMobilityOperation); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Имя объекта.  | ||||
| 		 */ | ||||
| 		public function get name():String { | ||||
| 			return _name; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set name(value:String):void { | ||||
| 			_name = value; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Сцена, которой принадлежит объект. | ||||
| 		 */ | ||||
| 		public function get scene():Scene3D { | ||||
| 			return _scene; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Родительский объект. | ||||
| 		 */ | ||||
| 		public function get parent():Object3D { | ||||
| 			return _parent; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Набор дочерних объектов. | ||||
| 		 */ | ||||
| 		public function get children():Set { | ||||
| 			return _children.clone(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Уровень мобильности. Результирующая мобильность объекта является суммой мобильностей объекта и всех его предков | ||||
| 		 * по иерархии объектов в сцене. Результирующая мобильность влияет на положение объекта в BSP-дереве. Менее мобильные | ||||
| 		 * объекты находятся ближе к корню дерева, чем более мобильные. | ||||
| 		 */ | ||||
| 		public function get mobility():int { | ||||
| 			return _mobility; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set mobility(value:int):void { | ||||
| 			if (_mobility != value) { | ||||
| 				_mobility = value; | ||||
| 				addOperationToScene(calculateMobilityOperation); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Координата X. | ||||
| 		 */ | ||||
| 		public function get x():Number { | ||||
| 			return _coords.x; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Координата Y. | ||||
| 		 */ | ||||
| 		public function get y():Number { | ||||
| 			return _coords.y; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Координата Z. | ||||
| 		 */ | ||||
| 		public function get z():Number { | ||||
| 			return _coords.z; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set x(value:Number):void { | ||||
| 			if (_coords.x != value) { | ||||
| 				_coords.x = value; | ||||
| 				addOperationToScene(changeCoordsOperation); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set y(value:Number):void { | ||||
| 			if (_coords.y != value) { | ||||
| 				_coords.y = value; | ||||
| 				addOperationToScene(changeCoordsOperation); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set z(value:Number):void { | ||||
| 			if (_coords.z != value) { | ||||
| 				_coords.z = value; | ||||
| 				addOperationToScene(changeCoordsOperation); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Координаты объекта. | ||||
| 		 */ | ||||
| 		public function get coords():Point3D { | ||||
| 			return _coords.clone(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set coords(value:Point3D):void { | ||||
| 			if (!_coords.equals(value)) { | ||||
| 				_coords.copy(value); | ||||
| 				addOperationToScene(changeCoordsOperation); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Угол поворота вокруг оси X, заданный в радианах. | ||||
| 		 */ | ||||
| 		public function get rotationX():Number { | ||||
| 			return _rotationX; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Угол поворота вокруг оси Y, заданный в радианах. | ||||
| 		 */ | ||||
| 		public function get rotationY():Number { | ||||
| 			return _rotationY; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Угол поворота вокруг оси Z, заданный в радианах. | ||||
| 		 */ | ||||
| 		public function get rotationZ():Number { | ||||
| 			return _rotationZ; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set rotationX(value:Number):void { | ||||
| 			if (_rotationX != value) { | ||||
| 				_rotationX = value; | ||||
| 				addOperationToScene(changeRotationOrScaleOperation); | ||||
| 			} | ||||
| 		}		 | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set rotationY(value:Number):void { | ||||
| 			if (_rotationY != value) { | ||||
| 				_rotationY = value; | ||||
| 				addOperationToScene(changeRotationOrScaleOperation); | ||||
| 			} | ||||
| 		}		 | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set rotationZ(value:Number):void { | ||||
| 			if (_rotationZ != value) { | ||||
| 				_rotationZ = value; | ||||
| 				addOperationToScene(changeRotationOrScaleOperation); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Коэффициент масштабирования вдоль оси X. | ||||
| 		 */ | ||||
| 		public function get scaleX():Number { | ||||
| 			return _scaleX; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Коэффициент масштабирования вдоль оси Y. | ||||
| 		 */ | ||||
| 		public function get scaleY():Number { | ||||
| 			return _scaleY; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Коэффициент масштабирования вдоль оси Z. | ||||
| 		 */ | ||||
| 		public function get scaleZ():Number { | ||||
| 			return _scaleZ; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set scaleX(value:Number):void { | ||||
| 			if (_scaleX != value) { | ||||
| 				_scaleX = value; | ||||
| 				addOperationToScene(changeRotationOrScaleOperation); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set scaleY(value:Number):void { | ||||
| 			if (_scaleY != value) { | ||||
| 				_scaleY = value; | ||||
| 				addOperationToScene(changeRotationOrScaleOperation); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set scaleZ(value:Number):void { | ||||
| 			if (_scaleZ != value) { | ||||
| 				_scaleZ = value; | ||||
| 				addOperationToScene(changeRotationOrScaleOperation); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Строковое представление объекта. | ||||
| 		 *  | ||||
| 		 * @return строковое представление объекта | ||||
| 		 */ | ||||
| 		public function toString():String { | ||||
| 			return "[" + ObjectUtils.getClassName(this) + " " + _name + "]"; | ||||
| 		} | ||||
| 				 | ||||
| 		/** | ||||
| 		 * Имя объекта по умолчанию. | ||||
| 		 *  | ||||
| 		 * @return имя объекта по умолчанию | ||||
| 		 */		 | ||||
| 		protected function defaultName():String { | ||||
| 			return "object" + ++counter; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Добавление операции в очередь. | ||||
| 		 *  | ||||
| 		 * @param operation добавляемая операция | ||||
| 		 */ | ||||
| 		alternativa3d function addOperationToScene(operation:Operation):void { | ||||
| 			if (_scene != null) { | ||||
| 				_scene.addOperation(operation); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление операции из очереди. | ||||
| 		 *  | ||||
| 		 * @param operation удаляемая операция | ||||
| 		 */ | ||||
| 		alternativa3d function removeOperationFromScene(operation:Operation):void { | ||||
| 			if (_scene != null) { | ||||
| 				_scene.removeOperation(operation); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Создание пустого объекта без какой-либо внутренней структуры. Например, если некоторый геометрический примитив при | ||||
| 		 * своём создании формирует набор вершин, граней и поверхностей, то этот метод не должен создавать вершины, грани и | ||||
| 		 * поверхности. Данный метод используется в методе clone() и должен быть переопределён в потомках для получения | ||||
| 		 * правильного объекта. | ||||
| 		 *  | ||||
| 		 * @return новый пустой объект | ||||
| 		 */ | ||||
| 		protected function createEmptyObject():Object3D { | ||||
| 			return new Object3D(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Копирование свойств объекта-источника. Данный метод используется в методе clone() и должен быть переопределён в | ||||
| 		 * потомках для получения правильного объекта. Каждый потомок должен в переопределённом методе копировать только те | ||||
| 		 * свойства, которые добавлены к базовому классу именно в нём. Копирование унаследованных свойств выполняется | ||||
| 		 * вызовом super.clonePropertiesFrom(source). | ||||
| 		 *  | ||||
| 		 * @param source объект, свойства которого копируются | ||||
| 		 */ | ||||
| 		protected function clonePropertiesFrom(source:Object3D):void { | ||||
| 			_name = source._name; | ||||
| 			_mobility = source._mobility; | ||||
| 			_coords.x = source._coords.x; | ||||
| 			_coords.y = source._coords.y; | ||||
| 			_coords.z = source._coords.z; | ||||
| 			_rotationX = source._rotationX; | ||||
| 			_rotationY = source._rotationY; | ||||
| 			_rotationZ = source._rotationZ; | ||||
| 			_scaleX = source._scaleX; | ||||
| 			_scaleY = source._scaleY; | ||||
| 			_scaleZ = source._scaleZ; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Клонирование объекта. Для реализации собственного клонирования наследники должны переопределять методы | ||||
| 		 * <code>createEmptyObject()</code> и <code>clonePropertiesFrom()</code>. | ||||
| 		 *  | ||||
| 		 * @return клонированный экземпляр объекта | ||||
| 		 *  | ||||
| 		 * @see #createEmptyObject() | ||||
| 		 * @see #clonePropertiesFrom() | ||||
| 		 */ | ||||
| 		public function clone():Object3D { | ||||
| 			var copy:Object3D = createEmptyObject(); | ||||
| 			copy.clonePropertiesFrom(this); | ||||
| 			 | ||||
| 			// Клонирование детей | ||||
| 			for (var key:* in _children) { | ||||
| 				var child:Object3D = key; | ||||
| 				copy.addChild(child.clone()); | ||||
| 			} | ||||
| 			 | ||||
| 			return copy; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Получение дочернего объекта с заданным именем. | ||||
| 		 *  | ||||
| 		 * @param name имя дочернего объекта | ||||
| 		 * @return любой дочерний объект с заданным именем или <code>null</code> в случае отсутствия таких объектов | ||||
| 		 */ | ||||
| 		public function getChildByName(name:String):Object3D { | ||||
| 			for (var key:* in _children) { | ||||
| 				var child:Object3D = key; | ||||
| 				if (child._name == name) { | ||||
| 					return child; | ||||
| 				} | ||||
| 			} | ||||
| 			return null; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,125 +0,0 @@ | ||||
| package alternativa.engine3d.core { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.types.Set; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * @private | ||||
| 	 */ | ||||
| 	public class Operation { | ||||
| 		 | ||||
| 		alternativa3d static const OBJECT_CALCULATE_TRANSFORMATION:uint = 0x01000000; | ||||
| 		alternativa3d static const OBJECT_CALCULATE_MOBILITY:uint = 0x02000000; | ||||
| 		alternativa3d static const VERTEX_CALCULATE_COORDS:uint = 0x03000000; | ||||
| 		alternativa3d static const FACE_CALCULATE_NORMAL:uint = 0x04000000; | ||||
| 		alternativa3d static const FACE_CALCULATE_UV:uint = 0x05000000; | ||||
| 		alternativa3d static const FACE_UPDATE_PRIMITIVE:uint = 0x06000000; | ||||
| 		alternativa3d static const SCENE_CALCULATE_BSP:uint = 0x07000000; | ||||
| 		alternativa3d static const FACE_UPDATE_MATERIAL:uint = 0x08000000; | ||||
| 		alternativa3d static const FACE_CALCULATE_FRAGMENTS_UV:uint = 0x09000000; | ||||
| 		alternativa3d static const CAMERA_CALCULATE_MATRIX:uint = 0x0A000000; | ||||
| 		alternativa3d static const CAMERA_CALCULATE_PLANES:uint = 0x0B000000; | ||||
| 		alternativa3d static const CAMERA_RENDER:uint = 0x0C000000; | ||||
| 		alternativa3d static const SCENE_CLEAR_PRIMITIVES:uint = 0x0D000000; | ||||
| 		 | ||||
| 		// Объект | ||||
| 		alternativa3d var object:Object; | ||||
| 		 | ||||
| 		// Метод | ||||
| 		alternativa3d var method:Function; | ||||
| 		 | ||||
| 		// Название метода | ||||
| 		alternativa3d var name:String; | ||||
| 		 | ||||
| 		// Последствия | ||||
| 		private var sequel:Operation;  | ||||
| 		private var sequels:Set; | ||||
| 		 | ||||
|   		// Приоритет операции | ||||
| 		alternativa3d var priority:uint; | ||||
|  | ||||
| 		// Находится ли операция в очереди | ||||
| 		alternativa3d var queued:Boolean = false; | ||||
| 		 | ||||
| 		public function Operation(name:String, object:Object = null, method:Function = null, priority:uint = 0) { | ||||
| 			this.object = object; | ||||
| 			this.method = method; | ||||
| 			this.name = name; | ||||
| 			this.priority = priority; | ||||
| 		} | ||||
| 		 | ||||
| 		// Добавить последствие | ||||
| 		alternativa3d function addSequel(operation:Operation):void { | ||||
| 			if (sequel == null) { | ||||
| 				if (sequels == null) { | ||||
| 					sequel = operation; | ||||
| 				} else { | ||||
| 					sequels[operation] = true; | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (sequel != operation) { | ||||
| 					sequels = new Set(true); | ||||
| 					sequels[sequel] = true; | ||||
| 					sequels[operation] = true; | ||||
| 					sequel = null; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Удалить последствие | ||||
| 		alternativa3d function removeSequel(operation:Operation):void { | ||||
| 			if (sequel == null) { | ||||
| 				if (sequels != null) { | ||||
| 					delete sequels[operation]; | ||||
| 					var key:*; | ||||
| 					var single:Boolean = false; | ||||
| 					for (key in sequels) { | ||||
| 						if (single) { | ||||
| 							single = false; | ||||
| 							break; | ||||
| 						} | ||||
| 						single = true; | ||||
| 					} | ||||
| 					if (single) { | ||||
| 						sequel = key; | ||||
| 						sequels = null; | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (sequel == operation) { | ||||
| 					sequel = null; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		alternativa3d function collectSequels(collector:Array):void { | ||||
| 			if (sequel == null) { | ||||
| 				// Проверяем последствия | ||||
| 				for (var key:* in sequels) { | ||||
| 					var operation:Operation = key; | ||||
| 					// Если операция ещё не в очереди | ||||
| 					if (!operation.queued) { | ||||
| 						// Добавляем её в очередь | ||||
| 						collector.push(operation); | ||||
| 						// Устанавливаем флаг очереди | ||||
| 						operation.queued = true; | ||||
| 						// Вызываем добавление в очередь её последствий | ||||
| 						operation.collectSequels(collector); | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (!sequel.queued) { | ||||
| 					collector.push(sequel); | ||||
| 					sequel.queued = true; | ||||
| 					sequel.collectSequels(collector); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		public function toString():String { | ||||
| 			return "[Operation " + (priority >>> 24) + "/" + (priority & 0xFFFFFF) + " " + object + "." + name + "]"; | ||||
| 		} | ||||
| 		 | ||||
| 	} | ||||
| } | ||||
| @@ -1,130 +0,0 @@ | ||||
| package alternativa.engine3d.core { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * @private | ||||
| 	 * Примитивный полигон (примитив), хранящийся в узле BSP-дерева. | ||||
| 	 */ | ||||
| 	public class PolyPrimitive { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Количество точек | ||||
| 		 */ | ||||
| 		alternativa3d var num:uint; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Точки | ||||
| 		 */ | ||||
| 		alternativa3d var points:Array = new Array(); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * UV-координаты | ||||
| 		 */ | ||||
| 		alternativa3d var uvs:Array = new Array(); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Грань | ||||
| 		 */ | ||||
| 		alternativa3d var face:Face; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Родительский примитив | ||||
| 		 */ | ||||
| 		alternativa3d var parent:PolyPrimitive; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Соседний примитив (при наличии родительского) | ||||
| 		 */ | ||||
| 		alternativa3d var sibling:PolyPrimitive; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Фрагменты | ||||
| 		 */ | ||||
| 		alternativa3d var backFragment:PolyPrimitive; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		alternativa3d var frontFragment:PolyPrimitive; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Рассечения | ||||
| 		 */ | ||||
| 		alternativa3d var splitTime1:Number; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		alternativa3d var splitTime2:Number; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * BSP-нода, в которой находится примитив | ||||
| 		 */ | ||||
| 		alternativa3d var node:BSPNode; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Значения для расчёта качества сплиттера | ||||
| 		 */ | ||||
| 		alternativa3d var splits:uint; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		alternativa3d var disbalance:int; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Качество примитива как сплиттера (меньше - лучше) | ||||
| 		 */ | ||||
| 		public var splitQuality:Number; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Приоритет в BSP-дереве. Чем ниже мобильность, тем примитив выше в дереве. | ||||
| 		 */ | ||||
| 		public var mobility:int; | ||||
|  | ||||
| 		// Хранилище неиспользуемых примитивов | ||||
| 		static private var collector:Array = new Array(); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Создать примитив | ||||
| 		 */ | ||||
| 		static alternativa3d function createPolyPrimitive():PolyPrimitive { | ||||
| 			// Достаём примитив из коллектора | ||||
| 			var primitive:PolyPrimitive = collector.pop(); | ||||
| 			// Если коллектор пуст, создаём новый примитив | ||||
| 			if (primitive == null) { | ||||
| 				primitive = new PolyPrimitive(); | ||||
| 			} | ||||
| 			//trace(primitive.num, primitive.points.length, primitive.face, primitive.parent, primitive.sibling, primitive.fragment1, primitive.fragment2, primitive.node); | ||||
| 			return primitive; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Кладёт примитив в коллектор для последующего реиспользования. | ||||
| 		 * Ссылка на грань и массивы точек зачищаются в этом методе. | ||||
| 		 * Ссылки на фрагменты (parent, sibling, back, front) должны быть зачищены перед запуском метода. | ||||
| 		 *  | ||||
| 		 * Исключение: | ||||
| 		 * при сборке примитивов в сцене ссылки на back и front зачищаются после запуска метода.  | ||||
| 		 *    | ||||
| 		 * @param primitive примитив на реиспользование | ||||
| 		 */ | ||||
| 		static alternativa3d function destroyPolyPrimitive(primitive:PolyPrimitive):void { | ||||
| 			primitive.face = null; | ||||
| 			for (var i:uint = 0; i < primitive.num; i++) { | ||||
| 				primitive.points.pop(); | ||||
| 				primitive.uvs.pop(); | ||||
| 			} | ||||
| 			collector.push(primitive); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Строковое представление объекта. | ||||
| 		 */ | ||||
| 		public function toString():String { | ||||
| 			return "[Primitive " + face._mesh._name + "]"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,350 +0,0 @@ | ||||
| package alternativa.engine3d.core { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.errors.FaceExistsError; | ||||
| 	import alternativa.engine3d.errors.FaceNotFoundError; | ||||
| 	import alternativa.engine3d.errors.InvalidIDError; | ||||
| 	import alternativa.engine3d.materials.SurfaceMaterial; | ||||
| 	import alternativa.types.Set; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Поверхность — набор граней, объединённых в группу. Поверхности используются для установки материалов, | ||||
| 	 * визуализирующих грани объекта. | ||||
| 	 */ | ||||
| 	public class Surface { | ||||
| 		// Операции | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Изменение набора граней | ||||
| 		 */		 | ||||
| 		alternativa3d var changeFacesOperation:Operation = new Operation("changeFaces", this); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Изменение материала | ||||
| 		 */		 | ||||
| 		alternativa3d var changeMaterialOperation:Operation = new Operation("changeMaterial", this); | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Меш | ||||
| 		 */ | ||||
| 		alternativa3d var _mesh:Mesh; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Материал | ||||
| 		 */ | ||||
| 		alternativa3d var _material:SurfaceMaterial; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Грани | ||||
| 		 */ | ||||
| 		alternativa3d var _faces:Set = new Set(); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра поверхности. | ||||
| 		 */		 | ||||
| 		public function Surface() {} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Добавление грани в поверхность. | ||||
| 		 *   | ||||
| 		 * @param face экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор грани полигонального объекта | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.FaceNotFoundError грань не найдена в полигональном объекте содержащем поверхность | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 * @throws alternativa.engine3d.errors.FaceExistsError поверхность уже содержит указанную грань | ||||
| 		 *  | ||||
| 		 * @see Face  | ||||
| 		 */ | ||||
| 		public function addFace(face:Object):void { | ||||
| 			var byLink:Boolean = face is Face; | ||||
| 			 | ||||
| 			// Проверяем на нахождение поверхности в меше | ||||
| 			if (_mesh == null) { | ||||
| 				throw new FaceNotFoundError(face, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем на null | ||||
| 			if (face == null) { | ||||
| 				throw new FaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем наличие грани в меше | ||||
| 			if (byLink) { | ||||
| 				// Если удаляем по ссылке | ||||
| 				if (Face(face)._mesh != _mesh) { | ||||
| 					// Если грань не в меше | ||||
| 					throw new FaceNotFoundError(face, this); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Если удаляем по ID | ||||
| 				if (_mesh._faces[face] == undefined) { | ||||
| 					// Если нет грани с таким ID | ||||
| 					throw new FaceNotFoundError(face, this); | ||||
| 				} else {  | ||||
| 					if (!(_mesh._faces[face] is Face)) { | ||||
| 						throw new InvalidIDError(face, this); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Находим грань | ||||
| 			var f:Face = byLink ? Face(face) : _mesh._faces[face]; | ||||
| 			 | ||||
| 			// Проверяем наличие грани в поверхности | ||||
| 			if (_faces.has(f)) { | ||||
| 				// Если грань уже в поверхности | ||||
| 				throw new FaceExistsError(f, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем грань на нахождение в другой поверхности | ||||
| 			if (f._surface != null) { | ||||
| 				// Удаляем её из той поверхности | ||||
| 				f._surface._faces.remove(f); | ||||
| 				f.removeFromSurface(f._surface); | ||||
| 			} | ||||
| 			 | ||||
| 			// Добавляем грань в поверхность | ||||
| 			_faces.add(f); | ||||
| 			f.addToSurface(this); | ||||
| 			 | ||||
| 			// Отправляем операцию изменения набора граней | ||||
| 			_mesh.addOperationToScene(changeFacesOperation); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Удаление грани из поверхности. | ||||
| 		 *   | ||||
| 		 * @param face экземпляр класса <code>alternativa.engine3d.core.Face</code> или идентификатор грани полигонального объекта | ||||
| 		 *  | ||||
| 		 * @throws alternativa.engine3d.errors.FaceNotFoundError поверхность не содержит указанную грань | ||||
| 		 * @throws alternativa.engine3d.errors.InvalidIDError указано недопустимое значение идентификатора | ||||
| 		 *  | ||||
| 		 * @see Face | ||||
| 		 */ | ||||
| 		public function removeFace(face:Object):void { | ||||
| 			var byLink:Boolean = face is Face; | ||||
| 			 | ||||
| 			// Проверяем на нахождение поверхности в меше | ||||
| 			if (_mesh == null) { | ||||
| 				throw new FaceNotFoundError(face, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем на null | ||||
| 			if (face == null) { | ||||
| 				throw new FaceNotFoundError(null, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Проверяем наличие грани в меше | ||||
| 			if (byLink) { | ||||
| 				// Если удаляем по ссылке | ||||
| 				if (Face(face)._mesh != _mesh) { | ||||
| 					// Если грань не в меше | ||||
| 					throw new FaceNotFoundError(face, this); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Если удаляем по ID | ||||
| 				if (_mesh._faces[face] == undefined) { | ||||
| 					// Если нет грани с таким ID | ||||
| 					throw new FaceNotFoundError(face, this); | ||||
| 				} else { | ||||
| 					if (!(_mesh._faces[face] is Face)) { | ||||
| 						throw new InvalidIDError(face, this); | ||||
| 					} | ||||
| 				} | ||||
| 				 | ||||
| 			} | ||||
| 			 | ||||
| 			// Находим грань | ||||
| 			var f:Face = byLink ? Face(face) : _mesh._faces[face]; | ||||
| 			 | ||||
| 			// Проверяем наличие грани в поверхности | ||||
| 			if (!_faces.has(f)) { | ||||
| 				// Если грань не в поверхности | ||||
| 				throw new FaceNotFoundError(f, this); | ||||
| 			} | ||||
| 			 | ||||
| 			// Удаляем грань из поверхности | ||||
| 			_faces.remove(f); | ||||
| 			f.removeFromSurface(this); | ||||
| 			 | ||||
| 			// Отправляем операцию изменения набора граней | ||||
| 			_mesh.addOperationToScene(changeFacesOperation); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Материал поверхности. При установке нового значения, устанавливаемый материал будет удалён из старой поверхности. | ||||
| 		 */ | ||||
| 		public function get material():SurfaceMaterial { | ||||
| 			return _material; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set material(value:SurfaceMaterial):void { | ||||
| 			if (_material != value) { | ||||
| 				// Если был материал | ||||
| 				if (_material != null) { | ||||
| 					// Удалить материал из поверхности | ||||
| 					_material.removeFromSurface(this); | ||||
| 					// Удалить материал из меша | ||||
| 					if (_mesh != null) { | ||||
| 						_material.removeFromMesh(_mesh); | ||||
| 						// Удалить материал из сцены | ||||
| 						if (_mesh._scene != null) { | ||||
| 							_material.removeFromScene(_mesh._scene); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				// Если новый материал | ||||
| 				if (value != null) { | ||||
| 					// Если материал был в другой поверхности | ||||
| 					if (value._surface != null) { | ||||
| 						// Удалить его оттуда | ||||
| 						value._surface.material = null; | ||||
| 					} | ||||
| 					// Добавить материал в поверхность | ||||
| 					value.addToSurface(this); | ||||
| 					// Добавить материал в меш | ||||
| 					if (_mesh != null) { | ||||
| 						value.addToMesh(_mesh); | ||||
| 						// Добавить материал в сцену | ||||
| 						if (_mesh._scene != null) { | ||||
| 							value.addToScene(_mesh._scene); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				// Сохраняем материал | ||||
| 				_material = value; | ||||
| 				// Отправляем операцию изменения материала | ||||
| 				addMaterialChangedOperationToScene(); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Набор граней поверхности. | ||||
| 		 */ | ||||
| 		public function get faces():Set { | ||||
| 			return _faces.clone(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Полигональный объект, которому принадлежит поверхность. | ||||
| 		 */		 | ||||
| 		public function get mesh():Mesh { | ||||
| 			return _mesh; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Идентификатор поверхности в полигональном объекте. Если поверхность не принадлежит ни одному объекту, | ||||
| 		 * значение идентификатора равно <code>null</code>. | ||||
| 		 */ | ||||
| 		public function get id():Object { | ||||
| 			return (_mesh != null) ? _mesh.getSurfaceId(this) : null; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Добавление в сцену. | ||||
| 		 *  | ||||
| 		 * @param scene | ||||
| 		 */ | ||||
| 		alternativa3d function addToScene(scene:Scene3D):void { | ||||
| 			// Добавляем на сцену материал | ||||
| 			if (_material != null) { | ||||
| 				_material.addToScene(scene); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление из сцены. | ||||
| 		 *  | ||||
| 		 * @param scene | ||||
| 		 */ | ||||
| 		alternativa3d function removeFromScene(scene:Scene3D):void { | ||||
| 			// Удаляем все операции из очереди | ||||
| 			scene.removeOperation(changeFacesOperation); | ||||
| 			scene.removeOperation(changeMaterialOperation); | ||||
| 			// Удаляем из сцены материал | ||||
| 			if (_material != null) { | ||||
| 				_material.removeFromScene(scene); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Добавление к мешу | ||||
| 		 * @param mesh | ||||
| 		 */		 | ||||
| 		alternativa3d function addToMesh(mesh:Mesh):void { | ||||
| 			// Подписка на операции меша | ||||
| 			 | ||||
| 			// Добавляем в меш материал | ||||
| 			if (_material != null) { | ||||
| 				_material.addToMesh(mesh); | ||||
| 			} | ||||
| 			// Сохранить меш | ||||
| 			_mesh = mesh; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление из меша | ||||
| 		 *  | ||||
| 		 * @param mesh | ||||
| 		 */ | ||||
| 		alternativa3d function removeFromMesh(mesh:Mesh):void { | ||||
| 			// Отписка от операций меша | ||||
| 			 | ||||
| 			// Удаляем из меша материал | ||||
| 			if (_material != null) { | ||||
| 				_material.removeFromMesh(mesh); | ||||
| 			} | ||||
| 			// Удалить ссылку на меш | ||||
| 			_mesh = null; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление граней | ||||
| 		 */ | ||||
| 		alternativa3d function removeFaces():void { | ||||
| 			for (var key:* in _faces) { | ||||
| 				var face:Face = key; | ||||
| 				_faces.remove(face); | ||||
| 				face.removeFromSurface(this); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Изменение материала | ||||
| 		 */ | ||||
| 		alternativa3d function addMaterialChangedOperationToScene():void { | ||||
| 			if (_mesh != null) { | ||||
| 				_mesh.addOperationToScene(changeMaterialOperation); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Строковое представление объекта. | ||||
| 		 *  | ||||
| 		 * @return строковое представление объекта | ||||
| 		 */ | ||||
| 		public function toString():String { | ||||
| 			var length:uint = _faces.length; | ||||
| 			var res:String = "[Surface ID:" + id + ((length > 0) ? " faces:" : ""); | ||||
| 			var i:uint = 0; | ||||
| 			for (var key:* in _faces) { | ||||
| 				var face:Face = key; | ||||
| 				res += face.id + ((i < length - 1) ? ", " : ""); | ||||
| 				i++; | ||||
| 			} | ||||
| 			res += "]"; | ||||
| 			return res; | ||||
| 		}		 | ||||
| 	} | ||||
| } | ||||
| @@ -1,250 +0,0 @@ | ||||
| package alternativa.engine3d.core { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.types.Matrix3D; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.types.Set; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Вершина полигона в трёхмерном пространстве. Вершина хранит свои координаты, а также ссылки на | ||||
| 	 * полигональный объект и грани этого объекта, которым она принадлежит.  | ||||
| 	 */ | ||||
| 	final public class Vertex { | ||||
| 		// Операции | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Изменение локальных координат | ||||
| 		 */ | ||||
| 		alternativa3d var changeCoordsOperation:Operation = new Operation("changeCoords", this); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Расчёт глобальных координат | ||||
| 		 */ | ||||
| 		alternativa3d var calculateCoordsOperation:Operation = new Operation("calculateCoords", this, calculateCoords, Operation.VERTEX_CALCULATE_COORDS); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Меш | ||||
| 		 */ | ||||
| 		alternativa3d var _mesh:Mesh; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Координаты точки | ||||
| 		 */ | ||||
| 		alternativa3d var _coords:Point3D; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Грани | ||||
| 		 */ | ||||
| 		alternativa3d var _faces:Set = new Set(); | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Координаты в сцене | ||||
| 		 */ | ||||
| 		alternativa3d var globalCoords:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра вершины. | ||||
| 		 *  | ||||
| 		 * @param x координата вершины по оси X | ||||
| 		 * @param y координата вершины по оси Y | ||||
| 		 * @param z координата вершины по оси Z | ||||
| 		 */ | ||||
| 		public function Vertex(x:Number = 0, y:Number = 0, z:Number = 0) { | ||||
| 			_coords = new Point3D(x, y, z); | ||||
| 			 | ||||
| 			// Изменение координат инициирует пересчёт глобальных координат | ||||
| 			changeCoordsOperation.addSequel(calculateCoordsOperation); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Вызывается из операции calculateCoordsOperation для расчета глобальных координат вершины | ||||
| 		 */ | ||||
| 		private function calculateCoords():void { | ||||
| 			globalCoords.copy(_coords); | ||||
| 			globalCoords.transform(_mesh.transformation); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set x(value:Number):void { | ||||
| 			if (_coords.x != value) { | ||||
| 				_coords.x = value; | ||||
| 				if (_mesh != null) { | ||||
| 					_mesh.addOperationToScene(changeCoordsOperation); | ||||
| 				} | ||||
| 			} | ||||
| 		}		 | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set y(value:Number):void { | ||||
| 			if (_coords.y != value) { | ||||
| 				_coords.y = value; | ||||
| 				if (_mesh != null) { | ||||
| 					_mesh.addOperationToScene(changeCoordsOperation); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set z(value:Number):void { | ||||
| 			if (_coords.z != value) { | ||||
| 				_coords.z = value; | ||||
| 				if (_mesh != null) { | ||||
| 					_mesh.addOperationToScene(changeCoordsOperation); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set coords(value:Point3D):void { | ||||
| 			if (!_coords.equals(value)) { | ||||
| 				_coords.copy(value); | ||||
| 				if (_mesh != null) { | ||||
| 					_mesh.addOperationToScene(changeCoordsOperation); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * координата вершины по оси X. | ||||
| 		 */ | ||||
| 		public function get x():Number { | ||||
| 			return _coords.x; | ||||
| 		}		 | ||||
|  | ||||
| 		/** | ||||
| 		 * координата вершины по оси Y. | ||||
| 		 */ | ||||
| 		public function get y():Number { | ||||
| 			return _coords.y; | ||||
| 		}		 | ||||
|  | ||||
| 		/** | ||||
| 		 * координата вершины по оси Z. | ||||
| 		 */ | ||||
| 		public function get z():Number { | ||||
| 			return _coords.z; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Координаты вершины. | ||||
| 		 */ | ||||
| 		public function get coords():Point3D { | ||||
| 			return _coords.clone(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Полигональный объект, которому принадлежит вершина. | ||||
| 		 */ | ||||
| 		public function get mesh():Mesh { | ||||
| 			return _mesh; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Множество граней, которым принадлежит вершина. Каждый элемент множества является объектом класса | ||||
| 		 * <code>altertnativa.engine3d.core.Face</code>. | ||||
| 		 *  | ||||
| 		 * @see Face | ||||
| 		 */		 | ||||
| 		public function get faces():Set { | ||||
| 			return _faces.clone(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Идентификатор вершины в полигональном объекте. Если вершина не принадлежит полигональному объекту, возвращается <code>null</code>. | ||||
| 		 */ | ||||
| 		public function get id():Object { | ||||
| 			return (_mesh != null) ? _mesh.getVertexId(this) : null; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param scene | ||||
| 		 */		 | ||||
| 		alternativa3d function addToScene(scene:Scene3D):void { | ||||
| 			// При добавлении на сцену расчитать глобальные координаты | ||||
| 			scene.addOperation(calculateCoordsOperation); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param scene | ||||
| 		 */		 | ||||
| 		alternativa3d function removeFromScene(scene:Scene3D):void { | ||||
| 			// Удаляем все операции из очереди | ||||
| 			scene.removeOperation(calculateCoordsOperation); | ||||
| 			scene.removeOperation(changeCoordsOperation); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param mesh | ||||
| 		 */ | ||||
| 		alternativa3d function addToMesh(mesh:Mesh):void { | ||||
| 			// Подписка на операции меша | ||||
| 			mesh.changeCoordsOperation.addSequel(calculateCoordsOperation); | ||||
| 			mesh.changeRotationOrScaleOperation.addSequel(calculateCoordsOperation); | ||||
| 			// Сохранить меш | ||||
| 			_mesh = mesh; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param mesh | ||||
| 		 */ | ||||
| 		alternativa3d function removeFromMesh(mesh:Mesh):void { | ||||
| 			// Отписка от операций меша | ||||
| 			mesh.changeCoordsOperation.removeSequel(calculateCoordsOperation); | ||||
| 			mesh.changeRotationOrScaleOperation.removeSequel(calculateCoordsOperation); | ||||
| 			// Удалить зависимые грани | ||||
| 			for (var key:* in _faces) { | ||||
| 				var face:Face = key; | ||||
| 				mesh.removeFace(face); | ||||
| 			} | ||||
| 			// Удалить ссылку на меш | ||||
| 			_mesh = null; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param face | ||||
| 		 */ | ||||
| 		alternativa3d function addToFace(face:Face):void { | ||||
| 			// Подписка грани на операции | ||||
| 			changeCoordsOperation.addSequel(face.calculateUVOperation); | ||||
| 			changeCoordsOperation.addSequel(face.calculateNormalOperation); | ||||
| 			// Добавить грань в список | ||||
| 			_faces.add(face); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @param face | ||||
| 		 */ | ||||
| 		alternativa3d function removeFromFace(face:Face):void { | ||||
| 			// Отписка грани от операций | ||||
| 			changeCoordsOperation.removeSequel(face.calculateUVOperation); | ||||
| 			changeCoordsOperation.removeSequel(face.calculateNormalOperation); | ||||
| 			// Удалить грань из списка | ||||
| 			_faces.remove(face); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Строковое представление объекта. | ||||
| 		 *  | ||||
| 		 * @return строковое представление объекта | ||||
| 		 */		 | ||||
| 		public function toString():String { | ||||
| 			return "[Vertex ID:" + id + " " + _coords.x.toFixed(2) + ", " + _coords.y.toFixed(2) + ", " + _coords.z.toFixed(2) + "]"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,17 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 99 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/display | ||||
| END | ||||
| Skin.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 107 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/display/Skin.as | ||||
| END | ||||
| View.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 107 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/display/View.as | ||||
| END | ||||
| @@ -1,52 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/display | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| Skin.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| a49c915b68556525f014d6b2b1477b38 | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| View.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| d9cfe36eb0034482d153d4b0f47c7759 | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
| @@ -1,66 +0,0 @@ | ||||
| package alternativa.engine3d.display { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.PolyPrimitive; | ||||
| 	import alternativa.engine3d.materials.SurfaceMaterial; | ||||
| 	 | ||||
| 	import flash.display.Graphics; | ||||
| 	import flash.display.Sprite; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * @private | ||||
| 	 * Контейнер, исползуемый материалами для отрисовки примитивов. Каждый примитив BSP-дерева рисуется в своём контейнере. | ||||
| 	 */ | ||||
| 	public class Skin extends Sprite { | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Графика скина (для быстрого доступа) | ||||
| 		 */ | ||||
| 		alternativa3d var gfx:Graphics = graphics; | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Ссылка на следующий скин | ||||
| 		 */ | ||||
| 		alternativa3d var nextSkin:Skin; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Примитив | ||||
| 		 */ | ||||
| 		alternativa3d var primitive:PolyPrimitive; | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Материал, связанный со скином. | ||||
| 		 */ | ||||
| 		alternativa3d var material:SurfaceMaterial; | ||||
| 		 | ||||
| 		// Хранилище неиспользуемых скинов | ||||
| 		static private var collector:Array = new Array(); | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Создание скина. | ||||
| 		 */ | ||||
| 		static alternativa3d function createSkin():Skin { | ||||
| 			// Достаём скин из коллектора | ||||
| 			var skin:Skin = collector.pop(); | ||||
| 			// Если коллектор пуст, создаём новый скин | ||||
| 			if (skin == null) { | ||||
| 				skin = new Skin(); | ||||
| 			} | ||||
| 			return skin; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление скина, все ссылки должны быть почищены. | ||||
| 		 */ | ||||
| 		static alternativa3d function destroySkin(skin:Skin):void { | ||||
| 			collector.push(skin); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,186 +0,0 @@ | ||||
| package alternativa.engine3d.display { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.engine3d.core.Face; | ||||
| 	 | ||||
| 	import flash.display.Sprite; | ||||
| 	import flash.geom.Point; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Область для вывода изображения с камеры. | ||||
| 	 */ | ||||
| 	public class View extends Sprite { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Область отрисовки спрайтов | ||||
| 		 */		 | ||||
| 		alternativa3d var canvas:Sprite; | ||||
| 		 | ||||
| 		private var _camera:Camera3D; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Ширина области вывода | ||||
| 		 */		 | ||||
| 		alternativa3d var _width:Number; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Высота области вывода | ||||
| 		 */		 | ||||
| 		alternativa3d var _height:Number; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра области вывода. | ||||
| 		 *  | ||||
| 		 * @param camera камера, изображение с которой должно выводиться  | ||||
| 		 * @param width ширина области вывода | ||||
| 		 * @param height высота области вывода | ||||
| 		 */ | ||||
| 		public function View(camera:Camera3D = null, width:Number = 0, height:Number = 0) { | ||||
| 			canvas = new Sprite(); | ||||
| 			canvas.mouseEnabled = false; | ||||
| 			canvas.mouseChildren = false; | ||||
| 			canvas.tabEnabled = false; | ||||
| 			canvas.tabChildren = false; | ||||
| 			addChild(canvas); | ||||
| 			 | ||||
| 			this.camera = camera; | ||||
| 			this.width = width; | ||||
| 			this.height = height; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Камера с которой ведётся отображение.  | ||||
| 		 */ | ||||
| 		public function get camera():Camera3D { | ||||
| 			return _camera; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set camera(value:Camera3D):void { | ||||
| 			if (_camera != value) { | ||||
| 				// Если была камера | ||||
| 				if (_camera != null) { | ||||
| 					// Удалить камеру | ||||
| 					_camera.removeFromView(this); | ||||
| 				} | ||||
| 				// Если новая камера | ||||
| 				if (value != null) { | ||||
| 					// Если камера была в другом вьюпорте | ||||
| 					if (value._view != null) { | ||||
| 						// Удалить её оттуда | ||||
| 						value._view.camera = null; | ||||
| 					} | ||||
| 					// Добавить камеру | ||||
| 					value.addToView(this); | ||||
| 				} else { | ||||
| 					// Зачистка скинов | ||||
| 					if (canvas.numChildren > 0) { | ||||
| 						var skin:Skin = Skin(canvas.getChildAt(0)); | ||||
| 						while (skin != null) { | ||||
| 							// Сохраняем следующий | ||||
| 							var next:Skin = skin.nextSkin; | ||||
| 							// Удаляем из канваса | ||||
| 							canvas.removeChild(skin); | ||||
| 							// Очистка скина | ||||
| 							if (skin.material != null) { | ||||
| 								skin.material.clear(skin); | ||||
| 							} | ||||
| 							// Зачищаем ссылки | ||||
| 							skin.nextSkin = null; | ||||
| 							skin.primitive = null; | ||||
| 							skin.material = null; | ||||
| 							// Удаляем | ||||
| 							Skin.destroySkin(skin); | ||||
| 							// Следующий устанавливаем текущим | ||||
| 							skin = next; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				// Сохраняем камеру | ||||
| 				_camera = value; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Ширина области вывода в пикселях. | ||||
| 		 */ | ||||
| 		override public function get width():Number { | ||||
| 			return _width; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		override public function set width(value:Number):void { | ||||
| 			if (_width != value) { | ||||
| 				_width = value; | ||||
| 				canvas.x = _width*0.5; | ||||
| 				if (_camera != null) { | ||||
| 					camera.addOperationToScene(camera.calculatePlanesOperation); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Высота области вывода в пикселях. | ||||
| 		 */ | ||||
| 		override public function get height():Number { | ||||
| 			return _height; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		override public function set height(value:Number):void { | ||||
| 			if (_height != value) { | ||||
| 				_height = value; | ||||
| 				canvas.y = _height*0.5; | ||||
| 				if (_camera != null) { | ||||
| 					camera.addOperationToScene(camera.calculatePlanesOperation); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод возвращает грань, находящуюся под указанной точкой в области вывода. | ||||
| 		 *  | ||||
| 		 * @param viewPoint координаты точки относительно области вывода | ||||
| 		 *  | ||||
| 		 * @return ближайшая к камере грань под заданной точкой области вывода | ||||
| 		 */ | ||||
| 		public function getFaceUnderPoint(viewPoint:Point):Face { | ||||
| 			var p:Point = localToGlobal(viewPoint); | ||||
| 			var objects:Array = canvas.getObjectsUnderPoint(p); | ||||
| 			var skin:Skin = objects.pop() as Skin; | ||||
| 			if (skin != null) { | ||||
| 				return skin.primitive.face; | ||||
| 			} | ||||
| 			return null; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Метод возвращает грани, находящиеся под указанной точкой в области вывода. | ||||
| 		 *  | ||||
| 		 * @param viewPoint координаты точки относительно области вывода | ||||
| 		 *  | ||||
| 		 * @return массив граней, расположенных под заданной точкой области вывода. Первым элементом массива является самая дальняя грань. | ||||
| 		 */ | ||||
| 		public function getFacesUnderPoint(viewPoint:Point):Array { | ||||
| 			var p:Point = localToGlobal(viewPoint); | ||||
| 			var objects:Array = canvas.getObjectsUnderPoint(p); | ||||
| 			var res:Array = new Array(); | ||||
| 			var length:uint = objects.length; | ||||
| 			for (var i:uint = 0; i < length; i++) { | ||||
| 				var skin:Skin = objects[i];  | ||||
| 				res.push(skin.primitive.face); | ||||
| 			} | ||||
| 			return res; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,83 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 98 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors | ||||
| END | ||||
| InvalidIDError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 116 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/InvalidIDError.as | ||||
| END | ||||
| ObjectNotFoundError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 121 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/ObjectNotFoundError.as | ||||
| END | ||||
| SurfaceNotFoundError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 122 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/SurfaceNotFoundError.as | ||||
| END | ||||
| ObjectExistsError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 119 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/ObjectExistsError.as | ||||
| END | ||||
| SurfaceExistsError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 120 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/SurfaceExistsError.as | ||||
| END | ||||
| Object3DNotFoundError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 123 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/Object3DNotFoundError.as | ||||
| END | ||||
| VertexNotFoundError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 121 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/VertexNotFoundError.as | ||||
| END | ||||
| FaceNotFoundError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 119 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/FaceNotFoundError.as | ||||
| END | ||||
| Engine3DError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 115 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/Engine3DError.as | ||||
| END | ||||
| Object3DHierarchyError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 124 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/Object3DHierarchyError.as | ||||
| END | ||||
| VertexExistsError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 119 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/VertexExistsError.as | ||||
| END | ||||
| FaceExistsError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 117 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/FaceExistsError.as | ||||
| END | ||||
| FaceNeedMoreVerticesError.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 127 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors/FaceNeedMoreVerticesError.as | ||||
| END | ||||
| @@ -1,184 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/errors | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| InvalidIDError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| bdff3ad52feb719e00dc92389c9e5e1e | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| ObjectNotFoundError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 6dd11fc2d6176278fb21fb7172f9ec0f | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| SurfaceNotFoundError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 420e1e6740301ff2bb70e1a0ae085a75 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| ObjectExistsError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| a25dfccfb0211e9199fbe392e5f0ff08 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| SurfaceExistsError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| ac6477b7def1b87a76360823782d1dca | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Object3DNotFoundError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 7ab91f78c0b7e42b0ce116e938e62c66 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| VertexNotFoundError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 2cf926b6a3bedb6035162d2cc1a14201 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| FaceNotFoundError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 6983fcb77a1b337338f6452e35f504bf | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Engine3DError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 47a129612e42512ac074b5273ce9be5d | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Object3DHierarchyError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 46b5ed9fa3674c3ec123b6b0b38e5fa6 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| VertexExistsError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 93465bfa7fb4dbaca1323d2e09c1c645 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| FaceExistsError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 6e778f9e94447b3c427f84bad486ae59 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| FaceNeedMoreVerticesError.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 35781dbd32ece10c9efca8ab9749c293 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
| @@ -1,27 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
|  | ||||
| 	import alternativa.utils.TextUtils; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Базовый класс для ошибок 3d-engine. | ||||
| 	 */ | ||||
| 	public class Engine3DError extends Error { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Источник ошибки - объект в котором произошла ошибка. | ||||
| 		 */ | ||||
| 		public var source:Object; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *   | ||||
| 		 * @param message описание ошибки | ||||
| 		 * @param source источник ошибки | ||||
| 		 */ | ||||
| 		public function Engine3DError(message:String = "", source:Object = null) { | ||||
| 			super(message); | ||||
| 			this.source = source; | ||||
| 			this.name = "Engine3DError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	 | ||||
| 	import alternativa.engine3d.core.Face; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.utils.TextUtils; | ||||
| 	import alternativa.engine3d.core.Surface; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Ошибка, возникающая при попытке добавить в какой-либо объект грань, уже содержащуюся в данном объекте.   | ||||
| 	 */ | ||||
| 	public class FaceExistsError extends ObjectExistsError { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param face экземпляр или идентификатор грани, которая уже содержится в объекте  | ||||
| 		 * @param source источник ошибки | ||||
| 		 */ | ||||
| 		public function FaceExistsError(face:Object = null, source:Object = null) { | ||||
| 			var message:String; | ||||
| 			if (source is Mesh) { | ||||
| 				message = "Mesh "; | ||||
| 			} else if (source is Surface) { | ||||
| 				message = "Surface "; | ||||
| 			} | ||||
| 			if (face is Face) { | ||||
| 				message += "%1. Face %2 already exists."; | ||||
| 			} else { | ||||
| 				message += "%1. Face with ID '%2' already exists."; | ||||
| 			} | ||||
| 			super(TextUtils.insertVars(message, source, face), face, source); | ||||
| 			this.name = "FaceExistsError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,29 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	 | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.utils.TextUtils; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Ошибка, обозначающая недостаточное количество вершин для создания грани. | ||||
| 	 * Для создания грани должно быть указано не менее трех вершин.  | ||||
| 	 */ | ||||
| 	public class FaceNeedMoreVerticesError extends Engine3DError { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Количество переданных для создания грани вершин  | ||||
| 		 */ | ||||
| 		public var count:uint; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *   | ||||
| 		 * @param mesh объект, в котором произошла ошибка | ||||
| 		 * @param count количество вершин, переданное для создания грани | ||||
| 		 */ | ||||
| 		public function FaceNeedMoreVerticesError(mesh:Mesh = null, count:uint = 0) { | ||||
| 			super(TextUtils.insertVars("Mesh %1. %2 vertices not enough for face creation.", mesh, count), mesh); | ||||
| 			this.count = count; | ||||
| 			this.name = "FaceNeedMoreVerticesError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,34 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	 | ||||
| 	import alternativa.engine3d.core.Face; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.utils.TextUtils; | ||||
|  | ||||
| 	/** | ||||
| 	 * Ошибка, возникающая, если грань не найдена в объекте.  | ||||
| 	 */ | ||||
| 	public class FaceNotFoundError extends ObjectNotFoundError { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *   | ||||
| 		 * @param face экземпляр или идентификатор грани | ||||
| 		 * @param source объект, в котором произошла ошибка | ||||
| 		 */ | ||||
| 		public function FaceNotFoundError(face:Object = null, source:Object = null) { | ||||
| 			var message:String; | ||||
| 			if (source is Mesh) { | ||||
| 				message = "Mesh "; | ||||
| 			} else { | ||||
| 				message = "Surface "; | ||||
| 			} | ||||
| 			if (face is Face) { | ||||
| 				message += "%1. Face %2 not found."; | ||||
| 			} else { | ||||
| 				message += "%1. Face with ID '%2' not found."; | ||||
| 			} | ||||
| 			super(TextUtils.insertVars(message, source, face), face, source); | ||||
| 			this.name = "FaceNotFoundError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,34 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	import alternativa.utils.TextUtils; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Surface; | ||||
| 	 | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Ошибка, обозначающая, что идентификатор зарезервирован и не может быть использован.  | ||||
| 	 */ | ||||
| 	public class InvalidIDError extends Engine3DError { | ||||
| 		/** | ||||
| 		 * Зарезервированный идентификатор  | ||||
| 		 */ | ||||
| 		public var id:Object; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *   | ||||
| 		 * @param id идентификатор | ||||
| 		 * @param source объект, в котором произошла ошибка | ||||
| 		 */ | ||||
| 		public function InvalidIDError(id:Object = null, source:Object = null) { | ||||
| 			var message:String; | ||||
| 			if (source is Mesh) { | ||||
| 				message = "Mesh %2. "; | ||||
| 			} else if (source is Surface) { | ||||
| 				message = "Surface %2. "; | ||||
| 			} | ||||
| 			super(TextUtils.insertVars(message + "ID %1 is reserved and cannot be used", [id, source]), source); | ||||
| 			this.id = id; | ||||
| 			this.name = "InvalidIDError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,29 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	 | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.utils.TextUtils; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Ошибка, связанная с нарушением иерархии объектов сцены.  | ||||
| 	 */ | ||||
| 	public class Object3DHierarchyError extends Engine3DError | ||||
| 	{ | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Объект сцены, нарушающий иерархию  | ||||
| 		 */ | ||||
| 		public var object:Object3D; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *    | ||||
| 		 * @param object объект, нарушающий иерархию | ||||
| 		 * @param source источник ошибки | ||||
| 		 */ | ||||
| 		public function Object3DHierarchyError(object:Object3D = null, source:Object3D = null) { | ||||
| 			super(TextUtils.insertVars("Object3D %1. Object %2 cannot be added", source, object), source); | ||||
| 			this.object = object; | ||||
| 			this.name = "Object3DHierarchyError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,22 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	 | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.utils.TextUtils; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Ошибка, возникающая, когда объект сцены не был найден в списке связанных с необходимым объектом сцены. | ||||
| 	 */ | ||||
| 	public class Object3DNotFoundError extends ObjectNotFoundError { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param object ненайденный объект сцены | ||||
| 		 * @param source объект сцены, в котором произошла ошибка | ||||
| 		 */ | ||||
| 		public function Object3DNotFoundError(object:Object3D = null, source:Object3D = null) { | ||||
| 			super(TextUtils.insertVars("Object3D %1. Object %2 not in child list", source, object), object, source); | ||||
| 			this.name = "Object3DNotFoundError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,26 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Ошибка, обозначающая, что объект уже присутствует в контейнере. | ||||
| 	 */ | ||||
| 	public class ObjectExistsError extends Engine3DError { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Экземпляр или идентификатор объекта, который уже присутствует в контейнере  | ||||
| 		 */ | ||||
| 		public var object:Object; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *   | ||||
| 		 * @param message описание ошибки | ||||
| 		 * @param object объект, который уже присутствует в контейнере | ||||
| 		 * @param source объект, вызвавший ошибку | ||||
| 		 */ | ||||
| 		public function ObjectExistsError(message:String = "", object:Object = null, source:Object = null) { | ||||
| 			super(message, source); | ||||
| 			this.object = object; | ||||
| 			this.name = "ObjectExistsError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,26 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Необходимый объект не был найден в контейнере.  | ||||
| 	 */ | ||||
| 	public class ObjectNotFoundError extends Engine3DError { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Объект, который отсутствует в контейнере.  | ||||
| 		 */ | ||||
| 		public var object:Object; | ||||
|  | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param message описание ошибки | ||||
| 		 * @param object отсутствующий объект | ||||
| 		 * @param source объект, вызвавший ошибку | ||||
| 		 */ | ||||
| 		public function ObjectNotFoundError(message:String = "", object:Object = null, source:Object = null) { | ||||
| 			super(message, source); | ||||
| 			this.object = object; | ||||
| 			this.name = "ObjectNotFoundError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,22 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	 | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.utils.TextUtils; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Ошибка, обозначающая, что поверхность уже присутствует в контейнере.  | ||||
| 	 */ | ||||
| 	public class SurfaceExistsError extends ObjectExistsError { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param surface поверхность, которая уже присутствует в контейнере | ||||
| 		 * @param mesh объект, вызвавший ошибку | ||||
| 		 */ | ||||
| 		public function SurfaceExistsError(surface:Object = null, mesh:Mesh = null)	{ | ||||
| 			super(TextUtils.insertVars("Mesh %1. Surface with ID '%2' already exists.", mesh, surface), surface, mesh); | ||||
| 			this.name = "SurfaceExistsError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,31 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	 | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Surface; | ||||
| 	import alternativa.utils.TextUtils; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Ошибка, обозначающая, что поверхность не найдена в контейнере.  | ||||
| 	 */ | ||||
| 	public class SurfaceNotFoundError extends ObjectNotFoundError { | ||||
|  | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param surface поверхность, которая отсутствует в объекте | ||||
| 		 * @param mesh объект, который вызвал ошибку | ||||
| 		 */ | ||||
| 		public function SurfaceNotFoundError(surface:Object = null, mesh:Mesh = null) { | ||||
| 			if (mesh == null) { | ||||
| 				 | ||||
| 			} | ||||
| 			if (surface is Surface) { | ||||
| 				message = "Mesh %1. Surface %2 not found."; | ||||
| 			} else { | ||||
| 				message = "Mesh %1. Surface with ID '%2' not found."; | ||||
| 			} | ||||
| 			super(TextUtils.insertVars(message, mesh, surface), surface, mesh); | ||||
| 			this.name = "SurfaceNotFoundError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,22 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	 | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.utils.TextUtils; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Ошибка, обозначающая, что вершина уже содержится в объекте.  | ||||
| 	 */ | ||||
| 	public class VertexExistsError extends ObjectExistsError { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param vertex вершина, которая уже есть в объекте | ||||
| 		 * @param mesh объект, вызвавший ошибку | ||||
| 		 */ | ||||
| 		public function VertexExistsError(vertex:Object = null, mesh:Mesh = null)	{ | ||||
| 			super(TextUtils.insertVars("Mesh %1. Vertex with ID '%2' already exists.", mesh, vertex), vertex, mesh); | ||||
| 			this.name = "VertexExistsError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,28 +0,0 @@ | ||||
| package alternativa.engine3d.errors { | ||||
| 	 | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Vertex; | ||||
| 	import alternativa.utils.TextUtils; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Ошибка, обозначающая, что вершина не найдена в объекте.  | ||||
| 	 */ | ||||
| 	public class VertexNotFoundError extends ObjectNotFoundError { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param vertex вершина, которая не найдена в объекте | ||||
| 		 * @param mesh объект, вызвавший ошибку | ||||
| 		 */ | ||||
| 		public function VertexNotFoundError(vertex:Object = null, mesh:Mesh = null) { | ||||
| 			if (vertex is Vertex) { | ||||
| 				message = "Mesh %1. Vertex %2 not found."; | ||||
| 			} else { | ||||
| 				message = "Mesh %1. Vertex with ID '%2' not found."; | ||||
| 			} | ||||
| 			super(TextUtils.insertVars(message, mesh, vertex), vertex, mesh); | ||||
| 			this.name = "VertexNotFoundError"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,35 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 99 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/loaders | ||||
| END | ||||
| Loader3DS.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 112 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/loaders/Loader3DS.as | ||||
| END | ||||
| LoaderOBJ.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 112 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/loaders/LoaderOBJ.as | ||||
| END | ||||
| LoaderMTL.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 112 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/loaders/LoaderMTL.as | ||||
| END | ||||
| MTLTextureMapInfo.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 120 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/loaders/MTLTextureMapInfo.as | ||||
| END | ||||
| MaterialInfo.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 115 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/loaders/MaterialInfo.as | ||||
| END | ||||
| @@ -1,88 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/loaders | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| Loader3DS.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 00968101ff4527373d553d422e7330fb | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| LoaderOBJ.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| fbea6bb68b725694c17d00505a0095e6 | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| LoaderMTL.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 8323b7d5fbc1556a49d9fd6d8af752c1 | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| MTLTextureMapInfo.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 42806a924c6d67153cb53141ba5e2737 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| MaterialInfo.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 8010397834f3e9cd80b825cbbf4983ec | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,285 +0,0 @@ | ||||
| package alternativa.engine3d.loaders { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.types.Map; | ||||
| 	import alternativa.utils.ColorUtils; | ||||
| 	 | ||||
| 	import flash.display.Bitmap; | ||||
| 	import flash.display.BitmapData; | ||||
| 	import flash.display.Loader; | ||||
| 	import flash.events.ErrorEvent; | ||||
| 	import flash.events.Event; | ||||
| 	import flash.events.EventDispatcher; | ||||
| 	import flash.events.IOErrorEvent; | ||||
| 	import flash.events.SecurityErrorEvent; | ||||
| 	import flash.geom.Point; | ||||
| 	import flash.net.URLLoader; | ||||
| 	import flash.net.URLRequest; | ||||
| 	import flash.system.LoaderContext; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * @private | ||||
| 	 * Загрузчик библиотеки материалов из файлов в формате MTL material format (Lightwave, OBJ). | ||||
| 	 * <p> | ||||
| 	 * На данный момент обеспечивается загрузка цвета, прозрачности и диффузной текстуры материала. | ||||
| 	 */ | ||||
| 	internal class LoaderMTL extends EventDispatcher { | ||||
| 		 | ||||
| 		private static const COMMENT_CHAR:String = "#"; | ||||
| 		private static const CMD_NEW_MATERIAL:String = "newmtl"; | ||||
| 		private static const CMD_DIFFUSE_REFLECTIVITY:String = "Kd"; | ||||
| 		private static const CMD_DISSOLVE:String = "d"; | ||||
| 		private static const CMD_MAP_DIFFUSE:String = "map_Kd"; | ||||
| 		 | ||||
| 		private static const REGEXP_TRIM:RegExp = /^\s*(.*?)\s*$/; | ||||
| 		private static const REGEXP_SPLIT_FILE:RegExp = /\r*\n/; | ||||
| 		private static const REGEXP_SPLIT_LINE:RegExp = /\s+/; | ||||
| 		 | ||||
| 		// Загрузчик файла MTL | ||||
| 		private var fileLoader:URLLoader; | ||||
| 		// Загрузчик файлов текстур | ||||
| 		private var bitmapLoader:Loader; | ||||
| 		// Контекст загрузки для bitmapLoader | ||||
| 		private var loaderContext:LoaderContext; | ||||
| 		// Базовый URL файла MTL | ||||
| 		private var baseUrl:String; | ||||
|  | ||||
| 		// Библиотека загруженных материалов | ||||
| 		private var _library:Map; | ||||
| 		// Список материалов, имеющих диффузные текстуры | ||||
| 		private var diffuseMaps:Map; | ||||
| 		// Имя текущего материала | ||||
| 		private var materialName:String; | ||||
| 		// параметры текущего материала | ||||
| 		private var currentMaterialInfo:MaterialInfo = new MaterialInfo(); | ||||
| 		 | ||||
| 		alternativa3d static var stubBitmapData:BitmapData; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создаёт новый экземпляр класса. | ||||
| 		 */ | ||||
| 		public function LoaderMTL() { | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Прекращение текущей загрузки. | ||||
| 		 */ | ||||
| 		public function close():void { | ||||
| 			try { | ||||
| 				fileLoader.close(); | ||||
| 			} catch (e:Error) { | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Библиотека материалов. Ключами являются наименования материалов, значениями -- объекты, наследники класса | ||||
| 		 * <code>alternativa.engine3d.loaders.MaterialInfo</code>. | ||||
| 		 * @see alternativa.engine3d.loaders.MaterialInfo | ||||
| 		 */ | ||||
| 		public function get library():Map { | ||||
| 			return _library; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет загрузку файла материалов, разбор его содержимого, загрузку текстур при необходимости и | ||||
| 		 * формирование библиотеки материалов. После окончания работы метода посылается сообщение | ||||
| 		 * <code>Event.COMPLETE</code> и становится доступна библиотека материалов через свойство <code>library</code>. | ||||
| 		 * <p> | ||||
| 		 * При возникновении ошибок, связанных с вводом-выводом или с безопасностью, посылаются сообщения <code>IOErrorEvent.IO_ERROR</code> и  | ||||
| 		 * <code>SecurityErrorEvent.SECURITY_ERROR</code> соответственно. | ||||
| 		 * <p> | ||||
| 		 * Если происходит ошибка при загрузке файла текстуры, то соответствующая текстура заменяется на текстуру-заглушку. | ||||
| 		 * <p> | ||||
| 		 * @param url URL MTL-файла | ||||
| 		 * @param loaderContext LoaderContext для загрузки файлов текстур | ||||
| 		 *   | ||||
| 		 * @see #library | ||||
| 		 */ | ||||
| 		public function load(url:String, loaderContext:LoaderContext = null):void { | ||||
| 			this.loaderContext = loaderContext; | ||||
| 			baseUrl = url.substring(0, url.lastIndexOf("/") + 1); | ||||
|  | ||||
| 			if (fileLoader == null) { | ||||
| 				fileLoader = new URLLoader(); | ||||
| 				fileLoader.addEventListener(Event.COMPLETE, parseMTLFile); | ||||
| 				fileLoader.addEventListener(IOErrorEvent.IO_ERROR, onError); | ||||
| 				fileLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError); | ||||
|  | ||||
| 				bitmapLoader = new Loader(); | ||||
| 				bitmapLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onBitmapLoadComplete); | ||||
| 				bitmapLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onBitmapLoadComplete); | ||||
| 				bitmapLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onBitmapLoadComplete); | ||||
| 			} | ||||
| 			 | ||||
| 			try { | ||||
| 				fileLoader.close(); | ||||
| 				bitmapLoader.close(); | ||||
| 			} catch (e:Error) { | ||||
| 				// Пропуск ошибки при попытке закрытия неактивных загрузчиков | ||||
| 			} | ||||
| 			 | ||||
| 			fileLoader.load(new URLRequest(url)); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Разбор содержимого загруженного файла материалов. | ||||
| 		 */ | ||||
| 		private function parseMTLFile(e:Event = null):void { | ||||
| 			var lines:Array = fileLoader.data.split(REGEXP_SPLIT_FILE); | ||||
| 			_library = new Map(); | ||||
| 			diffuseMaps = new Map(); | ||||
| 			for each (var line:String in lines) { | ||||
| 				parseLine(line); | ||||
| 			} | ||||
| 			defineMaterial(); | ||||
| 			 | ||||
| 			if (diffuseMaps.isEmpty()) { | ||||
| 				// Текстур нет, загрузка окончена | ||||
| 				dispatchEvent(new Event(Event.COMPLETE)); | ||||
| 			} else { | ||||
| 				// Загрузка файлов текстур | ||||
| 				loadNextBitmap(); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Разбор строки файла. | ||||
| 		 *  | ||||
| 		 * @param line строка файла | ||||
| 		 */ | ||||
| 		private	function parseLine(line:String):void { | ||||
| 			line = line.replace(REGEXP_TRIM,"$1") | ||||
| 			if (line.length == 0 || line.charAt(0) == COMMENT_CHAR) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var parts:Array = line.split(REGEXP_SPLIT_LINE); | ||||
| 			switch (parts[0]) { | ||||
| 				case CMD_NEW_MATERIAL: | ||||
| 					defineMaterial(parts); | ||||
| 					break; | ||||
| 				case CMD_DIFFUSE_REFLECTIVITY: | ||||
| 					readDiffuseReflectivity(parts); | ||||
| 					break; | ||||
| 				case CMD_DISSOLVE: | ||||
| 					readAlpha(parts); | ||||
| 					break; | ||||
| 				case CMD_MAP_DIFFUSE: | ||||
| 					parseDiffuseMapLine(parts); | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Определение нового материала. | ||||
| 		 */		 | ||||
| 		private function defineMaterial(parts:Array = null):void { | ||||
| 			if (materialName != null) { | ||||
| 				_library[materialName] = currentMaterialInfo; | ||||
| 			} | ||||
| 			if (parts != null) { | ||||
| 				materialName = parts[1]; | ||||
| 				currentMaterialInfo = new MaterialInfo(); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Чтение коэффициентов диффузного отражения. Считываются только коэффициенты, заданные в формате r g b. Для текущей | ||||
| 		 * версии движка данные коэффициенты преобразуются в цвет материала. | ||||
| 		 */ | ||||
| 		private function readDiffuseReflectivity(parts:Array):void { | ||||
| 			var r:Number = Number(parts[1]); | ||||
| 			// Проверка, заданы ли коэффициенты в виде r g b | ||||
| 			if (!isNaN(r)) { | ||||
| 				var g:Number = Number(parts[2]); | ||||
| 				var b:Number = Number(parts[3]); | ||||
| 				currentMaterialInfo.color = ColorUtils.rgb(255 * r, 255 * g, 255 * b);  | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Чтение коэффициента непрозрачности. Считывается только коэффициент, заданный числом | ||||
| 		 * (не поддерживается параметр -halo). | ||||
| 		 */ | ||||
| 		private function readAlpha(parts:Array):void { | ||||
| 			var alpha:Number = Number(parts[1]); | ||||
| 			if (!isNaN(alpha)) { | ||||
| 				currentMaterialInfo.alpha = alpha; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Разбор строки, задающей текстурную карту для диффузного отражения. | ||||
| 		 */ | ||||
| 		private function parseDiffuseMapLine(parts:Array):void { | ||||
| 			var info:MTLTextureMapInfo = MTLTextureMapInfo.parse(parts); | ||||
| 			diffuseMaps[materialName] = info; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Загрузка файла следующей текстуры. | ||||
| 		 */ | ||||
| 		private function loadNextBitmap():void { | ||||
| 			// Установка имени текущего текстурного материала, для которого выполняется загрузка текстуры | ||||
| 			for (materialName in diffuseMaps) { | ||||
| 				break; | ||||
| 			} | ||||
| 			bitmapLoader.load(new URLRequest(baseUrl + diffuseMaps[materialName].fileName), loaderContext); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 *  | ||||
| 		 */ | ||||
| 		private function createStubBitmap():void { | ||||
| 			if (stubBitmapData == null) { | ||||
| 				var size:uint = 10; | ||||
| 				stubBitmapData = new BitmapData(size, size, false, 0); | ||||
| 				for (var i:uint = 0; i < size; i++) { | ||||
| 					for (var j:uint = 0; j < size; j+=2) { | ||||
| 						stubBitmapData.setPixel((i % 2) ? j : (j+1), i, 0xFF00FF); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Обработка результата загрузки файла текстуры. | ||||
| 		 */ | ||||
| 		private function onBitmapLoadComplete(e:Event):void { | ||||
| 			var bmd:BitmapData; | ||||
| 			 | ||||
| 			if (e is ErrorEvent) { | ||||
| 				if (stubBitmapData == null) { | ||||
| 					createStubBitmap(); | ||||
| 				} | ||||
| 				bmd = stubBitmapData; | ||||
| 			} else { | ||||
| 				bmd = Bitmap(bitmapLoader.content).bitmapData; | ||||
| 			} | ||||
| 			 | ||||
| 			var mtlInfo:MTLTextureMapInfo = diffuseMaps[materialName]; | ||||
| 			delete diffuseMaps[materialName]; | ||||
| 			var info:MaterialInfo = _library[materialName]; | ||||
| 			 | ||||
| 			info.bitmapData = bmd; | ||||
| 			info.repeat = mtlInfo.repeat; | ||||
| 			info.mapOffset = new Point(mtlInfo.offsetU, mtlInfo.offsetV); | ||||
| 			info.mapSize = new Point(mtlInfo.sizeU, mtlInfo.sizeV); | ||||
| 			info.textureFileName = mtlInfo.fileName; | ||||
| 	 | ||||
| 			if (diffuseMaps.isEmpty()) { | ||||
| 				dispatchEvent(new Event(Event.COMPLETE)); | ||||
| 			} else { | ||||
| 				loadNextBitmap(); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 *  | ||||
| 		 * @param e | ||||
| 		 */ | ||||
| 		private function onError(e:IOErrorEvent):void { | ||||
| 			dispatchEvent(e); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,509 +0,0 @@ | ||||
| package alternativa.engine3d.loaders { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Face; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.engine3d.core.Surface; | ||||
| 	import alternativa.engine3d.core.Vertex; | ||||
| 	import alternativa.engine3d.materials.FillMaterial; | ||||
| 	import alternativa.engine3d.materials.TextureMaterial; | ||||
| 	import alternativa.engine3d.materials.TextureMaterialPrecision; | ||||
| 	import alternativa.types.Map; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.types.Texture; | ||||
| 	 | ||||
| 	import flash.display.BlendMode; | ||||
| 	import flash.events.ErrorEvent; | ||||
| 	import flash.events.Event; | ||||
| 	import flash.events.EventDispatcher; | ||||
| 	import flash.events.IOErrorEvent; | ||||
| 	import flash.events.SecurityErrorEvent; | ||||
| 	import flash.geom.Point; | ||||
| 	import flash.net.URLLoader; | ||||
| 	import flash.net.URLRequest; | ||||
| 	import flash.system.LoaderContext; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Загрузчик моделей из файла в формате OBJ. Так как OBJ не поддерживает иерархию объектов, все загруженные | ||||
| 	 * модели помещаются в один контейнер <code>Object3D</code>. | ||||
| 	 * <p> | ||||
| 	 * Поддерживаюся следующие команды формата OBJ: | ||||
| 	 * <p> | ||||
| 	 * <table border="1" style="border-collapse: collapse"> | ||||
| 	 * <tr> | ||||
| 	 *   <th width="30%">Команда</th> | ||||
| 	 *   <th>Описание</th> | ||||
| 	 *   <th>Действие</th></tr> | ||||
| 	 * <tr> | ||||
| 	 *   <td>o object_name</td> | ||||
| 	 *   <td>Объявление нового объекта с именем object_name</td> | ||||
| 	 *   <td>Если для текущего объекта были определены грани, то команда создаёт новый текущий объект с указанным именем, | ||||
| 	 *       иначе у текущего объекта просто меняется имя на указанное.</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 *   <td>v x y z</td> | ||||
| 	 *   <td>Объявление вершины с координатами x y z</td> | ||||
| 	 *   <td>Вершина помещается в общий список вершин сцены для дальнейшего использования</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 *   <td>vt u [v]</td> | ||||
| 	 *   <td>Объявление текстурной вершины с координатами u v</td> | ||||
| 	 *   <td>Вершина помещается в общий список текстурных вершин сцены для дальнейшего использования</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 *   <td>f v0[/vt0] v1[/vt1] ... vN[/vtN]</td> | ||||
| 	 *   <td>Объявление грани, состоящей из указанных вершин и опционально имеющую заданные текстурные координаты для вершин.</td> | ||||
| 	 *   <td>Грань добавляется к текущему активному объекту. Если есть активный материал, то грань также добавляется в поверхность | ||||
| 	 *       текущего объекта, соответствующую текущему материалу.</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 *   <td>usemtl material_name</td> | ||||
| 	 *   <td>Установка текущего материала с именем material_name</td> | ||||
| 	 *   <td>С момента установки текущего материала все грани, создаваемые в текущем объекте будут помещаться в поверхность, | ||||
| 	 *       соотвествующую этому материалу и имеющую идентификатор, совпадающий с его именем.</td> | ||||
| 	 * </tr> | ||||
| 	 * <tr> | ||||
| 	 *   <td>mtllib file1 file2 ...</td> | ||||
| 	 *   <td>Объявление файлов, содержащих определения материалов</td> | ||||
| 	 *   <td>Выполняется загрузка файлов и формирование библиотеки материалов</td> | ||||
| 	 * </tr> | ||||
| 	 * </table> | ||||
| 	 *  | ||||
| 	 * <p> | ||||
| 	 * Пример использования: | ||||
| 	 * <pre> | ||||
| 	 * var loader:LoaderOBJ = new LoaderOBJ(); | ||||
| 	 * loader.addEventListener(Event.COMPLETE, onLoadingComplete); | ||||
| 	 * loader.load("foo.obj"); | ||||
| 	 *  | ||||
| 	 * function onLoadingComplete(e:Event):void { | ||||
| 	 *   scene.root.addChild(e.target.content); | ||||
| 	 * } | ||||
| 	 * </pre> | ||||
| 	 */ | ||||
| 	public class LoaderOBJ extends EventDispatcher { | ||||
| 		 | ||||
| 		private static const COMMENT_CHAR:String = "#"; | ||||
| 		 | ||||
| 		private static const CMD_OBJECT_NAME:String = "o"; | ||||
| 		private static const CMD_GROUP_NAME:String = "g"; | ||||
| 		private static const CMD_VERTEX:String = "v"; | ||||
| 		private static const CMD_TEXTURE_VERTEX:String = "vt"; | ||||
| 		private static const CMD_FACE:String = "f"; | ||||
| 		private static const CMD_MATERIAL_LIB:String = "mtllib"; | ||||
| 		private static const CMD_USE_MATERIAL:String = "usemtl"; | ||||
|  | ||||
| 		private static const REGEXP_TRIM:RegExp = /^\s*(.*?)\s*$/; | ||||
| 		private static const REGEXP_SPLIT_FILE:RegExp = /\r*\n/; | ||||
| 		private static const REGEXP_SPLIT_LINE:RegExp = /\s+/; | ||||
| 		 | ||||
| 		private var basePath:String; | ||||
| 		private var objLoader:URLLoader; | ||||
| 		private var mtlLoader:LoaderMTL; | ||||
| 		private var loaderContext:LoaderContext; | ||||
| 		private var loadMaterials:Boolean; | ||||
| 		// Объект, содержащий все определённые в obj файле объекты | ||||
| 		private var _content:Object3D; | ||||
| 		// Текущий конструируемый объект | ||||
| 		private var currentObject:Mesh; | ||||
| 		// Стартовый индекс вершины в глобальном массиве вершин для текущего объекта | ||||
| 		private var vIndexStart:int = 0; | ||||
| 		// Стартовый индекс текстурной вершины в глобальном массиве текстурных вершин для текущего объекта | ||||
| 		private var vtIndexStart:int = 0; | ||||
| 		// Глобальный массив вершин, определённых во входном файле | ||||
| 		private var globalVertices:Array; | ||||
| 		// Глобальный массив текстурных вершин, определённых во входном файле | ||||
| 		private var globalTextureVertices:Array; | ||||
| 		// Имя текущего активного материала. Если значение равно null, то активного материала нет.  | ||||
| 		private var currentMaterialName:String; | ||||
| 		// Массив граней текущего объекта, которым назначен текущий материал | ||||
| 		private var materialFaces:Array; | ||||
| 		// Массив имён файлов, содержащих определения материалов | ||||
| 		private var materialFileNames:Array; | ||||
| 		private var currentMaterialFileIndex:int; | ||||
| 		private var materialLibrary:Map; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Сглаживание текстур при увеличении масштаба. | ||||
| 		 *  | ||||
| 		 * @see alternativa.engine3d.materials.TextureMaterial | ||||
| 		 */		 | ||||
| 		public var smooth:Boolean = false; | ||||
| 		/** | ||||
| 		 * Режим наложения цвета для создаваемых текстурных материалов. | ||||
| 		 *  | ||||
| 		 * @see alternativa.engine3d.materials.TextureMaterial | ||||
| 		 */ | ||||
| 		public var blendMode:String = BlendMode.NORMAL; | ||||
| 		/** | ||||
| 		 * Точность перспективной коррекции для создаваемых текстурных материалов. | ||||
| 		 *  | ||||
| 		 * @see alternativa.engine3d.materials.TextureMaterial | ||||
| 		 */		 | ||||
| 		public var precision:Number = TextureMaterialPrecision.MEDIUM; | ||||
|  | ||||
| 		/** | ||||
| 		 * Устанавливаемый уровень мобильности загруженных объектов. | ||||
| 		 */		 | ||||
| 		public var mobility:int = 0; | ||||
|  | ||||
| 		/** | ||||
| 		 * При установленном значении <code>true</code> выполняется преобразование координат геометрических вершин посредством | ||||
| 		 * поворота на 90 градусов относительно оси X. Смысл флага в преобразовании системы координат, в которой вверх направлена | ||||
| 		 * ось <code>Y</code>, в систему координат, использующуюся в Alternativa3D (вверх направлена ось <code>Z</code>).  | ||||
| 		 */		 | ||||
| 		public var rotateModel:Boolean; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создаёт новый экземпляр загрузчика. | ||||
| 		 */ | ||||
| 		public function LoaderOBJ() { | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Контейнер, содержащий все загруженные из OBJ-файла модели. | ||||
| 		 */ | ||||
| 		public function get content():Object3D { | ||||
| 			return _content; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Прекращение текущей загрузки. | ||||
| 		 */ | ||||
| 		public function close():void { | ||||
| 			try { | ||||
| 				objLoader.close(); | ||||
| 			} catch (e:Error) { | ||||
| 			} | ||||
| 			mtlLoader.close(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Загрузка сцены из OBJ-файла по указанному адресу. По окончании загрузки посылается сообщение <code>Event.COMPLETE</code>, | ||||
| 		 * после чего контейнер с загруженными объектами становится доступным через свойство <code>content</code>. | ||||
| 		 * <p> | ||||
| 		 * При возникновении ошибок, связанных с вводом-выводом или с безопасностью, посылаются сообщения <code>IOErrorEvent.IO_ERROR</code> и  | ||||
| 		 * <code>SecurityErrorEvent.SECURITY_ERROR</code> соответственно. | ||||
| 		 * <p> | ||||
| 		 * @param url URL OBJ-файла  | ||||
| 		 * @param loadMaterials флаг загрузки материалов. Если указано значение <code>true</code>, будут обработаны все файлы | ||||
| 		 * 		материалов, указанные в исходном OBJ-файле. | ||||
| 		 * @param context LoaderContext для загрузки файлов текстур | ||||
| 		 *  | ||||
| 		 * @see #content | ||||
| 		 */ | ||||
| 		public function load(url:String, loadMaterials:Boolean = true, context:LoaderContext = null):void { | ||||
| 			_content = null; | ||||
| 			this.loadMaterials = loadMaterials; | ||||
| 			this.loaderContext = context; | ||||
| 			basePath = url.substring(0, url.lastIndexOf("/") + 1); | ||||
| 			if (objLoader == null) { | ||||
| 				objLoader = new URLLoader(); | ||||
| 				objLoader.addEventListener(Event.COMPLETE, onObjLoadComplete); | ||||
| 				objLoader.addEventListener(IOErrorEvent.IO_ERROR, onObjLoadError); | ||||
| 				objLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onObjLoadError); | ||||
| 			} else { | ||||
| 				close(); | ||||
| 			} | ||||
| 			objLoader.load(new URLRequest(url)); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Обработка окончания загрузки obj файла. | ||||
| 		 *  | ||||
| 		 * @param e | ||||
| 		 */ | ||||
| 		private function onObjLoadComplete(e:Event):void { | ||||
| 			parse(objLoader.data); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Обработка ошибки при загрузке. | ||||
| 		 *  | ||||
| 		 * @param e | ||||
| 		 */ | ||||
| 		private function onObjLoadError(e:ErrorEvent):void { | ||||
| 			dispatchEvent(e); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет разбор данных, полученных из obj файла. | ||||
| 		 *  | ||||
| 		 * @param s содержимое obj файла  | ||||
| 		 * @param materialLibrary библиотека материалов | ||||
| 		 * @return объект, содержащий все трёхмерные объекты, определённые в obj файле | ||||
| 		 */ | ||||
| 		private function parse(data:String):void { | ||||
| 			_content = new Object3D(); | ||||
| 			currentObject = new Mesh(); | ||||
| 			currentObject.mobility = mobility; | ||||
| 			_content.addChild(currentObject); | ||||
| 			 | ||||
| 			globalVertices = new Array(); | ||||
| 			globalTextureVertices = new Array(); | ||||
| 			materialFileNames = new Array(); | ||||
| 			 | ||||
| 			var lines:Array = data.split(REGEXP_SPLIT_FILE); | ||||
| 			for each (var line:String in lines) { | ||||
| 				parseLine(line); | ||||
| 			} | ||||
| 			moveFacesToSurface(); | ||||
| 			// Вся геометрия загружена и сформирована. Выполняется загрузка информации о материалах. | ||||
| 			if (loadMaterials && materialFileNames.length > 0) { | ||||
| 				loadMaterialsLibrary(); | ||||
| 			} else { | ||||
| 				dispatchEvent(new Event(Event.COMPLETE)); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 *  | ||||
| 		 */ | ||||
| 		private function parseLine(line:String):void { | ||||
| 			line = line.replace(REGEXP_TRIM,"$1"); | ||||
| 			if (line.length == 0 || line.charAt(0) == COMMENT_CHAR) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var parts:Array = line.split(REGEXP_SPLIT_LINE); | ||||
| 			switch (parts[0]) { | ||||
| 				// Объявление нового объекта | ||||
| 				case CMD_OBJECT_NAME: | ||||
| 					defineObject(parts[1]); | ||||
| 					break; | ||||
| 				// Объявление вершины | ||||
| 				case CMD_VERTEX: | ||||
| 					globalVertices.push(new Point3D(Number(parts[1]), Number(parts[2]), Number(parts[3]))); | ||||
| 					break; | ||||
| 				// Объявление текстурной вершины | ||||
| 				case CMD_TEXTURE_VERTEX: | ||||
| 					globalTextureVertices.push(new Point3D(Number(parts[1]), Number(parts[2]), Number(parts[3]))); | ||||
| 					break; | ||||
| 				// Объявление грани | ||||
| 				case CMD_FACE: | ||||
| 					createFace(parts); | ||||
| 					break; | ||||
| 				case CMD_MATERIAL_LIB: | ||||
| 					storeMaterialFileNames(parts); | ||||
| 					break; | ||||
| 				case CMD_USE_MATERIAL: | ||||
| 					setNewMaterial(parts); | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Объявление нового объекта. | ||||
| 		 *  | ||||
| 		 * @param objectName имя объекта | ||||
| 		 */ | ||||
| 		private function defineObject(objectName:String):void { | ||||
| 			if (currentObject.faces.length == 0) { | ||||
| 				// Если у текущего объекта нет граней, то он остаётся текущим, но меняется имя | ||||
| 				currentObject.name = objectName; | ||||
| 			} else { | ||||
| 				// Если у текущего объекта есть грани, то обявление нового имени создаёт новый объект | ||||
| 				moveFacesToSurface(); | ||||
| 				currentObject = new Mesh(objectName); | ||||
| 				currentObject.mobility = mobility; | ||||
| 				_content.addChild(currentObject); | ||||
| 			} | ||||
| 			vIndexStart = globalVertices.length; | ||||
| 			vtIndexStart = globalTextureVertices.length; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание грани в текущем объекте. | ||||
| 		 *  | ||||
| 		 * @param parts массив, содержащий индексы вершин грани, начиная с элемента с индексом 1  | ||||
| 		 */		 | ||||
| 		private function createFace(parts:Array):void { | ||||
| 			// Стартовый индекс вершины в объекте для добавляемой грани | ||||
| 			var startVertexIndex:int = currentObject.vertices.length; | ||||
| 			// Создание вершин в объекте | ||||
| 			var faceVertexCount:int = parts.length - 1; | ||||
| 			var vtIndices:Array = new Array(3); | ||||
| 			// Массив идентификаторов вершин грани | ||||
| 			var faceVertices:Array = new Array(faceVertexCount); | ||||
| 			for (var i:int = 0; i < faceVertexCount; i++) { | ||||
| 				var indices:Array = parts[i + 1].split("/"); | ||||
| 				// Создание вершины | ||||
| 				var vIdx:int = int(indices[0]); | ||||
| 				// Если индекс положительный, то его значение уменьшается на единицу, т.к. в obj формате индексация начинается с 1. | ||||
| 				// Если индекс отрицательный, то выполняется смещение на его значение назад от стартового глобального индекса вершин для текущего объекта. | ||||
| 				var actualIndex:int = vIdx > 0 ? vIdx - 1 : vIndexStart + vIdx; | ||||
| 				 | ||||
| 				var vertex:Vertex = currentObject.vertices[actualIndex]; | ||||
| 				// Если вершины нет в объекте, она добавляется | ||||
| 				if (vertex == null) { | ||||
| 					var p:Point3D = globalVertices[actualIndex]; | ||||
| 					if (rotateModel) { | ||||
| 						// В формате obj направление "вверх" совпадает с осью Y, поэтому выполняется поворот координат на 90 градусов по оси X  | ||||
| 						vertex = currentObject.createVertex(p.x, -p.z, p.y, actualIndex); | ||||
| 					} else { | ||||
| 						vertex = currentObject.createVertex(p.x, p.y, p.z, actualIndex); | ||||
| 					} | ||||
| 				} | ||||
| 				faceVertices[i] = vertex; | ||||
| 				 | ||||
| 				// Запись индекса текстурной вершины | ||||
| 				if (i < 3) { | ||||
| 					vtIndices[i] = int(indices[1]); | ||||
| 				} | ||||
| 			} | ||||
| 			// Создание грани | ||||
| 			var face:Face = currentObject.createFace(faceVertices, currentObject.faces.length); | ||||
| 			// Установка uv координат | ||||
| 			if (vtIndices[0] != 0) { | ||||
| 				p = globalTextureVertices[vtIndices[0] - 1]; | ||||
| 				face.aUV = new Point(p.x, p.y); | ||||
| 				p = globalTextureVertices[vtIndices[1] - 1]; | ||||
| 				face.bUV = new Point(p.x, p.y); | ||||
| 				p = globalTextureVertices[vtIndices[2] - 1]; | ||||
| 				face.cUV = new Point(p.x, p.y); | ||||
| 			} | ||||
| 			// Если есть активный материал, то грань заносится в массив для последующего формирования поверхности в объекте | ||||
| 			if (currentMaterialName != null) { | ||||
| 				materialFaces.push(face); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Загрузка библиотек материалов. | ||||
| 		 *  | ||||
| 		 * @param parts массив, содержащий имена файлов материалов, начиная с элемента с индексом 1 | ||||
| 		 */ | ||||
| 		private function storeMaterialFileNames(parts:Array):void { | ||||
| 			for (var i:int = 1; i < parts.length; i++) { | ||||
| 				materialFileNames.push(parts[i]); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Установка нового текущего материала. | ||||
| 		 *  | ||||
| 		 * @param parts массив, во втором элементе которого содержится имя материала | ||||
| 		 */ | ||||
| 		private function setNewMaterial(parts:Array):void { | ||||
| 			// Все сохранённые грани добавляются в соответствующую поверхность текущего объекта | ||||
| 			moveFacesToSurface(); | ||||
| 			// Установка нового текущего материала | ||||
| 			currentMaterialName = parts[1]; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Добавление всех граней с текущим материалом в поверхность с идентификатором, совпадающим с именем материала.  | ||||
| 		 */ | ||||
| 		private function moveFacesToSurface():void { | ||||
| 			if (currentMaterialName != null && materialFaces.length > 0) { | ||||
| 				if (currentObject.hasSurface(currentMaterialName)) { | ||||
| 					// При наличии поверхности с таким идентификатором, грани добавляются в неё | ||||
| 					var surface:Surface = currentObject.getSurfaceById(currentMaterialName); | ||||
| 					for each (var face:* in materialFaces) { | ||||
| 						surface.addFace(face); | ||||
| 					} | ||||
| 				} else { | ||||
| 					// При отсутствии поверхности с таким идентификатором, создатся новая поверхность | ||||
| 					currentObject.createSurface(materialFaces, currentMaterialName); | ||||
| 				} | ||||
| 			} | ||||
| 			materialFaces = []; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Загрузка материалов. | ||||
| 		 */ | ||||
| 		private function loadMaterialsLibrary():void { | ||||
| 			if (mtlLoader == null) { | ||||
| 				mtlLoader = new LoaderMTL(); | ||||
| 				mtlLoader.addEventListener(Event.COMPLETE, onMaterialFileLoadComplete); | ||||
| 				mtlLoader.addEventListener(IOErrorEvent.IO_ERROR, onObjLoadError); | ||||
| 				mtlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onObjLoadError); | ||||
| 			} | ||||
| 			materialLibrary = new Map(); | ||||
| 			 | ||||
| 			currentMaterialFileIndex = -1; | ||||
| 			loadNextMaterialFile(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Обработка успешной загрузки библиотеки материалов. | ||||
| 		 */ | ||||
| 		private function onMaterialFileLoadComplete(e:Event):void { | ||||
| 			materialLibrary.concat(mtlLoader.library); | ||||
| 			// Загрузка следующего файла материалов | ||||
| 			loadNextMaterialFile(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 *  | ||||
| 		 */ | ||||
| 		private function loadNextMaterialFile():void { | ||||
| 			currentMaterialFileIndex++; | ||||
| 			if (currentMaterialFileIndex == materialFileNames.length) { | ||||
| 				setMaterials(); | ||||
| 				dispatchEvent(new Event(Event.COMPLETE)); | ||||
| 			} else { | ||||
| 				mtlLoader.load(basePath + materialFileNames[currentMaterialFileIndex], loaderContext); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Установка материалов. | ||||
| 		 */ | ||||
| 		private function setMaterials():void { | ||||
| 			if (materialLibrary != null) { | ||||
| 				for (var objectKey:* in _content.children) { | ||||
| 					var object:Mesh = objectKey; | ||||
| 					for (var surfaceKey:* in object.surfaces) { | ||||
| 						var surface:Surface = object.surfaces[surfaceKey]; | ||||
| 						// Поверхности имеют идентификаторы, соответствующие именам материалов | ||||
| 						var materialInfo:MaterialInfo = materialLibrary[surfaceKey]; | ||||
| 						if (materialInfo != null) { | ||||
| 							if (materialInfo.bitmapData == null) { | ||||
| 								surface.material = new FillMaterial(materialInfo.color, materialInfo.alpha, blendMode); | ||||
| 							} else { | ||||
| 								surface.material = new TextureMaterial(new Texture(materialInfo.bitmapData, materialInfo.textureFileName), materialInfo.alpha, materialInfo.repeat, (materialInfo.bitmapData != LoaderMTL.stubBitmapData) ? smooth : false, blendMode, -1, 0, precision); | ||||
| 								transformUVs(surface, materialInfo.mapOffset, materialInfo.mapSize); | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет преобразование UV-координат текстурированных граней. В связи с тем, что в формате MRL предусмотрено | ||||
| 		 * масштабирование и смещение текстурной карты в UV-пространстве, а в движке такой фунциональности нет, необходимо | ||||
| 		 * эмулировать преобразования текстуры преобразованием UV-координат граней. Преобразования выполняются исходя из предположения, | ||||
| 		 * что текстурное пространство сначала масштабируется относительно центра, а затем сдвигается на указанную величину | ||||
| 		 * смещения. | ||||
| 		 *  | ||||
| 		 * @param surface поверхность, грани которой обрабатываюся | ||||
| 		 * @param mapOffset смещение текстурной карты. Значение mapOffset.x указывает смещение по U, значение mapOffset.y | ||||
| 		 * 		указывает смещение по V. | ||||
| 		 * @param mapSize коэффициенты масштабирования текстурной карты. Значение mapSize.x указывает коэффициент масштабирования | ||||
| 		 * 		по оси U, значение mapSize.y указывает коэффициент масштабирования по оси V.  | ||||
| 		 */ | ||||
| 		private function transformUVs(surface:Surface, mapOffset:Point, mapSize:Point):void { | ||||
| 			for (var key:* in surface.faces) { | ||||
| 				var face:Face = key; | ||||
| 				var uv:Point = face.aUV; | ||||
| 				if (uv != null) { | ||||
| 					uv.x = 0.5 + (uv.x - 0.5 - mapOffset.x) * mapSize.x; | ||||
| 					uv.y = 0.5 + (uv.y - 0.5 - mapOffset.y) * mapSize.y; | ||||
| 					face.aUV = uv; | ||||
| 					uv = face.bUV; | ||||
| 					uv.x = 0.5 + (uv.x - 0.5 - mapOffset.x) * mapSize.x; | ||||
| 					uv.y = 0.5 + (uv.y - 0.5 - mapOffset.y) * mapSize.y; | ||||
| 					face.bUV = uv; | ||||
| 					uv = face.cUV; | ||||
| 					uv.x = 0.5 + (uv.x - 0.5 - mapOffset.x) * mapSize.x; | ||||
| 					uv.y = 0.5 + (uv.y - 0.5 - mapOffset.y) * mapSize.y; | ||||
| 					face.cUV = uv; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,120 +0,0 @@ | ||||
| package alternativa.engine3d.loaders { | ||||
| 	/** | ||||
| 	 * @private | ||||
| 	 * Класс содержит информацию о текстуре в формате MTL material format (Lightwave, OBJ) и функционал для разбора  | ||||
| 	 * описания текстуры. | ||||
| 	 * Описание формата можно посмотреть по адресу: http://local.wasp.uwa.edu.au/~pbourke/dataformats/mtl/ | ||||
| 	 */ | ||||
| 	internal class MTLTextureMapInfo { | ||||
| 		 | ||||
| 		// Ассоциация параметров команды объявления текстуры и методов для их чтения | ||||
| 		private static const optionReaders:Object = { | ||||
| 			"-clamp": clampReader, | ||||
| 			"-o": offsetReader, | ||||
| 			"-s": sizeReader, | ||||
|  | ||||
| 			"-blendu": stubReader, | ||||
| 			"-blendv": stubReader, | ||||
| 			"-bm": stubReader, | ||||
| 			"-boost": stubReader, | ||||
| 			"-cc": stubReader, | ||||
| 			"-imfchan": stubReader, | ||||
| 			"-mm": stubReader, | ||||
| 			"-t": stubReader, | ||||
| 			"-texres": stubReader | ||||
| 		}; | ||||
| 		 | ||||
| 		// Смещение в текстурном пространстве | ||||
| 		public var offsetU:Number = 0; | ||||
| 		public var offsetV:Number = 0; | ||||
| 		public var offsetW:Number = 0; | ||||
| 		 | ||||
| 		// Масштабирование текстурного пространства | ||||
| 		public var sizeU:Number = 1; | ||||
| 		public var sizeV:Number = 1; | ||||
| 		public var sizeW:Number = 1; | ||||
| 		 | ||||
| 		// Флаг повторения текстуры | ||||
| 		public var repeat:Boolean = true; | ||||
| 		// Имя файла текстуры  | ||||
| 		public var fileName:String; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод выполняет разбор данных о текстуре. | ||||
| 		 *  | ||||
| 		 * @param parts Данные о текстуре. Массив должен содержать части разделённой по пробелам входной строки MTL-файла. | ||||
| 		 * @return объект, содержащий данные о текстуре | ||||
| 		 */ | ||||
| 		public static function parse(parts:Array):MTLTextureMapInfo { | ||||
| 			var info:MTLTextureMapInfo = new MTLTextureMapInfo(); | ||||
| 			// Начальное значение индекса единица, т.к. первый элемент массива содержит тип текстуры | ||||
| 			var index:int = 1; | ||||
| 			var reader:Function; | ||||
| 			// Чтение параметров текстуры | ||||
| 			while ((reader = optionReaders[parts[index]]) != null) { | ||||
| 				index = reader(index, parts, info); | ||||
| 			} | ||||
| 			// Если не было ошибок, последний элемент массива должен содержать имя файла текстуры | ||||
| 			info.fileName = parts[index]; | ||||
| 			return info; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Читатель-заглушка. Пропускает все неподдерживаемые параметры. | ||||
| 		 */ | ||||
| 		private static function stubReader(index:int, parts:Array, info:MTLTextureMapInfo):int	{ | ||||
| 			index++; | ||||
| 			var maxIndex:int = parts.length - 1; | ||||
| 			while ((MTLTextureMapInfo.optionReaders[parts[index]] == null) && (index < maxIndex)) { | ||||
| 				index++; | ||||
| 			} | ||||
| 			return index; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Метод чтения параметров масштабирования текстурного пространства. | ||||
| 		 */ | ||||
| 		private static function sizeReader(index:int, parts:Array, info:MTLTextureMapInfo):int	{ | ||||
| 			info.sizeU = Number(parts[index + 1]); | ||||
| 			index += 2; | ||||
| 			var value:Number = Number(parts[index]); | ||||
| 			if (!isNaN(value)) { | ||||
| 				info.sizeV = value; | ||||
| 				index++; | ||||
| 				value = Number(parts[index]); | ||||
| 				if (!isNaN(value)) { | ||||
| 					info.sizeW = value; | ||||
| 					index++; | ||||
| 				} | ||||
| 			} | ||||
| 			return index; | ||||
| 		} | ||||
| 	 | ||||
| 		/** | ||||
| 		 * Метод чтения параметров смещения текстуры. | ||||
| 		 */ | ||||
| 		private static function offsetReader(index:int, parts:Array, info:MTLTextureMapInfo):int	{ | ||||
| 			info.offsetU = Number(parts[index + 1]); | ||||
| 			index += 2; | ||||
| 			var value:Number = Number(parts[index]); | ||||
| 			if (!isNaN(value)) { | ||||
| 				info.offsetV = value; | ||||
| 				index++; | ||||
| 				value = Number(parts[index]); | ||||
| 				if (!isNaN(value)) { | ||||
| 					info.offsetW = value; | ||||
| 					index++; | ||||
| 				} | ||||
| 			} | ||||
| 			return index; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод чтения параметра повторения текстуры. | ||||
| 		 */ | ||||
| 		private static function clampReader(index:int, parts:Array, info:MTLTextureMapInfo):int		{ | ||||
| 			info.repeat = parts[index + 1] == "off"; | ||||
| 			return index + 2; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,20 +0,0 @@ | ||||
| package alternativa.engine3d.loaders { | ||||
| 	import flash.display.BitmapData; | ||||
| 	import flash.geom.Point; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * @private | ||||
| 	 * Класс содержит обобщённую информацию о материале. | ||||
| 	 */ | ||||
| 	internal class MaterialInfo { | ||||
| 		public var color:uint; | ||||
| 		public var alpha:Number; | ||||
|  | ||||
| 		public var textureFileName:String; | ||||
| 		public var bitmapData:BitmapData; | ||||
| 		public var repeat:Boolean; | ||||
|  | ||||
| 		public var mapOffset:Point; | ||||
| 		public var mapSize:Point; | ||||
| 	} | ||||
| } | ||||
| @@ -1,53 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 101 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/materials | ||||
| END | ||||
| DevMaterial.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 116 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/materials/DevMaterial.as | ||||
| END | ||||
| Material.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 113 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/materials/Material.as | ||||
| END | ||||
| TextureMaterial.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 120 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/materials/TextureMaterial.as | ||||
| END | ||||
| FillMaterial.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 117 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/materials/FillMaterial.as | ||||
| END | ||||
| WireMaterial.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 117 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/materials/WireMaterial.as | ||||
| END | ||||
| DrawPoint.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 114 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/materials/DrawPoint.as | ||||
| END | ||||
| SurfaceMaterial.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 120 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/materials/SurfaceMaterial.as | ||||
| END | ||||
| TextureMaterialPrecision.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 129 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/materials/TextureMaterialPrecision.as | ||||
| END | ||||
| @@ -1,124 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/materials | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| DevMaterial.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 2c90564cfd20a00c9038a43690374794 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Material.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 227c5a7229790ccabc0237cf44a202fc | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| TextureMaterial.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 9b48ec3656ca2db5ff3e5cb5744363fb | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| FillMaterial.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| e33f75e92d1bd21a114dd5a4293dc0f3 | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| WireMaterial.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 49716fd06e5cb181e0886a7d63f1cd0f | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| DrawPoint.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| f110bd43b42437479c009f63140fd20a | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| SurfaceMaterial.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| def3c3e365f98998e0c5ba0f10c3c5cf | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| TextureMaterialPrecision.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| c0cfbffad39af9bef6a7441a8bca7ddb | ||||
| 2008-09-08T06:50:26.103978Z | ||||
| 468 | ||||
| mike | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
| @@ -1,198 +0,0 @@ | ||||
| package alternativa.engine3d.materials { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.engine3d.display.Skin; | ||||
| 	 | ||||
| 	import flash.display.BlendMode; | ||||
| 	import flash.display.Graphics; | ||||
| 	import alternativa.utils.ColorUtils; | ||||
| 	import alternativa.engine3d.core.PolyPrimitive; | ||||
| 	import alternativa.engine3d.core.BSPNode; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * @private | ||||
| 	 * Материал, заполняющий грань сплошной заливкой цветом в соответствии с уровнем мобильности. Помимо заливки материал может рисовать границу | ||||
| 	 * полигона линией заданной толщины и цвета. | ||||
| 	 */	 | ||||
| 	public class DevMaterial extends SurfaceMaterial { | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Цвет | ||||
| 		 */ | ||||
| 		alternativa3d var _color:uint; | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Толщина линий обводки  | ||||
| 		 */ | ||||
| 		alternativa3d var _wireThickness:Number; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Цвет линий обводки  | ||||
| 		 */ | ||||
| 		alternativa3d var _wireColor:uint; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param color цвет заливки | ||||
| 		 * @param alpha прозрачность | ||||
| 		 * @param blendMode режим наложения цвета | ||||
| 		 * @param wireThickness толщина линии обводки | ||||
| 		 * @param wireColor цвет линии обводки | ||||
| 		 */		 | ||||
| 		public function DevMaterial(color:uint = 0xFFFFFF, alpha:Number = 1, blendMode:String = BlendMode.NORMAL, wireThickness:Number = -1, wireColor:uint = 0) { | ||||
| 			super(alpha, blendMode); | ||||
| 			_color = color; | ||||
| 			_wireThickness = wireThickness; | ||||
| 			_wireColor = wireColor; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 *  | ||||
| 		 * @param camera | ||||
| 		 * @param skin | ||||
| 		 * @param length | ||||
| 		 * @param points | ||||
| 		 */		 | ||||
| 		override alternativa3d function draw(camera:Camera3D, skin:Skin, length:uint, points:Array):void { | ||||
| 			skin.alpha = _alpha; | ||||
| 			skin.blendMode = _blendMode; | ||||
|  | ||||
| 			var i:uint; | ||||
| 			var point:DrawPoint; | ||||
| 			var gfx:Graphics = skin.gfx; | ||||
| 			 | ||||
| 			/* | ||||
| 			//Мобильность | ||||
| 			var param:int = skin.primitive.mobility*10; | ||||
| 			*/ | ||||
| 			 | ||||
| 			/* | ||||
| 			// Уровень распиленности | ||||
| 			var param:int = 0; | ||||
| 			var prm:PolyPrimitive = skin.primitive; | ||||
| 			while (prm != null) { | ||||
| 				prm = prm.parent; | ||||
| 				param++; | ||||
| 			} | ||||
| 			param *= 10; | ||||
| 			*/ | ||||
|  | ||||
| 			// Уровень в BSP-дереве | ||||
| 			var param:int = 0; | ||||
| 			var node:BSPNode = skin.primitive.node; | ||||
| 			while (node != null) { | ||||
| 				node = node.parent; | ||||
| 				param++; | ||||
| 			} | ||||
| 			param *= 5; | ||||
| 			 | ||||
| 			var c:uint = ColorUtils.rgb(param, param, param); | ||||
| 			 | ||||
| 			if (camera._orthographic) { | ||||
| 				gfx.beginFill(c); | ||||
| 				if (_wireThickness >= 0) { | ||||
| 					gfx.lineStyle(_wireThickness, _wireColor); | ||||
| 				} | ||||
| 				point = points[0]; | ||||
| 				gfx.moveTo(point.x, point.y); | ||||
| 				for (i = 1; i < length; i++) { | ||||
| 					point = points[i]; | ||||
| 					gfx.lineTo(point.x, point.y); | ||||
| 				} | ||||
| 				if (_wireThickness >= 0) { | ||||
| 					point = points[0]; | ||||
| 					gfx.lineTo(point.x, point.y); | ||||
| 				} | ||||
| 			} else { | ||||
| 				gfx.beginFill(c); | ||||
| 				if (_wireThickness >= 0) { | ||||
| 					gfx.lineStyle(_wireThickness, _wireColor); | ||||
| 				} | ||||
| 				point = points[0]; | ||||
| 				var perspective:Number = camera.focalLength/point.z; | ||||
| 				gfx.moveTo(point.x*perspective, point.y*perspective); | ||||
| 				for (i = 1; i < length; i++) { | ||||
| 					point = points[i]; | ||||
| 					perspective = camera.focalLength/point.z; | ||||
| 					gfx.lineTo(point.x*perspective, point.y*perspective); | ||||
| 				} | ||||
| 				if (_wireThickness >= 0) { | ||||
| 					point = points[0]; | ||||
| 					perspective = camera.focalLength/point.z; | ||||
| 					gfx.lineTo(point.x*perspective, point.y*perspective); | ||||
| 				} | ||||
| 			}			 | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Цвет заливки. | ||||
| 		 */ | ||||
| 		public function get color():uint { | ||||
| 			return _color; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set color(value:uint):void { | ||||
| 			if (_color != value) { | ||||
| 				_color = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Толщина линии обводки. Если значение отрицательное, то отрисовка линии не выполняется. | ||||
| 		 */ | ||||
| 		public function get wireThickness():Number { | ||||
| 			return _wireThickness; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set wireThickness(value:Number):void { | ||||
| 			if (_wireThickness != value) { | ||||
| 				_wireThickness = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Цвет линии обводки. | ||||
| 		 */ | ||||
| 		public function get wireColor():uint { | ||||
| 			return _wireColor; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set wireColor(value:uint):void { | ||||
| 			if (_wireColor != value) { | ||||
| 				_wireColor = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */		 | ||||
| 		override public function clone():Material { | ||||
| 			var res:DevMaterial = new DevMaterial(_color, _alpha, _blendMode, _wireThickness, _wireColor);  | ||||
| 			return res; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,45 +0,0 @@ | ||||
| package alternativa.engine3d.materials { | ||||
| 	/** | ||||
| 	 * @private | ||||
| 	 * Точка, подготовленная к отрисовке. | ||||
| 	 */ | ||||
| 	public final class DrawPoint { | ||||
| 		/** | ||||
| 		 * Координата X в системе координат камеры. | ||||
| 		 */ | ||||
| 		public var x:Number; | ||||
| 		/** | ||||
| 		 * Координата Y в системе координат камеры. | ||||
| 		 */ | ||||
| 		public var y:Number; | ||||
| 		/** | ||||
| 		 * Координата Z в системе координат камеры. | ||||
| 		 */ | ||||
| 		public var z:Number; | ||||
| 		/** | ||||
| 		 * Координата U в текстурном пространстве. | ||||
| 		 */ | ||||
| 		public var u:Number; | ||||
| 		/** | ||||
| 		 * Координата V в текстурном пространстве. | ||||
| 		 */ | ||||
| 		public var v:Number; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создаёт новый экземпляр класса. | ||||
| 		 *  | ||||
| 		 * @param x координата X в системе координат камеры | ||||
| 		 * @param y координата Y в системе координат камеры | ||||
| 		 * @param z координата Z в системе координат камеры | ||||
| 		 * @param u координата U в текстурном пространстве | ||||
| 		 * @param v координата V в текстурном пространстве | ||||
| 		 */ | ||||
| 		public function DrawPoint(x:Number, y:Number, z:Number, u:Number = 0, v:Number = 0) { | ||||
| 			this.x = x; | ||||
| 			this.y = y; | ||||
| 			this.z = z; | ||||
| 			this.u = u; | ||||
| 			this.v = v; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,163 +0,0 @@ | ||||
| package alternativa.engine3d.materials { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.engine3d.display.Skin; | ||||
| 	 | ||||
| 	import flash.display.BlendMode; | ||||
| 	import flash.display.Graphics; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Материал, заполняющий грань сплошной одноцветной заливкой. Помимо заливки цветом, материал может рисовать границу | ||||
| 	 * грани линией заданной толщины и цвета. | ||||
| 	 */	 | ||||
| 	public class FillMaterial extends SurfaceMaterial { | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Цвет | ||||
| 		 */ | ||||
| 		alternativa3d var _color:uint; | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Толщина линий обводки  | ||||
| 		 */ | ||||
| 		alternativa3d var _wireThickness:Number; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Цвет линий обводки  | ||||
| 		 */ | ||||
| 		alternativa3d var _wireColor:uint; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param color цвет заливки | ||||
| 		 * @param alpha коэффициент непрозрачности материала. Значение 1 соответствует полной непрозрачности, значение 0 соответствует полной прозрачности. | ||||
| 		 * @param blendMode режим наложения цвета | ||||
| 		 * @param wireThickness толщина линии обводки | ||||
| 		 * @param wireColor цвет линии обводки | ||||
| 		 */		 | ||||
| 		public function FillMaterial(color:uint, alpha:Number = 1, blendMode:String = BlendMode.NORMAL, wireThickness:Number = -1, wireColor:uint = 0) { | ||||
| 			super(alpha, blendMode); | ||||
| 			_color = color; | ||||
| 			_wireThickness = wireThickness; | ||||
| 			_wireColor = wireColor; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @inheritDoc | ||||
| 		 */		 | ||||
| 		override alternativa3d function draw(camera:Camera3D, skin:Skin, length:uint, points:Array):void { | ||||
| 			skin.alpha = _alpha; | ||||
| 			skin.blendMode = _blendMode; | ||||
|  | ||||
| 			var i:uint; | ||||
| 			var point:DrawPoint; | ||||
| 			var gfx:Graphics = skin.gfx; | ||||
| 			 | ||||
| 			if (camera._orthographic) { | ||||
| 				gfx.beginFill(_color); | ||||
| 				if (_wireThickness >= 0) { | ||||
| 					gfx.lineStyle(_wireThickness, _wireColor); | ||||
| 				} | ||||
| 				point = points[0]; | ||||
| 				gfx.moveTo(point.x, point.y); | ||||
| 				for (i = 1; i < length; i++) { | ||||
| 					point = points[i]; | ||||
| 					gfx.lineTo(point.x, point.y); | ||||
| 				} | ||||
| 				if (_wireThickness >= 0) { | ||||
| 					point = points[0]; | ||||
| 					gfx.lineTo(point.x, point.y); | ||||
| 				} | ||||
| 			} else { | ||||
| 				gfx.beginFill(_color); | ||||
| 				if (_wireThickness >= 0) { | ||||
| 					gfx.lineStyle(_wireThickness, _wireColor); | ||||
| 				} | ||||
| 				point = points[0]; | ||||
| 				var perspective:Number = camera.focalLength/point.z; | ||||
| 				gfx.moveTo(point.x*perspective, point.y*perspective); | ||||
| 				for (i = 1; i < length; i++) { | ||||
| 					point = points[i]; | ||||
| 					perspective = camera.focalLength/point.z; | ||||
| 					gfx.lineTo(point.x*perspective, point.y*perspective); | ||||
| 				} | ||||
| 				if (_wireThickness >= 0) { | ||||
| 					point = points[0]; | ||||
| 					perspective = camera.focalLength/point.z; | ||||
| 					gfx.lineTo(point.x*perspective, point.y*perspective); | ||||
| 				} | ||||
| 			}			 | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Цвет заливки. | ||||
| 		 */ | ||||
| 		public function get color():uint { | ||||
| 			return _color; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set color(value:uint):void { | ||||
| 			if (_color != value) { | ||||
| 				_color = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Толщина линии обводки. Если значение отрицательное, то обводка не рисуется. | ||||
| 		 */ | ||||
| 		public function get wireThickness():Number { | ||||
| 			return _wireThickness; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set wireThickness(value:Number):void { | ||||
| 			if (_wireThickness != value) { | ||||
| 				_wireThickness = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Цвет линии обводки. | ||||
| 		 */ | ||||
| 		public function get wireColor():uint { | ||||
| 			return _wireColor; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set wireColor(value:uint):void { | ||||
| 			if (_wireColor != value) { | ||||
| 				_wireColor = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */		 | ||||
| 		override public function clone():Material { | ||||
| 			var res:FillMaterial = new FillMaterial(_color, _alpha, _blendMode, _wireThickness, _wireColor);  | ||||
| 			return res; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,20 +0,0 @@ | ||||
| package alternativa.engine3d.materials { | ||||
| 	import alternativa.engine3d.*; | ||||
|  | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Базовый класс для материалов. | ||||
| 	 */	 | ||||
| 	public class Material { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание клона материала. | ||||
| 		 *  | ||||
| 		 * @return клон материала | ||||
| 		 */ | ||||
| 		public function clone():Material { | ||||
| 			return new Material(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,197 +0,0 @@ | ||||
| package alternativa.engine3d.materials { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.PolyPrimitive; | ||||
| 	import alternativa.engine3d.core.Scene3D; | ||||
| 	import alternativa.engine3d.core.Surface; | ||||
| 	import alternativa.engine3d.display.Skin; | ||||
| 	 | ||||
| 	import flash.display.BlendMode; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
|  | ||||
| 	/** | ||||
| 	 * Базовый класс для материалов полигональных поверхностей. | ||||
| 	 */	 | ||||
| 	public class SurfaceMaterial extends Material { | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Поверхность | ||||
| 		 */ | ||||
| 		alternativa3d var _surface:Surface; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Альфа | ||||
| 		 */ | ||||
| 		alternativa3d var _alpha:Number; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Режим наложения цвета | ||||
| 		 */ | ||||
| 		alternativa3d var _blendMode:String = BlendMode.NORMAL; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Материал использует информация об UV-координатах | ||||
| 		 */ | ||||
| 		alternativa3d var useUV:Boolean = false; | ||||
|  | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param alpha коэффициент непрозрачности материала. Значение 1 соответствует полной непрозрачности, значение 0 соответствует полной прозрачности. | ||||
| 		 * @param blendMode режим наложения цвета | ||||
| 		 */ | ||||
| 		public function SurfaceMaterial(alpha:Number = 1, blendMode:String = BlendMode.NORMAL) { | ||||
| 			_alpha = alpha; | ||||
| 			_blendMode = blendMode; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Поверхность, которой назначен материал. | ||||
| 		 */ | ||||
| 		public function get surface():Surface { | ||||
| 			return _surface; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Добавление на сцену | ||||
| 		 *  | ||||
| 		 * @param scene | ||||
| 		 */		 | ||||
| 		alternativa3d function addToScene(scene:Scene3D):void {} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление из сцены | ||||
| 		 *  | ||||
| 		 * @param scene | ||||
| 		 */ | ||||
| 		alternativa3d function removeFromScene(scene:Scene3D):void {} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Добавление к мешу | ||||
| 		 *  | ||||
| 		 * @param mesh | ||||
| 		 */ | ||||
| 		alternativa3d function addToMesh(mesh:Mesh):void {} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление из меша | ||||
| 		 *  | ||||
| 		 * @param mesh | ||||
| 		 */ | ||||
| 		alternativa3d function removeFromMesh(mesh:Mesh):void {} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Добавление на поверхность | ||||
| 		 *  | ||||
| 		 * @param surface | ||||
| 		 */ | ||||
| 		alternativa3d function addToSurface(surface:Surface):void { | ||||
| 			// Сохраняем поверхность | ||||
| 			_surface = surface; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Удаление с поверхности | ||||
| 		 *  | ||||
| 		 * @param surface | ||||
| 		 */ | ||||
| 		alternativa3d function removeFromSurface(surface:Surface):void { | ||||
| 			// Удаляем ссылку на поверхность | ||||
| 			_surface = null; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Коэффициент непрозрачности материала. Значение 1 соответствует полной непрозрачности, значение 0 соответствует полной прозрачности. | ||||
| 		 */		 | ||||
| 		public function get alpha():Number { | ||||
| 			return _alpha; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set alpha(value:Number):void { | ||||
| 			if (_alpha != value) { | ||||
| 				_alpha = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Режим наложения цвета. | ||||
| 		 */		 | ||||
| 		public function get blendMode():String { | ||||
| 			return _blendMode; | ||||
| 		} | ||||
| 				 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set blendMode(value:String):void { | ||||
| 			if (_blendMode != value) { | ||||
| 				_blendMode = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Метод определяет, может ли материал нарисовать указанный примитив. Метод используется в системе отрисовки сцены и должен использоваться | ||||
| 		 * наследниками для указания видимости связанной с материалом поверхности или отдельного примитива. Реализация по умолчанию возвращает | ||||
| 		 * <code>true</code>. | ||||
| 		 *  | ||||
| 		 * @param primitive примитив для проверки | ||||
| 		 *  | ||||
| 		 * @return <code>true</code>, если материал может отрисовать указанный примитив, иначе <code>false</code> | ||||
| 		 */		 | ||||
| 		alternativa3d function canDraw(primitive:PolyPrimitive):Boolean { | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Метод очищает переданный скин (нарисованную графику, дочерние объекты и т.д.). | ||||
| 		 *  | ||||
| 		 * @param skin скин для очистки | ||||
| 		 */ | ||||
| 		alternativa3d function clear(skin:Skin):void { | ||||
| 			skin.gfx.clear(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Метод выполняет отрисовку в заданный скин. | ||||
| 		 *  | ||||
| 		 * @param camera камера, вызвавшая метод | ||||
| 		 * @param skin скин, в котором нужно рисовать | ||||
| 		 * @param length длина массива points | ||||
| 		 * @param points массив точек, определяющих отрисовываемый полигон. Каждый элемент массива является объектом класса | ||||
| 		 *   <code>alternativa.engine3d.materials.DrawPoint</code> | ||||
| 		 *  | ||||
| 		 * @see DrawPoint | ||||
| 		 */ | ||||
| 		alternativa3d function draw(camera:Camera3D, skin:Skin, length:uint, points:Array):void { | ||||
| 			skin.alpha = _alpha; | ||||
| 			skin.blendMode = _blendMode; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */		 | ||||
| 		override public function clone():Material { | ||||
| 			return new SurfaceMaterial(_alpha, _blendMode); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,370 +0,0 @@ | ||||
| package alternativa.engine3d.materials { | ||||
| 	import __AS3__.vec.Vector; | ||||
| 	 | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.engine3d.core.Face; | ||||
| 	import alternativa.engine3d.core.PolyPrimitive; | ||||
| 	import alternativa.engine3d.display.Skin; | ||||
| 	import alternativa.types.*; | ||||
| 	 | ||||
| 	import flash.display.BlendMode; | ||||
| 	import flash.geom.Matrix; | ||||
| 	import flash.display.BitmapData; | ||||
| 	import flash.display.Graphics; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	use namespace alternativatypes; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Материал, заполняющий грань текстурой. Помимо наложения текстуры, материал может рисовать границу грани линией | ||||
| 	 * заданной толщины и цвета. | ||||
| 	 */	 | ||||
| 	public class TextureMaterial extends SurfaceMaterial { | ||||
|  | ||||
| 		private static var stubBitmapData:BitmapData; | ||||
| 		private static var stubMatrix:Matrix; | ||||
| 		 | ||||
| 		private var gfx:Graphics; | ||||
| 		private var textureMatrix:Matrix = new Matrix(); | ||||
| 		private var focalLength:Number; | ||||
| 		private var distortion:Number; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Текстура | ||||
| 		 */		 | ||||
| 		alternativa3d var _texture:Texture; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Повтор текстуры | ||||
| 		 */		 | ||||
| 		alternativa3d var _repeat:Boolean; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Сглаженность текстуры | ||||
| 		 */		 | ||||
| 		alternativa3d var _smooth:Boolean; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Точность перспективной коррекции | ||||
| 		 */		 | ||||
| 		alternativa3d var _precision:Number; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Толщина линий обводки  | ||||
| 		 */ | ||||
| 		alternativa3d var _wireThickness:Number; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Цвет линий обводки  | ||||
| 		 */ | ||||
| 		alternativa3d var _wireColor:uint; | ||||
|  | ||||
| 		/** | ||||
| 		 * Создание экземпляра текстурного материала. | ||||
| 		 *  | ||||
| 		 * @param texture текстура материала | ||||
| 		 * @param alpha коэффициент непрозрачности материала. Значение 1 соответствует полной непрозрачности, значение 0 соответствует полной прозрачности. | ||||
| 		 * @param repeat повтор текстуры при заполнении | ||||
| 		 * @param smooth сглаживание текстуры при увеличении масштаба | ||||
| 		 * @param blendMode режим наложения цвета | ||||
| 		 * @param wireThickness толщина линии обводки | ||||
| 		 * @param wireColor цвет линии обводки | ||||
| 		 * @param precision точность перспективной коррекции. Может быть задана одной из констант класса | ||||
| 		 *   <code>TextureMaterialPrecision</code> или числом типа Number. Во втором случае, чем ближе заданное значение к единице, тем более | ||||
| 		 *   качественная перспективная коррекция будет выполнена, и тем больше времени будет затрачено на расчёт кадра. | ||||
| 		 *  | ||||
| 		 * @see TextureMaterialPrecision | ||||
| 		 */ | ||||
| 		public function TextureMaterial(texture:Texture, alpha:Number = 1, repeat:Boolean = true, smooth:Boolean = false, blendMode:String = BlendMode.NORMAL, wireThickness:Number = -1, wireColor:uint = 0, precision:Number = TextureMaterialPrecision.MEDIUM) { | ||||
| 			super(alpha, blendMode); | ||||
| 			_texture = texture; | ||||
| 			_repeat = repeat; | ||||
| 			_smooth = smooth; | ||||
| 			_wireThickness = wireThickness; | ||||
| 			_wireColor = wireColor; | ||||
| 			_precision = precision; | ||||
| 			useUV = true; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Метод определяет, может ли материал нарисовать указанный примитив. Метод используется в системе отрисовки сцены и должен использоваться | ||||
| 		 * наследниками для указания видимости связанной с материалом поверхности или отдельного примитива. | ||||
| 		 *  | ||||
| 		 * @param primitive примитив для проверки | ||||
| 		 *  | ||||
| 		 * @return <code>true</code>, если материал может отрисовать указанный примитив, иначе <code>false</code> | ||||
| 		 */ | ||||
| 		override alternativa3d function canDraw(primitive:PolyPrimitive):Boolean { | ||||
| 			return _texture != null; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		override alternativa3d function draw(camera:Camera3D, skin:Skin, length:uint, points:Array):void { | ||||
| 			skin.alpha = _alpha; | ||||
| 			skin.blendMode = _blendMode; | ||||
| 			 | ||||
| 			var i:uint; | ||||
| 			var point:DrawPoint; | ||||
| 			gfx = skin.gfx; | ||||
| 			 | ||||
| 			// Проверка на нулевую UV-матрицу | ||||
| 			if (skin.primitive.face.uvMatrixBase == null) { | ||||
| 				if (stubBitmapData == null) { | ||||
| 					// Создание текстуры-заглушки | ||||
| 					stubBitmapData = new BitmapData(2, 2, false, 0); | ||||
| 					stubBitmapData.setPixel(0, 0, 0xFF00FF); | ||||
| 					stubBitmapData.setPixel(1, 1, 0xFF00FF); | ||||
| 					stubMatrix = new Matrix(10, 0, 0, 10, 0, 0); | ||||
| 				}				 | ||||
| 				gfx.beginBitmapFill(stubBitmapData, stubMatrix); | ||||
| 				if (camera._orthographic) { | ||||
| 					if (_wireThickness >= 0) { | ||||
| 						gfx.lineStyle(_wireThickness, _wireColor); | ||||
| 					} | ||||
| 					point = points[0]; | ||||
| 					gfx.moveTo(point.x, point.y); | ||||
| 					for (i = 1; i < length; i++) { | ||||
| 						point = points[i]; | ||||
| 						gfx.lineTo(point.x, point.y); | ||||
| 					} | ||||
| 					if (_wireThickness >= 0) { | ||||
| 						point = points[0]; | ||||
| 						gfx.lineTo(point.x, point.y); | ||||
| 					} | ||||
| 				} else { | ||||
| 					if (_wireThickness >= 0) { | ||||
| 						gfx.lineStyle(_wireThickness, _wireColor); | ||||
| 					} | ||||
| 					point = points[0]; | ||||
| 					var perspective:Number = camera.focalLength/point.z; | ||||
| 					gfx.moveTo(point.x*perspective, point.y*perspective); | ||||
| 					for (i = 1; i < length; i++) { | ||||
| 						point = points[i]; | ||||
| 						perspective = camera.focalLength/point.z; | ||||
| 						gfx.lineTo(point.x*perspective, point.y*perspective); | ||||
| 					} | ||||
| 					if (_wireThickness >= 0) { | ||||
| 						point = points[0]; | ||||
| 						perspective = camera.focalLength/point.z; | ||||
| 						gfx.lineTo(point.x*perspective, point.y*perspective); | ||||
| 					} | ||||
| 				} | ||||
| 				return; | ||||
| 			} | ||||
| 			 | ||||
| 			if (camera._orthographic) { | ||||
| 				// Расчитываем матрицу наложения текстуры | ||||
| 				var face:Face = skin.primitive.face; | ||||
| 				// Если матрица не расчитана, считаем | ||||
| 				if (!camera.uvMatricesCalculated[face]) { | ||||
| 					camera.calculateUVMatrix(face, _texture._width, _texture._height); | ||||
| 				} | ||||
| 				gfx.beginBitmapFill(_texture._bitmapData, face.uvMatrix, _repeat, _smooth); | ||||
| 				if (_wireThickness >= 0) { | ||||
| 					gfx.lineStyle(_wireThickness, _wireColor); | ||||
| 				} | ||||
| 				point = points[0]; | ||||
| 				gfx.moveTo(point.x, point.y); | ||||
| 				for (i = 1; i < length; i++) { | ||||
| 					point = points[i]; | ||||
| 					gfx.lineTo(point.x, point.y); | ||||
| 				} | ||||
| 				if (_wireThickness >= 0) { | ||||
| 					point = points[0]; | ||||
| 					gfx.lineTo(point.x, point.y); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Отрисовка | ||||
| 				focalLength = camera.focalLength; | ||||
| 				//distortion = camera.focalDistortion*_precision; | ||||
| 				 | ||||
| 				var front:int = 0; | ||||
| 				var back:int = length - 1; | ||||
|  | ||||
| 				var newFront:int = 1; | ||||
| 				var newBack:int = (back > 0) ? (back - 1) : (length - 1); | ||||
| 				var direction:Boolean = true; | ||||
| 				 | ||||
| 				var a:DrawPoint = points[back]; | ||||
| 				var b:DrawPoint; | ||||
| 				var c:DrawPoint = points[front]; | ||||
| 				 | ||||
| 				var drawVertices:Vector.<Number> = new Vector.<Number>(); | ||||
| 				var drawUVTs:Vector.<Number> = new Vector.<Number>(); | ||||
| 				 | ||||
| 				for (i = 0; i < length; i++) { | ||||
| 					var p:DrawPoint = points[i]; | ||||
| 					var t:Number = focalLength/p.z; | ||||
| 					drawVertices[i << 1] = p.x*t; | ||||
| 					drawVertices[(i << 1) + 1] = p.y*t; | ||||
| 					drawUVTs.push(p.u, 1 - p.v, t); | ||||
| 				} | ||||
| 				 | ||||
| 				var drawIndices:Vector.<int> = new Vector.<int>(); | ||||
|  | ||||
| 				while (front != newBack) { | ||||
| 					if (direction) { | ||||
| /* 						a = points[front]; | ||||
| 						b = points[newFront]; | ||||
| 						c = points[back]; | ||||
|  */ | ||||
| 						drawIndices.push(front, newFront, back); | ||||
|  | ||||
| 						front = newFront; | ||||
| 						newFront = (front < length - 1) ? (front + 1) : 0; | ||||
| 					} else { | ||||
| /* 						a = points[newBack]; | ||||
| 						b = points[back]; | ||||
| 						c = points[front]; | ||||
|  */ | ||||
| 						drawIndices.push(newBack, back, front); | ||||
|  | ||||
| 						back = newBack; | ||||
| 						newBack = (back > 0) ? (back - 1) : (length - 1); | ||||
| 					} | ||||
|  | ||||
| 					direction = !direction; | ||||
| 				} | ||||
| 				gfx.beginBitmapFill(_texture.bitmapData, null, _repeat, _smooth); | ||||
| 				if (_wireThickness >= 0) { | ||||
| 					gfx.lineStyle(_wireThickness, _wireColor); | ||||
| 				} | ||||
| 				gfx.drawTriangles(drawVertices, drawIndices, drawUVTs); | ||||
| 				 | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Текстура материала. Материал не выполняет никаких действий по отрисовке, если не задана текстура. | ||||
| 		 */ | ||||
| 		public function get texture():Texture { | ||||
| 			return _texture; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set texture(value:Texture):void { | ||||
| 			if (_texture != value) { | ||||
| 				_texture = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Повтор текстуры при заливке. Более подробную информацию можно найти в описании метода | ||||
| 		 * <code>flash.display.Graphics#beginBitmapFill()</code>. | ||||
| 		 */ | ||||
| 		public function get repeat():Boolean { | ||||
| 			return _repeat; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set repeat(value:Boolean):void { | ||||
| 			if (_repeat != value) { | ||||
| 				_repeat = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Сглаживание текстуры при увеличении масштаба. Более подробную информацию можно найти в описании метода | ||||
| 		 * <code>flash.display.Graphics#beginBitmapFill()</code>. | ||||
| 		 */ | ||||
| 		public function get smooth():Boolean { | ||||
| 			return _smooth; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set smooth(value:Boolean):void { | ||||
| 			if (_smooth != value) { | ||||
| 				_smooth = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Толщина линии обводки полигона. Если значение отрицательное, то обводка не рисуется. | ||||
| 		 */ | ||||
| 		public function get wireThickness():Number { | ||||
| 			return _wireThickness; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set wireThickness(value:Number):void { | ||||
| 			if (_wireThickness != value) { | ||||
| 				_wireThickness = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Цвет линии обводки полигона. | ||||
| 		 */ | ||||
| 		public function get wireColor():uint { | ||||
| 			return _wireColor; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set wireColor(value:uint):void { | ||||
| 			if (_wireColor != value) { | ||||
| 				_wireColor = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Точность перспективной коррекции. | ||||
| 		 */ | ||||
| 		public function get precision():Number { | ||||
| 			return _precision; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */		 | ||||
| 		public function set precision(value:Number):void { | ||||
| 			if (_precision != value) { | ||||
| 				_precision = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @inheritDoc  | ||||
| 		 */ | ||||
| 		override public function clone():Material { | ||||
| 			var res:TextureMaterial = new TextureMaterial(_texture, _alpha, _repeat, _smooth, _blendMode, _wireThickness, _wireColor, _precision); | ||||
| 			return res; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,39 +0,0 @@ | ||||
| package alternativa.engine3d.materials { | ||||
|  | ||||
| 	/** | ||||
| 	 * Класс содержит константы точности перспективной коррекции текстурного материала. | ||||
| 	 *  | ||||
| 	 * @see TextureMaterial | ||||
| 	 */ | ||||
| 	public class TextureMaterialPrecision { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Адаптивная триангуляция не будет выполняться, только простая триангуляция.  | ||||
| 		 */ | ||||
| 		public static const NONE:Number = -1; | ||||
| 		/** | ||||
| 		 * Очень низкое качество адаптивной триангуляции. | ||||
| 		 */ | ||||
| 		public static const VERY_LOW:Number = 50; | ||||
| 		/** | ||||
| 		 * Низкое качество адаптивной триангуляции. | ||||
| 		 */ | ||||
| 		public static const LOW:Number = 25; | ||||
| 		/** | ||||
| 		 * Среднее качество адаптивной триангуляции.  | ||||
| 		 */ | ||||
| 		public static const MEDIUM:Number = 10; | ||||
| 		/** | ||||
| 		 * Высокое качество адаптивной триангуляции. | ||||
| 		 */ | ||||
| 		public static const HIGH:Number = 6; | ||||
| 		/** | ||||
| 		 * Очень высокое качество адаптивной триангуляции. | ||||
| 		 */ | ||||
| 		public static const VERY_HIGH:Number = 3; | ||||
| 		/** | ||||
| 		 * Максимальное качество адаптивной триангуляции. | ||||
| 		 */ | ||||
| 		public static const BEST:Number = 1; | ||||
| 	} | ||||
| } | ||||
| @@ -1,128 +0,0 @@ | ||||
| package alternativa.engine3d.materials { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Camera3D; | ||||
| 	import alternativa.engine3d.display.Skin; | ||||
| 	 | ||||
| 	import flash.display.BlendMode; | ||||
| 	import flash.display.Graphics; | ||||
| 	import alternativa.engine3d.core.PolyPrimitive; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Материал для рисования рёбер граней. | ||||
| 	 */	 | ||||
| 	public class WireMaterial extends SurfaceMaterial { | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Цвет | ||||
| 		 */ | ||||
| 		alternativa3d var _color:uint; | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Толщина линий | ||||
| 		 */ | ||||
| 		alternativa3d var _thickness:Number; | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание экземпляра класса. | ||||
| 		 *  | ||||
| 		 * @param thickness толщина линий | ||||
| 		 * @param color цвет линий | ||||
| 		 * @param alpha коэффициент непрозрачности линий. Значение 1 соответствует полной непрозрачности, значение 0 соответствует полной прозрачности. | ||||
| 		 * @param blendMode режим наложения цвета | ||||
| 		 */ | ||||
| 		public function WireMaterial(thickness:Number = 0, color:uint = 0, alpha:Number = 1, blendMode:String = BlendMode.NORMAL) { | ||||
| 			super(alpha, blendMode); | ||||
| 			_color = color; | ||||
| 			_thickness = thickness; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		override alternativa3d function canDraw(primitive:PolyPrimitive):Boolean { | ||||
| 			return _thickness >= 0; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		override alternativa3d function draw(camera:Camera3D, skin:Skin, length:uint, points:Array):void { | ||||
| 			skin.alpha = _alpha; | ||||
| 			skin.blendMode = _blendMode; | ||||
| 			 | ||||
| 			var i:uint; | ||||
| 			var point:DrawPoint; | ||||
| 			var gfx:Graphics = skin.gfx; | ||||
|  | ||||
| 			if (camera._orthographic) { | ||||
| 				gfx.lineStyle(_thickness, _color); | ||||
| 				point = points[length - 1]; | ||||
| 				gfx.moveTo(point.x, point.y); | ||||
| 				for (i = 0; i < length; i++) { | ||||
| 					point = points[i]; | ||||
| 					gfx.lineTo(point.x, point.y); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Отрисовка | ||||
| 				gfx.lineStyle(_thickness, _color); | ||||
| 				point = points[length - 1]; | ||||
| 				var perspective:Number = camera.focalLength/point.z; | ||||
| 				gfx.moveTo(point.x*perspective, point.y*perspective); | ||||
| 				for (i = 0; i < length; i++) { | ||||
| 					point = points[i]; | ||||
| 					perspective = camera.focalLength/point.z; | ||||
| 					gfx.lineTo(point.x*perspective, point.y*perspective); | ||||
| 				} | ||||
| 			}			 | ||||
| 		} | ||||
|  		 | ||||
| 		/** | ||||
| 		 * Цвет линий. | ||||
| 		 */ | ||||
| 		public function get color():uint { | ||||
| 			return _color; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set color(value:uint):void { | ||||
| 			if (_color != value) { | ||||
| 				_color = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Толщина линий. Если толщина отрицательная, то отрисовка не выполняется. | ||||
| 		 */ | ||||
| 		public function get thickness():Number { | ||||
| 			return _thickness; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set thickness(value:Number):void { | ||||
| 			if (_thickness != value) { | ||||
| 				_thickness = value; | ||||
| 				if (_surface != null) { | ||||
| 					_surface.addMaterialChangedOperationToScene(); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc  | ||||
| 		 */		 | ||||
| 		override public function clone():Material { | ||||
| 			return new WireMaterial(_thickness, _color, _alpha, _blendMode); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,29 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 99 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/physics | ||||
| END | ||||
| EllipsoidCollider.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 120 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/physics/EllipsoidCollider.as | ||||
| END | ||||
| Collision.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 112 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/physics/Collision.as | ||||
| END | ||||
| CollisionPlane.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 117 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/physics/CollisionPlane.as | ||||
| END | ||||
| CollisionSetMode.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 119 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/physics/CollisionSetMode.as | ||||
| END | ||||
| @@ -1,76 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/physics | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| EllipsoidCollider.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| a68ff1dbc7d289323d3b6b67dab4a3b6 | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| Collision.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 9265e483cf687b839aca51cfdbce1974 | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| CollisionPlane.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| a4710a4ba9c1aa59d95ffb993608f5ec | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| CollisionSetMode.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 759698912050d0ac2de3fa229ad45e84 | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
| @@ -1,30 +0,0 @@ | ||||
| package alternativa.engine3d.physics { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Face; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Параметры столкновения эллипсоида с гранью объекта. Плоскостью столкновения является касательная к | ||||
| 	 * эллипсоиду плоскость, проходящая через точку столкновения с гранью. | ||||
| 	 */ | ||||
| 	public class Collision { | ||||
| 		/** | ||||
| 		 * Грань, с которой произошло столкновение. | ||||
| 		 */ | ||||
| 		public var face:Face; | ||||
| 		/** | ||||
| 		 * Нормаль плоскости столкновения. | ||||
| 		 */ | ||||
| 		public var normal:Point3D; | ||||
| 		/** | ||||
| 		 * Смещение плоскости столкновения. | ||||
| 		 */ | ||||
| 		public var offset:Number; | ||||
| 		/** | ||||
| 		 * Координаты точки столкновения. | ||||
| 		 */ | ||||
| 		public var point:Point3D; | ||||
| 	} | ||||
| } | ||||
| @@ -1,60 +0,0 @@ | ||||
| package alternativa.engine3d.physics { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.BSPNode; | ||||
|  | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * @private | ||||
| 	 */ | ||||
| 	public class CollisionPlane { | ||||
| 		// Узел BSP дерева, который содержит плоскость | ||||
| 		public var node:BSPNode; | ||||
| 		// Индикатор положения объекта относительно плоскости (спереди или сзади) | ||||
| 		public var infront:Boolean; | ||||
| 		// Расстояние до плоскости в начальной точке (всегда положительное) | ||||
| 		public var sourceOffset:Number; | ||||
| 		// Расстояние до плоскости в конечной точке | ||||
| 		public var destinationOffset:Number; | ||||
| 		 | ||||
| 		// Хранилище неиспользуемых плоскостей | ||||
| 		static private var collector:Array = new Array(); | ||||
|  | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание плоскости | ||||
| 		 *  | ||||
| 		 * @param node | ||||
| 		 * @param infront | ||||
| 		 * @param sourceOffset | ||||
| 		 * @param destinationOffset | ||||
| 		 * @return  | ||||
| 		 */		 | ||||
| 		static alternativa3d function createCollisionPlane(node:BSPNode, infront:Boolean, sourceOffset:Number, destinationOffset:Number):CollisionPlane { | ||||
| 			 | ||||
| 			// Достаём плоскость из коллектора | ||||
| 			var plane:CollisionPlane = collector.pop(); | ||||
| 			// Если коллектор пуст, создаём новую плоскость | ||||
| 			if (plane == null) { | ||||
| 				plane = new CollisionPlane(); | ||||
| 			} | ||||
|  | ||||
| 			plane.node = node; | ||||
| 			plane.infront = infront; | ||||
| 			plane.sourceOffset = sourceOffset; | ||||
| 			plane.destinationOffset = destinationOffset; | ||||
|  | ||||
| 			return plane; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Удаление плоскости, все ссылки должны быть почищены | ||||
| 		 *  | ||||
| 		 * @param plane | ||||
| 		 */ | ||||
| 		static alternativa3d function destroyCollisionPlane(plane:CollisionPlane):void { | ||||
| 			plane.node = null; | ||||
| 			collector.push(plane); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,18 +0,0 @@ | ||||
| package alternativa.engine3d.physics { | ||||
| 	/** | ||||
| 	 * Константы, определяющие режим учёта объектов сцены, заданных в множестве <code>EllipsoidCollider.collisionSet</code> | ||||
| 	 * при определении столкновений. | ||||
| 	 *  | ||||
| 	 * @see EllipsoidCollider#collisionSet | ||||
| 	 */ | ||||
| 	public class CollisionSetMode	{ | ||||
| 		/** | ||||
| 		 * Грани объектов игнорируются при определении столкновений. | ||||
| 		 */		 | ||||
| 		static public const EXCLUDE:int = 1; | ||||
| 		/** | ||||
| 		 * Учитываются только столкновения с гранями, принадлежащим перечисленным в множестве объектам. | ||||
| 		 */		 | ||||
| 		static public const INCLUDE:int = 2; | ||||
| 	} | ||||
| } | ||||
| @@ -1,993 +0,0 @@ | ||||
| package alternativa.engine3d.physics { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.BSPNode; | ||||
| 	import alternativa.engine3d.core.PolyPrimitive; | ||||
| 	import alternativa.engine3d.core.Scene3D; | ||||
| 	import alternativa.types.Point3D; | ||||
| 	import alternativa.types.Set; | ||||
| 	import alternativa.utils.ObjectUtils; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Класс реализует алгоритм непрерывного определения столкновений эллипсоида с плоскими выпуклыми многоугольниками. | ||||
| 	 */ | ||||
| 	public class EllipsoidCollider { | ||||
| 		// Максимальное количество попыток найти свободное от столкновения со сценой направление    | ||||
| 		private static const MAX_COLLISIONS:uint = 50; | ||||
| 		// Радиус наибольшей сферы | ||||
| 		private var _radius:Number = 100; | ||||
| 		private var _radius2:Number = _radius * _radius; | ||||
| 		private var _radiusX:Number = _radius; | ||||
| 		private var _radiusY:Number = _radius; | ||||
| 		private var _radiusZ:Number = _radius; | ||||
| 		private var _radiusX2:Number = _radiusX * _radiusX; | ||||
| 		private var _radiusY2:Number = _radiusY * _radiusY; | ||||
| 		private var _radiusZ2:Number = _radiusZ * _radiusZ; | ||||
| 		// Коэффициенты масштабирования осей | ||||
| 		private var _scaleX:Number = 1; | ||||
| 		private var _scaleY:Number = 1; | ||||
| 		private var _scaleZ:Number = 1; | ||||
| 		// Квадраты коэффициентов масштабирования осей | ||||
| 		private var _scaleX2:Number = 1; | ||||
| 		private var _scaleY2:Number = 1; | ||||
| 		private var _scaleZ2:Number = 1; | ||||
|  | ||||
| 		private var collisionSource:Point3D; | ||||
| 		private var currentDisplacement:Point3D = new Point3D(); | ||||
| 		private var collisionDestination:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		private var collisionPlanes:Array = new Array(); | ||||
| 		private var collisionPrimitive:PolyPrimitive; | ||||
| 		private var collisionPrimitiveNearest:PolyPrimitive; | ||||
| 		private var collisionPlanePoint:Point3D = new Point3D(); | ||||
| 		private var collisionPrimitiveNearestLengthSqr:Number; | ||||
| 		private var collisionPrimitivePoint:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		private var collisionNormal:Point3D = new Point3D(); | ||||
| 		private var collisionPoint:Point3D = new Point3D(); | ||||
| 		private var collisionOffset:Number; | ||||
|  | ||||
| 		private var currentCoords:Point3D = new Point3D(); | ||||
| 		private var collision:Collision = new Collision(); | ||||
| 		private var collisionRadius:Number; | ||||
| 		private var radiusVector:Point3D = new Point3D(); | ||||
| 		private var p1:Point3D = new Point3D(); | ||||
| 		private var p2:Point3D = new Point3D();; | ||||
| 		private var localCollisionPlanePoint:Point3D = new Point3D(); | ||||
| 		 | ||||
| 		// Флаг использования упорщённого алгоритма. Включается когда эллипсоид представляет собой сферу. | ||||
| 		private var useSimpleAlgorithm:Boolean = true; | ||||
|  | ||||
| 		/** | ||||
| 		 * Сцена, в которой определяются столкновения.  | ||||
| 		 */ | ||||
| 		public var scene:Scene3D; | ||||
| 		/** | ||||
| 		 * Погрешность определения расстояний и координат. Две точки совпадают, если модуль разности любых соответствующих | ||||
| 		 * координат меньше указанной погрешности. | ||||
| 		 */ | ||||
| 		public var offsetThreshold:Number = 0.01; | ||||
| 		/** | ||||
| 		 * Множество объектов, учитываемых в процессе определения столкновений. В качестве объектов могут выступать экземпляры | ||||
| 		 * классов <code>Mesh</code> и <code>Surface</code>. Каким образом учитываются перечисленные в множестве объекты зависит | ||||
| 		 * от значения поля <code>collisionSetMode</code>. Значение <code>null</code> эквивалентно заданию пустого множества. | ||||
| 		 *  | ||||
| 		 * @see #collisionSetMode | ||||
| 		 * @see alternativa.engine3d.core.Mesh | ||||
| 		 * @see alternativa.engine3d.core.Surface | ||||
| 		 */ | ||||
| 		public var collisionSet:Set; | ||||
| 		/** | ||||
| 		 * Параметр определяет, каким образом учитываются объекты, перечисленные в множестве <code>collisionSet</code>. Если | ||||
| 		 * значение параметра равно <code>true</code>, то грани объектов из множества игнорируются при определении столкновений. | ||||
| 		 * При значении параметра <code>false</code> учитываются только столкновения с гранями, принадлежащим перечисленным | ||||
| 		 * в множестве объектам. | ||||
| 		 *  | ||||
| 		 * @default true | ||||
| 		 * @see #collisionSet | ||||
| 		 */ | ||||
| 		private var _collisionSetMode:int = CollisionSetMode.EXCLUDE; | ||||
|  | ||||
| 		/** | ||||
| 		 * Создаёт новый экземпляр класса. | ||||
| 		 *  | ||||
| 		 * @param scene сцена, в которой определяются столкновения | ||||
| 		 * @param scaleX радиус эллипсоида по оси X | ||||
| 		 * @param scaleY радиус эллипсоида по оси Y | ||||
| 		 * @param scaleZ радиус эллипсоида по оси Z | ||||
| 		 */ | ||||
| 		public function EllipsoidCollider(scene:Scene3D = null, radiusX:Number = 100, radiusY:Number = 100, radiusZ:Number = 100) { | ||||
| 			this.scene = scene; | ||||
| 			this.radiusX = radiusX; | ||||
| 			this.radiusY = radiusY; | ||||
| 			this.radiusZ = radiusZ; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function get collisionSetMode():int { | ||||
| 			return _collisionSetMode; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Параметр определяет, каким образом учитываются объекты, перечисленные в множестве <code>collisionSet</code>. | ||||
| 		 *  | ||||
| 		 * @default CollisionSetMode.EXCLUDE | ||||
| 		 * @see #collisionSet | ||||
| 		 * @see CollisionSetMode | ||||
| 		 */ | ||||
| 		public function set collisionSetMode(value:int):void { | ||||
| 			if (value != CollisionSetMode.EXCLUDE && value != CollisionSetMode.INCLUDE) { | ||||
| 				throw ArgumentError(ObjectUtils.getClassName(this) + ".collisionSetMode invalid value"); | ||||
| 			}	 | ||||
| 			_collisionSetMode = value; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Величина радиуса (полуоси) эллипсоида по оси X. При установке отрицательного значения берётся модуль. | ||||
| 		 *  | ||||
| 		 * @default 100  | ||||
| 		 */ | ||||
| 		public function get radiusX():Number { | ||||
| 			return _radiusX; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set radiusX(value:Number):void { | ||||
| 			_radiusX = value >= 0 ? value : -value; | ||||
| 			_radiusX2 = _radiusX * _radiusX; | ||||
| 			calculateScales(); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Величина радиуса (полуоси) эллипсоида по оси Y. При установке отрицательного значения берётся модуль. | ||||
| 		 *  | ||||
| 		 * @default 100  | ||||
| 		 */ | ||||
| 		public function get radiusY():Number { | ||||
| 			return _radiusY; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set radiusY(value:Number):void { | ||||
| 			_radiusY = value >= 0 ? value : -value; | ||||
| 			_radiusY2 = _radiusY * _radiusY; | ||||
| 			calculateScales(); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Величина радиуса (полуоси) эллипсоида по оси Z. При установке отрицательного значения берётся модуль. | ||||
| 		 *  | ||||
| 		 * @default 100  | ||||
| 		 */ | ||||
| 		public function get radiusZ():Number { | ||||
| 			return _radiusZ; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 */ | ||||
| 		public function set radiusZ(value:Number):void { | ||||
| 			_radiusZ = value >= 0 ? value : -value; | ||||
| 			_radiusZ2 = _radiusZ * _radiusZ; | ||||
| 			calculateScales(); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Расчёт коэффициентов масштабирования осей. | ||||
| 		 */ | ||||
| 		private function calculateScales():void { | ||||
| 			_radius = _radiusX; | ||||
| 			if (_radiusY > _radius) { | ||||
| 				_radius = _radiusY; | ||||
| 			} | ||||
| 			if (_radiusZ > _radius) { | ||||
| 				_radius = _radiusZ; | ||||
| 			} | ||||
| 			_radius2 = _radius * _radius; | ||||
| 			_scaleX = _radiusX / _radius; | ||||
| 			_scaleY = _radiusY / _radius; | ||||
| 			_scaleZ = _radiusZ / _radius; | ||||
| 			_scaleX2 = _scaleX * _scaleX; | ||||
| 			_scaleY2 = _scaleY * _scaleY; | ||||
| 			_scaleZ2 = _scaleZ * _scaleZ; | ||||
| 			 | ||||
| 			useSimpleAlgorithm = (_radiusX == _radiusY) && (_radiusX == _radiusZ); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Расчёт конечного положения эллипсоида по заданному начальному положению и вектору смещения. Если задано значение | ||||
| 		 * поля <code>scene</code>, то при вычислении конечного положения учитываются столкновения с объектами сцены, | ||||
| 		 * принимая во внимание множество <code>collisionSet</code> и режим работы <code>collisionSetMode</code>. Если | ||||
| 		 * значение поля <code>scene</code> равно <code>null</code>, то результат работы метода будет простой суммой двух | ||||
| 		 * входных векторов. | ||||
| 		 *  | ||||
| 		 * @param sourcePoint начальное положение центра эллипсоида в системе координат корневого объекта сцены | ||||
| 		 * @param displacementVector вектор перемещения эллипсоида в системе координат корневого объекта сцены. Если модуль | ||||
| 		 *   каждого компонента вектора не превышает значения <code>offsetThreshold</code>, эллипсоид остаётся в начальной точке. | ||||
| 		 * @param destinationPoint в эту переменную записывается расчётное положение центра эллипсоида в системе координат | ||||
| 		 *   корневого объекта сцены | ||||
| 		 *  | ||||
| 		 * @see #scene | ||||
| 		 * @see #collisionSet | ||||
| 		 * @see #collisionSetMode | ||||
| 		 * @see #offsetThreshold | ||||
| 		 */ | ||||
| 		public function calculateDestination(sourcePoint:Point3D, displacementVector:Point3D, destinationPoint:Point3D):void { | ||||
| 			// Расчеты не производятся, если перемещение мало | ||||
| 			if (displacementVector.x < offsetThreshold && displacementVector.x > -offsetThreshold && | ||||
| 					displacementVector.y < offsetThreshold && displacementVector.y > -offsetThreshold && | ||||
| 					displacementVector.z < offsetThreshold && displacementVector.z > -offsetThreshold) { | ||||
| 				destinationPoint.x = sourcePoint.x; | ||||
| 				destinationPoint.y = sourcePoint.y; | ||||
| 				destinationPoint.z = sourcePoint.z; | ||||
| 				return; | ||||
| 			} | ||||
| 			 | ||||
| 			// Начальные координаты | ||||
| 			currentCoords.x = sourcePoint.x; | ||||
| 			currentCoords.y = sourcePoint.y; | ||||
| 			currentCoords.z = sourcePoint.z; | ||||
| 			// Начальный вектор перемещения | ||||
| 			currentDisplacement.x = displacementVector.x; | ||||
| 			currentDisplacement.y = displacementVector.y; | ||||
| 			currentDisplacement.z = displacementVector.z; | ||||
| 			// Начальная точка назначения | ||||
| 			destinationPoint.x = sourcePoint.x + currentDisplacement.x; | ||||
| 			destinationPoint.y = sourcePoint.y + currentDisplacement.y; | ||||
| 			destinationPoint.z = sourcePoint.z + currentDisplacement.z; | ||||
|  | ||||
| 			if (useSimpleAlgorithm) { | ||||
| 				calculateDestinationS(sourcePoint, destinationPoint); | ||||
| 			} else { | ||||
| 				calculateDestinationE(sourcePoint, destinationPoint);				 | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Вычисление точки назначения для сферы. | ||||
| 		 * @param sourcePoint | ||||
| 		 * @param destinationPoint | ||||
| 		 */ | ||||
| 		private function calculateDestinationS(sourcePoint:Point3D, destinationPoint:Point3D):void { | ||||
| 			var collisionCount:uint = 0; | ||||
| 			var hasCollision:Boolean; | ||||
| 			do { | ||||
| 				hasCollision = getCollision(currentCoords, currentDisplacement, collision); | ||||
| 				if (hasCollision ) { | ||||
| 					// Вынос точки назначения из-за плоскости столкновения на высоту радиуса сферы над плоскостью по направлению нормали | ||||
| 					var offset:Number = _radius + offsetThreshold + collision.offset - destinationPoint.x*collision.normal.x - destinationPoint.y*collision.normal.y - destinationPoint.z*collision.normal.z; | ||||
| 					destinationPoint.x += collision.normal.x * offset; | ||||
| 					destinationPoint.y += collision.normal.y * offset; | ||||
| 					destinationPoint.z += collision.normal.z * offset; | ||||
| 					// Коррекция текущих кординат центра сферы для следующей итерации  | ||||
| 					currentCoords.x = collision.point.x + collision.normal.x * (_radius + offsetThreshold); | ||||
| 					currentCoords.y = collision.point.y + collision.normal.y * (_radius + offsetThreshold); | ||||
| 					currentCoords.z = collision.point.z + collision.normal.z * (_radius + offsetThreshold); | ||||
| 					// Коррекция вектора скорости. Результирующий вектор направлен вдоль плоскости столкновения. | ||||
| 					currentDisplacement.x = destinationPoint.x - currentCoords.x; | ||||
| 					currentDisplacement.y = destinationPoint.y - currentCoords.y; | ||||
| 					currentDisplacement.z = destinationPoint.z - currentCoords.z; | ||||
| 					 | ||||
| 					// Если смещение слишком мало, останавливаемся | ||||
| 					if (currentDisplacement.x < offsetThreshold && currentDisplacement.x > -offsetThreshold && | ||||
| 							currentDisplacement.y < offsetThreshold && currentDisplacement.y > -offsetThreshold && | ||||
| 							currentDisplacement.z < offsetThreshold && currentDisplacement.z > -offsetThreshold) { | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 			} while (hasCollision && (++collisionCount < MAX_COLLISIONS)); | ||||
| 			// Если количество итераций достигло максимально возможного значения, то остаемся на старом месте | ||||
| 			if (collisionCount == MAX_COLLISIONS) { | ||||
| 				destinationPoint.x = sourcePoint.x; | ||||
| 				destinationPoint.y = sourcePoint.y; | ||||
| 				destinationPoint.z = sourcePoint.z; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * Вычисление точки назначения для эллипсоида. | ||||
| 		 * @param destinationPoint | ||||
| 		 * @return  | ||||
| 		 */ | ||||
| 		private function calculateDestinationE(sourcePoint:Point3D, destinationPoint:Point3D):void { | ||||
| 			var collisionCount:uint = 0; | ||||
| 			var hasCollision:Boolean; | ||||
| 			// Цикл выполняется до тех пор, пока не будет найдено ни одного столкновения на очередной итерации или пока не | ||||
| 			// будет достигнуто максимально допустимое количество столкновений, что означает зацикливание алгоритма и | ||||
| 			// необходимость принудительного выхода. | ||||
| 			do { | ||||
| 				hasCollision = getCollision(currentCoords, currentDisplacement, collision); | ||||
| 				if (hasCollision) { | ||||
| 					// Вынос точки назначения из-за плоскости столкновения на высоту эффективного радиуса эллипсоида над плоскостью по направлению нормали | ||||
| 					var offset:Number = collisionRadius + offsetThreshold + collision.offset - destinationPoint.x * collision.normal.x - destinationPoint.y * collision.normal.y - destinationPoint.z * collision.normal.z; | ||||
| 					destinationPoint.x += collision.normal.x * offset; | ||||
| 					destinationPoint.y += collision.normal.y * offset; | ||||
| 					destinationPoint.z += collision.normal.z * offset; | ||||
| 					// Коррекция текущих кординат центра эллипсоида для следующей итерации | ||||
| 					collisionRadius = (collisionRadius + offsetThreshold) / collisionRadius; | ||||
| 					currentCoords.x = collision.point.x - collisionRadius * radiusVector.x; | ||||
| 					currentCoords.y = collision.point.y - collisionRadius * radiusVector.y; | ||||
| 					currentCoords.z = collision.point.z - collisionRadius * radiusVector.z; | ||||
| 					// Коррекция вектора смещения. Результирующий вектор направлен параллельно плоскости столкновения. | ||||
| 					currentDisplacement.x = destinationPoint.x - currentCoords.x; | ||||
| 					currentDisplacement.y = destinationPoint.y - currentCoords.y; | ||||
| 					currentDisplacement.z = destinationPoint.z - currentCoords.z; | ||||
| 					// Если смещение слишком мало, останавливаемся | ||||
| 					if (currentDisplacement.x < offsetThreshold && currentDisplacement.x > -offsetThreshold && | ||||
| 							currentDisplacement.y < offsetThreshold && currentDisplacement.y > -offsetThreshold && | ||||
| 							currentDisplacement.z < offsetThreshold && currentDisplacement.z > -offsetThreshold) { | ||||
| 						destinationPoint.x = currentCoords.x; | ||||
| 						destinationPoint.y = currentCoords.y; | ||||
| 						destinationPoint.z = currentCoords.z; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 			} while (hasCollision && (++collisionCount < MAX_COLLISIONS)); | ||||
| 			// Если количество итераций достигло максимально возможного значения, то остаемся на старом месте | ||||
| 			if (collisionCount == MAX_COLLISIONS) { | ||||
| 				destinationPoint.x = sourcePoint.x; | ||||
| 				destinationPoint.y = sourcePoint.y; | ||||
| 				destinationPoint.z = sourcePoint.z; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Метод определяет наличие столкновения при смещении эллипсоида из заданной точки на величину указанного вектора | ||||
| 		 * перемещения, принимая во внимание множество <code>collisionSet</code> и режим работы <code>collisionSetMode</code>. | ||||
| 		 *   | ||||
| 		 * @param sourcePoint начальное положение центра эллипсоида в системе координат корневого объекта сцены | ||||
| 		 * @param displacementVector вектор перемещения эллипсоида в системе координат корневого объекта сцены | ||||
| 		 * @param collision в эту переменную будут записаны данные о плоскости и точке столкновения в системе координат | ||||
| 		 *   корневого объекта сцены | ||||
| 		 *  | ||||
| 		 * @return <code>true</code>, если эллипсоид при заданном перемещении столкнётся с каким-либо полигоном сцены, | ||||
| 		 * <code>false</code> если столкновений нет или не задано значение поля <code>scene</code>. | ||||
| 		 *  | ||||
| 		 * @see #scene | ||||
| 		 * @see #collisionSet | ||||
| 		 * @see #collisionSetMode | ||||
| 		 */ | ||||
| 		public function getCollision(sourcePoint:Point3D, displacementVector:Point3D, collision:Collision):Boolean { | ||||
| 			if (scene == null) { | ||||
| 				return false; | ||||
| 			} | ||||
|  | ||||
| 			collisionSource = sourcePoint; | ||||
| 			 | ||||
| 			currentDisplacement.x = displacementVector.x; | ||||
| 			currentDisplacement.y = displacementVector.y; | ||||
| 			currentDisplacement.z = displacementVector.z; | ||||
| 			 | ||||
| 			collisionDestination.x = collisionSource.x + currentDisplacement.x; | ||||
| 			collisionDestination.y = collisionSource.y + currentDisplacement.y; | ||||
| 			collisionDestination.z = collisionSource.z + currentDisplacement.z; | ||||
| 			 | ||||
| 			collectPotentialCollisionPlanes(scene.bsp); | ||||
| 			collisionPlanes.sortOn("sourceOffset", Array.NUMERIC | Array.DESCENDING); | ||||
| 			 | ||||
| 			var plane:CollisionPlane; | ||||
| 			// Пока не найдём столкновение с примитивом или плоскости не кончатся | ||||
| 			if (useSimpleAlgorithm) { | ||||
| 				while ((plane = collisionPlanes.pop()) != null) { | ||||
| 					if (collisionPrimitive == null) { | ||||
| 						calculateCollisionWithPlaneS(plane); | ||||
| 					} | ||||
| 					CollisionPlane.destroyCollisionPlane(plane); | ||||
| 				} | ||||
| 			} else { | ||||
| 				while ((plane = collisionPlanes.pop()) != null) { | ||||
| 					if (collisionPrimitive == null) { | ||||
| 						calculateCollisionWithPlaneE(plane); | ||||
| 					} | ||||
| 					CollisionPlane.destroyCollisionPlane(plane); | ||||
| 				}				 | ||||
| 			} | ||||
| 			 | ||||
| 			var collisionFound:Boolean = collisionPrimitive != null; | ||||
| 			if (collisionFound) { | ||||
| 				collision.face = collisionPrimitive.face; | ||||
| 				collision.normal = collisionNormal; | ||||
| 				collision.offset = collisionOffset; | ||||
| 				collision.point = collisionPoint; | ||||
| 			} | ||||
| 			 | ||||
| 			collisionPrimitive = null; | ||||
| 			collisionSource = null; | ||||
|  | ||||
| 			return collisionFound; | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Сбор потенциальных плоскостей столкновения. | ||||
| 		 * | ||||
| 		 * @param node текущий узел BSP-дерева | ||||
| 		 */ | ||||
| 		private function collectPotentialCollisionPlanes(node:BSPNode):void { | ||||
| 			if (node == null) { | ||||
| 				return; | ||||
| 			} | ||||
| 			 | ||||
| 			var sourceOffset:Number = collisionSource.x * node.normal.x + collisionSource.y * node.normal.y + collisionSource.z * node.normal.z - node.offset; | ||||
| 			var destinationOffset:Number = collisionDestination.x * node.normal.x + collisionDestination.y * node.normal.y + collisionDestination.z * node.normal.z - node.offset; | ||||
| 			var plane:CollisionPlane; | ||||
| 			 | ||||
| 			if (sourceOffset >= 0) { | ||||
| 				// Исходное положение центра перед плоскостью ноды | ||||
| 				// Проверяем передние ноды | ||||
| 				collectPotentialCollisionPlanes(node.front); | ||||
| 				// Грубая оценка пересечения с плоскостью по радиусу ограничивающей сферы эллипсоида | ||||
| 				if (destinationOffset < _radius) { | ||||
| 					// Нашли потенциальное пересечение с плоскостью | ||||
| 					plane = CollisionPlane.createCollisionPlane(node, true, sourceOffset, destinationOffset); | ||||
| 					collisionPlanes.push(plane); | ||||
| 					// Проверяем задние ноды | ||||
| 					collectPotentialCollisionPlanes(node.back); | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Исходное положение центра за плоскостью ноды | ||||
| 				// Проверяем задние ноды | ||||
| 				collectPotentialCollisionPlanes(node.back); | ||||
| 				// Грубая оценка пересечения с плоскостью по радиусу ограничивающей сферы эллипсоида | ||||
| 				if (destinationOffset > -_radius) { | ||||
| 					// Столкновение возможно только в случае если в ноде есть примитивы, направленные назад | ||||
| 					if (node.backPrimitives != null) { | ||||
| 						// Нашли потенциальное пересечение с плоскостью | ||||
| 						plane = CollisionPlane.createCollisionPlane(node, false, -sourceOffset, -destinationOffset); | ||||
| 						collisionPlanes.push(plane); | ||||
| 					} | ||||
| 					// Проверяем передние ноды | ||||
| 					collectPotentialCollisionPlanes(node.front); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Определение пересечения сферы с примитивами, лежащими в заданной плоскости.  | ||||
| 		 *  | ||||
| 		 * @param plane плоскость, содержащая примитивы для проверки  | ||||
| 		 */ | ||||
| 		private function calculateCollisionWithPlaneS(plane:CollisionPlane):void { | ||||
| 			collisionPlanePoint.copy(collisionSource); | ||||
|  | ||||
| 			var normal:Point3D = plane.node.normal; | ||||
| 			// Если сфера врезана в плоскость | ||||
| 			if (plane.sourceOffset <= _radius) { | ||||
| 				if (plane.infront) { | ||||
| 					collisionPlanePoint.x -= normal.x * plane.sourceOffset; | ||||
| 					collisionPlanePoint.y -= normal.y * plane.sourceOffset; | ||||
| 					collisionPlanePoint.z -= normal.z * plane.sourceOffset; | ||||
| 				} else { | ||||
| 					collisionPlanePoint.x += normal.x * plane.sourceOffset; | ||||
| 					collisionPlanePoint.y += normal.y * plane.sourceOffset; | ||||
| 					collisionPlanePoint.z += normal.z * plane.sourceOffset; | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Находим центр сферы во время столкновения с плоскостью | ||||
| 				var time:Number = (plane.sourceOffset - _radius) / (plane.sourceOffset - plane.destinationOffset); | ||||
| 				collisionPlanePoint.x = collisionSource.x + currentDisplacement.x * time; | ||||
| 				collisionPlanePoint.y = collisionSource.y + currentDisplacement.y * time; | ||||
| 				collisionPlanePoint.z = collisionSource.z + currentDisplacement.z * time; | ||||
|  | ||||
| 				// Устанавливаем точку пересечения cферы с плоскостью | ||||
| 				if (plane.infront) { | ||||
| 					collisionPlanePoint.x -= normal.x * _radius; | ||||
| 					collisionPlanePoint.y -= normal.y * _radius; | ||||
| 					collisionPlanePoint.z -= normal.z * _radius; | ||||
| 				} else { | ||||
| 					collisionPlanePoint.x += normal.x * _radius; | ||||
| 					collisionPlanePoint.y += normal.y * _radius; | ||||
| 					collisionPlanePoint.z += normal.z * _radius; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Проверяем примитивы плоскости | ||||
| 			var primitive:*; | ||||
| 			collisionPrimitiveNearestLengthSqr = Number.MAX_VALUE; | ||||
| 			collisionPrimitiveNearest = null; | ||||
| 			if (plane.infront) { | ||||
| 				if ((primitive = plane.node.primitive) != null) { | ||||
| 					if (((_collisionSetMode == CollisionSetMode.EXCLUDE) && (collisionSet == null || !(collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) || | ||||
| 					 		((_collisionSetMode == CollisionSetMode.INCLUDE) && (collisionSet != null) && (collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) { | ||||
| 						calculateCollisionWithPrimitiveS(plane.node.primitive); | ||||
| 					} | ||||
| 				} else { | ||||
| 					for (primitive in plane.node.frontPrimitives) { | ||||
| 						if (((_collisionSetMode == CollisionSetMode.EXCLUDE) && (collisionSet == null || !(collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) || | ||||
| 						 		((_collisionSetMode == CollisionSetMode.INCLUDE) && (collisionSet != null) && (collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) { | ||||
| 							calculateCollisionWithPrimitiveS(primitive); | ||||
| 							if (collisionPrimitive != null) break; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				for (primitive in plane.node.backPrimitives) { | ||||
| 					if (((_collisionSetMode == CollisionSetMode.EXCLUDE) && (collisionSet == null || !(collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) || | ||||
| 					 		((_collisionSetMode == CollisionSetMode.INCLUDE) && (collisionSet != null) && (collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) { | ||||
| 						calculateCollisionWithPrimitiveS(primitive); | ||||
| 						if (collisionPrimitive != null) break; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if (collisionPrimitive != null) { | ||||
| 				// Если точка пересечения попала в примитив | ||||
|  | ||||
| 				// Нормаль плоскости при столкновении - нормаль плоскости | ||||
| 				if (plane.infront) { | ||||
| 					collisionNormal.x = normal.x; | ||||
| 					collisionNormal.y = normal.y; | ||||
| 					collisionNormal.z = normal.z; | ||||
| 					collisionOffset = plane.node.offset; | ||||
| 				} else { | ||||
| 					collisionNormal.x = -normal.x; | ||||
| 					collisionNormal.y = -normal.y; | ||||
| 					collisionNormal.z = -normal.z; | ||||
| 					collisionOffset = -plane.node.offset; | ||||
| 				} | ||||
|  | ||||
| 				// Точка столкновения в точке столкновения с плоскостью | ||||
| 				collisionPoint.x = collisionPlanePoint.x; | ||||
| 				collisionPoint.y = collisionPlanePoint.y; | ||||
| 				collisionPoint.z = collisionPlanePoint.z; | ||||
|  | ||||
| 			} else { | ||||
| 				// Если точка пересечения не попала ни в один примитив, проверяем столкновение с ближайшей | ||||
|  | ||||
| 				// Вектор из ближайшей точки в центр сферы | ||||
| 				var nearestPointToSourceX:Number = collisionSource.x - collisionPrimitivePoint.x; | ||||
| 				var nearestPointToSourceY:Number = collisionSource.y - collisionPrimitivePoint.y;  | ||||
| 				var nearestPointToSourceZ:Number = collisionSource.z - collisionPrimitivePoint.z; | ||||
|  | ||||
| 				// Если движение в сторону точки | ||||
| 				if (nearestPointToSourceX * currentDisplacement.x + nearestPointToSourceY * currentDisplacement.y + nearestPointToSourceZ * currentDisplacement.z <= 0) { | ||||
|  | ||||
| 					// Ищем нормализованный вектор обратного направления | ||||
| 					var vectorLength:Number = Math.sqrt(currentDisplacement.x * currentDisplacement.x + currentDisplacement.y * currentDisplacement.y + currentDisplacement.z * currentDisplacement.z); | ||||
| 					var vectorX:Number = -currentDisplacement.x / vectorLength; | ||||
| 					var vectorY:Number = -currentDisplacement.y / vectorLength; | ||||
| 					var vectorZ:Number = -currentDisplacement.z / vectorLength; | ||||
|  | ||||
| 					// Длина вектора из ближайшей точки в центр сферы | ||||
| 					var nearestPointToSourceLengthSqr:Number = nearestPointToSourceX * nearestPointToSourceX + nearestPointToSourceY * nearestPointToSourceY + nearestPointToSourceZ * nearestPointToSourceZ; | ||||
|  | ||||
| 					// Проекция вектора из ближайшей точки в центр сферы на нормализованный вектор обратного направления | ||||
| 					var projectionLength:Number = nearestPointToSourceX * vectorX + nearestPointToSourceY * vectorY + nearestPointToSourceZ * vectorZ; | ||||
|  | ||||
| 					var projectionInsideSphereLengthSqr:Number = _radius2 - nearestPointToSourceLengthSqr + projectionLength * projectionLength; | ||||
|  | ||||
| 					if (projectionInsideSphereLengthSqr > 0) { | ||||
| 						// Находим расстояние из ближайшей точки до сферы | ||||
| 						var distance:Number = projectionLength - Math.sqrt(projectionInsideSphereLengthSqr); | ||||
|  | ||||
| 						if (distance < vectorLength) { | ||||
| 							// Столкновение сферы с ближайшей точкой произошло | ||||
|  | ||||
| 							// Точка столкновения в ближайшей точке | ||||
| 							collisionPoint.x = collisionPrimitivePoint.x; | ||||
| 							collisionPoint.y = collisionPrimitivePoint.y; | ||||
| 							collisionPoint.z = collisionPrimitivePoint.z; | ||||
|  | ||||
| 							// Находим нормаль плоскости столкновения | ||||
| 							var nearestPointToSourceLength:Number = Math.sqrt(nearestPointToSourceLengthSqr); | ||||
| 							collisionNormal.x = nearestPointToSourceX / nearestPointToSourceLength; | ||||
| 							collisionNormal.y = nearestPointToSourceY / nearestPointToSourceLength; | ||||
| 							collisionNormal.z = nearestPointToSourceZ / nearestPointToSourceLength; | ||||
|  | ||||
| 							// Смещение плоскости столкновения | ||||
| 							collisionOffset = collisionPoint.x * collisionNormal.x + collisionPoint.y * collisionNormal.y + collisionPoint.z * collisionNormal.z;  | ||||
| 							collisionPrimitive = collisionPrimitiveNearest; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Определение столкновения сферы с примитивом. | ||||
| 		 *  | ||||
| 		 * @param primitive примитив, столкновение с которым проверяется | ||||
| 		 */ | ||||
| 		private function calculateCollisionWithPrimitiveS(primitive:PolyPrimitive):void { | ||||
|  | ||||
| 			var length:uint = primitive.num; | ||||
| 			var points:Array = primitive.points; | ||||
| 			var normal:Point3D = primitive.face.globalNormal; | ||||
| 			var inside:Boolean = true; | ||||
|  | ||||
| 			for (var i:uint = 0; i < length; i++) { | ||||
|  | ||||
| 				var p1:Point3D = points[i]; | ||||
| 				var p2:Point3D = points[(i < length - 1) ? (i + 1) : 0]; | ||||
|  | ||||
| 				var edgeX:Number = p2.x - p1.x; | ||||
| 				var edgeY:Number = p2.y - p1.y; | ||||
| 				var edgeZ:Number = p2.z - p1.z; | ||||
|  | ||||
| 				var vectorX:Number = collisionPlanePoint.x - p1.x; | ||||
| 				var vectorY:Number = collisionPlanePoint.y - p1.y; | ||||
| 				var vectorZ:Number = collisionPlanePoint.z - p1.z; | ||||
|  | ||||
| 				var crossX:Number = vectorY * edgeZ - vectorZ * edgeY; | ||||
| 				var crossY:Number = vectorZ * edgeX - vectorX * edgeZ; | ||||
| 				var crossZ:Number = vectorX * edgeY - vectorY * edgeX; | ||||
|  | ||||
| 				if (crossX * normal.x + crossY * normal.y + crossZ * normal.z > 0) { | ||||
| 					// Точка за пределами полигона | ||||
| 					inside = false; | ||||
|  | ||||
| 					var edgeLengthSqr:Number = edgeX * edgeX + edgeY * edgeY + edgeZ * edgeZ; | ||||
| 					var edgeDistanceSqr:Number = (crossX * crossX + crossY * crossY + crossZ * crossZ) / edgeLengthSqr; | ||||
|  | ||||
| 					// Если расстояние до прямой меньше текущего ближайшего | ||||
| 					if (edgeDistanceSqr < collisionPrimitiveNearestLengthSqr) { | ||||
|  | ||||
| 						// Ищем нормализованный вектор ребра | ||||
| 						var edgeLength:Number = Math.sqrt(edgeLengthSqr); | ||||
| 						var edgeNormX:Number = edgeX / edgeLength; | ||||
| 						var edgeNormY:Number = edgeY / edgeLength; | ||||
| 						var edgeNormZ:Number = edgeZ / edgeLength; | ||||
|  | ||||
| 						// Находим расстояние до точки перпендикуляра вдоль ребра | ||||
| 						var t:Number = edgeNormX * vectorX + edgeNormY * vectorY + edgeNormZ * vectorZ; | ||||
|  | ||||
| 						var vectorLengthSqr:Number; | ||||
| 						if (t < 0) { | ||||
| 							// Ближайшая точка - первая | ||||
| 							vectorLengthSqr = vectorX * vectorX + vectorY * vectorY + vectorZ * vectorZ; | ||||
| 							if (vectorLengthSqr < collisionPrimitiveNearestLengthSqr) { | ||||
| 								collisionPrimitiveNearestLengthSqr = vectorLengthSqr; | ||||
| 								collisionPrimitivePoint.x = p1.x; | ||||
| 								collisionPrimitivePoint.y = p1.y; | ||||
| 								collisionPrimitivePoint.z = p1.z; | ||||
| 								collisionPrimitiveNearest = primitive; | ||||
| 							} | ||||
| 						} else { | ||||
| 							if (t > edgeLength) { | ||||
| 								// Ближайшая точка - вторая | ||||
| 								vectorX = collisionPlanePoint.x - p2.x; | ||||
| 								vectorY = collisionPlanePoint.y - p2.y; | ||||
| 								vectorZ = collisionPlanePoint.z - p2.z; | ||||
| 								vectorLengthSqr = vectorX * vectorX + vectorY * vectorY + vectorZ * vectorZ; | ||||
| 								if (vectorLengthSqr < collisionPrimitiveNearestLengthSqr) { | ||||
| 									collisionPrimitiveNearestLengthSqr = vectorLengthSqr; | ||||
| 									collisionPrimitivePoint.x = p2.x; | ||||
| 									collisionPrimitivePoint.y = p2.y; | ||||
| 									collisionPrimitivePoint.z = p2.z; | ||||
| 									collisionPrimitiveNearest = primitive; | ||||
| 								} | ||||
| 							} else { | ||||
| 								// Ближайшая точка на ребре | ||||
| 								collisionPrimitiveNearestLengthSqr = edgeDistanceSqr; | ||||
| 								collisionPrimitivePoint.x = p1.x + edgeNormX * t; | ||||
| 								collisionPrimitivePoint.y = p1.y + edgeNormY * t; | ||||
| 								collisionPrimitivePoint.z = p1.z + edgeNormZ * t; | ||||
| 								collisionPrimitiveNearest = primitive; | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Если попали в примитив | ||||
| 			if (inside) { | ||||
| 				collisionPrimitive = primitive; | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Проверка на действительное столкновение эллипсоида с плоскостью. | ||||
| 		 */ | ||||
| 		private function calculateCollisionWithPlaneE(plane:CollisionPlane):void { | ||||
| 			var normalX:Number = plane.node.normal.x; | ||||
| 			var normalY:Number = plane.node.normal.y; | ||||
| 			var normalZ:Number = plane.node.normal.z; | ||||
| 			// Смещение по направлению к плоскости вдоль нормали. Положительное смещение означает приближение к плоскости, отрицательное -- удаление | ||||
| 			// от плоскости, в этом случае столкновения не происходит. | ||||
| 			var displacementAlongNormal:Number = currentDisplacement.x * normalX + currentDisplacement.y * normalY + currentDisplacement.z * normalZ; | ||||
| 			if (plane.infront) { | ||||
| 				displacementAlongNormal = -displacementAlongNormal; | ||||
| 			} | ||||
| 			// Выходим из функции в случае удаления от плоскости | ||||
| 			if (displacementAlongNormal < 0) { | ||||
| 				return; | ||||
| 			} | ||||
| 			// Определение ближайшей к плоскости точки эллипсоида | ||||
| 			var k:Number = _radius / Math.sqrt(normalX * normalX * _scaleX2 + normalY * normalY * _scaleY2 + normalZ * normalZ * _scaleZ2); | ||||
| 			// Положение точки в локальной системе координат эллипсоида | ||||
| 			var localClosestX:Number = k * normalX * _scaleX2; | ||||
| 			var localClosestY:Number = k * normalY * _scaleY2; | ||||
| 			var localClosestZ:Number = k * normalZ * _scaleZ2; | ||||
| 			// Глобальные координаты точки | ||||
| 			var px:Number = collisionSource.x + localClosestX; | ||||
| 			var py:Number = collisionSource.y + localClosestY; | ||||
| 			var pz:Number = collisionSource.z + localClosestZ; | ||||
| 			// Растояние от найденной точки эллипсоида до плоскости | ||||
| 			var closestPointDistance:Number = px * normalX + py * normalY + pz * normalZ - plane.node.offset; | ||||
| 			if (!plane.infront) { | ||||
| 				closestPointDistance = -closestPointDistance; | ||||
| 			} | ||||
| 			if (closestPointDistance > plane.sourceOffset) { | ||||
| 				// Найдена наиболее удалённая точка, расчитываем вторую | ||||
| 				px = collisionSource.x - localClosestX; | ||||
| 				py = collisionSource.y - localClosestY; | ||||
| 				pz = collisionSource.z - localClosestZ; | ||||
| 				closestPointDistance = px * normalX + py * normalY + pz * normalZ - plane.node.offset; | ||||
| 				if (!plane.infront) { | ||||
| 					closestPointDistance = -closestPointDistance; | ||||
| 				} | ||||
| 			} | ||||
| 			// Если расстояние от ближайшей точки эллипсоида до плоскости больше, чем смещение эллипсоида вдоль нормали плоскости, | ||||
| 			// то столкновения не произошло и нужно завершить выполнение функции  | ||||
| 			if (closestPointDistance > displacementAlongNormal) { | ||||
| 				return; | ||||
| 			} | ||||
| 			// Если добрались до этого места, значит произошло столкновение с плоскостью. Требуется определить точку столкновения | ||||
| 			// с ближайшим полигоном, лежащим в этой плоскости | ||||
| 			if (closestPointDistance <= 0 ) { | ||||
| 				// Эллипсоид пересекается с плоскостью, ищем проекцию ближайшей точки эллипсоида на плоскость | ||||
| 				if (plane.infront) { | ||||
| 					collisionPlanePoint.x = px - normalX * closestPointDistance; | ||||
| 					collisionPlanePoint.y = py - normalY * closestPointDistance; | ||||
| 					collisionPlanePoint.z = pz - normalZ * closestPointDistance; | ||||
| 				} else { | ||||
| 					collisionPlanePoint.x = px + normalX * closestPointDistance; | ||||
| 					collisionPlanePoint.y = py + normalY * closestPointDistance; | ||||
| 					collisionPlanePoint.z = pz + normalZ * closestPointDistance; | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Эллипсоид не пересекается с плоскостью, ищем точку контакта | ||||
| 				var t:Number = closestPointDistance / displacementAlongNormal; | ||||
| 				collisionPlanePoint.x = px + currentDisplacement.x * t; | ||||
| 				collisionPlanePoint.y = py + currentDisplacement.y * t; | ||||
| 				collisionPlanePoint.z = pz + currentDisplacement.z * t; | ||||
| 			} | ||||
| 			// Проверяем примитивы плоскости | ||||
| 			var primitive:*; | ||||
| 			collisionPrimitiveNearestLengthSqr = Number.MAX_VALUE; | ||||
| 			collisionPrimitiveNearest = null; | ||||
| 			if (plane.infront) { | ||||
| 				if ((primitive = plane.node.primitive) != null) { | ||||
| 					if (((_collisionSetMode == CollisionSetMode.EXCLUDE) && (collisionSet == null || !(collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) || | ||||
| 					 		((_collisionSetMode == CollisionSetMode.INCLUDE) && (collisionSet != null) && (collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) { | ||||
| 						calculateCollisionWithPrimitiveE(primitive); | ||||
| 					} | ||||
| 				} else { | ||||
| 					for (primitive in plane.node.frontPrimitives) { | ||||
| 						if (((_collisionSetMode == CollisionSetMode.EXCLUDE) && (collisionSet == null || !(collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) || | ||||
| 						 		((_collisionSetMode == CollisionSetMode.INCLUDE) && (collisionSet != null) && (collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) { | ||||
| 							calculateCollisionWithPrimitiveE(primitive); | ||||
| 							if (collisionPrimitive != null) { | ||||
| 								break; | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				for (primitive in plane.node.backPrimitives) { | ||||
| 					if (((_collisionSetMode == CollisionSetMode.EXCLUDE) && (collisionSet == null || !(collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) || | ||||
| 					 		((_collisionSetMode == CollisionSetMode.INCLUDE) && (collisionSet != null) && (collisionSet[primitive.face._mesh] || collisionSet[primitive.face._surface]))) { | ||||
| 						calculateCollisionWithPrimitiveE(primitive); | ||||
| 						if (collisionPrimitive != null) { | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			if (collisionPrimitive != null) { | ||||
| 				// Если точка пересечения попала в примитив | ||||
| 				// Нормаль плоскости при столкновении - нормаль плоскости примитива | ||||
| 				if (plane.infront) { | ||||
| 					collisionNormal.x = normalX; | ||||
| 					collisionNormal.y = normalY; | ||||
| 					collisionNormal.z = normalZ; | ||||
| 					collisionOffset = plane.node.offset; | ||||
| 				} else { | ||||
| 					collisionNormal.x = -normalX; | ||||
| 					collisionNormal.y = -normalY; | ||||
| 					collisionNormal.z = -normalZ; | ||||
| 					collisionOffset = -plane.node.offset; | ||||
| 				} | ||||
| 				// Радиус эллипсоида в точке столкновения | ||||
| 				collisionRadius = localClosestX * collisionNormal.x + localClosestY * collisionNormal.y + localClosestZ * collisionNormal.z; | ||||
| 				if (collisionRadius < 0) { | ||||
| 					collisionRadius = -collisionRadius; | ||||
| 				} | ||||
| 				radiusVector.x = px - collisionSource.x; | ||||
| 				radiusVector.y = py - collisionSource.y; | ||||
| 				radiusVector.z = pz - collisionSource.z; | ||||
| 				// Точка столкновения совпадает с точкой столкновения с плоскостью примитива | ||||
| 				collisionPoint.x = collisionPlanePoint.x; | ||||
| 				collisionPoint.y = collisionPlanePoint.y; | ||||
| 				collisionPoint.z = collisionPlanePoint.z; | ||||
| 			} else { | ||||
| 				// Если точка пересечения не попала внутрь примитива, находим пересечение с ближайшей точкой ближайшего примитива | ||||
| 				// Трансформированная в пространство эллипсоида ближайшая точка на примитиве | ||||
| 				px = collisionPrimitivePoint.x; | ||||
| 				py = collisionPrimitivePoint.y; | ||||
| 				pz = collisionPrimitivePoint.z; | ||||
| 				 | ||||
| 				var collisionExists:Boolean; | ||||
| 				// Квадрат расстояния из центра эллипсоида до точки примитива  | ||||
| 				var r2:Number = px*px + py*py + pz*pz; | ||||
| 				if (r2 < _radius2) { | ||||
| 					// Точка оказалась внутри эллипсоида, находим точку на поверхности эллипсоида, лежащую на том же радиусе | ||||
| 					k = _radius / Math.sqrt(r2); | ||||
| 					px *= k * _scaleX; | ||||
| 					py *= k * _scaleY; | ||||
| 					pz *= k * _scaleZ; | ||||
| 					 | ||||
| 					collisionExists = true; | ||||
| 				} else { | ||||
| 					// Точка вне эллипсоида, находим пересечение луча, направленного противоположно скорости эллипсоида из точки | ||||
| 					// примитива, с поверхностью эллипсоида | ||||
| 					// Трансформированный в пространство эллипсоида противоположный вектор скорости | ||||
| 					var vx:Number = - currentDisplacement.x / _scaleX; | ||||
| 					var vy:Number = - currentDisplacement.y / _scaleY; | ||||
| 					var vz:Number = - currentDisplacement.z / _scaleZ; | ||||
| 					// Нахождение точки пересечения сферы и луча, направленного вдоль вектора скорости | ||||
| 					var a:Number = vx*vx + vy*vy + vz*vz; | ||||
| 					var b:Number = 2 * (px*vx + py*vy + pz*vz); | ||||
| 					var c:Number = r2 - _radius2; | ||||
| 					var d:Number = b*b - 4*a*c; | ||||
| 					// Решение есть только при действительном дискриминанте квадратного уравнения | ||||
| 					if (d >=0) { | ||||
| 						// Выбирается минимальное время, т.к. нужна первая точка пересечения | ||||
| 						t = -0.5 * (b + Math.sqrt(d)) / a; | ||||
| 						// Точка лежит на луче только если время положительное | ||||
| 						if (t >= 0 && t <= 1) { | ||||
| 							// Координаты точки пересечения луча с эллипсоидом, переведённые обратно в нормальное пространство | ||||
| 							px = (px + t * vx) * _scaleX; | ||||
| 							py = (py + t * vy) * _scaleY; | ||||
| 							pz = (pz + t * vz) * _scaleZ; | ||||
|  | ||||
| 							collisionExists = true; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				if (collisionExists) { | ||||
| 					// Противоположная нормаль к эллипсоиду в точке пересечения | ||||
| 					collisionNormal.x = - px / _scaleX2; | ||||
| 					collisionNormal.y = - py / _scaleY2; | ||||
| 					collisionNormal.z = - pz / _scaleZ2; | ||||
| 					collisionNormal.normalize(); | ||||
| 					// Радиус эллипсоида в точке столкновения | ||||
| 					collisionRadius = px * collisionNormal.x + py * collisionNormal.y + pz * collisionNormal.z; | ||||
| 					if (collisionRadius < 0) { | ||||
| 						collisionRadius = -collisionRadius; | ||||
| 					} | ||||
| 					radiusVector.x = px; | ||||
| 					radiusVector.y = py; | ||||
| 					radiusVector.z = pz; | ||||
| 					// Точка столкновения в ближайшей точке | ||||
| 					collisionPoint.x = collisionPrimitivePoint.x * _scaleX + currentCoords.x; | ||||
| 					collisionPoint.y = collisionPrimitivePoint.y * _scaleY + currentCoords.y; | ||||
| 					collisionPoint.z = collisionPrimitivePoint.z * _scaleZ + currentCoords.z; | ||||
| 					// Смещение плоскости столкновения | ||||
| 					collisionOffset = collisionPoint.x * collisionNormal.x + collisionPoint.y * collisionNormal.y + collisionPoint.z * collisionNormal.z;  | ||||
| 					collisionPrimitive = collisionPrimitiveNearest; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @private | ||||
| 		 * Определение наличия столкновения эллипсоида с примитивом. Все расчёты выполняются в пространстве эллипсоида, где он выглядит | ||||
| 		 * как сфера. По окончании работы может быть установлена переменная collisionPrimitive в случае попадания точки | ||||
| 		 * столкновения внутрь примитива или collisionPrimitiveNearest в случае столкновения с ребром примитива через | ||||
| 		 * минимальное время. | ||||
| 		 *  | ||||
| 		 * @param primitive примитив, столкновение с которым проверяется | ||||
| 		 */ | ||||
| 		private function calculateCollisionWithPrimitiveE(primitive:PolyPrimitive):void { | ||||
| 			var length:uint = primitive.num; | ||||
| 			var points:Array = primitive.points; | ||||
| 			var normal:Point3D = primitive.face.globalNormal; | ||||
| 			var inside:Boolean = true; | ||||
|  | ||||
| 			var point1:Point3D; | ||||
| 			var point2:Point3D = points[length - 1]; | ||||
| 			p2.x = (point2.x - currentCoords.x) / _scaleX; | ||||
| 			p2.y = (point2.y - currentCoords.y) / _scaleY; | ||||
| 			p2.z = (point2.z - currentCoords.z) / _scaleZ; | ||||
| 			 | ||||
| 			localCollisionPlanePoint.x = (collisionPlanePoint.x - currentCoords.x) / _scaleX; | ||||
| 			localCollisionPlanePoint.y = (collisionPlanePoint.y - currentCoords.y) / _scaleY; | ||||
| 			localCollisionPlanePoint.z = (collisionPlanePoint.z - currentCoords.z) / _scaleZ; | ||||
| 			// Обход всех рёбер примитива | ||||
| 			for (var i:uint = 0; i < length; i++) { | ||||
| 				point1 = point2; | ||||
| 				point2 = points[i]; | ||||
| 				 | ||||
| 				p1.x = p2.x; | ||||
| 				p1.y = p2.y; | ||||
| 				p1.z = p2.z; | ||||
|  | ||||
| 				p2.x = (point2.x - currentCoords.x) / _scaleX; | ||||
| 				p2.y = (point2.y - currentCoords.y) / _scaleY; | ||||
| 				p2.z = (point2.z - currentCoords.z) / _scaleZ; | ||||
|  | ||||
| 				// Расчёт векторного произведения вектора ребра на радиус-вектор точки столкновения относительно начала ребра | ||||
| 				// с целью определения положения точки столкновения относительно полигона | ||||
| 				var edgeX:Number = p2.x - p1.x; | ||||
| 				var edgeY:Number = p2.y - p1.y; | ||||
| 				var edgeZ:Number = p2.z - p1.z; | ||||
|  | ||||
| 				var vectorX:Number = localCollisionPlanePoint.x - p1.x; | ||||
| 				var vectorY:Number = localCollisionPlanePoint.y - p1.y; | ||||
| 				var vectorZ:Number = localCollisionPlanePoint.z - p1.z; | ||||
|  | ||||
| 				var crossX:Number = vectorY * edgeZ - vectorZ * edgeY; | ||||
| 				var crossY:Number = vectorZ * edgeX - vectorX * edgeZ; | ||||
| 				var crossZ:Number = vectorX * edgeY - vectorY * edgeX; | ||||
|  | ||||
| 				if (crossX * normal.x + crossY * normal.y + crossZ * normal.z > 0) { | ||||
| 					// Точка за пределами полигона | ||||
| 					inside = false; | ||||
|  | ||||
| 					var edgeLengthSqr:Number = edgeX * edgeX + edgeY * edgeY + edgeZ * edgeZ; | ||||
| 					var edgeDistanceSqr:Number = (crossX * crossX + crossY * crossY + crossZ * crossZ) / edgeLengthSqr; | ||||
|  | ||||
| 					// Если расстояние до прямой меньше текущего ближайшего | ||||
| 					if (edgeDistanceSqr < collisionPrimitiveNearestLengthSqr) { | ||||
| 						// Ищем нормализованный вектор ребра | ||||
| 						var edgeLength:Number = Math.sqrt(edgeLengthSqr); | ||||
| 						var edgeNormX:Number = edgeX / edgeLength; | ||||
| 						var edgeNormY:Number = edgeY / edgeLength; | ||||
| 						var edgeNormZ:Number = edgeZ / edgeLength; | ||||
| 						// Находим расстояние до точки перпендикуляра вдоль ребра | ||||
| 						var t:Number = edgeNormX * vectorX + edgeNormY * vectorY + edgeNormZ * vectorZ; | ||||
| 						var vectorLengthSqr:Number; | ||||
| 						if (t < 0) { | ||||
| 							// Ближайшая точка - первая | ||||
| 							vectorLengthSqr = vectorX * vectorX + vectorY * vectorY + vectorZ * vectorZ; | ||||
| 							if (vectorLengthSqr < collisionPrimitiveNearestLengthSqr) { | ||||
| 								collisionPrimitiveNearestLengthSqr = vectorLengthSqr; | ||||
| 								collisionPrimitivePoint.x = p1.x; | ||||
| 								collisionPrimitivePoint.y = p1.y; | ||||
| 								collisionPrimitivePoint.z = p1.z; | ||||
| 								collisionPrimitiveNearest = primitive; | ||||
| 							} | ||||
| 						} else { | ||||
| 							if (t > edgeLength) { | ||||
| 								// Ближайшая точка - вторая | ||||
| 								vectorX = localCollisionPlanePoint.x - p2.x; | ||||
| 								vectorY = localCollisionPlanePoint.y - p2.y; | ||||
| 								vectorZ = localCollisionPlanePoint.z - p2.z; | ||||
| 								vectorLengthSqr = vectorX * vectorX + vectorY * vectorY + vectorZ * vectorZ; | ||||
| 								if (vectorLengthSqr < collisionPrimitiveNearestLengthSqr) { | ||||
| 									collisionPrimitiveNearestLengthSqr = vectorLengthSqr; | ||||
| 									collisionPrimitivePoint.x = p2.x; | ||||
| 									collisionPrimitivePoint.y = p2.y; | ||||
| 									collisionPrimitivePoint.z = p2.z; | ||||
| 									collisionPrimitiveNearest = primitive; | ||||
| 								} | ||||
| 							} else { | ||||
| 								// Ближайшая точка на ребре | ||||
| 								collisionPrimitiveNearestLengthSqr = edgeDistanceSqr; | ||||
| 								collisionPrimitivePoint.x = p1.x + edgeNormX * t; | ||||
| 								collisionPrimitivePoint.y = p1.y + edgeNormY * t; | ||||
| 								collisionPrimitivePoint.z = p1.z + edgeNormZ * t; | ||||
| 								collisionPrimitiveNearest = primitive; | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Если попали в примитив | ||||
| 			if (inside) { | ||||
| 				collisionPrimitive = primitive; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,41 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 102 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/primitives | ||||
| END | ||||
| Plane.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 111 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/primitives/Plane.as | ||||
| END | ||||
| GeoSphere.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 115 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/primitives/GeoSphere.as | ||||
| END | ||||
| Cone.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 110 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/primitives/Cone.as | ||||
| END | ||||
| Sphere.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 112 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/primitives/Sphere.as | ||||
| END | ||||
| Box.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 109 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/primitives/Box.as | ||||
| END | ||||
| GeoPlane.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 114 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/primitives/GeoPlane.as | ||||
| END | ||||
| @@ -1,100 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/engine3d/primitives | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| Plane.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| b4c9c0e49de8cd3f4040482cb679d638 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| GeoSphere.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 8abcc53f0a951d85fde9466754beb2f9 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Cone.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 1a4abe159a3bd66b895545b0768399a9 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Sphere.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 307a9a635773182ee6de473ff6fab9d9 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| Box.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| be9c2bf9df095181eacdee4e3c5a66e1 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| GeoPlane.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 1ff3ee62127da83b17c3e4a163979d14 | ||||
| 2008-08-25T13:44:47.077292Z | ||||
| 176 | ||||
| int | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
| @@ -1,316 +0,0 @@ | ||||
| package alternativa.engine3d.primitives { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.engine3d.core.Surface; | ||||
| 	 | ||||
| 	import flash.geom.Point; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Прямоугольный параллелепипед. | ||||
| 	 */ | ||||
| 	public class Box extends Mesh { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создание нового параллелепипеда.  | ||||
| 		 * <p>Параллелепипед после создания будет содержать в себе шесть поверхностей.  | ||||
| 		 * <code>"front"</code>, <code>"back"</code>, <code>"left"</code>, <code>"right"</code>, <code>"top"</code>, <code>"bottom"</code> | ||||
| 		 * на каждую из которых может быть установлен свой материал.</p> | ||||
| 		 *  | ||||
| 		 * @param width ширина. Размерность по оси X. Не может быть меньше нуля. | ||||
| 		 * @param length длина. Размерность по оси Y. Не может быть меньше нуля. | ||||
| 		 * @param height высота. Размерность по оси Z. Не может быть меньше нуля. | ||||
| 		 * @param widthSegments количество сегментов по ширине | ||||
| 		 * @param lengthSegments количество сегментов по длине | ||||
| 		 * @param heightSegments количество сегментов по по высоте | ||||
| 		 * @param reverse задает направление нормалей граней. Если указано значение <code>true</code>, то нормали будут направлены внутрь фигуры. | ||||
| 		 * @param triangulate флаг триангуляции. Если указано значение <code>true</code>, четырехугольники в параллелепипеде будут триангулированы. | ||||
| 		 */ | ||||
| 		public function Box(width:Number = 100, length:Number = 100, height:Number = 100, widthSegments:uint = 1, lengthSegments:uint = 1, heightSegments:uint = 1, reverse:Boolean = false, triangulate:Boolean = false) { | ||||
| 			super(); | ||||
| 			 | ||||
| 			if ((widthSegments == 0) || (lengthSegments == 0) || (heightSegments == 0)) { | ||||
| 				return; | ||||
| 			} | ||||
| 			width = (width < 0)? 0 : width; | ||||
| 			length = (length < 0)? 0 : length; | ||||
| 			height = (height < 0)? 0 : height; | ||||
| 			 | ||||
| 			var wh:Number = width/2; | ||||
| 			var lh:Number = length/2; | ||||
| 			var hh:Number = height/2; | ||||
| 			var ws:Number = width/widthSegments; | ||||
| 			var ls:Number = length/lengthSegments; | ||||
| 			var hs:Number = height/heightSegments; | ||||
| 			var x:int; | ||||
| 			var y:int; | ||||
| 			var z:int; | ||||
| 			 | ||||
| 			// Создание точек | ||||
| 			for (x = 0; x <= widthSegments; x++) { | ||||
| 				for (y = 0; y <= lengthSegments; y++) { | ||||
| 					for (z = 0; z <= heightSegments; z++) { | ||||
| 						if (x == 0 || x == widthSegments || y == 0 || y == lengthSegments || z == 0 || z == heightSegments) { | ||||
| 							createVertex(x*ws - wh, y*ls - lh, z*hs - hh, x + "_" + y + "_" + z); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Создание поверхностей | ||||
| 			var front:Surface = createSurface(null, "front"); | ||||
| 			var back:Surface = createSurface(null, "back"); | ||||
| 			var left:Surface = createSurface(null, "left"); | ||||
| 			var right:Surface = createSurface(null, "right"); | ||||
| 			var top:Surface = createSurface(null, "top"); | ||||
| 			var bottom:Surface = createSurface(null, "bottom"); | ||||
|  | ||||
| 			// Создание граней | ||||
| 			var wd:Number = 1/widthSegments; | ||||
| 			var ld:Number = 1/lengthSegments; | ||||
| 			var hd:Number = 1/heightSegments; | ||||
| 			var faceId:String; | ||||
| 			 | ||||
| 			// Для оптимизаций UV при триангуляции | ||||
| 			var aUV:Point; | ||||
| 			var cUV:Point; | ||||
| 			 | ||||
| 			// Построение верхней грани | ||||
| 			for (y = 0; y < lengthSegments; y++) { | ||||
| 				for (x = 0; x < widthSegments; x++) { | ||||
| 					faceId = "top_"+x+"_"+y; | ||||
| 					if (reverse) { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point(x*wd, (lengthSegments - y)*ld); | ||||
| 							cUV = new Point((x + 1)*wd, (lengthSegments - y - 1)*ld); | ||||
| 							createFace([x + "_" + y + "_" + heightSegments, x + "_" + (y + 1) + "_" + heightSegments, (x + 1) + "_" + (y + 1) + "_" + heightSegments], faceId + ":1"); | ||||
| 							setUVsToFace(aUV, new Point(x*wd, (lengthSegments - y - 1)*ld), cUV, faceId + ":1"); | ||||
| 							createFace([(x + 1) + "_" + (y + 1) + "_" + heightSegments, (x + 1) + "_" + y + "_" + heightSegments, x + "_" + y + "_" + heightSegments], faceId + ":0"); | ||||
| 							setUVsToFace(cUV, new Point((x + 1)*wd, (lengthSegments - y)*ld), aUV, faceId + ":0"); | ||||
| 						} else { | ||||
| 							createFace([x + "_" + y + "_" + heightSegments, x + "_" + (y + 1) + "_" + heightSegments, (x + 1) + "_" + (y + 1) + "_" + heightSegments, (x + 1) + "_" + y + "_" + heightSegments], faceId); | ||||
| 							setUVsToFace(new Point(x*wd, (lengthSegments - y)*ld), new Point(x*wd, (lengthSegments - y - 1)*ld), new Point((x + 1)*wd, (lengthSegments - y - 1)*ld), faceId); | ||||
| 						} | ||||
| 					} else { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point(x*wd, y*ld); | ||||
| 							cUV = new Point((x + 1)*wd, (y + 1)*ld); | ||||
| 							createFace([x + "_" + y + "_" + heightSegments, (x + 1) + "_" + y + "_" + heightSegments, (x + 1) + "_" + (y + 1) + "_" + heightSegments], faceId + ":0"); | ||||
| 							setUVsToFace(aUV, new Point((x + 1)*wd, y*ld), cUV, faceId + ":0"); | ||||
| 							createFace([(x + 1) + "_" + (y + 1) + "_" + heightSegments, x + "_" + (y + 1) + "_" + heightSegments, x + "_" + y + "_" + heightSegments], faceId + ":1"); | ||||
| 							setUVsToFace(cUV, new Point(x*wd, (y + 1)*ld), aUV, faceId + ":1"); | ||||
| 						} else { | ||||
| 							createFace([x + "_" + y + "_" + heightSegments, (x + 1) + "_" + y + "_" + heightSegments, (x + 1) + "_" + (y + 1) + "_" + heightSegments, x + "_" + (y + 1) + "_" + heightSegments], faceId); | ||||
| 							setUVsToFace(new Point(x*wd, y*ld), new Point((x + 1)*wd, y*ld), new Point((x + 1)*wd, (y + 1)*ld), faceId); | ||||
| 						} | ||||
| 					} | ||||
| 					if (triangulate) { | ||||
| 						top.addFace(faceId + ":0"); | ||||
| 						top.addFace(faceId + ":1"); | ||||
| 					} else { | ||||
| 						top.addFace(faceId); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Построение нижней грани | ||||
| 			for (y = 0; y < lengthSegments; y++) { | ||||
| 				for (x = 0; x < widthSegments; x++) { | ||||
| 					faceId = "bottom_" + x + "_" + y; | ||||
| 					if (reverse) { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point((widthSegments - x)*wd, (lengthSegments - y)*ld); | ||||
| 							cUV = new Point((widthSegments - x - 1)*wd, (lengthSegments - y - 1)*ld); | ||||
| 							createFace([x + "_" + y + "_" + 0, (x + 1) + "_" + y + "_" + 0, (x + 1) + "_" + (y + 1) + "_" + 0], faceId + ":0"); | ||||
| 							setUVsToFace(aUV, new Point((widthSegments - x - 1)*wd, (lengthSegments - y)*ld), cUV, faceId + ":0"); | ||||
| 							createFace([(x + 1) + "_" + (y + 1) + "_" + 0, x + "_" + (y + 1) + "_" + 0, x + "_" + y + "_" + 0], faceId + ":1"); | ||||
| 							setUVsToFace(cUV, new Point((widthSegments - x)*wd, (lengthSegments - y - 1)*ld), aUV, faceId + ":1"); | ||||
| 						} else { | ||||
| 							createFace([x + "_" + y + "_"+0, (x + 1) + "_" + y + "_" + 0, (x + 1) + "_" + (y + 1) + "_" + 0, x + "_" + (y + 1) + "_" + 0], faceId); | ||||
| 							setUVsToFace(new Point((widthSegments - x)*wd, (lengthSegments - y)*ld), new Point((widthSegments - x - 1)*wd, (lengthSegments - y)*ld), new Point((widthSegments - x - 1)*wd, (lengthSegments - y - 1)*ld), faceId); | ||||
| 						} | ||||
| 					} else { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point((widthSegments - x)*wd, y*ld); | ||||
| 							cUV = new Point((widthSegments - x - 1)*wd, (y + 1)*ld); | ||||
| 							createFace([x + "_" + y + "_" + 0, x + "_" + (y + 1) + "_" + 0, (x + 1) + "_" + (y + 1) + "_" + 0], faceId + ":1"); | ||||
| 							setUVsToFace(aUV, new Point((widthSegments - x)*wd, (y + 1)*ld), cUV, faceId + ":1"); | ||||
| 							createFace([(x + 1) + "_" + (y + 1) + "_" + 0, (x + 1) + "_" + y + "_" + 0, x + "_" + y + "_" + 0], faceId + ":0"); | ||||
| 							setUVsToFace(cUV, new Point((widthSegments - x - 1)*wd, y*ld), aUV, faceId + ":0"); | ||||
| 						} else { | ||||
| 							createFace([x + "_" + y + "_" + 0, x + "_" + (y + 1) +"_" + 0, (x + 1) + "_" + (y + 1) + "_" + 0, (x + 1) + "_" + y + "_" + 0], faceId); | ||||
| 							setUVsToFace(new Point((widthSegments - x)*wd, y*ld), new Point((widthSegments - x)*wd, (y + 1)*ld), new Point((widthSegments - x - 1)*wd, (y + 1)*ld), faceId); | ||||
| 						} | ||||
| 					} | ||||
| 					if (triangulate) { | ||||
| 						bottom.addFace(faceId + ":0"); | ||||
| 						bottom.addFace(faceId + ":1"); | ||||
| 					} else { | ||||
| 						bottom.addFace(faceId); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Построение фронтальной грани | ||||
| 			for (z = 0; z < heightSegments; z++) { | ||||
| 				for (x = 0; x < widthSegments; x++) { | ||||
| 					faceId = "front_"+x+"_"+z; | ||||
| 					if (reverse) { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point((widthSegments - x)*wd, z*hd); | ||||
| 							cUV = new Point((widthSegments - x - 1)*wd, (z + 1)*hd); | ||||
| 							createFace([x + "_" + 0 + "_" + z, x + "_" + 0 + "_" + (z + 1), (x + 1) + "_" + 0 + "_" + (z + 1)], faceId + ":1"); | ||||
| 							setUVsToFace(aUV, new Point((widthSegments - x)*wd, (z + 1)*hd), cUV, faceId + ":1"); | ||||
| 							createFace([(x + 1) + "_" + 0 + "_" + (z + 1), (x + 1) + "_" + 0 + "_" + z, x + "_" + 0 + "_" + z], faceId + ":0"); | ||||
| 							setUVsToFace(cUV, new Point((widthSegments - x - 1)*wd, z*hd), aUV, faceId + ":0"); | ||||
| 						} else { | ||||
| 							createFace([x + "_" + 0 + "_" + z, x + "_" + 0 + "_" + (z + 1), (x + 1) + "_" + 0 + "_" + (z + 1), (x + 1) + "_" + 0 + "_" + z], faceId); | ||||
| 							setUVsToFace(new Point((widthSegments - x)*wd, z*hd), new Point((widthSegments - x)*wd, (z + 1)*hd), new Point((widthSegments - x - 1)*wd, (z + 1)*hd), faceId); | ||||
| 						} | ||||
| 					} else { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point(x*wd, z*hd); | ||||
| 							cUV = new Point((x + 1)*wd, (z + 1)*hd); | ||||
| 							createFace([x + "_" + 0 + "_" + z, (x + 1) + "_" + 0 + "_" + z, (x + 1) + "_" + 0 + "_" + (z + 1)], faceId + ":0"); | ||||
| 							setUVsToFace(aUV, new Point((x + 1)*wd, z*hd), cUV, faceId + ":0"); | ||||
| 							createFace([(x + 1) + "_" + 0 + "_" + (z + 1), x + "_" + 0 + "_" + (z + 1), x + "_" + 0 + "_" + z], faceId + ":1"); | ||||
| 							setUVsToFace(cUV, new Point(x*wd, (z + 1)*hd), aUV, faceId + ":1"); | ||||
| 						} else { | ||||
| 							createFace([x + "_" + 0 + "_" + z, (x + 1) + "_" + 0 + "_" + z, (x + 1) + "_" + 0 + "_" + (z + 1), x + "_" + 0 + "_" + (z + 1)], faceId); | ||||
| 							setUVsToFace(new Point(x*wd, z*hd), new Point((x + 1)*wd, z*hd), new Point((x + 1)*wd, (z + 1)*hd), faceId); | ||||
| 						} | ||||
| 					} | ||||
| 					if (triangulate) { | ||||
| 						front.addFace(faceId + ":0"); | ||||
| 						front.addFace(faceId + ":1"); | ||||
| 					} else { | ||||
| 						front.addFace(faceId); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Построение задней грани | ||||
| 			for (z = 0; z < heightSegments; z++) { | ||||
| 				for (x = 0; x < widthSegments; x++) { | ||||
| 					faceId = "back_"+x+"_"+z; | ||||
| 					if (reverse) { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point(x * wd, (z + 1) * hd); | ||||
| 							cUV = new Point((x + 1) * wd, z * hd); | ||||
| 							createFace([x + "_" + lengthSegments+"_" + (z + 1), x + "_"+lengthSegments + "_" + z, (x + 1) + "_" + lengthSegments + "_" + z], faceId + ":0"); | ||||
| 							setUVsToFace(aUV, new Point(x * wd, z * hd), cUV, faceId + ":0"); | ||||
| 							createFace([(x + 1) + "_" + lengthSegments + "_" + z, (x + 1) + "_" + lengthSegments + "_" + (z + 1), x + "_" + lengthSegments + "_" + (z + 1)], faceId + ":1"); | ||||
| 							setUVsToFace(cUV, new Point((x + 1) * wd, (z + 1) * hd), aUV, faceId + ":1"); | ||||
| 						} else { | ||||
| 							createFace([x + "_" + lengthSegments + "_" + z, (x + 1) + "_" + lengthSegments + "_" + z, (x + 1) + "_" + lengthSegments + "_" + (z + 1), x + "_" + lengthSegments + "_" + (z + 1)], faceId); | ||||
| 							setUVsToFace(new Point(x*wd, z*hd), new Point((x + 1)*wd, z*hd), new Point((x + 1)*wd, (z + 1)*hd), faceId); | ||||
| 						} | ||||
| 					} else { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point((widthSegments - x)*wd, (z + 1)*hd); | ||||
| 							cUV = new Point((widthSegments - x - 1)*wd, z*hd); | ||||
| 							createFace([x + "_" + lengthSegments + "_" + z, x + "_" + lengthSegments + "_" + (z + 1), (x + 1) + "_" + lengthSegments + "_" + z], faceId + ":0"); | ||||
| 							setUVsToFace(new Point((widthSegments - x)*wd, z*hd), aUV, cUV, faceId + ":0"); | ||||
| 							createFace([x + "_" + lengthSegments + "_" + (z + 1), (x + 1) + "_" + lengthSegments + "_" + (z + 1), (x + 1) + "_" + lengthSegments + "_" + z], faceId + ":1"); | ||||
| 							setUVsToFace(aUV, new Point((widthSegments - x - 1)*wd, (z + 1)*hd), cUV, faceId + ":1"); | ||||
| 						} else { | ||||
| 							createFace([x + "_" + lengthSegments + "_" + z, x + "_" + lengthSegments + "_" + (z + 1), (x + 1) + "_" + lengthSegments + "_" + (z + 1), (x + 1) + "_" + lengthSegments + "_" + z], faceId); | ||||
| 							setUVsToFace(new Point((widthSegments - x)*wd, z*hd), new Point((widthSegments - x)*wd, (z + 1)*hd), new Point((widthSegments - x - 1)*wd, (z + 1)*hd), faceId); | ||||
| 						} | ||||
| 					} | ||||
| 					if (triangulate) { | ||||
| 						back.addFace(faceId + ":0"); | ||||
| 						back.addFace(faceId + ":1"); | ||||
| 					} else { | ||||
| 						back.addFace(faceId); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Построение левой грани | ||||
| 			for (y = 0; y < lengthSegments; y++) { | ||||
| 				for (z = 0; z < heightSegments; z++) { | ||||
| 					faceId = "left_" + y + "_" + z; | ||||
| 					if (reverse) { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point(y*ld, (z + 1)*hd); | ||||
| 							cUV = new Point((y + 1)*ld, z*hd); | ||||
| 							createFace([0 + "_" + y + "_" + (z + 1), 0 + "_" + y + "_" + z, 0 + "_" + (y + 1) + "_" + z], faceId + ":0"); | ||||
| 							setUVsToFace(aUV, new Point(y*ld, z*hd), cUV, faceId + ":0"); | ||||
| 							createFace([0 + "_" + (y + 1) + "_" + z, 0 + "_" + (y + 1) + "_" + (z + 1), 0 + "_" + y + "_" + (z + 1)], faceId + ":1"); | ||||
| 							setUVsToFace(cUV, new Point((y + 1)*ld, (z + 1)*hd), aUV, faceId + ":1"); | ||||
| 						} else { | ||||
| 							createFace([0 + "_" + y + "_" + z, 0 + "_" + (y + 1) + "_" + z, 0 + "_" + (y + 1) + "_" + (z + 1), 0 + "_" + y + "_" + (z + 1)], faceId); | ||||
| 							setUVsToFace(new Point(y*ld, z*hd), new Point((y + 1)*ld, z*hd), new Point((y + 1)*ld, (z + 1)*hd), faceId); | ||||
| 						} | ||||
| 					} else { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point((lengthSegments - y - 1)*ld, z*hd); | ||||
| 							cUV = new Point((lengthSegments - y)*ld, (z + 1)*hd); | ||||
| 							createFace([0 + "_" + (y + 1) + "_" + z, 0 + "_" + y + "_" + z, 0 + "_" + y + "_" + (z + 1)], faceId + ":0"); | ||||
| 							setUVsToFace(aUV, new Point((lengthSegments - y)*ld, z*hd), cUV, faceId + ":0"); | ||||
| 							createFace([0 + "_" + y + "_" + (z + 1), 0 + "_" + ((y + 1)) + "_" + (z + 1), 0 + "_" + (y + 1) + "_" + z], faceId + ":1"); | ||||
| 							setUVsToFace(cUV, new Point((lengthSegments - y - 1)*ld, (z + 1)*hd), aUV, faceId + ":1"); | ||||
| 						} else { | ||||
| 							createFace([0 + "_" + y + "_" + z, 0 + "_" + y + "_" + (z + 1), 0 + "_" + ((y + 1)) + "_" + (z + 1), 0 + "_" + ((y + 1)) + "_" + z], faceId); | ||||
| 							setUVsToFace(new Point((lengthSegments - y)*ld, z*hd), new Point((lengthSegments - y)*ld, (z + 1)*hd), new Point((lengthSegments - y - 1)*ld, (z + 1)*hd), faceId); | ||||
| 						} | ||||
| 					} | ||||
| 					if (triangulate) { | ||||
| 						left.addFace(faceId + ":0"); | ||||
| 						left.addFace(faceId + ":1"); | ||||
| 					} else { | ||||
| 						left.addFace(faceId); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Построение правой грани | ||||
| 			for (y = 0; y < lengthSegments; y++) { | ||||
| 				for (z = 0; z < heightSegments; z++) { | ||||
| 					faceId = "right_" + y + "_" + z; | ||||
| 					if (reverse) { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point((lengthSegments - y)*ld, z*hd); | ||||
| 							cUV = new Point((lengthSegments - y - 1)*ld, (z + 1)*hd); | ||||
| 							createFace([widthSegments + "_" + y + "_" + z, widthSegments + "_" + y + "_" + (z + 1), widthSegments + "_" + (y + 1) + "_" + (z + 1)], faceId + ":1"); | ||||
| 							setUVsToFace(aUV, new Point((lengthSegments - y)*ld, (z + 1)*hd), cUV, faceId + ":1"); | ||||
| 							createFace([widthSegments + "_" + (y + 1) + "_" + (z + 1), widthSegments + "_" + (y + 1) + "_" + z, widthSegments + "_" + y + "_" + z], faceId + ":0"); | ||||
| 							setUVsToFace(cUV, new Point((lengthSegments - y - 1)*ld, z*hd), aUV, faceId + ":0"); | ||||
| 						} else { | ||||
| 							createFace([widthSegments + "_" + y + "_" + z, widthSegments + "_" + y + "_" + (z + 1), widthSegments + "_" + (y + 1) + "_" + (z + 1), widthSegments + "_" + (y + 1) + "_" + z], faceId); | ||||
| 							setUVsToFace(new Point((lengthSegments - y)*ld, z*hd), new Point((lengthSegments - y)*ld, (z + 1)*hd), new Point((lengthSegments - y - 1)*ld, (z + 1)*hd), faceId); | ||||
| 						} | ||||
| 					} else { | ||||
| 						if (triangulate) { | ||||
| 							aUV = new Point(y*ld, z*hd); | ||||
| 							cUV = new Point((y + 1)*ld, (z + 1)*hd); | ||||
| 							createFace([widthSegments + "_" + y + "_" + z, widthSegments + "_" + (y + 1) + "_" + z, widthSegments + "_" + (y + 1) + "_" + (z + 1)], faceId + ":0"); | ||||
| 							setUVsToFace(aUV, new Point((y + 1)*ld, z*hd), cUV, faceId + ":0"); | ||||
| 							createFace([widthSegments + "_" + (y + 1) + "_" + (z + 1), widthSegments + "_" + y + "_" + (z + 1), widthSegments + "_" + y + "_" + z], faceId + ":1"); | ||||
| 							setUVsToFace(cUV, new Point(y*ld, (z + 1)*hd), aUV, faceId + ":1"); | ||||
| 						} else { | ||||
| 							createFace([widthSegments + "_" + y + "_" + z, widthSegments + "_" + (y + 1) + "_" + z, widthSegments + "_" + (y + 1) + "_" + (z + 1), widthSegments + "_" + y + "_" + (z + 1)], faceId); | ||||
| 							setUVsToFace(new Point(y*ld, z*hd), new Point((y + 1)*ld, z*hd), new Point((y + 1)*ld, (z + 1)*hd), faceId); | ||||
| 						} | ||||
| 					} | ||||
| 					if (triangulate) { | ||||
| 						right.addFace(faceId + ":0"); | ||||
| 						right.addFace(faceId + ":1"); | ||||
| 					} else { | ||||
| 						right.addFace(faceId); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */		 | ||||
| 		protected override function createEmptyObject():Object3D { | ||||
| 			return new Box(0, 0, 0, 0); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,264 +0,0 @@ | ||||
| package alternativa.engine3d.primitives { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.engine3d.core.Surface; | ||||
| 	import alternativa.engine3d.core.Vertex; | ||||
| 	import alternativa.utils.MathUtils; | ||||
| 	import alternativa.engine3d.core.Face; | ||||
| 	import flash.geom.Point; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Усеченный конус или цилиндр. | ||||
| 	 */ | ||||
| 	public class Cone extends Mesh { | ||||
|  | ||||
| 		/** | ||||
| 		 * Создает примитив усеченный конус или цилиндр. | ||||
| 		 * <p>Различные значения параметров позволяют создавать различные примитивы.  | ||||
| 		 * Так при установленном параметре <code>topRadius = 0</code> или <code>bottomRadius = 0</code> будет построен конус. При установленном </code>bottomRadius = topRadius</code> будет построен цилиндр.</p> | ||||
| 		 * <p>По умолчанию параметр <code>triangulate</code> установлен в <code>false</code> и на примитив не может быть наложена текстура.  | ||||
| 		 * Только при установленном параметре <code>triangulate</code> в <code>true</code> это возможно.</p> | ||||
| 		 * <p>После создания примитив всегда содержит в себе поверхность <code>"side"</code>. | ||||
| 		 * При установленном параметре <code>bottomRadius</code> не равном нулю в примитиве создается поверхность <code>"bottom"</code>,  | ||||
| 		 * при установленном параметре <code>topRadius</code> в примитиве создается поверхность <code>"top"</code>. | ||||
| 		 * На каждую из поверхностей может быть наложен свой материал</p> | ||||
| 		 *  | ||||
| 		 * @param height высота примтива. Размерность по оси Z. Не может быть меньше нуля. | ||||
| 		 * @param bottomRadius нижний радиус примитива | ||||
| 		 * @param topRadius верхний радиус примтива | ||||
| 		 * @param heightSegments количество сегментов по высоте примитива | ||||
| 		 * @param radialSegments количество сегментов по радиусу примтива | ||||
| 		 * @param reverse задает направление нормалей. При значении <code>true</code> нормли будут направлены внутрь примитива. | ||||
| 		 * @param triangulate флаг триангуляции. При значении <code>true</code> все четырехугольные грани примитива будут триангулированы | ||||
| 		 * и появится возможность наложить на примитив текстуру. | ||||
| 		 */ | ||||
| 		public function Cone(height:Number = 100, bottomRadius:Number = 100, topRadius:Number = 0, heightSegments:uint = 1, radialSegments:uint = 12, reverse:Boolean = false, triangulate:Boolean = false) { | ||||
|  | ||||
| 			if ((radialSegments < 3) || (heightSegments < 1) || (heightSegments == 1 && topRadius == 0 && bottomRadius == 0)) { | ||||
| 				return; | ||||
| 			} | ||||
| 			height = (height < 0)? 0 : height; | ||||
| 			bottomRadius = (bottomRadius < 0)? 0 : bottomRadius; | ||||
| 			topRadius = (topRadius < 0)? 0 : topRadius; | ||||
|  | ||||
| 			const radialSegment:Number = MathUtils.DEG360/radialSegments; | ||||
| 			const radiusSegment:Number = (bottomRadius - topRadius)/heightSegments; | ||||
| 			const heightSegment:Number = height/heightSegments; | ||||
| 			const halfHeight:Number = height*0.5 | ||||
| 			const uSegment:Number = 1/radialSegments; | ||||
| 			const vSegment:Number = 1/heightSegments; | ||||
| 			 | ||||
| 			// Создание вершин | ||||
| 			if (topRadius == 0 || triangulate) { | ||||
| 				var poleUp:Vertex = createVertex(0, 0, halfHeight, "poleUp"); | ||||
| 			} | ||||
| 			if (bottomRadius == 0 || triangulate) { | ||||
| 				var poleDown:Vertex = createVertex(0, 0, -halfHeight, "poleDown"); | ||||
| 			} | ||||
|  | ||||
| 			var radial:uint; | ||||
| 			var segment:uint; | ||||
|  | ||||
| 			var topSegment:uint = heightSegments - int(topRadius == 0); | ||||
| 			var bottomSegment:uint = int(bottomRadius == 0) ; | ||||
| 			for (segment = bottomSegment; segment <= topSegment; segment++) { | ||||
| 				for (radial = 0; radial < radialSegments; radial++) { | ||||
| 					var currentAngle:Number = radialSegment*radial; | ||||
| 					var currentRadius:Number = bottomRadius - (radiusSegment*segment); | ||||
| 					createVertex(Math.cos(currentAngle)*currentRadius, Math.sin(currentAngle)*currentRadius, heightSegment*segment - halfHeight, radial + "_" + segment); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Создание граней и поверхности | ||||
| 			var face:Face; | ||||
|  | ||||
| 			var points:Array; | ||||
|  | ||||
|  			var side:Surface = createSurface(null, "side"); | ||||
|  | ||||
| 			if (topRadius == 0) { | ||||
| 				// Создание граней у верхнего полюса | ||||
| 				var prevRadial:uint = radialSegments - 1; | ||||
| 				var centerUV:Point = new Point(0.5, 1);  | ||||
|  				var v:Number =  topSegment*vSegment; | ||||
|  				if (reverse) { | ||||
| 					for (radial = 0; radial < radialSegments; radial++) { | ||||
| 						face = createFace([poleUp, radial + "_" + topSegment, prevRadial + "_" + topSegment], prevRadial + "_" + topSegment); | ||||
| 						if (triangulate) { | ||||
| 							setUVsToFace(centerUV, new Point(1 - (prevRadial + 1)*uSegment, v) , new Point(1 - prevRadial*uSegment, v), face); | ||||
| 						} | ||||
| 						side.addFace(face); | ||||
| 						prevRadial = radial; | ||||
| 					} | ||||
| 				} else  { | ||||
| 					for (radial = 0; radial < radialSegments; radial++) { | ||||
| 						face = createFace([poleUp, prevRadial + "_" + topSegment, radial + "_" + topSegment], prevRadial + "_" + topSegment); | ||||
| 						if (triangulate) { | ||||
| 							setUVsToFace(centerUV, new Point(prevRadial*uSegment, v), new Point((prevRadial + 1)*uSegment, v), face); | ||||
| 						} | ||||
| 						side.addFace(face); | ||||
| 						prevRadial = radial; | ||||
| 					} | ||||
| 				} | ||||
|  			} else { | ||||
| 				// Создание граней верхней крышки | ||||
| 				var top:Surface = createSurface(null, "top"); | ||||
| 				if (triangulate) { | ||||
| 					prevRadial = radialSegments - 1;  | ||||
| 					centerUV = new Point(0.5, 0.5); | ||||
| 					var UV:Point; | ||||
| 					var prevUV:Point; | ||||
|  					if (reverse) { | ||||
| 						prevUV = new Point(0.5 - Math.cos(-radialSegment)*0.5, Math.sin(-radialSegment)*0.5 + 0.5); | ||||
| 						for (radial = 0; radial < radialSegments; radial++) { | ||||
| 							face = createFace([poleUp, radial + "_" + topSegment, prevRadial + "_" + topSegment], "top_" + prevRadial); | ||||
| 							currentAngle = radial * radialSegment; | ||||
| 							UV = new Point(0.5 - Math.cos(currentAngle)*0.5, Math.sin(currentAngle)*0.5 + 0.5); | ||||
| 							setUVsToFace(centerUV, UV, prevUV, face);  | ||||
| 							top.addFace(face); | ||||
| 							prevUV = UV; | ||||
| 							prevRadial = radial; | ||||
| 						} | ||||
| 					} else  { | ||||
|  						prevUV = new Point(Math.cos(-radialSegment)*0.5 + 0.5, Math.sin(-radialSegment)*0.5 + 0.5); | ||||
| 						for (radial = 0; radial < radialSegments; radial++) { | ||||
| 							face = createFace([poleUp, prevRadial + "_" + topSegment, radial + "_" + topSegment], "top_" + prevRadial); | ||||
| 							currentAngle = radial*radialSegment; | ||||
| 							UV = new Point(Math.cos(currentAngle)*0.5 + 0.5, Math.sin(currentAngle)*0.5 + 0.5); 							 | ||||
| 							setUVsToFace(centerUV, prevUV, UV, face);  | ||||
| 							top.addFace(face); | ||||
| 							prevUV = UV; | ||||
| 							prevRadial = radial; | ||||
| 						} | ||||
| 					} | ||||
|    				} else { | ||||
| 					points = new Array(); | ||||
| 					if (reverse) { | ||||
| 						for (radial = (radialSegments - 1); radial < uint(-1); radial--) { | ||||
| 							points.push(radial + "_" + topSegment); | ||||
| 						} | ||||
| 					} else { | ||||
| 						for (radial = 0; radial < radialSegments; radial++) { | ||||
| 							points.push(radial + "_" + topSegment); | ||||
| 						} | ||||
| 					} | ||||
| 					top.addFace(createFace(points, "top")); | ||||
| 				} | ||||
| 			} | ||||
| 			// Создание боковых граней | ||||
| 			var face2:Face; | ||||
| 			var aUV:Point; | ||||
| 			var cUV:Point; | ||||
| 			for (segment = bottomSegment; segment < topSegment; segment++) { | ||||
| 				prevRadial = radialSegments - 1; | ||||
| 				v = segment * vSegment; | ||||
| 				for (radial = 0; radial < radialSegments; radial++) { | ||||
| 					if (triangulate) { | ||||
| 						if (reverse) { | ||||
| 							face = createFace([radial + "_" + (segment + 1), radial + "_" + segment, prevRadial + "_" + segment], prevRadial + "_" + segment + ":0"); | ||||
| 							face2 = createFace([prevRadial + "_" + segment, prevRadial + "_" + (segment + 1), radial + "_" + (segment + 1)], prevRadial + "_" + segment + ":1"); | ||||
| 							aUV = new Point(1 - (prevRadial + 1)*uSegment, v + vSegment) | ||||
| 							cUV = new Point(1 - prevRadial*uSegment, v); | ||||
| 							setUVsToFace(aUV, new Point(1 - (prevRadial + 1)*uSegment, v), cUV, face); | ||||
| 							setUVsToFace(cUV, new Point(1 - prevRadial*uSegment, v + vSegment), aUV, face2);  | ||||
| 						} else { | ||||
| 							face = createFace([prevRadial + "_" + segment, radial + "_" + segment, radial + "_" + (segment + 1)], prevRadial + "_" + segment + ":0"); | ||||
| 							face2 = createFace([radial + "_" + (segment + 1), prevRadial + "_" + (segment + 1), prevRadial + "_" + segment], prevRadial + "_" + segment + ":1"); | ||||
| 							aUV = new Point(prevRadial*uSegment, v) | ||||
| 							cUV = new Point((prevRadial + 1)*uSegment, v + vSegment); | ||||
| 							setUVsToFace(aUV, new Point((prevRadial + 1)*uSegment, v), cUV, face); | ||||
| 							setUVsToFace(cUV, new Point(prevRadial*uSegment, v + vSegment), aUV, face2);  | ||||
| 						} | ||||
| 						side.addFace(face); | ||||
| 						side.addFace(face2); | ||||
| 					} else { | ||||
| 						if (reverse) { | ||||
| 							side.addFace(createFace([prevRadial + "_" + segment, prevRadial + "_" + (segment + 1), radial + "_" + (segment + 1), radial + "_" + segment], prevRadial + "_" + segment)); | ||||
| 						} else { | ||||
| 							side.addFace(createFace([prevRadial + "_" + segment, radial + "_" + segment, radial + "_" + (segment + 1), prevRadial + "_" + (segment + 1)], prevRadial + "_" + segment)); | ||||
| 						} | ||||
| 					} | ||||
| 					prevRadial = radial; | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			if (bottomRadius == 0) { | ||||
| 				// Создание граней у нижнего полюса | ||||
| 				prevRadial = radialSegments - 1;  | ||||
| 				centerUV = new Point(0.5, 0); | ||||
|  				v =  bottomSegment*vSegment; | ||||
|  				if (reverse) { | ||||
| 					for (radial = 0; radial < radialSegments; radial++) { | ||||
| 						face = createFace([poleDown, prevRadial + "_" + bottomSegment, radial + "_" + bottomSegment], prevRadial + "_0"); | ||||
| 						if (triangulate) { | ||||
| 							setUVsToFace(centerUV, new Point(1 - prevRadial*uSegment, v), new Point(1 - (prevRadial + 1)*uSegment, v), face); | ||||
| 						} | ||||
| 						side.addFace(face); | ||||
| 						prevRadial = radial; | ||||
| 					} | ||||
| 				} else  { | ||||
| 					for (radial = 0; radial < radialSegments; radial++) { | ||||
| 						face = createFace([poleDown, radial + "_" + bottomSegment, prevRadial + "_" + bottomSegment], prevRadial + "_0"); | ||||
| 						if (triangulate) { | ||||
| 							setUVsToFace(centerUV, new Point((prevRadial + 1)*uSegment, v), new Point(prevRadial*uSegment, v), face); | ||||
| 						} | ||||
| 						side.addFace(face); | ||||
| 						prevRadial = radial; | ||||
| 					} | ||||
| 				} | ||||
|  			} else { | ||||
| 				// Создание граней нижней крышки | ||||
| 				var bottom:Surface = createSurface(null, "bottom"); | ||||
| 				if (triangulate) { | ||||
| 					prevRadial = radialSegments - 1;  | ||||
| 					centerUV = new Point(0.5, 0.5); | ||||
|  					if (reverse) { | ||||
|  						prevUV = new Point(Math.cos(-radialSegment)*0.5 + 0.5, Math.sin(-radialSegment)*0.5 + 0.5); | ||||
| 						for (radial = 0; radial < radialSegments; radial++) { | ||||
| 							face = createFace([poleDown, prevRadial + "_" + bottomSegment, radial + "_" + bottomSegment], "bottom_" + prevRadial); | ||||
| 							currentAngle = radial*radialSegment; | ||||
| 							UV = new Point(Math.cos(currentAngle)*0.5 + 0.5, Math.sin(currentAngle)*0.5 + 0.5); 							 | ||||
| 							setUVsToFace(centerUV, prevUV, UV, face);  | ||||
| 							bottom.addFace(face); | ||||
| 							prevUV = UV; | ||||
| 							prevRadial = radial; | ||||
| 						} | ||||
| 					} else  { | ||||
| 						prevUV = new Point(0.5 - Math.cos(-radialSegment)*0.5, Math.sin(-radialSegment)*0.5 + 0.5); | ||||
| 						for (radial = 0; radial < radialSegments; radial++) { | ||||
| 							face = createFace([poleDown, radial + "_" + bottomSegment, prevRadial + "_" + bottomSegment], "bottom_" + prevRadial); | ||||
| 							currentAngle = radial * radialSegment; | ||||
| 							UV = new Point(0.5 - Math.cos(currentAngle)*0.5, Math.sin(currentAngle)*0.5 + 0.5); | ||||
| 							setUVsToFace(centerUV, UV, prevUV, face);  | ||||
| 							bottom.addFace(face); | ||||
| 							prevUV = UV; | ||||
| 							prevRadial = radial; | ||||
| 						} | ||||
| 					} | ||||
|    				} else { | ||||
| 					points = new Array(); | ||||
| 					if (reverse) { | ||||
| 						for (radial = 0; radial < radialSegments; radial++) { | ||||
| 							points.push(radial + "_" + bottomSegment); | ||||
| 						} | ||||
| 					} else { | ||||
| 						for (radial = (radialSegments - 1); radial < uint(-1); radial--) { | ||||
| 							points.push(radial + "_" + bottomSegment); | ||||
| 						} | ||||
| 					} | ||||
| 					bottom.addFace(createFace(points, "bottom")); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		protected override function createEmptyObject():Object3D { | ||||
| 			return new Cone(0, 0, 0, 0); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,197 +0,0 @@ | ||||
| package alternativa.engine3d.primitives { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Face; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.engine3d.core.Surface; | ||||
| 	 | ||||
| 	import flash.geom.Point; | ||||
|  | ||||
| 	use namespace alternativa3d; | ||||
|  | ||||
| 	/** | ||||
| 	 * Геоплоскость. | ||||
| 	 */ | ||||
| 	public class GeoPlane extends Mesh { | ||||
|  | ||||
| 		/** | ||||
| 		 * Создает геоплоскость. | ||||
| 		 * <p>Геоплоскость это плоскость с сетчатой структурой граней.</p> | ||||
| 		 * <p>Примитив после создания содержит в cебе одну или две поверхности, в зависимости от значения параметров.  | ||||
| 		 * При значении <code>reverse</code> установленном в <code>true</code> примитив будет содержать грань - <code>"back"</code>. | ||||
| 		 * При значении <code>reverse</code> установленном в <code>false</code> примитив будет содержать грань - <code>"front"</code>. | ||||
| 		 * Параметр <code>twoSided</code> указывает методу создать обе поверхности.</p> | ||||
| 		 *   | ||||
| 		 * @param width ширина. Размерность по оси X. Не может быть меньше нуля. | ||||
| 		 * @param length длина. Размерность по оси Y. Не может быть меньше нуля. | ||||
| 		 * @param widthSegments количество сегментов по ширине | ||||
| 		 * @param lengthSegments количество сегментов по длине | ||||
| 		 * @param twoSided если значение параметра равно <code>true</code>, то создаётся двусторонняя поверхность | ||||
| 		 * @param reverse флаг инвертирования нормалей | ||||
| 		 */ | ||||
| 		public function GeoPlane(width:Number = 100, length:Number = 100, widthSegments:uint = 1, lengthSegments:uint = 1, twoSided:Boolean = true, reverse:Boolean = false) { | ||||
| 			super(); | ||||
| 			 | ||||
| 			if ((widthSegments == 0) || (lengthSegments == 0)) { | ||||
| 				return; | ||||
| 			} | ||||
| 			width = (width < 0)? 0 : width; | ||||
| 			length = (length < 0)? 0 : length; | ||||
| 			 | ||||
| 			// Середина | ||||
| 			var wh:Number = width/2; | ||||
| 			var hh:Number = length/2; | ||||
| 			// Размеры сегмента | ||||
| 			var ws:Number = width/widthSegments; | ||||
| 			var hs:Number = length/lengthSegments; | ||||
| 			 | ||||
| 			// Размеры UV-сегмента | ||||
| 			var us:Number = 1/widthSegments; | ||||
| 			var vs:Number = 1/lengthSegments; | ||||
| 			 | ||||
| 			// Создание точек | ||||
| 			var x:uint; | ||||
| 			var y:uint; | ||||
| 			var frontUV:Array = new Array(); | ||||
| 			var backUV:Array = ((lengthSegments & 1) == 0) ? null : new Array(); | ||||
| 			for (y = 0; y <= lengthSegments; y++) { | ||||
| 				frontUV[y] = new Array(); | ||||
| 				if (backUV != null) { | ||||
| 					backUV[y] = new Array(); | ||||
| 				} | ||||
| 				for (x = 0; x <= widthSegments; x++) { | ||||
| 					if ((y & 1) == 0) { | ||||
| 						// Если чётный ряд | ||||
| 						createVertex(x*ws - wh, y*hs - hh, 0, y + "_" + x); | ||||
| 						frontUV[y][x] = new Point(x*us, y*vs); | ||||
| 						if (backUV != null) { | ||||
| 							backUV[y][x] = new Point(x*us, 1 - y*vs); | ||||
| 						} | ||||
| 					} else { | ||||
| 						// Если нечётный ряд | ||||
| 						if (x == 0) { | ||||
| 							// Первая точка | ||||
| 							createVertex(-wh, y*hs - hh, 0, y + "_" + x); | ||||
| 							frontUV[y][x] = new Point(0, y*vs); | ||||
| 							if (backUV != null) { | ||||
| 								backUV[y][x] = new Point(0, 1 - y*vs); | ||||
| 							} | ||||
| 						} else { | ||||
| 							createVertex(x*ws - wh - ws/2, y*hs - hh, 0, y + "_" + x); | ||||
| 							frontUV[y][x] = new Point((x - 0.5)*us, y*vs); | ||||
| 							if (backUV != null) { | ||||
| 								backUV[y][x] = new Point((x - 0.5)*us, 1 - y*vs); | ||||
| 							} | ||||
| 							if (x == widthSegments) { | ||||
| 								// Последняя точка | ||||
| 								createVertex(wh, y*hs - hh, 0, y + "_" + (x + 1)); | ||||
| 								frontUV[y][x + 1] = new Point(1, y*vs); | ||||
| 								if (backUV != null) { | ||||
| 									backUV[y][x + 1] = new Point(1, 1 - y*vs); | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Создание поверхностей | ||||
| 			var front:Surface; | ||||
| 			var back:Surface; | ||||
| 			 | ||||
| 			if (twoSided || !reverse) { | ||||
| 				front = createSurface(null, "front"); | ||||
| 			} | ||||
| 			if (twoSided || reverse) { | ||||
| 				back = createSurface(null, "back"); | ||||
| 			} | ||||
|  | ||||
| 			// Создание полигонов | ||||
| 			var face:Face; | ||||
| 			for (y = 0; y < lengthSegments; y++) { | ||||
| 				for (var n:uint = 0; n <= (widthSegments << 1); n++) { | ||||
| 					x = n >> 1; | ||||
| 					if ((y & 1) == 0) { | ||||
| 						// Если чётный ряд | ||||
| 						if ((n & 1) == 0) { | ||||
| 							// Если остриём вверх | ||||
| 							if (twoSided || !reverse) { | ||||
| 								face = createFace([y + "_" + x, (y + 1) + "_" + (x + 1), (y + 1) + "_" + x]); | ||||
| 								setUVsToFace(frontUV[y][x], frontUV[y + 1][x + 1], frontUV[y + 1][x], face);  | ||||
| 								front.addFace(face); | ||||
| 							} | ||||
| 							if (twoSided || reverse) { | ||||
| 								face = createFace([y + "_" + x, (y + 1) + "_" + x, (y + 1) + "_" + (x + 1)]); | ||||
| 								if (backUV != null) { | ||||
| 									setUVsToFace(backUV[y][x], backUV[y + 1][x], backUV[y + 1][x + 1], face); | ||||
| 								} else { | ||||
| 									setUVsToFace(frontUV[lengthSegments - y][x], frontUV[lengthSegments - y - 1][x], frontUV[lengthSegments - y - 1][x + 1], face); | ||||
| 								} | ||||
| 								back.addFace(face); | ||||
| 							} | ||||
| 						} else { | ||||
| 							// Если остриём вниз  | ||||
| 							if (twoSided || !reverse) { | ||||
| 								face = createFace([y + "_" + x, y + "_" + (x + 1), (y + 1) + "_" + (x + 1)]); | ||||
| 								setUVsToFace(frontUV[y][x], frontUV[y][x + 1], frontUV[y + 1][x + 1], face); | ||||
| 								front.addFace(face); | ||||
| 							} | ||||
| 							if (twoSided || reverse) { | ||||
| 								face = createFace([y + "_" + x, (y + 1) + "_" + (x + 1), y + "_" + (x + 1)]); | ||||
| 								if (backUV != null) { | ||||
| 									setUVsToFace(backUV[y][x], backUV[y + 1][x + 1], backUV[y][x + 1], face); | ||||
| 								} else { | ||||
| 									setUVsToFace(frontUV[lengthSegments - y][x], frontUV[lengthSegments - y - 1][x + 1], frontUV[lengthSegments - y][x + 1], face); | ||||
| 								} | ||||
| 								back.addFace(face); | ||||
| 							} | ||||
| 						} | ||||
| 					} else { | ||||
| 						// Если нечётный ряд | ||||
| 						if ((n & 1) == 0) { | ||||
| 							// Если остриём вниз  | ||||
| 							if (twoSided || !reverse) { | ||||
| 								face = createFace([y + "_" + x, y + "_" + (x + 1), (y + 1) + "_" + x]); | ||||
| 								setUVsToFace(frontUV[y][x], frontUV[y][x + 1], frontUV[y + 1][x], face); | ||||
| 								front.addFace(face); | ||||
| 							} | ||||
| 							if (twoSided || reverse) { | ||||
| 								face = createFace([y + "_" + x, (y + 1) + "_" + x, y + "_" + (x + 1)]); | ||||
| 								if (backUV != null) { | ||||
| 									setUVsToFace(backUV[y][x], backUV[y + 1][x], backUV[y][x + 1], face); | ||||
| 								} else { | ||||
| 									setUVsToFace(frontUV[lengthSegments - y][x], frontUV[lengthSegments - y - 1][x], frontUV[lengthSegments - y][x + 1], face); | ||||
| 								} | ||||
| 								back.addFace(face); | ||||
| 							} | ||||
| 						} else { | ||||
| 							// Если остриём вверх | ||||
| 							if (twoSided || !reverse) { | ||||
| 								face = createFace([y + "_" + (x + 1), (y + 1) + "_" + (x + 1), (y + 1) + "_" + x]); | ||||
| 								setUVsToFace(frontUV[y][x+1], frontUV[y + 1][x + 1], frontUV[y + 1][x], face); | ||||
| 								front.addFace(face); | ||||
| 							} | ||||
| 							if (twoSided || reverse) { | ||||
| 								face = createFace([y + "_" + (x + 1), (y + 1) + "_" + x, (y + 1) + "_" + (x + 1)]); | ||||
| 								if (backUV != null) { | ||||
| 									setUVsToFace(backUV[y][x + 1], backUV[y + 1][x], backUV[y + 1][x + 1], face); | ||||
| 								} else { | ||||
| 									setUVsToFace(frontUV[lengthSegments - y][x + 1], frontUV[lengthSegments - y - 1][x], frontUV[lengthSegments - y - 1][x + 1], face); | ||||
| 								} | ||||
| 								back.addFace(face); | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */		 | ||||
| 		protected override function createEmptyObject():Object3D { | ||||
| 			return new GeoPlane(0, 0, 0); | ||||
| 		}		 | ||||
| 	} | ||||
| } | ||||
| @@ -1,321 +0,0 @@ | ||||
| package alternativa.engine3d.primitives { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.engine3d.core.Surface; | ||||
| 	import alternativa.engine3d.core.Vertex; | ||||
| 	import alternativa.utils.MathUtils; | ||||
| 	import alternativa.engine3d.core.Face; | ||||
| 	import flash.geom.Point; | ||||
| 	import alternativa.types.Point3D; | ||||
|  | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Геосфера. | ||||
| 	 */	 | ||||
| 	public class GeoSphere extends Mesh	{ | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создает геосферу. | ||||
| 		 * <p>Геосфера после создания содержит в себе одну поверхность с идентификатором по умолчанию.</p> | ||||
| 		 * <p>Текстурные координаты у геосферы не находятся в промежутке <code>[0, 1]</code>, | ||||
| 		 *  поэтому для материала с текстурой необходимо устанавливать флаг repeat.   | ||||
| 		 *  | ||||
| 		 * @param radius радиус геосферы. Не может быть меньше нуля. | ||||
| 		 * @param segments количество сегментов геосферы | ||||
| 		 * @param reverse флаг направления нормалей. При значении <code>true</code> нормали направлены внуть геосферы. | ||||
| 		 */ | ||||
| 		public function GeoSphere(radius:Number = 100, segments:uint = 2, reverse:Boolean = false) { | ||||
| 			if (segments == 0) { | ||||
| 				return; | ||||
| 			} | ||||
| 			radius = (radius < 0)? 0 : radius; | ||||
| 			 | ||||
| 			const sections:uint = 20; | ||||
|  | ||||
| 			//var nfaces:uint = sections*segments*segments; | ||||
| 			//var nverts:Number = nfaces/2 + 2; | ||||
| 			var points:Array = new Array(); | ||||
|  | ||||
| 			var i:uint; | ||||
| 			var f:uint; | ||||
|  | ||||
| 			var theta:Number; | ||||
| 			var sin:Number; | ||||
| 			var cos:Number; | ||||
| 			// z расстояние до нижней и верхней крышки полюса   | ||||
| 			var subz:Number = 4.472136E-001*radius; | ||||
| 			// радиус на расстоянии subz  | ||||
| 			var subrad:Number = 2*subz; | ||||
| 			points.push(createVertex(0, 0, radius, "poleUp")); | ||||
| 			// Создание вершин верхней крышки | ||||
| 			for (i = 0; i < 5; i++) { | ||||
| 				theta = MathUtils.DEG360*i/5; | ||||
| 				sin = Math.sin(theta); | ||||
| 				cos = Math.cos(theta); | ||||
| 				points.push(createVertex(subrad*cos, subrad*sin, subz)); | ||||
| 			} | ||||
| 			// Создание вершин нижней крышки | ||||
| 			for (i = 0; i < 5; i++) { | ||||
| 				theta = MathUtils.DEG180*((i << 1) + 1)/5; | ||||
| 				sin = Math.sin(theta); | ||||
| 				cos = Math.cos(theta); | ||||
| 				points.push(createVertex(subrad*cos, subrad*sin, -subz)); | ||||
| 			} | ||||
| 			points.push(createVertex(0, 0, -radius, "poleDown")); | ||||
| 			 | ||||
| 			for (i = 1; i < 6; i++) { | ||||
| 				interpolate(0, i, segments, points); | ||||
| 			} | ||||
| 			for (i = 1; i < 6; i++) { | ||||
| 				interpolate(i, i % 5 + 1, segments, points); | ||||
| 			} | ||||
| 			for (i = 1; i < 6; i++) { | ||||
| 				interpolate(i, i + 5, segments, points); | ||||
| 			} | ||||
| 			for (i = 1; i < 6; i++) { | ||||
| 				interpolate(i, (i + 3) % 5 + 6, segments, points); | ||||
| 			} | ||||
| 			for (i = 1; i < 6; i++) { | ||||
| 				interpolate(i + 5, i % 5 + 6, segments, points); | ||||
| 			} | ||||
| 			for (i = 6; i < 11; i++) { | ||||
| 				interpolate(11, i, segments, points); | ||||
| 			} | ||||
| 			for (f = 0; f < 5; f++) { | ||||
| 				for (i = 1; i <= segments - 2; i++) { | ||||
| 					interpolate(12 + f*(segments - 1) + i, 12 + (f + 1) % 5*(segments - 1) + i, i + 1, points); | ||||
| 				} | ||||
| 			} | ||||
| 			for (f = 0; f < 5; f++) { | ||||
| 				for (i = 1; i <= segments - 2; i++) { | ||||
| 					interpolate(12 + (f + 15)*(segments - 1) + i, 12 + (f + 10)*(segments - 1) + i, i + 1, points); | ||||
| 				} | ||||
| 			} | ||||
| 			for (f = 0; f < 5; f++) { | ||||
| 				for (i = 1; i <= segments - 2; i++) { | ||||
| 					interpolate(12 + ((f + 1) % 5 + 15)*(segments - 1) + segments - 2 - i, 12 + (f + 10)*(segments - 1) + segments - 2 - i, i + 1, points); | ||||
| 				} | ||||
| 			} | ||||
| 			for (f = 0; f < 5; f++) { | ||||
| 				for (i = 1; i <= segments - 2; i++) { | ||||
| 					interpolate(12 + ((f + 1) % 5 + 25)*(segments - 1) + i, 12 + (f + 25)*(segments - 1) + i, i + 1, points); | ||||
| 				} | ||||
| 			} | ||||
| 			// Создание граней | ||||
| 			var face:Face; | ||||
| 			var surface:Surface = createSurface(); | ||||
| 			for (f = 0; f < sections; f++) { | ||||
| 				for (var row:uint = 0; row < segments; row++) { | ||||
| 					for (var column:uint = 0; column <= row; column++) { | ||||
| 						var a:uint = findVertices(segments, f, row, column); | ||||
| 						var b:uint = findVertices(segments, f, row + 1, column); | ||||
| 						var c:uint = findVertices(segments, f, row + 1, column + 1); | ||||
| 						var va:Vertex = points[a]; | ||||
| 						var vb:Vertex = points[b]; | ||||
| 						var vc:Vertex = points[c]; | ||||
| 						var aUV:Point; | ||||
| 						var bUV:Point; | ||||
| 						var cUV:Point; | ||||
| 						var coordA:Point3D = va._coords; | ||||
| 						var coordB:Point3D = vb._coords; | ||||
| 						var coordC:Point3D = vc._coords; | ||||
| 						 | ||||
| 						if (coordA.y >= 0 && (coordA.x < 0) && (coordB.y < 0 || coordC.y < 0)) { | ||||
| 							aUV = new Point(Math.atan2(coordA.y, coordA.x)/MathUtils.DEG360 - 0.5, Math.asin(coordA.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 						} else { | ||||
| 							aUV = new Point(Math.atan2(coordA.y, coordA.x)/MathUtils.DEG360 + 0.5, Math.asin(coordA.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 						} | ||||
|  						if (coordB.y >= 0 && (coordB.x < 0) && (coordA.y < 0 || coordC.y < 0)) { | ||||
| 							bUV = new Point(Math.atan2(coordB.y, coordB.x)/MathUtils.DEG360 - 0.5, Math.asin(coordB.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 						} else { | ||||
| 							bUV = new Point(Math.atan2(coordB.y, coordB.x)/MathUtils.DEG360 + 0.5, Math.asin(coordB.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 						} | ||||
| 						if (coordC.y >= 0 && (coordC.x < 0) && (coordA.y < 0 || coordB.y < 0)) { | ||||
| 							cUV = new Point(Math.atan2(coordC.y, coordC.x)/MathUtils.DEG360 - 0.5, Math.asin(coordC.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 						} else { | ||||
| 							cUV = new Point(Math.atan2(coordC.y, coordC.x)/MathUtils.DEG360 + 0.5, Math.asin(coordC.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 						} | ||||
|     					// полюс | ||||
| 						if (a == 0 || a == 11) { | ||||
| 							aUV.x = bUV.x + (cUV.x - bUV.x)*0.5; | ||||
| 						} | ||||
| 						if (b == 0 || b == 11) { | ||||
| 							bUV.x = aUV.x + (cUV.x - aUV.x)*0.5; | ||||
| 						} | ||||
| 						if (c == 0 || c == 11) { | ||||
| 							cUV.x = aUV.x + (bUV.x - aUV.x)*0.5; | ||||
| 						} | ||||
|    | ||||
| 						if (reverse) { | ||||
| 							face = createFace([va, vc, vb], (column << 1) + "_" + row + "_" + f); | ||||
| 							aUV.x = 1 - aUV.x; | ||||
| 							bUV.x = 1 - bUV.x; | ||||
| 							cUV.x = 1 - cUV.x; | ||||
| 							setUVsToFace(aUV, cUV, bUV, face); | ||||
| 						} else { | ||||
| 							face = createFace([va, vb, vc], (column << 1) + "_" + row + "_" + f); | ||||
|  							setUVsToFace(aUV, bUV, cUV, face); | ||||
| 						} | ||||
| 						surface.addFace(face); | ||||
| 						//trace(a + "_" + b + "_" + c); | ||||
|  						if (column < row) { | ||||
|  							b = findVertices(segments, f, row, column + 1); | ||||
| 							var vd:Vertex = points[b]; | ||||
| 							coordB = vd._coords; | ||||
| 							 | ||||
| 							if (coordA.y >= 0 && (coordA.x < 0) && (coordB.y < 0 || coordC.y < 0)) { | ||||
| 								aUV = new Point(Math.atan2(coordA.y, coordA.x)/MathUtils.DEG360 - 0.5, Math.asin(coordA.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 							} else { | ||||
| 								aUV = new Point(Math.atan2(coordA.y, coordA.x)/MathUtils.DEG360 + 0.5, Math.asin(coordA.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 							} | ||||
| 	 						if (coordB.y >= 0 && (coordB.x < 0) && (coordA.y < 0 || coordC.y < 0)) { | ||||
| 								bUV = new Point(Math.atan2(coordB.y, coordB.x)/MathUtils.DEG360 - 0.5, Math.asin(coordB.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 							} else { | ||||
| 								bUV = new Point(Math.atan2(coordB.y, coordB.x)/MathUtils.DEG360 + 0.5, Math.asin(coordB.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 							} | ||||
| 							if (coordC.y >= 0 && (coordC.x < 0) && (coordA.y < 0 || coordB.y < 0)) { | ||||
| 								cUV = new Point(Math.atan2(coordC.y, coordC.x)/MathUtils.DEG360 - 0.5, Math.asin(coordC.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 							} else { | ||||
| 								cUV = new Point(Math.atan2(coordC.y, coordC.x)/MathUtils.DEG360 + 0.5, Math.asin(coordC.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 							} | ||||
| 							if (a == 0 || a == 11)  { | ||||
| 								aUV.x = bUV.x + (cUV.x - bUV.x)*0.5; | ||||
| 							} | ||||
| 							if (b == 0 || b == 11) { | ||||
| 								bUV.x = aUV.x + (cUV.x - aUV.x)*0.5; | ||||
| 							} | ||||
| 							if (c == 0 || c == 11)  { | ||||
| 								cUV.x = aUV.x + (bUV.x - aUV.x)*0.5; | ||||
| 							} | ||||
| 							 | ||||
| 							if (reverse) { | ||||
| 								face = createFace([va, vd, vc], ((column << 1) + 1) + "_" + row + "_" + f); | ||||
| 								aUV.x = 1 - aUV.x; | ||||
| 								bUV.x = 1 - bUV.x; | ||||
| 								cUV.x = 1 - cUV.x; | ||||
| 								setUVsToFace(aUV, bUV, cUV, face); | ||||
| 							} else { | ||||
| 								face = createFace([va, vc, vd], ((column << 1) + 1) + "_" + row + "_" + f); | ||||
| 								setUVsToFace(aUV, cUV, bUV, face); | ||||
| 							} | ||||
| 							surface.addFace(face); | ||||
| 						} | ||||
|  					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| /* 		private function getUVSpherical(point:Point3D, radius:Number = 0, reverse:Boolean = false):Point { | ||||
| 			if (radius == 0) { | ||||
| 				radius = point.length;	 | ||||
| 			} | ||||
| 			if (reverse) { | ||||
| 				var u:Number = 0.5 - Math.atan2(point.y, point.x)/MathUtils.DEG360;  | ||||
| 			} else { | ||||
| 				u = Math.atan2(point.y, point.x)/MathUtils.DEG360 + 0.5;  | ||||
| 			} | ||||
| 			return new Point(u, Math.asin(point.z/radius)/MathUtils.DEG180 + 0.5); | ||||
| 		} | ||||
|  */ | ||||
| 		private function interpolate(v1:uint, v2:uint, num:uint, points:Array):void { | ||||
| 			if (num < 2) { | ||||
| 				return; | ||||
| 			} | ||||
| 			var a:Vertex = Vertex(points[v1]); | ||||
| 			var b:Vertex = Vertex(points[v2]); | ||||
| 			var cos:Number = (a.x*b.x + a.y*b.y + a.z*b.z)/(a.x*a.x + a.y*a.y + a.z*a.z); | ||||
| 			cos = (cos < -1) ? -1 : ((cos > 1) ? 1 : cos); | ||||
| 			var theta:Number = Math.acos(cos); | ||||
| 			var sin:Number = Math.sin(theta); | ||||
| 			for (var e:uint = 1; e < num; e++) { | ||||
| 				var theta1:Number = theta*e/num; | ||||
| 				var theta2:Number = theta*(num - e)/num; | ||||
| 				var st1:Number = Math.sin(theta1); | ||||
| 				var st2:Number = Math.sin(theta2); | ||||
| 				points.push(createVertex((a.x*st2 + b.x*st1)/sin, (a.y*st2 + b.y*st1)/sin, (a.z*st2 + b.z*st1)/sin)); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		private function findVertices(segments:uint, section:uint, row:uint, column:uint):uint { | ||||
| 			if (row == 0) { | ||||
| 				if (section < 5) { | ||||
| 					return (0); | ||||
| 				} | ||||
| 				if (section > 14) { | ||||
| 					return (11); | ||||
| 				} | ||||
| 				return (section - 4); | ||||
| 			} | ||||
| 			if (row == segments && column == 0) { | ||||
| 				if (section < 5) { | ||||
| 					return (section + 1); | ||||
| 				} | ||||
| 				if (section < 10) { | ||||
| 					return ((section + 4) % 5 + 6); | ||||
| 				} | ||||
| 				if (section < 15) { | ||||
| 					return ((section + 1) % 5 + 1); | ||||
| 				} | ||||
| 				return ((section + 1) % 5 + 6); | ||||
| 			} | ||||
| 			if (row == segments && column == segments) { | ||||
| 				if (section < 5) { | ||||
| 					return ((section + 1) % 5 + 1); | ||||
| 				} | ||||
| 				if (section < 10) { | ||||
| 					return (section + 1); | ||||
| 				} | ||||
| 				if (section < 15) { | ||||
| 					return (section - 9); | ||||
| 				} | ||||
| 				return (section - 9); | ||||
| 			} | ||||
| 			if (row == segments) { | ||||
| 				if (section < 5) { | ||||
| 					return (12 + (5 + section)*(segments - 1) + column - 1); | ||||
| 				} | ||||
| 				if (section < 10) { | ||||
| 					return (12 + (20 + (section + 4) % 5)*(segments - 1) + column - 1); | ||||
| 				} | ||||
| 				if (section < 15) { | ||||
| 					return (12 + (section - 5)*(segments - 1) + segments - 1 - column); | ||||
| 				} | ||||
| 				return (12 + (5 + section)*(segments - 1) + segments - 1 - column); | ||||
| 			} | ||||
| 			if (column == 0) { | ||||
| 				if (section < 5) { | ||||
| 					return (12 + section*(segments - 1) + row - 1); | ||||
| 				} | ||||
| 				if (section < 10) { | ||||
| 					return (12 + (section % 5 + 15)*(segments - 1) + row - 1); | ||||
| 				} | ||||
| 				if (section < 15) { | ||||
| 					return (12 + ((section + 1) % 5 + 15)*(segments - 1) + segments - 1 - row); | ||||
| 				} | ||||
| 				return (12 + ((section + 1) % 5 + 25)*(segments - 1) + row - 1); | ||||
| 			} | ||||
| 			if (column == row) { | ||||
| 				if (section < 5) { | ||||
| 					return (12 + (section + 1) % 5*(segments - 1) + row - 1); | ||||
| 				} | ||||
| 				if (section < 10) { | ||||
| 					return (12 + (section % 5 + 10)*(segments - 1) + row - 1); | ||||
| 				} | ||||
| 				if (section < 15) { | ||||
| 					return (12 + (section % 5 + 10)*(segments - 1) + segments - row - 1); | ||||
| 				} | ||||
| 				return (12 + (section % 5 + 25)*(segments - 1) + row - 1); | ||||
| 			} | ||||
| 			return (12 + 30*(segments - 1) + section*(segments - 1)*(segments - 2)/2 + (row - 1)*(row - 2)/2 + column - 1); | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		protected override function createEmptyObject():Object3D { | ||||
| 			return new GeoSphere(0, 0); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,117 +0,0 @@ | ||||
| package alternativa.engine3d.primitives { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.engine3d.core.Surface; | ||||
| 	 | ||||
| 	import flash.geom.Point; | ||||
|  | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Плоскость. | ||||
| 	 */ | ||||
| 	public class Plane extends Mesh { | ||||
| 		 | ||||
| 		/** | ||||
| 		 * Создает плоскость. | ||||
| 		 * <p>Примитив после создания содержит в cебе одну или две поверхности, в зависимости от значения параметров.  | ||||
| 		 * При значении <code>reverse</code> установленном в <code>true</code> примитив будет содержать грань - <code>"back"</code>. | ||||
| 		 * При значении <code>reverse</code> установленном в <code>false</code> примитив будет содержать грань - <code>"front"</code>. | ||||
| 		 * Параметр <code>twoSided</code> указывает методу создать обе поверхности.</p> | ||||
| 		 *  | ||||
| 		 * @param width ширина. Размерность по оси Х. Не может быть меньше нуля. | ||||
| 		 * @param length длина. Размерность по оси Y. Не может быть меньше нуля. | ||||
| 		 * @param widthSegments количество сегментов по ширине | ||||
| 		 * @param lengthSegments количество сегментов по длине  | ||||
| 		 * @param twoSided если значении параметра равно <code>true</code>, то формируется двусторонняя плоскость | ||||
| 		 * @param reverse инвертирование нормалей | ||||
| 		 * @param triangulate флаг триангуляции. Если указано значение <code>true</code>, четырехугольники в плоскости будут триангулированы.  | ||||
| 		 */ | ||||
| 		public function Plane(width:Number = 100, length:Number = 100, widthSegments:uint = 1, lengthSegments:uint = 1, twoSided:Boolean = true, reverse:Boolean = false, triangulate:Boolean = false) { | ||||
| 			super(); | ||||
| 			 | ||||
| 			if ((widthSegments == 0) || (lengthSegments == 0)) { | ||||
| 				return; | ||||
| 			} | ||||
| 			width = (width < 0)? 0 : width; | ||||
| 			length = (length < 0)? 0 : length; | ||||
| 			 | ||||
| 			// Середина | ||||
| 			var wh:Number = width/2; | ||||
| 			var lh:Number = length/2; | ||||
| 			 | ||||
| 			// Размеры сегмента | ||||
| 			var ws:Number = width/widthSegments; | ||||
| 			var ls:Number = length/lengthSegments; | ||||
| 			 | ||||
| 			// Размеры UV-сегмента | ||||
| 			var wd:Number = 1/widthSegments; | ||||
| 			var ld:Number = 1/lengthSegments; | ||||
| 			 | ||||
| 			// Создание точек и UV | ||||
| 			var x:int; | ||||
| 			var y:int; | ||||
| 			var uv:Array = new Array(); | ||||
| 			for (y = 0; y <= lengthSegments; y++) { | ||||
| 				uv[y] = new Array(); | ||||
| 				for (x = 0; x <= widthSegments; x++) { | ||||
| 					uv[y][x] = new Point(x*wd, y*ld); | ||||
| 					createVertex(x*ws - wh, y*ls - lh, 0, x+"_"+y); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 			// Создание поверхностей | ||||
| 			var front:Surface; | ||||
| 			var back:Surface; | ||||
| 			 | ||||
| 			if (twoSided || !reverse) { | ||||
| 				front = createSurface(null, "front"); | ||||
| 			} | ||||
| 			if (twoSided || reverse) { | ||||
| 				back = createSurface(null, "back"); | ||||
| 			} | ||||
| 			 | ||||
| 			// Создание полигонов | ||||
| 			for (y = 0; y < lengthSegments; y++) { | ||||
| 				for (x = 0; x < widthSegments; x++) { | ||||
| 					if (twoSided || !reverse) { | ||||
| 						if (triangulate) { | ||||
| 							createFace([x + "_" + y, (x + 1) + "_" + y, (x + 1) + "_" + (y + 1)], "front" + x + "_" + y + ":0"); | ||||
| 							setUVsToFace(uv[y][x], uv[y][x + 1], uv[y + 1][x + 1], "front" + x + "_" + y + ":0"); | ||||
| 							createFace([(x + 1) + "_" + (y + 1), x + "_" + (y + 1), x + "_" + y], "front" + x + "_" + y + ":1"); | ||||
| 							setUVsToFace(uv[y + 1][x + 1], uv[y + 1][x], uv[y][x], "front" + x + "_" + y + ":1"); | ||||
| 							front.addFace("front" + x + "_" + y + ":0"); | ||||
| 							front.addFace("front" + x + "_" + y + ":1"); | ||||
| 						} else { | ||||
| 							createFace([x + "_" + y, (x + 1) + "_" + y, (x + 1) + "_" + (y + 1), x + "_" + (y + 1)], "front" + x + "_" + y); | ||||
| 							setUVsToFace(uv[y][x], uv[y][x + 1], uv[y + 1][x + 1], "front" + x + "_" + y); | ||||
| 							front.addFace("front" + x + "_" + y); | ||||
| 						} | ||||
| 					} | ||||
| 					if (twoSided || reverse) { | ||||
| 						if (triangulate) { | ||||
| 							createFace([x + "_" + y, x + "_" + (y + 1), (x + 1) + "_" + (y + 1)], "back" + x + "_" + y + ":0"); | ||||
| 							setUVsToFace(uv[lengthSegments - y][x], uv[lengthSegments - y - 1][x], uv[lengthSegments - y - 1][x + 1], "back" + x + "_" + y + ":0"); | ||||
| 							createFace([(x + 1) + "_" + (y + 1), (x + 1) + "_" + y, x + "_" + y], "back" + x + "_" + y + ":1"); | ||||
| 							setUVsToFace(uv[lengthSegments - y - 1][x + 1], uv[lengthSegments - y][x + 1], uv[lengthSegments - y][x], "back" + x + "_" + y + ":1"); | ||||
| 							back.addFace("back" + x + "_" + y + ":0"); | ||||
| 							back.addFace("back"+x+"_"+y + ":1"); | ||||
| 						} else { | ||||
| 							createFace([x + "_" + y, x + "_" + (y + 1), (x + 1) + "_" + (y + 1), (x + 1) + "_" + y], "back" + x + "_" + y); | ||||
| 							setUVsToFace(uv[lengthSegments - y][x], uv[lengthSegments - y - 1][x], uv[lengthSegments - y - 1][x + 1], "back" + x + "_" + y); | ||||
| 							back.addFace("back" + x + "_" + y); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */		 | ||||
| 		protected override function createEmptyObject():Object3D { | ||||
| 			return new Plane(0, 0, 0); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,144 +0,0 @@ | ||||
| package alternativa.engine3d.primitives { | ||||
| 	import alternativa.engine3d.*; | ||||
| 	import alternativa.engine3d.core.Mesh; | ||||
| 	import alternativa.engine3d.core.Object3D; | ||||
| 	import alternativa.engine3d.core.Surface; | ||||
| 	import alternativa.engine3d.core.Vertex; | ||||
| 	import alternativa.utils.MathUtils; | ||||
| 	import alternativa.engine3d.core.Face; | ||||
| 	import flash.geom.Point; | ||||
| 	 | ||||
| 	use namespace alternativa3d; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Сфера. | ||||
| 	 */ | ||||
| 	public class Sphere extends Mesh { | ||||
|  | ||||
| 		/** | ||||
| 		 * Создает сферу. | ||||
| 		 * <p>После создания примитив содержит в себе одну поверхность с идентификатором по умолчанию.</p> | ||||
| 		 * <p>По умолчанию параметр <code>triangulate</code> установлен в <code>false</code> и на сферу нельзя наложить текстуру.  | ||||
| 		 * Только при установленном <code>triangulate</code> в <code>true</code> это возможно.</p> | ||||
| 		 *  | ||||
| 		 * @param radius Радиус сферы. Не может быть меньше нуля. | ||||
| 		 * @param radialSegments количество сегментов по экватору сферы | ||||
| 		 * @param heightSegments количество сегментов по высоте | ||||
| 		 * @param reverse флаг инвертирования нормалей. При параметре установленном в <code>true</code> нормали направлены внутрь сферы. | ||||
| 		 * @param triangulate флаг триангуляции. Если указано значение <code>true</code>, грани будут триангулированы, | ||||
| 		 *  и будет возможно наложить на примитив текстуру. | ||||
| 		 */ | ||||
| 		public function Sphere(radius:Number = 100, radialSegments:uint = 8, heightSegments:uint = 8, reverse:Boolean = false, triangulate:Boolean = false) { | ||||
| 			if ((radialSegments < 3) || (heightSegments < 2)) { | ||||
| 				return; | ||||
| 			} | ||||
| 			radius = (radius < 0)? 0 : radius; | ||||
|  | ||||
| 			var poleUp:Vertex = createVertex(0, 0, radius, "poleUp"); | ||||
| 			var poleDown:Vertex = createVertex(0, 0, -radius, "poleDown"); | ||||
|  | ||||
| 			const radialAngle:Number = MathUtils.DEG360/radialSegments; | ||||
| 			const heightAngle:Number = MathUtils.DEG360/(heightSegments << 1); | ||||
|  | ||||
| 			var radial:uint; | ||||
| 			var segment:uint; | ||||
|  | ||||
| 			// Создание вершин  | ||||
| 			for (segment = 1; segment < heightSegments; segment++) { | ||||
| 				var currentHeightAngle:Number = heightAngle*segment; | ||||
| 				var segmentRadius:Number = Math.sin(currentHeightAngle)*radius; | ||||
| 				var segmentZ:Number = Math.cos(currentHeightAngle)*radius; | ||||
| 				for (radial = 0; radial < radialSegments; radial++) { | ||||
| 					var currentRadialAngle:Number = radialAngle*radial; | ||||
| 					createVertex(-Math.sin(currentRadialAngle)*segmentRadius, Math.cos(currentRadialAngle)*segmentRadius, segmentZ, radial + "_" + segment); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Создание граней и поверхности | ||||
| 			var surface:Surface = createSurface(); | ||||
|  | ||||
| 			var prevRadial:uint = radialSegments - 1;  | ||||
| 			var lastSegmentString:String = "_" + (heightSegments - 1); | ||||
| 			 | ||||
| 			var uStep:Number = 1/radialSegments; | ||||
| 			var vStep:Number = 1/heightSegments; | ||||
| 			 | ||||
| 			var face:Face; | ||||
| 			 | ||||
| 			// Для триангуляции | ||||
| 			var aUV:Point; | ||||
| 			var cUV:Point; | ||||
|  | ||||
| 			var u:Number; | ||||
|  | ||||
| 			if (reverse) { | ||||
| 				for (radial = 0; radial < radialSegments; radial++) { | ||||
| 					// Грани верхнего полюса | ||||
| 					surface.addFace(createFace([poleUp, radial + "_1", prevRadial + "_1"], prevRadial + "_0")); | ||||
| 					// Грани нижнего полюса | ||||
| 					surface.addFace(createFace([radial + lastSegmentString, poleDown, prevRadial + lastSegmentString], prevRadial + lastSegmentString)); | ||||
| 	 | ||||
| 					// Если включена триангуляция  | ||||
| 					if (triangulate) { | ||||
| 						// Триангулируем середину и просчитываем маппинг | ||||
| 						u = uStep*prevRadial; | ||||
| 						setUVsToFace(new Point(1 - u, 1), new Point(1 - u - uStep, 1 - vStep), new Point(1 - u, 1 - vStep), prevRadial + "_0"); | ||||
| 						// Грани середки | ||||
| 						for (segment = 1; segment < (heightSegments - 1); segment++) { | ||||
| 							aUV = new Point(1 - u - uStep, 1 - (vStep*(segment + 1))); | ||||
| 							cUV = new Point(1 - u, 1 - vStep*segment); | ||||
| 							surface.addFace(createFace([radial + "_" + (segment + 1), prevRadial + "_" + (segment + 1), prevRadial + "_" + segment], prevRadial + "_" + segment + ":0")); | ||||
| 							surface.addFace(createFace([prevRadial + "_" + segment, radial + "_" + segment, radial + "_" + (segment + 1)], prevRadial + "_" + segment + ":1")); | ||||
| 							setUVsToFace(aUV, new Point(1 - u, 1 - (vStep*(segment + 1))), cUV, prevRadial + "_" + segment + ":0"); | ||||
| 							setUVsToFace(cUV, new Point(1 - u - uStep, 1 - (vStep*segment)), aUV, prevRadial + "_" + segment + ":1");  | ||||
| 						} | ||||
| 						setUVsToFace(new Point(1 - u - uStep, vStep), new Point(1 - u, 0), new Point(1 - u, vStep), prevRadial + lastSegmentString);  | ||||
|  | ||||
| 					} else { | ||||
| 						// Просто создаем середину | ||||
| 						// Грани середки | ||||
| 						for (segment = 1; segment < (heightSegments - 1); segment++) { | ||||
| 							surface.addFace(createFace([radial + "_" + (segment + 1), prevRadial + "_" + (segment + 1), prevRadial + "_" + segment, radial + "_" + segment], prevRadial + "_" + segment)); | ||||
| 						} | ||||
| 					} | ||||
| 					prevRadial = (radial == 0) ? 0 : prevRadial + 1; | ||||
| 				} | ||||
| 			} else { | ||||
| 				for (radial = 0; radial < radialSegments; radial++) { | ||||
| 					// Грани верхнего полюса | ||||
| 					surface.addFace(createFace([poleUp, prevRadial + "_1", radial + "_1"], prevRadial + "_0")); | ||||
| 					// Грани нижнего полюса | ||||
| 					surface.addFace(createFace([prevRadial + lastSegmentString, poleDown, radial + lastSegmentString], prevRadial + lastSegmentString)); | ||||
|  | ||||
| 					if (triangulate) { | ||||
| 						u = uStep*prevRadial; | ||||
| 						setUVsToFace(new Point(u, 1), new Point(u, 1 - vStep), new Point(u + uStep, 1 - vStep), prevRadial + "_0"); | ||||
| 						// Грани середки | ||||
| 						for (segment = 1; segment < (heightSegments - 1); segment++) { | ||||
| 							aUV = new Point(u, 1 - (vStep*segment)); | ||||
| 							cUV = new Point(u + uStep, 1 - vStep * (segment + 1)); | ||||
| 							surface.addFace(createFace([prevRadial + "_" + segment, prevRadial + "_" + (segment + 1), radial + "_" + (segment + 1)], prevRadial + "_" + segment + ":0")); | ||||
| 							surface.addFace(createFace([radial + "_" + (segment + 1), radial + "_" + segment, prevRadial + "_" + segment], prevRadial + "_" + segment + ":1")); | ||||
| 							setUVsToFace(aUV, new Point(u, 1 - (vStep*(segment + 1))), cUV, prevRadial + "_" + segment + ":0"); | ||||
| 							setUVsToFace(cUV, new Point(u + uStep, 1 - (vStep*segment)), aUV, prevRadial + "_" + segment + ":1");  | ||||
| 						} | ||||
| 						setUVsToFace(new Point(u, vStep), new Point(u, 0), new Point(u + uStep, vStep), prevRadial + lastSegmentString);  | ||||
| 					} else { | ||||
| 						// Грани середки | ||||
| 						for (segment = 1; segment < (heightSegments - 1); segment++) { | ||||
| 							surface.addFace(createFace([prevRadial + "_" + segment, prevRadial + "_" + (segment + 1), radial + "_" + (segment + 1), radial + "_" + segment], prevRadial + "_" + segment)); | ||||
| 						} | ||||
| 					} | ||||
| 					prevRadial = (radial == 0) ? 0 : prevRadial + 1; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		/** | ||||
| 		 * @inheritDoc | ||||
| 		 */ | ||||
| 		protected override function createEmptyObject():Object3D { | ||||
| 			return new Sphere(0, 0); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,11 +0,0 @@ | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 88 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/utils | ||||
| END | ||||
| MeshUtils.as | ||||
| K 25 | ||||
| svn:wc:ra_dav:version-url | ||||
| V 101 | ||||
| /!svn/ver/497/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/utils/MeshUtils.as | ||||
| END | ||||
| @@ -1,40 +0,0 @@ | ||||
| 8 | ||||
|  | ||||
| dir | ||||
| 46043 | ||||
| http://svndev.alternativaplatform.com/platform/clients/fp10/libraries/Alternativa3D/tags/5.4.1/alternativa/utils | ||||
| http://svndev.alternativaplatform.com | ||||
|  | ||||
|  | ||||
|  | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
|  | ||||
| svn:special svn:externals svn:needs-lock | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| d9e2387a-1f3e-40e2-b57f-9df5970a2fa5 | ||||
|  | ||||
| MeshUtils.as | ||||
| file | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| 2010-10-28T04:31:16.000000Z | ||||
| 459f39b9e1f6fcc945933edba503a32b | ||||
| 2008-08-29T11:41:21.451513Z | ||||
| 304 | ||||
| wolf | ||||
|  | ||||
| @@ -1 +0,0 @@ | ||||
| 8 | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 Pyogenics
					Pyogenics