Главная > Анимация, Графика, Основы, Статьи > Пример простой 3D визуализации

Пример простой 3D визуализации

Прочитав туториал "Simple 3D Drawing in Flash CS3", я решил разобраться в исходниках и немного их "классифицировать" (для большей универсальности). И у меня получились следующие классы:

  • Object3D - прототип 3D объекта;
  • Sphere - параметрически описанный объект сферы
  • View3D - класс, рисующий 3D объект
  • Main - основной класс документа

Смотреть результат.

Сегодня я просто выложу исходники, а теорию разберем в другой раз (если это кому-нибудь станет интересно)

Класс Object3D:

package lib3d {

	/**
	 * @author Michael
	 */
	public class Object3D extends Object {
		public var tilesArray : Array = new Array;
		public var tilesColors : Array = new Array;
		private var _nMesh : Number = 10;

		public function set nMesh(n : Number) : void {
			_nMesh = n;
			setTilesArray();
		}

		public function get nMesh() : Number {
			return _nMesh ;
		}
		public function Object3D() {
			setTilesArray();
		}

		public function setTilesArray() : void {

		}
	}
}

Класс Sphere:

package lib3d {
	import lib3d.Object3D;

	/**
	 * @author Michael
	 */
	public class Sphere extends Object3D {


		public function Sphere() {
			super();
		}

		override public function setTilesArray() : void {
			var i : int;
			var j : int;
			var istep : Number;
			var jstep : Number;
			istep = 1.75 * Math.PI / nMesh;
			jstep = Math.PI / nMesh;
			tilesArray = new Array();
			for(i = 0;i <= nMesh; i++) {
				tilesArray[i] = new Array();
				for(j = 0;j <= nMesh;j++) {
					tilesArray[i][j] =
						[
							90 * Math.cos(istep * i) * Math.sin(jstep * j),
							90 * Math.sin(istep * i) * Math.sin(jstep * j),
							90 * Math.cos(jstep * j)
						];
				}
			}
		}
	}
}

Класс View3D:

package lib3d {
	import flash.display.Shape;
	import flash.display.Sprite;

	/**
	 * @author Michael
	 */
	public class View3D extends Sprite {
		private var shCube : Shape = new Shape();
		private var tilesArray : Array = new Array;
		private var tilesColors : Array = new Array;
		private var nMesh : Number = 0;
		private var renderObject:Object3D;

		public var fLen : Number = 1000;
		public var firstRun : Boolean = true;
		public function View3D(obj : Object3D) {
			renderObject = obj;
			addChild(shCube);
			Init();
		}
		public function Init():void{
			tilesArray = renderObject.tilesArray;
			tilesColors = renderObject.tilesColors;
			nMesh = renderObject.nMesh;
			firstRun = true;
		}

		public function renderView(t : Number,p : Number) : void {
			var i : int;
			var j : int;
			var n : int;
			var distArray:Array = new Array();
			var dispArray:Array = new Array();
			var tilesNewArray:Array = new Array();
			var midPoint : Array = new Array();
			var dist : Number;
			var depLen : Number;
			var coloFactor:Number = 1.5;
			t = t * Math.PI / 180;
			p = p * Math.PI / 180;
			shCube.graphics.clear();
			for(i = 0;i <= nMesh;i++) {
				tilesNewArray[i] = new Array();
				for(j = 0;j <= nMesh;j++) {
					// Задаем новые координаты с учетом поворота
					tilesNewArray[i][j] = pointNewView(tilesArray[i][j], t, p);
				}
			}

			for(i = 0;i < nMesh;i++) {
				if(firstRun) {
					tilesColors[i] = new Array();
				}
				// Вычисляем среднюю точку для сортировки
				for(j = 0;j < nMesh;j++) {
					midPoint[0] =
						(
							tilesNewArray[i][j][0] +
							tilesNewArray[i + 1][j][0] +
							tilesNewArray[i][j + 1][0] +
							tilesNewArray[i + 1][j + 1][0]
						) / 4;

					midPoint[1] =
						(
							tilesNewArray[i][j][1] +
							tilesNewArray[i + 1][j][1] +
							tilesNewArray[i][j + 1][1] +
							tilesNewArray[i + 1][j + 1][1]
						) / 4;

					midPoint[2] =
						(
							tilesNewArray[i][j][2] +
							tilesNewArray[i + 1][j][2] +
							tilesNewArray[i][j + 1][2] +
							tilesNewArray[i + 1][j + 1][2]
						) / 4;

					dist = Math.sqrt(Math.pow(fLen - midPoint[0], 2) +
										Math.pow(midPoint[1], 2) +
										Math.pow(midPoint[2], 2)
						);

					distArray.push([dist,i,j]);
					if(firstRun) {
						tilesColors[i][j] = combineRGB(
						(2 * coloFactor * midPoint[2] + 100) * 0.8 + 70,
						(2 * coloFactor * midPoint[1] + 100) * 0.8 + 70,
						(2 * coloFactor * midPoint[0] + 100) * 0.8 + 70);
					}
				}
			}

			// Сортируем по средним точкам для прорисовки
			distArray.sort(byDist);

			for(i = 0;i <= nMesh;i++) {
				dispArray[i] = [];
				for(j = 0;j <= nMesh;j++) {
					dispArray[i][j] = [fLen / (fLen - tilesNewArray[i][j][0]) * tilesNewArray[i][j][1],
								-fLen / (fLen - tilesNewArray[i][j][0]) * tilesNewArray[i][j][2]];
				}
			}

			depLen = distArray.length;

			shCube.graphics.lineStyle(0, 0x333333,0.2);
			for(n = 0;n < depLen;n++) {
				i = distArray[n][1];
				j = distArray[n][2];
				shCube.graphics.moveTo(dispArray[i][j][0], dispArray[i][j][1]);
				shCube.graphics.beginFill(tilesColors[i][j], 1);
				shCube.graphics.lineTo(dispArray[i][j + 1][0], dispArray[i][j + 1][1]);
				shCube.graphics.lineTo(dispArray[i + 1][j + 1][0], dispArray[i + 1][j + 1][1]);
				shCube.graphics.lineTo(dispArray[i + 1][j][0], dispArray[i + 1][j][1]);
				shCube.graphics.lineTo(dispArray[i][j][0], dispArray[i][j][1]);
				shCube.graphics.endFill();
			}

			firstRun = false;
		}


		private function pointNewView(v : Array, theta : Number, phi : Number) : Array {
			var newCoords : Array = [];

			newCoords[0] = v[0] * Math.cos(theta) * Math.sin(phi) +
						v[1] * Math.sin(theta) * Math.sin(phi) +
						v[2] * Math.cos(phi);

			newCoords[1] = -v[0] * Math.sin(theta) + v[1] * Math.cos(theta);

			newCoords[2] = -v[0] * Math.cos(theta) * Math.cos(phi) -
						v[1] * Math.sin(theta) * Math.cos(phi) +
						v[2] * Math.sin(phi);

			return newCoords;
		}

		private function combineRGB(red : Number,green : Number,blue : Number) : Number {

			var RGB : Number;
			if(red > 255) {
				red = 255;
			}
			if(green > 255) {
				green = 255;
			}
			if(blue > 255) {
				blue = 255;
			}

			if(red < 0) {
				red = 0;
			}
			if(green < 0) {
				green = 0;
			}
			if(blue < 0) {
				blue = 0;
			}


			RGB = ( red << 16 ) | ( green << 8 ) | blue;

			return RGB;
		}

		private function byDist(v : Array,w : Array) : Number {
			if (v[0] > w[0]) {
				return -1;
			} else if (v[0] < w[0]) {
				return 1;
			} else {
				return 0;
			}
		}
	}
}

Класс Main:

package {
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.text.TextField;

	import fl.controls.Slider;
	import fl.events.SliderEvent;

	import lib3d.Sphere;
	import lib3d.View3D;

	/**
	 * @author Michael
	 */
	public class Main extends Sprite {
		private var sphere : Sphere;
		private var sphereView : View3D;
		private var doRotate : Boolean = false;
		private var prevX : Number;
		private var prevY : Number;
		private var curTheta : Number = -10;
		private var curPhi : Number = 60;
		private var shBack : Sprite = new Sprite();
		private var mySlider : Slider = new Slider();
		private var info:TextField = new TextField();

		public function Main() {
			// Создаем объект "Сфера"
			sphere = new Sphere();

			// Создаем "просмотрщик" 3D-объекта
			sphereView = new View3D(sphere);
			sphereView.x = 300;
			sphereView.y = 200;

			// Рисуем бакграунд
			drawBack();

			// Добавляем слайдер для указания количества поверхностей объекта
			addChild(mySlider);
			mySlider.y=380;
			mySlider.x=200;
			mySlider.width=200;
			mySlider.minimum=10;
			mySlider.maximum=30;
			mySlider.value=20;
			mySlider.name="MySlider";
			mySlider.liveDragging = true;
			mySlider.addEventListener(SliderEvent.CHANGE, sliderChange);

			addChild(info);
			info.x=420;
			info.y=375;
			info.height=20;
			info.text = 'Детализация: '+ String(mySlider.value);

			shBack.addChild(sphereView);

			// Начальная прорисовка 3D-объекта
			sphere.nMesh = mySlider.value;
			sphereView.Init();
			sphereView.renderView(curTheta, curPhi);

			// Добавляем обработчики мыши для поворота объекта
			shBack.addEventListener(MouseEvent.ROLL_OUT, boardOut);
			shBack.addEventListener(MouseEvent.MOUSE_MOVE, boardMove);
			shBack.addEventListener(MouseEvent.MOUSE_DOWN, boardDown);
			shBack.addEventListener(MouseEvent.MOUSE_UP, boardUp);
		}

		// Обработчик слайдера
		private function sliderChange(event : SliderEvent) : void {
			sphere.nMesh = mySlider.value;
			sphereView.Init();
			sphereView.renderView(curTheta, curPhi);
			info.text = 'Детализация: '+ String(mySlider.value);
		}

		private function drawBack() : void {
			shBack.graphics.lineStyle(1, 0x000000);
			shBack.graphics.beginFill(0x000000);
			shBack.graphics.drawRect(0, 0, 600, 360);
			shBack.graphics.endFill();
			addChild(shBack);
		}

		// Обработчики событий мыши
		private function boardOut(e : MouseEvent) : void {
			doRotate = false;
		}

		private function boardDown(e : MouseEvent) : void {
			prevX = mouseX;
			prevY = mouseY;
			doRotate = true;
		}

		private function boardUp(e : MouseEvent) : void {
			doRotate = false;
		}

		private function boardMove(e : MouseEvent) : void {
			var locX : Number = prevX;
			var locY : Number = prevY;
			if(doRotate) {
				prevX = mouseX;
				prevY = mouseY;
				curTheta -= (prevX - locX);
				curPhi -= (prevY - locY);

				// Прорисовываем с новыми углами поворота
				sphereView.renderView(curTheta, curPhi);

				e.updateAfterEvent();
			}
		}
	}
}