mirror of
				https://github.com/MapMakersAndProgrammers/alternativa3d-archive.git
				synced 2025-10-30 17:05:17 -07:00 
			
		
		
		
	Remove .svn folders
This commit is contained in:
		| @@ -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) + "]"; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Pyogenics
					Pyogenics