我正在尝试开发一个Java3D方法,以便将宇宙从当前视角方向旋转到物体中心方向的增量。
换句话说,我希望3D宇宙在100个短步骤内旋转,这样我点击的物体就会逐渐移动到屏幕中心。
我已经查看了StackOverflow(以及Web上)关于3D旋转问题的各种答案,但几乎所有答案都是针对旋转对象而不是世界本身。
我还尝试回顾我的线性代数知识,但这并没有帮助我找到实现我的要求的Java特定函数。
到目前为止,我已经尝试定义一组增量XYZ坐标,并在每次循环通过lookAt()动态使用它们。那几乎可以工作,但我看不到如何保留或获取从一个完整的旋转过程到下一个旋转过程的视点值;每个旋转过程都从原点开始。
我还尝试过通过获取目标和起始变换之间的差异,并将其除以增量数(并删除缩放值)来定义旋转矩阵,然后在每次循环通过将该增量旋转矩阵添加到当前视图方向中。对于增量值为1,这样做效果很好。但是将旋转分成两个或更多的增量总是会生成“BadTransformException:ViewPlatform上的非共形变换”错误。(我已经阅读了Java3D API参考文档中关于此异常的简单文档;对于我来说,它可能就像乌尔都语一样难以理解。似乎没有任何谷歌可以看到的关于“仿射”、“剪切”、“共形”或“均匀”等3D上下文术语的简明英文定义。)
然后,我试图修改我的代码,提供一个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();
}
}
通常情况下,对于这些事情,通过退后一步并重新思考问题可能会有更好的解决方法。我也乐意接受建设性的建议。
非常感谢您对此的任何帮助!
更新
让我试着更具体地定义目标。
我有一个包含许多球体物体的Java3D universe。我可以单击每个对象并动态获取其预定义的XYZ坐标。
在任何时刻,我都在以特定的XYZ位置和视图方向使用“相机”查看所有当前可见对象,这些对象包含在变换中,其中包含旋转矩阵和平移向量。
(请注意:我可以独立于单击对象使用鼠标旋转宇宙并穿过它。因此,当视图变换包含相机的当前旋转矩阵和平移向量未指向具有已知XYZ坐标的任何目标对象时,会出现这种情况。)
给定相机变换和对象的XYZ坐标,我想围绕我的当前摄像机位置旋转宇宙,直到所选对象位于屏幕中心。我要将此作为一系列离散增量旋转来完成,每次旋转都会呈现出可见宇宙在视窗中“旋转”,直到所选对象居中为止。(之后我要将其转化为对象的平移;这部分至少是有效的!)
例如:假设相机位于原点,“上方”沿Y轴为1.0,所选对象位于直接在我左侧10个单位的中心。假设我有一个180度的视野,我可以点击屏幕最左边可见的半球,并且位于屏幕顶部和底部之间。
当我下达指令时,宇宙中的每个可见对象都应该呈现出一系列均匀间隔的步骤(假设为50),从我的左侧移动到右侧,直到所选对象正好位于屏幕中心。
从编程角度来看,我需要找出Java3D代码,通过该代码我可以围绕一条想象的线旋转宇宙,该线穿过我的相机位置(目前位于0,0,0),并且与宇宙坐标系的Y轴完全对齐。(即,旋转轴扫过一个平面,其中Z始终等于相机位置的Z分量。)
复杂的要求是:
- 相机可以在三维空间中的任何位置进行平移,不一定是原点。
- 物体可以在三维空间中的任何位置,相对于相机的当前位置和视角,包括可见但在屏幕之外(视锥体之外)的位置。
- 旋转应该采用最短路径——不要一次旋转超过180度。
- 在旋转过程中,可见宇宙不应该出现任何“跳跃”或“扭曲”,即当前的“上”向量(而不是宇宙的绝对“上”向量)应该保持不变。
所以问题是:给定一个保存了(虚拟)相机当前平移和旋转信息的变换,以及目标物体在宇宙空间中的XYZ坐标,什么样的Java3D代码能够使得宇宙围绕相机以N个等步骤旋转,直到物体位于屏幕中心?
这个解决方案大致分为两部分:首先,使用一些3D数学(用Java3D表示)来计算增量旋转信息,只需相机变换和物体的XYZ坐标;其次,使用一个循环,[将增量旋转应用于当前的视图变换并更新屏幕],直到循环计数器等于增量的数量。
就是那个3D数学部分让我束手无策。我没有看到并且无法想出一种方法来从当前相机变换和目标对象位置获取某种形式的增量旋转信息,然后将其应用于相机变换。至少,我还没有找到任何不会导致跳跃、扭曲或不平等增量移动步骤(或“在ViewPlatform上方的非共面变换”异常)的方法。
一定有一个简单的解决方案....