회전하는 입방체 면 위에 비디오 재생하기

By Sergey Malenkov, November 5, 2008

이 예제는 회전하는 입방체의 면 위에 비디오를 보여주기 위한 JavaFX 기술의 사용방법을 보여 준다. 예제는 사용자가 비디오를 재생하려 입방체의 면을 클릭하거나, 입방체를 회전하기 위해 면을 드래그 하는 것을 가능하게 한다.

코드 이해하기

VideoCube class는 사용자가 마우스로 회전시키거나 큐브 면에서 비디오를 재생 할 수 있는 3-D 큐브를 생성한다.

Point class는 그림 1과 같다. 이것은 3-D 공간내의 점을 나타내고 좌표 기반에서 회전 기능을 제공한다.

Source Code
class Point {
  var x: Number;
  var y: Number;
  var z: Number;

  function rotateX(cos, sin) {
    var tmp = cos * y - sin * z;
    z = cos * z + sin * y;
    y = tmp;
  }
  function rotateY(cos, sin) {
    var tmp = cos * x + sin * z;
    z = cos * z - sin * x;
    x = tmp;
  }
  function rotateZ(cos, sin) {
    var tmp = cos * x - sin * y;
    y = cos * y + sin * x;
    x = tmp;
  }
}
  

그림 1: 3D를 지원하는 Point class

그림 2처럼 입방체의 지점들이 선언된다. 마지막 4개의 점은 자동적으로 계산됨을 주의하라.

Source Code
def ful = Point { x:-r   y:-r   z: r }
def fur = Point { x: r   y:-r   z: r }
def flr = Point { x: r   y: r   z: r }
def fll = Point { x:-r   y: r   z: r }

def bul = Point { x: bind - ful.x   y: bind - ful.y   z: bind - ful.z }
def bur = Point { x: bind - fur.x   y: bind - fur.y   z: bind - fur.z }
def blr = Point { x: bind - flr.x   y: bind - flr.y   z: bind - flr.z }
def bll = Point { x: bind - fll.x   y: bind - fll.y   z: bind - fll.z }
  

그림 2: 입방체 점들의 초기화

회전 애니메이션은 그림 3과 같다. 초당 25 프레임으로 보여지기 위해 40ms 마다 좌표를 다시 계산한다. axay 변수들은 적당한 xy 축 위에 입방체가 결정되는 각을 정의한다. 첫번째 4개의 점들만 변경된 것을 확인하라.

Source Code
var ax =  0.002;   def cx = bind Math.cos(ax);   def sx = bind Math.sin(ax);
var ay = -0.006;   def cy = bind Math.cos(ay);   def sy = bind Math.sin(ay);

def rotation = Timeline {
  repeatCount: Timeline.INDEFINITE
  keyFrames: KeyFrame {
    time: 40ms
    action: function() {
      if (ax != 0) {
        ful.rotateX(cx, sx);
        fur.rotateX(cx, sx);
        flr.rotateX(cx, sx);
        fll.rotateX(cx, sx);
      }
      if (ay != 0) {
        ful.rotateY(cy, sy);
        fur.rotateY(cy, sy);
        flr.rotateY(cy, sy);
        fll.rotateY(cy, sy);
      }
    }
  }
}
rotation.play()
  

그림 3: 초당 25번 재연산 발생

PerspectiveTransform class는 마우스 좌표의 변환을 지원을 수행하지 않는 효과이다. 시점 변경기반의 폴리곤은 그림 4와같이, 제어 함수를 제공한다. 마우스 버튼 클릭시에는, 대화 상자가 열리는 것이 보인다. 마우스 드래그시에는, 회전각이 변경된다.

Source Code
        Polygon {
          points: bind [
            pt.ulx, pt.uly,
            pt.urx, pt.ury,
            pt.lrx, pt.lry,
            pt.llx, pt.lly
          ]
          cursor: Cursor.HAND
          blocksMouse: true
          onMouseClicked: function (event) {
            rotation.pause();
            if (JFileChooser.APPROVE_OPTION == chooser.showOpenDialog(null)) {
              source = chooser.getSelectedFile().toURI().toString()
            }
            rotation.play();
          }
          onMouseDragged: function(event) {
            ax = if (-5 < event.dragY and event.dragY < 5) then 0 else - event.dragY / 10000;
            ay = if (-5 < event.dragX and event.dragX < 5) then 0 else   event.dragX / 10000;
          }
        }
  

그림 4: Mouse Events 지원

비디오가 안 열린다면, 그림 5에서와 같이 간단한 애니메이션이 선언된다. 이 노드들의 집단은 시점 변경을 사용함으로 변형된다.

Source Code
        Group {
          effect: pt
          visible: bind error
          content: [
            Rectangle {
              x: -100   width: 200
              y: -100   height: 200
              fill:   bind background
              stroke: bind foreground
              strokeWidth: 2
            }
            Circle { radius: 85   fill: bind foreground }
            Circle { radius: 80   fill: Color.web("#c7b668") }
            Circle { radius: 70   fill: Color.web("#645f37") }
            Circle { radius: 65   fill: bind background }
            Text {
              x: -34
              y:  40
              content: bind "{number}"
              fill: bind foreground
              font: Font { size: 120   embolden: true }
            }
            Line { startX: -80   endX: 80   stroke: bind foreground   strokeWidth: 2 }
            Line { startY: -80   endY: 80   stroke: bind foreground   strokeWidth: 2 }
            Arc {
              radiusX: 80
              radiusY: 80
              startAngle: bind start
              length: bind length
              type: ArcType.ROUND
              opacity: 0.2
            }
          ]
        }
  

그림 5: 입방체 표면에서의 애니메이션

비디오가 열린다면, 그림 6처럼 media view가 재생된다. 이 노드는 시점 변경을 사용함으로 변형된다. 부피는 면의 위치에 종속됨을 주의하라.

Source Code
        MediaView {
          effect: pt
          visible: bind not error
          mediaPlayer: MediaPlayer {
            media: bind media
            autoPlay: true
            repeatCount: MediaPlayer.REPEAT_FOREVER
            volume: bind if (z > 0)
                    then 0.25 * z / r
                    else 0
          }
        }
  

그림 6: 변형된 비디오의 재생

코드 변형

scene은 그림 7처럼 생성된다. 각각의 면마다 사용된 점들의 순서를 변경해 줄 수 있다. 점들의 순서 변경은 면위의 비디오를 회전하거나 거울처럼 반사하는 것이 가능해진다. source 속성은 예제가 시작될 때 자동적으로 읽어질 비디오를 알려준다.

Source Code
  scene: Scene {
    content: [
      Face { number: 1   ul: ful   ur: fur   lr: flr   ll: fll   source: "{__DIR__}VideoCube.avi" }
      Face { number: 2   ul: flr   ur: fur   lr: bll   ll: bul }
      Face { number: 3   ul: bur   ur: fll   lr: flr   ll: bul }
      Face { number: 4   ul: bll   ur: fur   lr: ful   ll: blr }
      Face { number: 5   ul: bur   ur: blr   lr: ful   ll: fll }
      Face { number: 6   ul: bur   ur: bul   lr: bll   ll: blr }
    ]
  }
  

그림 7: Scene 생성