Java3D:按增量旋转Universe

时间:2012-05-17 01:40:49

标签: java rotation java-3d

我正在尝试开发一种Java3D方法,用于以从当前查看方向到对象中心方向的增量旋转Universe。

换句话说,我希望3D宇宙旋转100步,这样我点击的对象就会逐渐移动到屏幕的中心。

我已经在StackOverflow(以及Web)上回顾了3D旋转问题的各种答案,但几乎所有这些问题都是针对旋转对象而不是世界本身。

我也试过检查我的线性代数,但这并没有帮助我识别出满足我要求的特定于Java的函数。

到目前为止,我已尝试定义一组增量XYZ坐标,并在每次循环中动态使用lookAt()。这几乎可行,但我没有看到任何方法来保存或获得从一个完整的旋转传递到下一个的视点值;每个轮换通道都会从原点开始。

我还尝试通过获取目标和开始变换之间的差异并除以增量数(并删除缩放值)来定义旋转矩阵,然后将该增量旋转矩阵添加到当前视图方向在每次通过循环。这适用于增量值为1.但是将旋转分成两个或更多增量总是会生成" BadTransformException:ViewPlatform"之上的非全等变换。错误。 (我已经在Java3D API参考中阅读了这个例外的微薄文档;它可能也是用乌尔都语编写的,我可以用它来解决所有问题。似乎没有关于3D上下文的简单英语定义类似于"仿射"或"剪切"或"全等"或#34;制服" Google可以看到的任何地方。)

然后我试图提示我的代码提供AxisAngle4d,获得角度(以弧度表示),将该角度除以我想要的增量,然后按增量角度值旋转。这让世界旋转,好吧,但是我选择的物体附近,而不是我能看到的任何模式。

在绝望中我尝试在提取的角度上使用rotX和rotY(将Z设置为端点),甚至盲目地将几个Math.cos()和Math.sin()包装器放在那里。仍然没有快乐。

我的直觉告诉我,我已经掌握了基础知识,并且Java3D中有一个相对简单的解决方案。但显然我有一个理解墙。我没有继续这样做,而是认为我会继续前进,看看这里是否有人可以在Java3D中提出解决方案。代码是首选,但我愿意尝试遵循线性代数中的解释,如果这将使我得到代码解决方案。

以下是我使用Java的Timer方法调度旋转增量的方法的核心。我需要帮助的部分就在ActionListener之前。据推测,魔术代码会在哪里创建某种增量旋转值,我可以(在循环中)应用到当前视图方向,以便旋转宇宙而不会得到"非一致&#34 ;错误。

  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();
    }
  }

通常情况就是如此,通过退回并重新思考问题,可能有更好的方法来做我想在这里完成的事情。我也愿意接受建设性的建议。

非常感谢您对此的任何帮助!


更新

让我尝试更具体地定义目标。

我有一个包含许多Sphere对象的Java3D Universe。我可以单击每个对象并动态获取其预定义的XYZ坐标。

在任何时候,我都会用相机拍摄所有目前可见的物体&#34;在特定的XYZ位置和视图方向,它们包含在保持旋转矩阵和平移向量的变换中。

(注意:我可以旋转宇宙并使用鼠标平移它,而不是单击对象。所以有时候包含相机的当前旋转矩阵和平移向量的视图变换没有指向在具有已知XYZ坐标的任何目标对象上。)

鉴于相机变换和对象的XYZ坐标,我想围绕当前相机位置旋转Universe,直到所选对象在屏幕中居中。我希望这样做是一系列离散的增量旋转,每个旋转都被渲染,以便可见宇宙看起来像旋转&#34;在查看窗口中,直到所选对象居中。 (我跟着这个对象进行了翻译;这个部分至少起作用了!)

示例:假设相机位于原点,&#34; up&#34;沿Y轴是1.0,所选对象直接在我的左边以十个单位为中心。假设我有一个180度的视野,我可以点击一直到屏幕左边可见的球体的一半,并且在屏幕的顶部和底部之间。

当我说出这个词的时候,宇宙中的每个可见物体都应该从我的左边到右边以一系列均匀间隔的步骤(让我们说是50)移动,直到所选物体完全居中于屏幕。

在编码术语中,我需要编写Java3D代码,通过该代码,我可以围绕一条穿过我的摄像机位置(当前位于0,0,0)的假想线旋转宇宙,并且与Y-完美对齐宇宙轴的坐标系。 (即,旋转轴扫过一个平面,其中Z总是等于相机位置的Z分量。)

复杂的要求是:

  1. 相机可以在除原点之外的3D空间中翻译。
  2. 相对于相机的当前位置和视图,对象可以位于3D空间中的任何位置,包括完全可见但在屏幕外(视锥体外)。
  3. 旋转应采用最短的路径 - 不要一次旋转超过180度的宇​​宙。
  4. 不应该有任何&#34;跳跃&#34;或者&#34;扭曲&#34;可见宇宙作为旋转过程的第一步;即,当前&#34; up&#34;应该保留矢量(不是宇宙的绝对&#34;向上&#34;矢量)。
  5. 所以有一个问题:给定一个变换来保存(虚拟)摄像机的当前平移和旋转信息,以及目标对象的宇宙空间中的XYZ坐标,Java3D代码将旋转宇宙在相机周围以N等步,直到物体在屏幕中居中?

    据推测,这个解决方案分为两个部分:第一,一些3D数学(用Java3D表示)来计算仅给出相机变换和对象的XYZ坐标的增量旋转信息;第二,一个循环[将增量旋转应用于当前查看变换并更新屏幕],直到循环计数器等于增量数。

    这是3D数学部分击败了我。我没有看到并且无法从当前相机变换和目标对象位置获取某种形式的增量旋转信息,然后我可以应用于相机变换。至少,我还没有找到任何不会引起跳跃或扭曲或不等增量移动步骤的方式(或者在ViewPlatform&#34之外的#34;非一致转换;例外)。

    必须有一个简单的解决方案......

1 个答案:

答案 0 :(得分:0)

因此,如果我理解正确,你的目标是旋转相机,使其以所选对象为中心,但旋转不应该是任意向量,而应保持相机的“向上”方向。

可能有效的解决方案:

  1. 首先,计算所需的“向上”矢量的旋转角度(我们称之为A),以便摄像机面向您想要的物体。
  2. 其次,计算沿“向上”矢量所需的平移距离/方向(让我们称之为D),以便物体在必要时与摄像机对齐。这可能只是相机/物体之间Z / Y坐标的差异。
  3. 通过将A / D潜水为N来查找dA和dD,您想要采取的增量数量来平滑运动。
  4. 在定时器/时间循环中,将A / D分别按dA / dD递增N次,将它们设为最终值。请记住,您正在旋转相机的“向上”向量和当前位置,而不是关于原点..
  5. 如果您想要更平滑,更逼真的旋转,请考虑使用SLERP