Search Unity

Unity.Mathematics available on github

Discussion in 'Data Oriented Technology Stack' started by xoofx, Apr 10, 2018.

  1. elcionap

    elcionap

    Joined:
    Jan 11, 2016
    Posts:
    133
    They do have comments but for some reason (at least with me, VS 2019, Unity 2019.3b8) they are not showing up in the editor as there is no xml generated in Library/ScriptAssemblies folder or referenced in the project. But, to exemplify, NextInt(int, int) has this:
    /// <summary>Returns a uniformly random int value in the interval [min, max).</summary>

    []'s
     
    Radu392 and Antypodish like this.
  2. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    7,232
    Is this actually something that Unity team can fix on their side?
     
    Radu392 likes this.
  3. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    557
    You gain x1.5 performance if you extend the "legacy" Vector3 class with your own methods. All those math operators are slow because they involve constructor calls. As far as I can tell, Mathematics has the same issue.

    Code (CSharp):
    1.  
    2. /// <summary>
    3. /// Add vector b to this vector. Up to 1.47 times faster than the default operator (Unity 2019.3).
    4. /// </summary>
    5. public static void Add( ref this Vector3 v, Vector3 b )
    6. {
    7.     v.x += b.x;
    8.     v.y += b.y;
    9.     v.z += b.z;
    10. }
    11.  
     
    hippocoder likes this.
  4. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    26,434
    Isn't this something the compiler should recognise and deal with though? Looks like I'm going to be adding a lot of methods :)
     
    dadude123 likes this.
  5. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    557
    I'd love to know. My tests are based on this, executed in the editor:

    Code (CSharp):
    1. int count = 10000000; // 10M
    2.  
    3. Vector3 a = new Vector3( 1, 1, 1 );
    4. Vector3 b = new Vector3( 2, 2, 2 );
    5. Vector3 c = Vector3.zero;
    6.  
    7. Profiler.BeginSample( "DefaultAdd" );
    8. for( int i = 0; i < count; i++ ) c = a + b;
    9. Profiler.EndSample();
    10.  
    11. Profiler.BeginSample( "CustomAdd" );
    12. for( int i = 0; i < count; i++ ) a.Add( b );
    13. Profiler.EndSample();
    Is there a better/more correct way to test performance?
     
    Last edited: Dec 20, 2019
    doarp likes this.
  6. doarp

    doarp

    Joined:
    Sep 24, 2019
    Posts:
    136
    You defaultsdd test had an extra step: assignment. To make it fair assign in the custom add ad well.
    Also can you compare this with manual addition of vector elements without a function call?
     
  7. runner78

    runner78

    Joined:
    Mar 14, 2015
    Posts:
    147
    I don't know if that change the result, but this two operation are not the same.
    The first add a + b and store the result in c without change a,
    The last you add b to a directly and not store to c.
     
  8. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    557
    @doarp and @runner78 you are both correct that the comparison is unfair in the sense that the custom Add method is modifying one of the vectors in contrast to the default + operator that creates and returns a new vector (that is furthermore being assigned). But in many cases you don't actually need a new vector. And if you do, you can just create temporary vectors before going into heavy loops.

    I did a new test.

    Code (CSharp):
    1. int count = 10000000; // 10M
    2.  
    3. Vector3 a = new Vector3( 1, 1, 1 );
    4. Vector3 b = new Vector3( 2, 2, 2 );
    5. Vector3 c = new Vector3( 0, 0, 0 );
    6. float ax = 1, ay = 1, az = 1;
    7. float bx = 2, by = 2, bz = 2;
    8. float cx, cy, cz;
    9.  
    10. Profiler.BeginSample( "BaseAdd" );
    11. for( int i = 0; i < count; i++ ){
    12.     cx = ax + bx;
    13.     cy = ay + by;
    14.     cz = az + bz;
    15. }
    16. Profiler.EndSample();
    17.  
    18. Profiler.BeginSample( "DefaultAdd" );
    19. for( int i = 0; i < count; i++ ) c = a + b;
    20. Profiler.EndSample();
    21.  
    22. Profiler.BeginSample( "CustomAdd" );
    23. for( int i = 0; i < count; i++ ) a.Add( b );
    24. Profiler.EndSample();
    On my machine:
    BaseAdd 43ms (x1)
    DefaultAdd 249ms (x5.79)
    CustomAdd 161ms (x3.74)

    Method calls are expensive. At least when seen from Profiler.
     
    Last edited: Dec 20, 2019
  9. runner78

    runner78

    Joined:
    Mar 14, 2015
    Posts:
    147
    can you try a += b? I am curious if mono make some optimizations.
     
  10. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    557
    New test, in order of performance:

    BaseAdd 43ms (x1)
    CustomAdd 163ms (x3.79)
    DefaultAdd 245ms (x5.69)
    DefaultEqualsAdd 248ms (x5.77)
    MathematicsAdd 251ms (x5.84)

    MathematicsAdd being:
    Code (CSharp):
    1. float3 a2 = new float3( 1, 1, 1 );
    2. float3 b2 = new float3( 2, 2, 2 );
    3. float3 c2 = new float3( 0, 0, 0 );
    4.  
    5. Profiler.BeginSample( "MathematicsAdd" );
    6. for( int i = 0; i < count; i++ ) c2 = a2 + b2;
    7. Profiler.EndSample();
    So the questions is: can I count on similar numbers in builds?
     
  11. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    454
    Always profile the builds, and i'd be curious to know if it makes a difference in il2cpp too. Thanks for doing the hard work!
     
  12. doarp

    doarp

    Joined:
    Sep 24, 2019
    Posts:
    136
    Interesting. I would would replace the base add version with
    a.x = a.x + b.x
    etc..
    As the goal is to add vectors.

    Also try using InlineAggresive on the custom add
     
  13. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    557
    HOLY COW! Completely different numbers in 2020.1! Something radical must have happened deep down the machine room. Same Windows laptop now says:

    BaseAdd 13ms (x1)
    MathematicsAdd 83ms (x6.38)
    CustomAdd 128ms (x9.85)
    DefaultAdd 190ms (x14.62)
    DefaultEqualsAdd 200ms (x15.38)

    Still profiling in the editor. Didn't figure out how to get input from builds yet. I connect to the app, but nothing is comming in.
     
  14. doarp

    doarp

    Joined:
    Sep 24, 2019
    Posts:
    136
    Be sure to mark “development mode” in build window.
     
  15. runner78

    runner78

    Joined:
    Mar 14, 2015
    Posts:
    147
    C# Stopwatch and UI?
     
    doarp likes this.
  16. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    557
    Thanks, I did. I also enabled "Autoconnect Profiler". I also tried connecting to 127.0.0.1. No incoming frames.
     
  17. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    557
    On a different note ...

    I am uploading a Unity.Mathematics.uint2x4 value as a constant in a ComputeShader and noticed that the indexer access is different in HLSL.

    Unity.Mathematics uint2x4: value[0] will return uint2.
    uint2x4 in HSL: value[0] will return uint4.

    Is this on purpose?

    EDIT: The parameter names in the uint2x4 constructor are correct.
    public uint2x4( uint m00, uint m01, uint m02, uint m03, uint m10, uint m11, uint m12, uint m13 );
    But the indexer is not.
     
    Last edited: Dec 20, 2019
  18. DavidSWu

    DavidSWu

    Joined:
    Jun 20, 2016
    Posts:
    183
    Have you looked at the output code?
    I am surprised that the optimizer even outputs code for base add. It should be able to figure out the net effect.
    Also you should do many adds per iteration, otherwise you are just testing branch prediction and loop overhead.
     
    Cynicat and doarp like this.
  19. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    557
    @sheredom & @Dale_Kim I am still very puzzled by this. Could anyone from UT try and explain why the performance is so different in 2020?
     
    Nyarlathothep likes this.
  20. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,412
     
    cecarlsen and Nyarlathothep like this.
  21. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    557
    Thanks @alexzzzz, spot on. So, do I understand correctly that Unity 2020.1 uses IL2CPP as default in the Editor instead of Mono?
     
  22. Dale_Kim

    Dale_Kim

    Unity Technologies

    Joined:
    May 8, 2019
    Posts:
    37
    No, when you're in the editor and enter play mode, you will always be using Mono. IL2CPP is only for player builds when you use the IL2CPP backend.

    The reason why you're seeing such different numbers in 2020 is because we've changed the mono compilation in the editor to run with full optimizations until you attach a debugger. As soon as you attach the debugger, it will switch over to an unoptimized version of the code.

    Unity.Mathematics as a whole performs quite poorly in Mono without burst, but we're looking into ways of speeding it up.
     
    GilCat likes this.
  23. cecarlsen

    cecarlsen

    Joined:
    Jun 30, 2006
    Posts:
    557
    @Dale_Kim thanks for shedding some light upon this. Can you elaborate on what "full optimizations" embraces? I imagine its more than stripping the code for comments.
     
    Last edited: Jan 29, 2020
  24. Nyanpas

    Nyanpas

    Joined:
    Dec 29, 2016
    Posts:
    160
    After adding the Unity.Mathematics library to a small project in 2019.2.17f1 it takes forever to load after pressing "Play" and it also takes forever to stop after pressing it again to stop.

    Why?

    [edit] It seems it could be Unity-version specific, or maybe(?) even related to Pro or Personal accounts? I tried duplicating the project in my Personal account and even though I am on an inferior laptop to my toppest of the line workstation, I never get additional start-up time when hitting "play" in the editor...
     
    Last edited: Feb 1, 2020
  25. Nyanpas

    Nyanpas

    Joined:
    Dec 29, 2016
    Posts:
    160
    [edit] Seems that a float2 is 8 bytes, and having two float2s is 16 bytes, which pushed the struct size past the 32 bytes limit(?) which activates(?) string.memcpy(). I've redone the struct to only save indices as ints instead, because even though they are also 4 bytes in size as a float, I only need half of them.

    Also adding this as a new post because I feel it is important:

    Why does adding a float2 to a struct add string.memcpy()?

    I have a struct Edge that takes in 2D-positions, and I thought I could use float2s instead of Vector2, but the only way to avoid string.memcpy() every time I use a list item of List<Edge> is to use float x, y.
     
    Last edited: Feb 3, 2020
  26. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    157
    No idea why but sin() and cos() are 10-15 times slower than usual Math.Sin

    Math.Sin() or Mathf.Sin() or (float)Math.Sin(): ~136
    sin(): ~2166

    Thought it could be the
    using static Unity.Mathematics.math
    but it didn't make a difference.

    Tested on x86_64 build windows standalone, unity 2019.2.17, il2cpp with burst

    Tried package manager version and the newest one from github. - same result
    Tried adding
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    - same result

    Changed:
    public static float sin(float x) { return (float)System.Math.Sin((float)x); }

    to
    public static float sin(float x) { return (float)System.Math.Sin(x); }
    - same result
     
    interpol_kun and Nyanpas like this.
  27. Nyanpas

    Nyanpas

    Joined:
    Dec 29, 2016
    Posts:
    160
    Very lucky I saw this post right now as I was about to replace all my sins :rolleyes: in my code.
     
  28. PeppeJ2

    PeppeJ2

    Joined:
    May 13, 2014
    Posts:
    8
    Hi, in mathematics it's possible to add a scalar value to a float3 producing very unexpected and very hard to debug behavior in some cases. Here's an example:

    Code (CSharp):
    1.  
    2. float3 x = new float3(1, 1, 1);
    3. float3 y = x + (new float3(1, 1, 1) * 2 + 1 * 4);
    4.  
    5. Vector3 x2 = new Vector3(1, 1, 1);
    6. Vector3 y2 = x2 + (new Vector3(1, 1, 1) * 2 + 1 * 4);
    The mathematics version compiles just fine and swallows that it actually adds 5 to all values where as the Vector3 version will throw a compile error that you can't add a scalar value to a Vector3.

    As far as I'm concerned adding a scalar value on top of a float3 doesn't make any sense mathematically and therefor should NOT be allowed by default. Just like it wasn't allowed on Vector3s. Doing this in an method would be a lot clearer than doing it with the operator.
     
    Nyanpas likes this.
  29. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    157
    That's the case in glsl, it makes senso imo.

    From https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf
     
    florianhanke, Lorash and Cynicat like this.
  30. Cynicat

    Cynicat

    Joined:
    Jun 12, 2013
    Posts:
    267
    Except you will get an error from the type system in the case you laid out... you made the variable type float3, meaning you expect it to produce a float3, if you made the return type a float (like the bug your implying would suggest) it would throw an error. This is standard in HLSL and GLSL for a reason, it makes a lot of real world code much shorter and simpler. I've used those for years and have never had a problem with the implicit conversion patterns in those languages. I personally hate having to wrap things in "new Vector3()" and "Vector3.Scale" all the time. Another thing is programming isn't pure mathematics, we don't have the same problems with vector identity that geometry has, since our computations are always perfectly computable functions.
     
  31. Lorash

    Lorash

    Joined:
    May 6, 2019
    Posts:
    135
    That's standard with HLSL, GLSL, etc. For instance if you sample a normal map texture that usually gives you values between 0..1 however what you really want is -1..+1 so the natural transformation is
    myvalue*2-1
    .

    I think it's nice that for different people we have two different namespaces with the semantics and features they're familiar with (like type safety on the Vector3 side of things), even though I'm personally unlikely to directly mix fake-xLSL into my C#. I don't know if Burst gives the same love to Vector3 (both Unity's and the System.Numerics.Vectors kind that will be used by many, if not most third-party .NET mathematics libraries) but if it isn't already, it would be a welcome addition.
     
  32. tim_jones

    tim_jones

    Unity Technologies

    Joined:
    May 2, 2019
    Posts:
    64
    Burst is specifically designed to transform Unity.Mathematics vector types into efficient SIMD types - so our recommendation for high-performance maths code is to use this combination of Burst + Unity.Mathematics. Doing something similar with UnityEngine.VectorN and System.Numerics.Vectors types isn't on our roadmap, but feedback like this is always appreciated.
     
    Cynicat likes this.
  33. Lorash

    Lorash

    Joined:
    May 6, 2019
    Posts:
    135
    System.Numerics.Vectors types are specifically designed to be efficient SIMD types themselves https://docs.microsoft.com/en-us/dotnet/standard/numerics#simd-enabled-types so it might make sense to have them on said roadmap.
     
  34. duzbot

    duzbot

    Joined:
    Aug 31, 2017
    Posts:
    13
    how would i go about rotating an entity as per the HelloCube examples?
    quaternion.AxisAngle() doesn't exist anymore
    Code (CSharp):
    1.                  rotation.Value = math.mul(
    2.                      math.normalize(rotation.Value),
    3.                      quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * deltaTime));
    For some reason I had
    Code (CSharp):
    1. using static Unity.Mathematics.math;
    2.  
    So quaternion is working as normal
     
    Last edited: Feb 13, 2020
  35. Dale_Kim

    Dale_Kim

    Unity Technologies

    Joined:
    May 8, 2019
    Posts:
    37
    Do you have code you can share?
     
  36. Dale_Kim

    Dale_Kim

    Unity Technologies

    Joined:
    May 8, 2019
    Posts:
    37
    I'm confused. Do you still have a problem with using quaternion.AxisAngle() ?
     
  37. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    157
    Ehh a for loop...

    Can't be reproduced in Performance tester (does it use il2cpp or can it be changed?).

    I've used custom class that calculates averages with stopwatch in normal game build before.

    Here is simpler version with recorder for development build, I even gave sin() advantage by putting it last (prewarm or whatever it's called)
    Code (CSharp):
    1.         CustomSampler sampler1;
    2.         CustomSampler sampler2;
    3.         CustomSampler sampler3;
    4.         void Awake()
    5.         {
    6.             sampler1 = CustomSampler.Create( "Math" );
    7.             sampler2 = CustomSampler.Create( "Mathf" );
    8.             sampler3 = CustomSampler.Create( "sin" );
    9.         }
    10.         private void Update()
    11.         {
    12.             sampler1.Begin();
    13.             for ( int i = 0; i < 1000000; i++ )
    14.             {
    15.                 float x = (float)Math.Sin(2.435f);
    16.             }
    17.             sampler1.End();
    18.             sampler2.Begin();
    19.             for ( int i = 0; i < 1000000; i++ )
    20.             {
    21.                 float x = Mathf.Sin(2.435f);
    22.             }
    23.             sampler2.End();
    24.             sampler3.Begin();
    25.             for ( int i = 0; i < 1000000; i++ )
    26.             {
    27.                 float x = Unity.Mathematics.math.sin(2.435f);
    28.             }
    29.             sampler3.End();
    30.             Debug.Log( $"math: {sampler1.GetRecorder().elapsedNanoseconds}" );
    31.             Debug.Log( $"mathf: {sampler2.GetRecorder().elapsedNanoseconds}" );
    32.             Debug.Log( $"sin: {sampler3.GetRecorder().elapsedNanoseconds}" );
    Results from random frame:
    math: 426800
    mathf: 638300
    sin: 7173800

    More results:
    Seems like (float)Math.Sin is faster than Mathf.Sin.

    About the math vs Mathf - 2 differences between them:
    - math is class, Mathf is struct
    - math is a gigantic file compared to Mathf

    Anyway something is definitely wrong with compiler (il2cpp?).
     
    Last edited: Feb 14, 2020
    Nyanpas likes this.
  38. Nyanpas

    Nyanpas

    Joined:
    Dec 29, 2016
    Posts:
    160
    Interesting finding about Math. I tend to avoid using things from those libraries, as for an example Array.Sort() comes with so much overhead I had to implement my own sort instead to save about 2ms per call.

    I guess some are more optimised than others, and I don't want performans penalties on operations that are executed a lot.
     
    Kamyker likes this.
  39. duzbot

    duzbot

    Joined:
    Aug 31, 2017
    Posts:
    13
    Sorry for confusion. I did have a problem not being able to use quaternion.GetAxis() whilst I had this in my script:
    Code (CSharp):
    1. using static Unity.Mathematics.math;
    I don't understand how it appeared or what it does. After removing it, I could use quaternion.GetAxis() as normal
     
  40. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    157
    Seems like it is il2cpp:

    Mathematics.math.sin:
    Code (CSharp):
    1.  
    2.         float L_13 = math_sin_m7CB1E8DF153CC3E22A6C366F3F5F98571CF77BA9((2.435f), /*hidden argument*/NULL);
    Code (CSharp):
    1. IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR float math_sin_m7CB1E8DF153CC3E22A6C366F3F5F98571CF77BA9 (float ___x0, const RuntimeMethod* method)
    2. {
    3.     static bool s_Il2CppMethodInitialized;
    4.     if (!s_Il2CppMethodInitialized)
    5.     {
    6.         il2cpp_codegen_initialize_method (math_sin_m7CB1E8DF153CC3E22A6C366F3F5F98571CF77BA9_MetadataUsageId);
    7.         s_Il2CppMethodInitialized = true;
    8.     }
    9.     float V_0 = 0.0f;
    10.     {
    11.         // public static float sin(float x) { return (float)System.Math.Sin((float)x); }
    12.         float L_0 = ___x0;
    13.         IL2CPP_RUNTIME_CLASS_INIT(Math_tFB388E53C7FDC6FCCF9A19ABF5A4E521FBD52E19_il2cpp_TypeInfo_var);
    14.         double L_1 = sin((((double)((double)(((float)((float)L_0)))))));
    15.         V_0 = (((float)((float)L_1)));
    16.         goto IL_000d;
    17.     }
    18.  
    19. IL_000d:
    20.     {
    21.         // public static float sin(float x) { return (float)System.Math.Sin((float)x); }
    22.         float L_2 = V_0;
    23.         return L_2;
    24.     }
    25. }
    (float)Math.Sin:

    Code (CSharp):
    1. // float x = (float)Math.Sin(2.435f);
    2.         IL2CPP_RUNTIME_CLASS_INIT(Math_tFB388E53C7FDC6FCCF9A19ABF5A4E521FBD52E19_il2cpp_TypeInfo_var);
    3.         double L_1 = sin((2.434999942779541));
    4.         V_1 = (((float)((float)L_1)));
    (weird how it casts twice to float)

    Mathf.Sin:
    Code (CSharp):
    1.         // float x = Mathf.Sin(2.435f);
    2.         IL2CPP_RUNTIME_CLASS_INIT(Mathf_tFBDE6467D269BFE410605C7D806FD9991D4A89CB_il2cpp_TypeInfo_var);
    3.         float L_7 = sinf((2.435f));
    All of them could be faster as IL2CPP_RUNTIME_CLASS_INIT is not needed (right?). Something that was already reported but postponed :( https://issuetracker.unity3d.com/is...tialized-and-il2cpp-runtime-class-init-checks

    In the end Mathmatics.math.sin (and probably all functions) are much slower because it checks if Mathmatics.math class is initialized and also adds last NULL parameter.

    Also:
    Code (CSharp):
    1.         double L_1 = sin((((double)((double)(((float)((float)L_0)))))));
    2.         V_0 = (((float)((float)L_1)));
    Why is it casting twice everything?
     
    Nyanpas likes this.
  41. Kamyker

    Kamyker

    Joined:
    May 14, 2013
    Posts:
    157
    Oh wait there's more. In burst Unity.Mathematics.math.sin is faster.
    Code (CSharp):
    1.         [BurstCompile( CompileSynchronously = true )]
    2.         struct BurstMath
    3.         {
    4.             [BurstCompile( CompileSynchronously = true )]
    5.             private static float Sin( float a ) => (float)Math.Sin( a );
    6.             [BurstCompile( CompileSynchronously = true )]
    7.             private static float SinF( float a ) => Mathf.Sin( a );
    8.             [BurstCompile( CompileSynchronously = true )]
    9.             private static float SinMath( float a ) => Unity.Mathematics.math.sin( a );
    10.  
    11.             public delegate float FuncFloatFloat(float a);
    12.  
    13.             public readonly static FuncFloatFloat sin =
    14.                 BurstCompiler.CompileFunctionPointer<FuncFloatFloat>( Sin ).Invoke;
    15.             public readonly static FuncFloatFloat sinf =
    16.                 BurstCompiler.CompileFunctionPointer<FuncFloatFloat>( SinF ).Invoke;
    17.             public readonly static FuncFloatFloat sinMath =
    18.                 BurstCompiler.CompileFunctionPointer<FuncFloatFloat>( SinMath ).Invoke;
    19.         }
    1kk loop:
    Profiler BurstMath: 382944 ticks
    Profiler BurstMathf: 384548 ticks
    Profiler BurstMath Unity: 316240 ticks
     
    Nyanpas likes this.
  42. LuisHidalgo

    LuisHidalgo

    Joined:
    May 17, 2017
    Posts:
    2
    Hi, do you mind sharing the code for RotateTowards? I was trying to reimplement the Vector3.RotateTowards function but so far mine gives me completely different values than the original one :(
     
  43. Dale_Kim

    Dale_Kim

    Unity Technologies

    Joined:
    May 8, 2019
    Posts:
    37
    The 'using static' comes from our template when you automatically try to import references while writing your code. It's a known issue that it can hide some symbols. We will be fixing this in the future.
     
    Nyanpas likes this.
  44. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    1,802
    Code (CSharp):
    1. [MethodImpl(MethodImplOptions.AggressiveInlining)]
    2. public static quaternion RotateTowards(
    3.     quaternion from,
    4.     quaternion to,
    5.     float maxDegreesDelta)
    6. {
    7.     float num = Angle(from, to);
    8.     return num < float.Epsilon ? to : math.slerp(from, to, math.min(1f, maxDegreesDelta / num));
    9. }
    10.  
    11. [MethodImpl(MethodImplOptions.AggressiveInlining)]
    12. public static float Angle(this quaternion q1, quaternion q2)
    13. {
    14.     var dot    = math.dot(q1, q2);
    15.     return !(dot > 0.999998986721039) ? (float) (math.acos(math.min(math.abs(dot), 1f)) * 2.0) : 0.0f;
    16. }
     
    azaroth75 likes this.
  45. ecurtz

    ecurtz

    Joined:
    May 13, 2009
    Posts:
    639
    Any progress on this? It's really annoying to not be able to serialize half types.
     
  46. Lorash

    Lorash

    Joined:
    May 6, 2019
    Posts:
    135
    Unrelated to the problem at hand but for a float,
    < float.Epsilon
    is literally equivalent to
    <= 0
    . If you really want to have tolerance you'll need a bigger value.
     
    Last edited: Mar 3, 2020
  47. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    1,802
    I haven't any problem. It's solution for OP.

    Not. It should be exact float.Epsilon. Look at Unity RotateTowards source code (below) and you'll see the same (they using double comparsion with 0, for float it should be float.Epsilon).
    Because of this in Angle implementation:
    !(dot > 0.999998986721039) ? (float) (math.acos(math.min(math.abs(dot), 1f)) * 2.0) : 0.0f;
    upload_2020-2-28_13-46-2.png
     
  48. Lorash

    Lorash

    Joined:
    May 6, 2019
    Posts:
    135
    You absolutely should use tolerance when comparing floating-point values to each other. However in the case of floats, `float.Epsilon` is an unsuitable number because by definition it's the next number after 0. It's too small. The one single nonnegative number that's smaller than `float.Epsilon` is 0.0f so you don't really have any tolerance at all, you just silenced a legitimate compiler warning without resolving the real issue underneath.
     
  49. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    1,802
    Read again, especially this part:
    upload_2020-3-3_14-14-59.png

    This is exact comparsion between 0.0f and float.Epsilon which we need.
     
  50. Lorash

    Lorash

    Joined:
    May 6, 2019
    Posts:
    135
    No amount of highlights will change the fact that
    < float.Epsilon
    is literally the same as
    <= 0.0f
    . The very same
    0.0f
    that you highlighted. To disprove me, simply come up with any numerical value that when passed to this method makes it return true:
    bool Test(float x) => (x < float.Epsilon && !(x <= 0)) || (x >= float.Epsilon && !(x > 0));
    . Substitute 0 with 0.0f as desired.
     
unityunity