2012-05-17 73 views
3

我正在嘗試開發一種Java3D方法,以從當前查看方向到對象中心方向的增量旋轉Universe。Java3D:以增量旋轉宇宙

換句話說,我希望3D宇宙能夠旋轉100步,這樣我點擊的物體就會逐漸移動到屏幕的中心。

我已經在StackOverflow(以及在Web上)上回顧了有關3D旋轉問題的各種答案,但它們幾乎都是特定於旋轉對象的,而不是世界本身。

我也試着回顧一下我的線性代數,但這並不能幫助我確定完成我的需求的Java特定函數。

到目前爲止,我已經嘗試過定義一組增量XYZ座標,並在每次通過循環時動態使用lookAt()。這幾乎奏效,但我沒有看到任何方法來保存或獲得從一個完整的旋轉通道到下一個通道的視點值;每個旋轉過程開始尋找原點。

我也嘗試通過獲得目標和開始變換之間的差異併除以增量的數量(併除去縮放值)來定義旋轉矩陣,然後將該增量旋轉矩陣添加到當前視圖方向每個通過循環。對於遞增值1,這很好。但是,將旋轉分割爲兩個或更多個增量總是會生成「BadTransformException:ViewPlatform上方的不一致變換」錯誤。 (我已經閱讀了Java3D API參考中的這個異常的微不足道的文檔;它可能已經寫在烏爾都語中了,我可以從中得到所有的答案。似乎沒有簡單英語的3D上下文術語定義,如Google可以看到任何地方的「仿射」或「剪切」或「一致」或「統一」)。然後,我嘗試將我的代碼提供給AxisAngle4d,獲得角度(以弧度表示),將角度分成我想要的增量,並通過增量角度值旋轉。這讓世界旋轉了,沒事,但是我沒有選擇任何東西,也沒有看到我能看到的任何圖案。

無奈之下,我嘗試在提取的角度上使用rotX和rotY(將Z設置爲端點),甚至盲目地向其中扔了幾個Math.cos()和Math.sin()包裝器。仍然沒有快樂。

我的直覺告訴我,我已經掌握了基礎知識,並且在Java3D中有一個相對簡單的解決方案。但顯然有一個我正在打的理解牆。我不想繼續這樣做,我想我會繼續前進,看看有沒有人可以在Java3D中提出解決方案。代碼是首選,但我願意嘗試遵循線性代數的解釋,如果這將使我得到一個代碼解決方案。

以下是我用於使用Java的Timer方法計劃旋轉增量的方法的核心。我需要幫助的部分就在ActionListener之前。據推測,這就是魔法代碼會創造某種增量旋轉值的地方,我可以將其應用於當前視圖方向,以便旋轉宇宙而不會出現「不一致」錯誤。

private void flyRotate(double endX, double endY, double endZ) 
    { 
    // Rotate universe by increments until target object is centered in view 
    // 
    // REQUIREMENTS 
    // 1. Rotate the universe by NUMROTS increments from an arbitrary (non-origin) 
    // 3D position and starting viewpoint to an ending viewpoint using the 
    // shortest path and preserving the currently defined "up" vector. 
    // 2. Use the Java Timer() method to schedule the visual update for each 
    // incremental rotation. 
    // 
    // GLOBALS 
    // rotLoop contains the integer loop counter for rotations (init'd to 0) 
    // viewTransform3D contains rotation/translation for current viewpoint 
    // t3d is a reusable Transform3D variable 
    // vtg contains the view platform transform group 
    // NUMROTS contains the number of incremental rotations to perform 
    // 
    // INPUTS 
    // endX, endY, endZ contain the 3D position of the target object 
    // 
    // NOTE: Java3D v1.5.1 or later is required for the Vector3D getX(), 
    // getY(), and getZ() methods to work. 

    final int delay = 20; // milliseconds between firings 
    final int pause = 10; // milliseconds before starting 

    // Get translation components of starting viewpoint vector 
    Vector3d viewVector = new Vector3d(); 
    viewTransform3D.get(viewVector); 
    final double startX = viewVector.getX(); 
    final double startY = viewVector.getY(); 
    final double startZ = viewVector.getZ(); 

    // Don't try to rotate to the location of the current viewpoint 
    if (startX != endX || startY != endY || startZ != endZ) 
    { 
     // Get a copy of the starting view transform 
     t3d = new Transform3D(viewTransform3D); 

     // Define the initial eye/camera position and the "up" vector 
     // Note: "up = +Y" is just the initial naive implementation 
     Point3d eyePoint = new Point3d(startX,startY,startZ); 
     Vector3d upVector = new Vector3d(0.0,1.0,0.0); 

     // Get target view transform 
     // (Presumably something like this is necessary to get a transform 
     // containing the ending rotation values.) 
     Transform3D tNew = new Transform3D(); 
     Point3d viewPointTarg = new Point3d(endX,endY,endZ); 
     tNew.lookAt(eyePoint,viewPointTarg,upVector); 
     tNew.invert(); 

     // Get a copy of the target view transform usable by the Listener 
     final Transform3D tRot = new Transform3D(tNew); 

     // 
     // (obtain either incremental rotation angle 
     // or congruent rotation transform here) 
     // 

     ActionListener taskPerformer = new ActionListener() 
     { 
     public void actionPerformed(ActionEvent evt) 
     { 
      if (++rotLoop <= NUMROTS) 
      { 
      // Apply incremental angle or rotation transform to the 
      // current view 
      t3d = magic(tRot); 

      // Communicate the rotation to the view platform transform group 
      vtg.setTransform(t3d); 
      } 
      else 
      { 
      timerRot.stop(); 
      rotLoop = 0; 
      viewTransform3D = t3d; 
      } 
     } 
     }; 

     // Set timer for rotation steps 
     timerRot = new javax.swing.Timer(delay,taskPerformer); 
     timerRot.setInitialDelay(pause); 
     timerRot.start(); 
    } 
    } 

正如人們經常用這些東西的情況下,有可能是一個更好的方式做什麼,我試圖通過退一步和反思的問題在這裏完成。我也樂意接受建設性的建議。

非常感謝您的幫助!


UPDATE

讓我儘量具體明確的目標多一點。

我有一個包含許多Sphere對象的Java3D Universe。我可以單擊每個對象並動態獲取其預定義的XYZ座標。

在任何時刻,我期待在當前所有可見的對象與「照相機」在特定的XYZ位置和視線方向,這是包含在變換保持旋轉矩陣和平移向量。 (注意:我既可以旋轉宇宙,也可以使用鼠標單獨轉換它,而不必單擊對象,所以有時候包含相機當前旋轉矩陣和平移矢量的視圖變換不會指向任何目標與已知的XYZ物體座標。)

由於攝像機變換和對象的XYZ座標,我想繞我目前的相機位置的宇宙,直到選擇的對象在屏幕中央。我想這樣做是由一系列不連續的增量旋轉構成的,每個旋轉都被渲染以便可見宇宙在查看窗口中看起來「旋轉」,直到所選對象居中爲止。 (我正在翻譯這個對象;這部分至少在工作!)

例如:假設相機在原點,沿着Y軸的「向上」是1.0,並且所選的對象以十個單位爲中心直接放在我的左邊。假設我有一個180度的視野,我可以點擊屏幕左側和屏幕頂部和底部之間一半可見的球體。

當我說出這個詞的時候,宇宙中的每個可見物體都應該出現在從我的左邊到我的右邊的均勻間隔步驟序列中移動(比如說50),直到所選物體完全居中在屏幕中心。

在編碼方面,我需要制定Java3D代碼,通過它我可以圍繞穿過我的相機位置(當前位於0,0,0)並且與Y完全對齊的假想線旋轉宇宙宇宙座標系的軸。 (即,旋轉軸通過的平面,其中Z是始終等於攝像機的位置的Z分量掃過。)

的複雜化的要求是:

  1. 相機可以在3D空間中的某處翻譯除原產地以外。
  2. 對於相機的當前位置和視圖,對象可以位於3D空間的任何位置,包括完全可見但在屏幕外(視域外)。
  3. 旋轉應該走最短的路徑 - 一次旋轉超過180度的宇​​宙。
  4. 作爲旋轉過程的第一步,不應該有可見宇宙的任何「跳躍」或「扭曲」;即當前的「向上」矢量(而不是宇宙的絕對「向上」矢量)應該被保留。

因此,有這樣一個問題:給定一個變換控股(虛擬)攝像機的當前平移和旋轉信息,XYZ在目標對象的宇宙空間座標,什麼Java3D的代碼將旋轉攝像頭的N周圍的宇宙等到物體居中在屏幕上?假設這個解決方案分爲兩部分:首先,一些3D數學(以Java3D表示)計算增量旋轉信息,只給出相機變換和對象的XYZ座標;第二,一個循環[將增量旋轉應用於當前查看變換並更新屏幕],直到循環計數器等於增量數。

這就是那個擊敗我的3D數學部分。我沒有看到,也沒有辦法從當前相機變換和目標對象位置獲取某種形式的增量旋轉信息,然後我可以將其應用於相機變換。至少,我還沒有找到任何不會導致跳躍或扭曲或不等增量移動步驟(或「ViewPlatform上方的不一致轉換」異常)的方式。

必須有一個簡單的解決方案....

+1

我可能會錯過這裏的東西,但爲什麼不只是旋轉並移動你的世界中的「相機/眼睛/視口」?這應該會產生同樣的效果。 –

+0

我同意;棘手的部分是將旋轉分解爲一系列增量旋轉 - 這就是需要解決方案的原因。 –

+0

注意:一個完美的解決方案也將保留當前的「向上」向量(在執行增量旋轉步驟之前不扭轉宇宙),並且將用於起始點以外的起點。 ;) –

回答

0

所以,如果我理解正確的話,你的目標是,使其與所選對象爲中心,以旋轉的攝像頭,但是旋轉不應該是任意向量,而應該保留相機的「向上」方向。

一個解決方案,可能有幫助:

  1. 首先,計算旋轉角度(姑且稱之爲A)關於必要的「向上」矢量使相機朝向自己想要的對象。
  2. 其次,沿着「向上」向量計算必要的平移距離/方向(我們稱之爲D),以便物體根據需要與相機對齊。這可能只是相機/物體之間Z/Y座標的差異。
  3. 通過將A/D潛水到N來找到dA和dD,N是要平滑運動的增量數。
  4. 在一個定時器/時間循環增量A/D中,通過dA/dD分別進行N次,使它們達到最終值。請記住,您正在旋轉相機的「向上」矢量和當前位置,而不是原點。

如果您想要更平滑,更逼真的旋轉,請考慮使用SLERP

+0

我認爲這是朝着正確的方向 - 謝謝。 –