Jump to content

Gabor64738

Members
  • Content Count

    5
  • Joined

  • Last visited

Everything posted by Gabor64738

  1. Gabor64738

    Rotate a cube to align with a 3D line

    I am trying to rotate a 3D object (StrokeCube) to align its Y axis with a line segment defined by two points (two Spheres). Is there an easy way to do this using say CreateLookAtRH, or similar? The Help and https://docwiki.embarcadero.com/ does not describe this and similar methods and I cannot find any example code. Having searched and read for a few days/nights, I've gone down the scary path of rotation matrices etc. This is the process I have so far: Create Vector1 as the AbsoluteUp (TVector3D) of the StrokeCube. Create Vector2 that runs from Sphere1 to Sphere2 (subtracting them), also TVector3D. Normalise the two vectors. Create RotationAxis by calculating their cross product (RotationAxis perpendicular to both). Normalise RotationAxis. Calculate Angle between Vector1 and Vector2 as arccos of their dot product divided by (Vector1Length*Vector2Length). R := mat.CreateRotation() with the normalised RotationAxis and the Angle. Getting the X Y Z rotations for the StrokeCube out of R does not give correct results. I've tried many variants, e.g. https://learnopencv.com/rotation-matrix-to-euler-angles/ and https://uk.mathworks.com/matlabcentral/answers/493771-euler-3d-rotation-between-two-vectors. My questions: Is there an easy way to align a 3D object (StrokeCube) to a line defined by two points? If not, then is my processing correct so far? What matches the X Y Z rotations of an FMX 3D object? One of the Euler sequences, Quaternions, or something else? How do I extract the necessary rotations from the rotation matrix? Thanks for your help. Update: I've tried these but they don't rotate the Segment (StrokeCube): Segment.AbsoluteMatrix.CreateRotation(Vector3DP1P2n, Angle); Segment.AbsoluteMatrix.CreateLookAtRH(TPoint3D.Create(M1.Position.X,M1.Position.Y,M1.Position.Z), TPoint3D.Create(M2.Position.X,M2.Position.Y,M2.Position.Z), TPoint3D.Create(0,0,0)); Segment.LocalMatrix.CreateRotation(Vector3DP1P2n, Angle); Segment.LocalMatrix.CreateLookAtRH(TPoint3D.Create(M1.Position.X, M1.Position.Y, M1.Position.Z), TPoint3D.Create(M2.Position.X,M2.Position.Y,M2.Position.Z), TPoint3D.Create(0,0,0));
  2. Gabor64738

    Rotate a cube to align with a 3D line

    Uwe's perfect solution deserves a medal! This is most probably the shortest, quickest and therefore most efficient solution. No need for changing the 3D matrix of the object with the class helper, which has some side effects, e.g. the RotationAngle returns zeros. Writing the matrix does have some advantages though, so it goes into my toolbox. Here is the code and screenshot: // See thread: https://en.delphipraxis.net/topic/13848-rotate-a-cube-to-align-with-a-3d-line/ // Create a Viewport3D. // As its child, create a Dummy. // As its children, create a StrokeCube (name: Segment) and two Spheres (names: M1 M2). // Make Segment Height 6, Width 2, Depth 1 // Create a Cylinder as Segment's child, Height 6, Width 0.01, Depth 0.01 // Make the M1 and M2 H/W/D 0.2 // Position M1 and M2 randomly. // To test, change the position of M1 and/or M2 with a TrackBar and call this procedure in its OnChange event. procedure TForm1.Rotate(Sender: TObject); var M2M1distanceX, M2M1distanceZ, M2M1distanceXZ, M2M1distanceY: single; begin // Translate Segment to M1. Segment.Position.X:= M1.Position.X; Segment.Position.Y:= M1.Position.Y; Segment.Position.Z:= M1.Position.Z; // Reset Segment's rotations to zero. Segment.ResetRotationAngle; // X distance between M2 and M1. M2M1distanceX:= M2.Position.X-M1.Position.X; // Z distance between M1 and M2. M2M1distanceZ:= M2.Position.Z-M1.Position.Z; // Rotate Segment about Y. Segment.RotationAngle.Y:= RadToDeg(arctan2(M2M1distanceX,M2M1distanceZ)); // Distance between the projections of M1 and M2 on the XZ plane. M2M1distanceXZ:= sqrt(sqr(M2M1distanceX)+sqr(M2M1distanceZ)); // Vertical distance between M1 and M2. M2M1distanceY:= M2.Position.Y-M1.Position.Y; // Rotate Segment about X. Segment.RotationAngle.X:= RadToDeg(arctan2(M2M1distanceXZ,M2M1distanceY)); end;
  3. Gabor64738

    Rotate a cube to align with a 3D line

    To be able to see the effects of my rotations, I do have the StrokeCube in a Dummy and I rotate it with this code, but only horizontally with the mouse. The vertical rotations I sort out by rotating the camera on a circle up and down. I made this a few years ago and it gives the exact behaviour one expects in a 3D world. Others suggested having two nested Dummies with the objects inside, one Dummy rotating in the X direction, other in the Y direction. Using this logic to align my StrokeCube with a Vector is interesting...
  4. Gabor64738

    Rotate a cube to align with a 3D line

    Yes, sorry, as I sunk back into the code, I moved away from what you said and ended up rotating about X and Z (not Y ). Now that I read your instruction more carefully, it does make perfect sense: rotate the StrokeCube about its vertical Y axis in the XZ plane. This rotation will keep the rotated Z axis in the XZ plane, so the next rotation about the Z axis will align the Y axis of the StrokeCube with the Vector. In fact, 90 degrees less rotation about Y can be followed by the next rotation about the rotated X axis, and will give the same aligned StrokeCube, but with 90 degrees rotated about its Y axis. I'll go back and try these, thanks. In the meantime, I've explored directly writing the 3D matrix of the StrokeCube and it works wonderfully! The idea comes from Paul Toth and the trick is to use a class helper for TControl3D which makes the matrix writeable. See https://github.com/tothpaul/Delphi/blob/master/Google Cardboard/4 FullDemo/Main.pas I'm using FMX/FireMonkey in Delphi 10.3.3 where an object's 3D matrix is read-only. Has it been changed in a more recent version to writeable?
  5. Gabor64738

    Rotate a cube to align with a 3D line

    Thanks for your reply, Uwe. Setting StrokeCube.RotationAngle.X and StrokeCube.RotationAngle.Z would only work if these rotations were applied about the global X and Z axes. The first rotation about X works fine because the local X axis is the same as the global X axis (after the obligatory first step of StrokeCube.ResetRotationAngles), but the second rotation about Z happens about the rotated Z axis of the StrokeCube. The result is not pretty. Also, the order of the two rotations (X Z or Z X) gives two different results, as we know is the case with Euler angles. The CreateRotation function will lead to the solution, but the (rotated) matrix it returns cannot be applied to a 3D object (StrokeCube) because a 3D object's matrix is read-only. By default, the only way the StrokeCube can be rotated is with its StrokeCube.RotationAngle, about the local X Y Z axes of the object. BUT, I found a promising lead last night (dawn), code that opens up the 4x4 matrix of a 3D object making it writeable! I will try this out and report back here. Still, there may be an easy way to rotate a 3D object to align its Y axis with a line segment, using the local X and Z rotations of the object, correcting for the fact that the second axis is rotated due to the first rotation. If you know how to do it, please let us know.
×