Open dicepd opened 6 years ago
I found this about quats https://blog.molecular-matters.com/2013/05/24/a-faster-quaternion-vector-multiplication/ I amy code this up and test when I have finished the quat tests and added to from Matrix
It have also some interesting articles. It will be no difficult to implement
Right when I get to Slerp ordinary Slerp works fine. How ever Slerp with spin is fundamentally flawed from the first line of code onward. It may give increasing angles but not in a defined manner.
cost:=VectorAngleCosine(QStart.ImagPart, QEnd.ImagPart); <--- Imag part of quat holds half angles
Again no one has used this in Scene, another for the bin? I will check in as is, so you can play with the behaviour to see if I am 'doing it wrong'
Ok maybe you can scale with quat, something I had not seen before
You don’t seem to find it in writing anywhere, but quaternions support uniform scaling just fine: p’ = q p q (simplified rotation form)…multiply q by non-zero scalar ‘s’: (qs) p (qs) = ss (q p q*) = ss p’. So sqrt(s) is a uniform scale factor and these obviously naturally compose.
Many things don't work here with Quaternion :
"CreateTwoUnitAffine:Sub3 Z failed " expected: <0,7071> but was: <1>
"CreateTwoUnitHmg:Sub3 Z failed " expected: <0,7071> but was: <1>
"TestOpMul:Sub5 X failed " expected: <-0,5> but was: <0,5>
"Normalize:Sub1 X failed " expected: <0,5> but was: <2>
"MultiplyAsSecond:Sub10 Y failed " expected: <0,5> but was: <-0,5>
"SlerpSingle:Sub3 Z failed " expected: <0,3827> but was: <1>
"SlerpSingle:Sub3 Z failed " expected: <0,9239> but was: <0,3536>
Quaternion * Quaternion no match(ImagePart.X: -117,29015 ,ImagePart.Y: -18,92155 ,ImagePart.Z: 77,51254 , RealPart.W: 54,67465) --> (ImagePart.X: 133,66414 ,ImagePart.Y: 7,49755 ,ImagePart.Z: -46,91354 , RealPart.W: 54,67465)
Quaternion Normalize no match(ImagePart.X: 0,00000 ,ImagePart.Y: 0,00000 ,ImagePart.Z: 0,00000 , RealPart.W: 0,00000) --> (ImagePart.X: 0,00000 ,ImagePart.Y: 0,00000 ,ImagePart.Z: 0,00000 , RealPart.W: 0,00000)
What the change you did exactly ? Normalize and mul worked before
Are you normalising 3 element or 4 with Quats you have to normalise all four not just the inaginary vector
"CreateTwoUnitAffine:Sub3 Z failed " expected: <0,7071> but was: <1>
this result would point to that, Quats are 4 dimensional beasts so are normalised as such. It is not a vector + flag or Vector + length. So if you copied Vector normalise then this would be the cause.
I' m doing nothing just ran the test
I found article in french http://mecaspa.cannes-aero-patrimoine.net/SCAO/QUATERN/complements/som_quat.htm#note_calcul_perso but don't understand well all yet. I need little more time
Ok I just looked at the win64 SSE and the above is exactly the reason for most of the errors. only error in win64 native is slerp spin
by comment andps xmm2, [RIP+cSSE_MASK_NO_W]
in normailize, now the SlerpSingle func is working but not others
Now onto the gimbal lock code in Euler Create. What I think has happened here is the original code in the sub function was the first written, someone read about gimbal lock and decided to 'protect' original code with some code found somewhere, not realising that the original code was gimbal lock safe as it creates 3 quats and returns the product of the three, This is inherently safe from gimbal lock as it is combining quats not euler angles.
maybe this
Made mul conform to first second as there is a MulAsSecond (not tested yet) which does non Math order.
This cured a lot of ills in the pascal. So tests reflect what it naturally broke. Try swapping A and B registers
Try swapping A and B registers
In which function ???? i'm lost, normailze ? or Mul
I have checked in the fixes
Normalize could do with reworking and simplifying but it does work
Ok, i'll compare with old code, i don't see what you're doing. So now only 1 fail :
That one is to be expected that is the routine that makes no sense to me.
Thinking ahead a bit while I finish Quat and Matrix what functionallity are we missing from fustrum. That I think, is the last piece of the jigsaw to a simple software world line render. We have plane and the ability to test for in plane. Do we need a create from Quat/EyeVector or similar.
Yes, you're right missing some functions, but not many. For a 3D word render. After some learning somethings about quaternion. I'm thinking it will good if we'll add RotateWithQuaternion functions in TGLZVectorHelper. From what I read, we would have several advantages to using quaternions in rotations rather than matrices
The one issue I had withquat was needing to scale. Take boidz as an example with a simple 3D mesh . Place master boidz at center fustrum at suitable scale. For each boid work out where center of boid[i] is in relation to master boid locally orient boid work out scale near to far add Boid orient + boid scale + boid pos to a matrix and apply
Without quats that is a lot of Mat manipulation for each item with quat we have delta orient, and if scale works then a quat move and scale from two vectors center of viewplane (fixed for all) and point project center boid to viewplane, so two quat create and two quat mul array. No idea if this is more efficient for low poly but may be, for high poly maybe not. But then it maybe more efficent to create the two quats convert to Mat and Add two mat.
I found this for unity (it's in french) https://www.youtube.com/watch?v=EWQCsjp3NvY it describe very well how Unity work with quaternion and why. I understand much better quaternion with this and making the link with maths formulas. In terms of performance computing quaternion is less expensive than matrices some infos (near twice less operations) I'm also falling on that and this. in an article. What do you think about quaternion structure and functions ?
I'm trying to fix slerpSpin
Like we can see the result is in W
Ok by accessing var in with aq4.Realpart instead aq4.Z get :
After some mino changes, now i've :
Have you swap components results in functionnal test ? or it is something wrong
After some minors change, the problems come always from the Z
Now the result don't match. It's a pain
if i normailze the result i get :
sorry just back from shopping, tests may may not be correct as I had no idea if spin was 180 or 360 thats what I was trying to determine when nothing worked in a sane manner
now if i negate result.z : ( and i change slerpsingle to SlerpSpin, for not to be confused)
-"SlerpSpin:Sub7 Z failed (ImagePart.X: 0,00000 ,ImagePart.Y: 0,00000 ,ImagePart.Z: -0,17365 , RealPart.W: -0,98481)" expected: <0,766> but was: <-0,1736>
I don't understand why result is false at this stage
this is the change i've made for testing :
function TGLZQuaternion.Slerp(const QEnd: TGLZQuaternion; Spin: Integer; t: Single): TGLZQuaternion;
var
to1: array[0..4] of Single;
phi,omega, cosom, sinom, scale0, scale1: Extended;
// t goes from 0 to 1
// absolute rotations
begin
// calc cosine
cosom:= Self.ImagePart.X*QEnd.ImagePart.X
+Self.ImagePart.Y*QEnd.ImagePart.Y
+Self.ImagePart.Z*QEnd.ImagePart.Z
+Self.RealPart*QEnd.RealPart;
// adjust signs (if necessary)
if cosom<0 then
begin
cosom := -cosom;
to1[0] := - QEnd.ImagePart.X;
to1[1] := - QEnd.ImagePart.Y;
to1[2] := - QEnd.ImagePart.Z;
to1[3] := - QEnd.RealPart;
end
else
begin
to1[0] := QEnd.ImagePart.X;
to1[1] := QEnd.ImagePart.Y;
to1[2] := QEnd.ImagePart.Z;
to1[3] := QEnd.RealPart;
end;
// calculate coefficients
if ((1.0-cosom)>cEpsilon) then
begin
//Cosom := Cosom
omega:=GLZMath.ArcCos(cosom);
Omega :=(Omega + DegToRadian(Spin*360));
sinom:=1/Sin(omega);
phi:=omega;// + Spin * cPi;
scale0:=sin(omega - t * phi) * sinom;
scale1:=sin(t * phi) * sinom;
//scale0:=Sin((1.0-t)*omega)*sinom;
//scale1:=Sin(t*omega)*sinom;
end
else // "from" and "to" quaternions are very close
begin
// ... so we can do a linear interpolation
scale0:=1.0-t;
scale1:=t;
end;
// calculate final values
Result.ImagePart.V[0] := scale0 * Self.ImagePart.V[0] + scale1 * to1[0];
Result.ImagePart.V[1] := scale0 * Self.ImagePart.V[1] + scale1 * to1[1];
Result.ImagePart.V[2] := -(scale0 * Self.ImagePart.V[2] + scale1 * to1[2]); // Don't understand why we need negate here
Result.RealPart := (scale0 * Self.RealPart + scale1 * to1[3]);
Result.Normalize;
end;
sorry just back from shopping
No, problems Peter :) 👍
Ok changed Omega :=(Omega + DegToRadian(Spin*360));
by Omega :=(Omega + DegToRadian(Spin*180));
it's remove the negation of Z but now it's failed on W'sign :'(
Here we need determine if is -180 or 180, i think. It seem depend of the quarter part where is the "Spin". (sorry don't say how to explain, clearly, what is in my mind)
ok Plugged that in, get a quat of [ 0, 0, -0.3826834, 0.9238795 ] which in converted to euler angle is [ x: 0, y: 0, z: -44.9999976 ] now to work out what quad that is half of to me it looks like -45 which would be 315 , now how that relates to (90 + x*pi) / 2 will have to find out. X being unkown as yet.
Using this site as checks https://www.andre-gaschler.com/rotationconverter/
omega:=GLZMath.ArcCos(cosom);
//Omega :=(Omega + DegToRadian((Spin*180)+180));
sinom:=1/Sin(omega);
phi:=omega;// + Spin * cPi; // Try to negate
scale0:=sin(omega - t * phi) * sinom;
scale1:=sin(t * phi) * sinom;
something is not clear here with the sign of omega or the range formed by Scale0 and Scale1
if i make` Omega :=(Omega + ((90 + Spin cPi) 0.5));
if i make Omega :=(Omega + ((-90 + Spin cPi) 0.5));
Try the online calc quaternions can have differing signs but be the same rotation. Due to the half angle nature of quat they can make unique rotations in the -2pi to +2pi. Only when you convert back to euler can you see the equivalence. which make the things so dammed hard to test.;)
By spining we must find the direction for know on what quarter we are. not ?
ok if i understand we must negate value of axis x,y or z if the angle is less than zero and thats it. i'm right ? Your SlerpSpin make me confused
Axis x 1 y 1 z 0 Angle (degrees) -45 result = [ -0.2705981, -0.2705981, 0, 0.9238795 ]
Axis x 1 y 1 z 0 Angle (degrees) 45 result = [0.2705981, 0.2705981, 0, 0.9238795 ]
Axis x 0 y 0 z 1 Angle (degrees) -45 = [ 0, 0, -0.3826834, 0.9238795 ] Axis x 0 y 0 z 1 Angle (degrees) 45 = [ 0, 0, 0.3826834, 0.9238795 ]
(720 - 90) / 2 = 315 so perhaps we are doubling the spin rotation and negating the initial rotation. this is still from the unchanged code you posted, will plug some more numbers in to test
Ok that is it from changing 90 to 20 we get 720 - 20 = 700 : 700 / 2 = 350 and I get a euler answer of -10
so need to half spin value and negate initial somehow.
Quat to Mat4 done and checked in. Now for Mat4 to Quat.
Always don't understand SlerpSpin.
I don't understand "(720 - 90) / 2 = 315" = ((360*2) - 90) / 2
Why 360 our destination is at 90°
I don't understand slerpSpin in the same maner like you.
why in
if ((1.0-cosom)>cEpsilon) then
begin
//Cosom := Cosom
omega:=GLZMath.ArcCos(cosom);
RadiansToDeg(Omega) = 45 and not 90 like the initial aqt2.Create(90,ZVector);
at firts i'm understading SlerpSpin like this :
so normally we have made a rotation of 90° = (90° 2) 0.5 or something escape me
And for Matrix test
if this is the case an Angular lerp is needed here
Ok by making like i think SlerpSpin is ok
First change :
procedure TGLZQuaternion.Create(const angle : Single; const axis : TGLZAffineVector);
//procedure TGLZQuaternion.Create(const angle : Single; const axis : TGLZVector);
var
f, s, c : Single;
vaxis : TGLZVector;
begin
GLZMath.SinCos(DegToRadian(angle*cOneHalf), s, c); //--> Angle div by 2 here
Self.RealPart:=c;
vaxis.AsVector3f := axis;
vAxis.w :=1; // -----> Need set as affine
f:=s/vAxis.Length;
Self.ImagePart.V[0]:=axis.V[0]*f;
Self.ImagePart.V[1]:=axis.V[1]*f;
Self.ImagePart.V[2]:=axis.V[2]*f;
end;
after
function TGLZQuaternion.Slerp(const QEnd: TGLZQuaternion; Spin: Integer; t: Single): TGLZQuaternion; begin Result := Self.Slerp(Qend,t*spin); end;
and for finish :
procedure TQuaternionFunctionalTestCase.TestSlerpSpin;
begin
// aqt1.AsVector4f := WHmgVector; // null rotation as start point.
aqt1.create(1e-14,ZVector); // null rotation as start point.
aqt2.Create(90,ZVector); // 90 = 90
aqt4 := aqt1.Slerp(aqt2, 2, 0.5); // 90 [ 0, 0, 0.7071068, 0.7071068 ]
AssertEquals('SlerpSpin:Sub1 X failed ', 0.0, aqt4.X);
AssertEquals('SlerpSpin:Sub2 Y failed ', 0.0, aqt4.Y);
AssertEquals('SlerpSpin:Sub3 Z failed ', 0.7071068, aqt4.Z);
AssertEquals('SlerpSpin:Sub4 W failed ', 0.7071068, aqt4.W);
aqt4 := aqt1.Slerp(aqt2,2,2/9); // 40 [ 0, 0, 0.3420185, 0.9396932 ]
AssertEquals('SlerpSpin:Sub5 X failed ', 0.0, aqt4.X);
AssertEquals('SlerpSpin:Sub6 Y failed ', 0.0, aqt4.Y);
AssertEquals('SlerpSpin:Sub7 Z failed ', 0.3420185, aqt4.Z);
AssertEquals('SlerpSpin:Sub8 W failed ', 0.9396932, aqt4.W);
aqt4 := aqt1.Slerp(aqt2,2,8/9); // 160 [ 0, 0, 0.9848078, 0.1736482 ]
AssertEquals('SlerpSpin:Sub9 X failed ', 0.0, aqt4.X);
AssertEquals('SlerpSpin:Sub10 Y failed ', 0.0, aqt4.Y);
AssertEquals('SlerpSpin:Sub11 Z failed ', 0.9848078, aqt4.Z);
AssertEquals('SlerpSpin:Sub12 W failed ', 0.1736482, aqt4.W);
end;
This is the behaviour you want ?
Ok I just did the last two parts and it works, you have changed the spin to mean rotation * spin which is fine by me. previous code looked like it was adding some multiple of pi.
vAxis.w :=1; // -----> Need set as affine
This has no effect, with it or without it. Best not putting it in as it may make us add some asembler code where not required.
And for Matrix test "ConvertToMatrix:Sub1 m33 failed " expected: <0,8936> but was: <1,0026>
Fixed
thinking about it need to test for large number of 'spirals' and see what the limits are, if any.
If it not working just go on Bin, not really necessary. If you see others functions like this don't hesitate to trash it
have you seen these
patterns before for creating tri-strips without degenerates?
I see the problem, but I do not understand what you want to talk about and about what. Can you be more precise
What fun this has been so far.
Just to let you know the changes.
Made mul conform to first second as there is a MulFirst (not tested yet) which does non Math order. Commented out large parts of CreateEuler, trying to hit the exceptions that they are guarding against, but not managed yet. Rotations conform to existing rotations. Euler create gives correct numbers.
Default create rpy not done as we will probably have to decide on world up for the system as this is meant to be the quick quat that defaults to world.