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

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

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

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

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

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

Класс Object3D:

  1. package lib3d {
  2.  
  3. /**
  4. * @author Michael
  5. */
  6. public class Object3D extends Object {
  7. public var tilesArray : Array = new Array;
  8. public var tilesColors : Array = new Array;
  9. private var _nMesh : Number = 10;
  10.  
  11. public function set nMesh(n : Number) : void {
  12. _nMesh = n;
  13. setTilesArray();
  14. }
  15.  
  16. public function get nMesh() : Number {
  17. return _nMesh ;
  18. }
  19. public function Object3D() {
  20. setTilesArray();
  21. }
  22.  
  23. public function setTilesArray() : void {
  24.  
  25. }
  26. }
  27. }
  28.  

Класс Sphere:

  1. package lib3d {
  2. import lib3d.Object3D;
  3.  
  4. /**
  5. * @author Michael
  6. */
  7. public class Sphere extends Object3D {
  8.  
  9.  
  10. public function Sphere() {
  11. super();
  12. }
  13.  
  14. override public function setTilesArray() : void {
  15. var i : int;
  16. var j : int;
  17. var istep : Number;
  18. var jstep : Number;
  19. istep = 1.75 * Math.PI / nMesh;
  20. jstep = Math.PI / nMesh;
  21. tilesArray = new Array();
  22. for(i = 0;i <= nMesh; i++) {
  23. tilesArray[i] = new Array();
  24. for(j = 0;j <= nMesh;j++) {
  25. tilesArray[i][j] =
  26. [
  27. 90 * Math.cos(istep * i) * Math.sin(jstep * j),
  28. 90 * Math.sin(istep * i) * Math.sin(jstep * j),
  29. 90 * Math.cos(jstep * j)
  30. ];
  31. }
  32. }
  33. }
  34. }
  35. }

Класс View3D:

  1. package lib3d {
  2. import flash.display.Shape;
  3. import flash.display.Sprite;
  4.  
  5. /**
  6. * @author Michael
  7. */
  8. public class View3D extends Sprite {
  9. private var shCube : Shape = new Shape();
  10. private var tilesArray : Array = new Array;
  11. private var tilesColors : Array = new Array;
  12. private var nMesh : Number = 0;
  13. private var renderObject:Object3D;
  14.  
  15. public var fLen : Number = 1000;
  16. public var firstRun : Boolean = true;
  17. public function View3D(obj : Object3D) {
  18. renderObject = obj;
  19. addChild(shCube);
  20. Init();
  21. }
  22. public function Init():void{
  23. tilesArray = renderObject.tilesArray;
  24. tilesColors = renderObject.tilesColors;
  25. nMesh = renderObject.nMesh;
  26. firstRun = true;
  27. }
  28.  
  29. public function renderView(t : Number,p : Number) : void {
  30. var i : int;
  31. var j : int;
  32. var n : int;
  33. var distArray:Array = new Array();
  34. var dispArray:Array = new Array();
  35. var tilesNewArray:Array = new Array();
  36. var midPoint : Array = new Array();
  37. var dist : Number;
  38. var depLen : Number;
  39. var coloFactor:Number = 1.5;
  40. t = t * Math.PI / 180;
  41. p = p * Math.PI / 180;
  42. shCube.graphics.clear();
  43. for(i = 0;i <= nMesh;i++) {
  44. tilesNewArray[i] = new Array();
  45. for(j = 0;j <= nMesh;j++) {
  46. // Задаем новые координаты с учетом поворота
  47. tilesNewArray[i][j] = pointNewView(tilesArray[i][j], t, p);
  48. }
  49. }
  50.  
  51. for(i = 0;i < nMesh;i++) {
  52. if(firstRun) {
  53. tilesColors[i] = new Array();
  54. }
  55. // Вычисляем среднюю точку для сортировки
  56. for(j = 0;j < nMesh;j++) {
  57. midPoint[0] =
  58. (
  59. tilesNewArray[i][j][0] +
  60. tilesNewArray[i + 1][j][0] +
  61. tilesNewArray[i][j + 1][0] +
  62. tilesNewArray[i + 1][j + 1][0]
  63. ) / 4;
  64.  
  65. midPoint[1] =
  66. (
  67. tilesNewArray[i][j][1] +
  68. tilesNewArray[i + 1][j][1] +
  69. tilesNewArray[i][j + 1][1] +
  70. tilesNewArray[i + 1][j + 1][1]
  71. ) / 4;
  72.  
  73. midPoint[2] =
  74. (
  75. tilesNewArray[i][j][2] +
  76. tilesNewArray[i + 1][j][2] +
  77. tilesNewArray[i][j + 1][2] +
  78. tilesNewArray[i + 1][j + 1][2]
  79. ) / 4;
  80.  
  81. dist = Math.sqrt(Math.pow(fLen - midPoint[0], 2) +
  82. Math.pow(midPoint[1], 2) +
  83. Math.pow(midPoint[2], 2)
  84. );
  85.  
  86. distArray.push([dist,i,j]);
  87. if(firstRun) {
  88. tilesColors[i][j] = combineRGB(
  89. (2 * coloFactor * midPoint[2] + 100) * 0.8 + 70,
  90. (2 * coloFactor * midPoint[1] + 100) * 0.8 + 70,
  91. (2 * coloFactor * midPoint[0] + 100) * 0.8 + 70);
  92. }
  93. }
  94. }
  95.  
  96. // Сортируем по средним точкам для прорисовки
  97. distArray.sort(byDist);
  98.  
  99. for(i = 0;i <= nMesh;i++) {
  100. dispArray[i] = [];
  101. for(j = 0;j <= nMesh;j++) {
  102. dispArray[i][j] = [fLen / (fLen - tilesNewArray[i][j][0]) * tilesNewArray[i][j][1],
  103. -fLen / (fLen - tilesNewArray[i][j][0]) * tilesNewArray[i][j][2]];
  104. }
  105. }
  106.  
  107. depLen = distArray.length;
  108.  
  109. shCube.graphics.lineStyle(0, 0x333333,0.2);
  110. for(n = 0;n < depLen;n++) {
  111. i = distArray[n][1];
  112. j = distArray[n][2];
  113. shCube.graphics.moveTo(dispArray[i][j][0], dispArray[i][j][1]);
  114. shCube.graphics.beginFill(tilesColors[i][j], 1);
  115. shCube.graphics.lineTo(dispArray[i][j + 1][0], dispArray[i][j + 1][1]);
  116. shCube.graphics.lineTo(dispArray[i + 1][j + 1][0], dispArray[i + 1][j + 1][1]);
  117. shCube.graphics.lineTo(dispArray[i + 1][j][0], dispArray[i + 1][j][1]);
  118. shCube.graphics.lineTo(dispArray[i][j][0], dispArray[i][j][1]);
  119. shCube.graphics.endFill();
  120. }
  121.  
  122. firstRun = false;
  123. }
  124.  
  125.  
  126. private function pointNewView(v : Array, theta : Number, phi : Number) : Array {
  127. var newCoords : Array = [];
  128.  
  129. newCoords[0] = v[0] * Math.cos(theta) * Math.sin(phi) +
  130. v[1] * Math.sin(theta) * Math.sin(phi) +
  131. v[2] * Math.cos(phi);
  132.  
  133. newCoords[1] = -v[0] * Math.sin(theta) + v[1] * Math.cos(theta);
  134.  
  135. newCoords[2] = -v[0] * Math.cos(theta) * Math.cos(phi) -
  136. v[1] * Math.sin(theta) * Math.cos(phi) +
  137. v[2] * Math.sin(phi);
  138.  
  139. return newCoords;
  140. }
  141.  
  142. private function combineRGB(red : Number,green : Number,blue : Number) : Number {
  143.  
  144. var RGB : Number;
  145. if(red > 255) {
  146. red = 255;
  147. }
  148. if(green > 255) {
  149. green = 255;
  150. }
  151. if(blue > 255) {
  152. blue = 255;
  153. }
  154.  
  155. if(red < 0) {
  156. red = 0;
  157. }
  158. if(green < 0) {
  159. green = 0;
  160. }
  161. if(blue < 0) {
  162. blue = 0;
  163. }
  164.  
  165.  
  166. RGB = ( red << 16 ) | ( green << 8 ) | blue;
  167.  
  168. return RGB;
  169. }
  170.  
  171. private function byDist(v : Array,w : Array) : Number {
  172. if (v[0] > w[0]) {
  173. return -1;
  174. } else if (v[0] < w[0]) {
  175. return 1;
  176. } else {
  177. return 0;
  178. }
  179. }
  180. }
  181. }
  182.  

Класс Main:

  1. package {
  2. import flash.display.Sprite;
  3. import flash.events.MouseEvent;
  4. import flash.text.TextField;
  5.  
  6. import fl.controls.Slider;
  7. import fl.events.SliderEvent;
  8.  
  9. import lib3d.Sphere;
  10. import lib3d.View3D;
  11.  
  12. /**
  13. * @author Michael
  14. */
  15. public class Main extends Sprite {
  16. private var sphere : Sphere;
  17. private var sphereView : View3D;
  18. private var doRotate : Boolean = false;
  19. private var prevX : Number;
  20. private var prevY : Number;
  21. private var curTheta : Number = -10;
  22. private var curPhi : Number = 60;
  23. private var shBack : Sprite = new Sprite();
  24. private var mySlider : Slider = new Slider();
  25. private var info:TextField = new TextField();
  26.  
  27. public function Main() {
  28. // Создаем объект "Сфера"
  29. sphere = new Sphere();
  30.  
  31. // Создаем "просмотрщик" 3D-объекта
  32. sphereView = new View3D(sphere);
  33. sphereView.x = 300;
  34. sphereView.y = 200;
  35.  
  36. // Рисуем бакграунд
  37. drawBack();
  38.  
  39. // Добавляем слайдер для указания количества поверхностей объекта
  40. addChild(mySlider);
  41. mySlider.y=380;
  42. mySlider.x=200;
  43. mySlider.width=200;
  44. mySlider.minimum=10;
  45. mySlider.maximum=30;
  46. mySlider.value=20;
  47. mySlider.name="MySlider";
  48. mySlider.liveDragging = true;
  49. mySlider.addEventListener(SliderEvent.CHANGE, sliderChange);
  50.  
  51. addChild(info);
  52. info.x=420;
  53. info.y=375;
  54. info.height=20;
  55. info.text = 'Детализация: '+ String(mySlider.value);
  56.  
  57. shBack.addChild(sphereView);
  58.  
  59. // Начальная прорисовка 3D-объекта
  60. sphere.nMesh = mySlider.value;
  61. sphereView.Init();
  62. sphereView.renderView(curTheta, curPhi);
  63.  
  64. // Добавляем обработчики мыши для поворота объекта
  65. shBack.addEventListener(MouseEvent.ROLL_OUT, boardOut);
  66. shBack.addEventListener(MouseEvent.MOUSE_MOVE, boardMove);
  67. shBack.addEventListener(MouseEvent.MOUSE_DOWN, boardDown);
  68. shBack.addEventListener(MouseEvent.MOUSE_UP, boardUp);
  69. }
  70.  
  71. // Обработчик слайдера
  72. private function sliderChange(event : SliderEvent) : void {
  73. sphere.nMesh = mySlider.value;
  74. sphereView.Init();
  75. sphereView.renderView(curTheta, curPhi);
  76. info.text = 'Детализация: '+ String(mySlider.value);
  77. }
  78.  
  79. private function drawBack() : void {
  80. shBack.graphics.lineStyle(1, 0x000000);
  81. shBack.graphics.beginFill(0x000000);
  82. shBack.graphics.drawRect(0, 0, 600, 360);
  83. shBack.graphics.endFill();
  84. addChild(shBack);
  85. }
  86.  
  87. // Обработчики событий мыши
  88. private function boardOut(e : MouseEvent) : void {
  89. doRotate = false;
  90. }
  91.  
  92. private function boardDown(e : MouseEvent) : void {
  93. prevX = mouseX;
  94. prevY = mouseY;
  95. doRotate = true;
  96. }
  97.  
  98. private function boardUp(e : MouseEvent) : void {
  99. doRotate = false;
  100. }
  101.  
  102. private function boardMove(e : MouseEvent) : void {
  103. var locX : Number = prevX;
  104. var locY : Number = prevY;
  105. if(doRotate) {
  106. prevX = mouseX;
  107. prevY = mouseY;
  108. curTheta -= (prevX - locX);
  109. curPhi -= (prevY - locY);
  110.  
  111. // Прорисовываем с новыми углами поворота
  112. sphereView.renderView(curTheta, curPhi);
  113.  
  114. e.updateAfterEvent();
  115. }
  116. }
  117. }
  118. }
  119.