├── README.md ├── ffloat_sinLut.cs.meta ├── ffloat_tanLut.cs.meta ├── fMathTest.cs.meta ├── fVector2.cs.meta ├── fVector3.cs.meta ├── ffloat.cs.meta ├── fQuaternion.cs.meta ├── LICENSE ├── ffloat_sinLut.cs ├── ffloat_tanLut.cs ├── fVector2.cs ├── fVector3.cs ├── fQuaternion.cs ├── fMathTest.cs └── ffloat.cs /README.md: -------------------------------------------------------------------------------- 1 | # fMath 2 | fixed point math library for deterministic lockstep network game in Unity 3 | -------------------------------------------------------------------------------- /ffloat_sinLut.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0a3083b3fd2c6a54dbfa7ea332913c1f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /ffloat_tanLut.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 47d7afef71bfd09438f0cbec0c683624 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /fMathTest.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e41d8e49f3a2aaa44b5543358a675d28 3 | timeCreated: 1518089834 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /fVector2.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 57df5f9ae9344744d916c4afaf249a1e 3 | timeCreated: 1516278378 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /fVector3.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 859eaf1887b06264695d9d9cae2a2399 3 | timeCreated: 1516278387 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /ffloat.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 20636a271ba45b14ab53e93497fd696c 3 | timeCreated: 1516278367 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /fQuaternion.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 33ffb6bcca253f24788f7055ce0fec8a 3 | timeCreated: 1516281025 4 | licenseType: Free 5 | MonoImporter: 6 | externalObjects: {} 7 | serializedVersion: 2 8 | defaultReferences: [] 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 8izips 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ffloat_sinLut.cs: -------------------------------------------------------------------------------- 1 | partial struct ffloat { 2 | public static readonly int[] _sinLut = new int[] { 3 | 0x0, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 4 | 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 5 | 0x100, 0x110, 0x120, 0x130, 0x140, 0x150, 0x160, 0x170, 6 | 0x180, 0x190, 0x1A0, 0x1B0, 0x1C0, 0x1D0, 0x1E0, 0x1F0, 7 | 0x200, 0x210, 0x21F, 0x22F, 0x23F, 0x24F, 0x25F, 0x26F, 8 | 0x27F, 0x28F, 0x29E, 0x2AE, 0x2BE, 0x2CE, 0x2DE, 0x2ED, 9 | 0x2FD, 0x30D, 0x31D, 0x32C, 0x33C, 0x34C, 0x35B, 0x36B, 10 | 0x37B, 0x38A, 0x39A, 0x3AA, 0x3B9, 0x3C9, 0x3D9, 0x3E8, 11 | 0x3F8, 0x407, 0x417, 0x426, 0x436, 0x445, 0x455, 0x464, 12 | 0x473, 0x483, 0x492, 0x4A2, 0x4B1, 0x4C0, 0x4D0, 0x4DF, 13 | 0x4EE, 0x4FD, 0x50D, 0x51C, 0x52B, 0x53A, 0x549, 0x559, 14 | 0x568, 0x577, 0x586, 0x595, 0x5A4, 0x5B3, 0x5C2, 0x5D1, 15 | 0x5E0, 0x5EF, 0x5FE, 0x60C, 0x61B, 0x62A, 0x639, 0x648, 16 | 0x656, 0x665, 0x674, 0x682, 0x691, 0x6A0, 0x6AE, 0x6BD, 17 | 0x6CB, 0x6DA, 0x6E8, 0x6F7, 0x705, 0x714, 0x722, 0x730, 18 | 0x73F, 0x74D, 0x75B, 0x76A, 0x778, 0x786, 0x794, 0x7A2, 19 | 0x7B0, 0x7BE, 0x7CC, 0x7DA, 0x7E8, 0x7F6, 0x804, 0x812, 20 | 0x820, 0x82E, 0x83B, 0x849, 0x857, 0x865, 0x872, 0x880, 21 | 0x88D, 0x89B, 0x8A8, 0x8B6, 0x8C3, 0x8D1, 0x8DE, 0x8EB, 22 | 0x8F9, 0x906, 0x913, 0x920, 0x92E, 0x93B, 0x948, 0x955, 23 | 0x962, 0x96F, 0x97C, 0x989, 0x996, 0x9A2, 0x9AF, 0x9BC, 24 | 0x9C9, 0x9D5, 0x9E2, 0x9EF, 0x9FB, 0xA08, 0xA14, 0xA21, 25 | 0xA2D, 0xA39, 0xA46, 0xA52, 0xA5E, 0xA6A, 0xA77, 0xA83, 26 | 0xA8F, 0xA9B, 0xAA7, 0xAB3, 0xABF, 0xACA, 0xAD6, 0xAE2, 27 | 0xAEE, 0xAF9, 0xB05, 0xB11, 0xB1C, 0xB28, 0xB33, 0xB3F, 28 | 0xB4A, 0xB55, 0xB61, 0xB6C, 0xB77, 0xB82, 0xB8E, 0xB99, 29 | 0xBA4, 0xBAF, 0xBBA, 0xBC4, 0xBCF, 0xBDA, 0xBE5, 0xBF0, 30 | 0xBFA, 0xC05, 0xC0F, 0xC1A, 0xC24, 0xC2F, 0xC39, 0xC44, 31 | 0xC4E, 0xC58, 0xC62, 0xC6C, 0xC76, 0xC80, 0xC8A, 0xC94, 32 | 0xC9E, 0xCA8, 0xCB2, 0xCBC, 0xCC5, 0xCCF, 0xCD9, 0xCE2, 33 | 0xCEC, 0xCF5, 0xCFE, 0xD08, 0xD11, 0xD1A, 0xD23, 0xD2D, 34 | 0xD36, 0xD3F, 0xD48, 0xD51, 0xD59, 0xD62, 0xD6B, 0xD74, 35 | 0xD7C, 0xD85, 0xD8E, 0xD96, 0xD9E, 0xDA7, 0xDAF, 0xDB7, 36 | 0xDC0, 0xDC8, 0xDD0, 0xDD8, 0xDE0, 0xDE8, 0xDF0, 0xDF8, 37 | 0xE00, 0xE07, 0xE0F, 0xE17, 0xE1E, 0xE26, 0xE2D, 0xE35, 38 | 0xE3C, 0xE43, 0xE4A, 0xE52, 0xE59, 0xE60, 0xE67, 0xE6E, 39 | 0xE75, 0xE7C, 0xE82, 0xE89, 0xE90, 0xE96, 0xE9D, 0xEA3, 40 | 0xEAA, 0xEB0, 0xEB7, 0xEBD, 0xEC3, 0xEC9, 0xECF, 0xED5, 41 | 0xEDB, 0xEE1, 0xEE7, 0xEED, 0xEF3, 0xEF8, 0xEFE, 0xF04, 42 | 0xF09, 0xF0F, 0xF14, 0xF19, 0xF1F, 0xF24, 0xF29, 0xF2E, 43 | 0xF33, 0xF38, 0xF3D, 0xF42, 0xF47, 0xF4B, 0xF50, 0xF55, 44 | 0xF59, 0xF5E, 0xF62, 0xF66, 0xF6B, 0xF6F, 0xF73, 0xF77, 45 | 0xF7B, 0xF7F, 0xF83, 0xF87, 0xF8B, 0xF8F, 0xF93, 0xF96, 46 | 0xF9A, 0xF9D, 0xFA1, 0xFA4, 0xFA8, 0xFAB, 0xFAE, 0xFB1, 47 | 0xFB4, 0xFB7, 0xFBA, 0xFBD, 0xFC0, 0xFC3, 0xFC6, 0xFC8, 48 | 0xFCB, 0xFCD, 0xFD0, 0xFD2, 0xFD5, 0xFD7, 0xFD9, 0xFDB, 49 | 0xFDD, 0xFDF, 0xFE1, 0xFE3, 0xFE5, 0xFE7, 0xFE9, 0xFEA, 50 | 0xFEC, 0xFED, 0xFEF, 0xFF0, 0xFF2, 0xFF3, 0xFF4, 0xFF5, 51 | 0xFF6, 0xFF7, 0xFF8, 0xFF9, 0xFFA, 0xFFB, 0xFFC, 0xFFC, 52 | 0xFFD, 0xFFD, 0xFFE, 0xFFE, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 53 | 0xFFF, 0x1000, 54 | }; 55 | } -------------------------------------------------------------------------------- /ffloat_tanLut.cs: -------------------------------------------------------------------------------- 1 | partial struct ffloat { 2 | public static readonly int[] _tanLut = new int[] { 3 | 0x0, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 4 | 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 5 | 0x101, 0x111, 0x121, 0x131, 0x141, 0x151, 0x161, 0x172, 6 | 0x182, 0x192, 0x1A2, 0x1B2, 0x1C3, 0x1D3, 0x1E3, 0x1F3, 7 | 0x204, 0x214, 0x224, 0x235, 0x245, 0x255, 0x266, 0x276, 8 | 0x287, 0x297, 0x2A8, 0x2B8, 0x2C9, 0x2D9, 0x2EA, 0x2FA, 9 | 0x30B, 0x31B, 0x32C, 0x33D, 0x34E, 0x35E, 0x36F, 0x380, 10 | 0x391, 0x3A2, 0x3B2, 0x3C3, 0x3D4, 0x3E5, 0x3F6, 0x407, 11 | 0x418, 0x42A, 0x43B, 0x44C, 0x45D, 0x46E, 0x480, 0x491, 12 | 0x4A2, 0x4B4, 0x4C5, 0x4D7, 0x4E8, 0x4FA, 0x50B, 0x51D, 13 | 0x52F, 0x541, 0x552, 0x564, 0x576, 0x588, 0x59A, 0x5AC, 14 | 0x5BE, 0x5D0, 0x5E2, 0x5F5, 0x607, 0x619, 0x62C, 0x63E, 15 | 0x651, 0x663, 0x676, 0x689, 0x69B, 0x6AE, 0x6C1, 0x6D4, 16 | 0x6E7, 0x6FA, 0x70D, 0x720, 0x734, 0x747, 0x75A, 0x76E, 17 | 0x781, 0x795, 0x7A9, 0x7BD, 0x7D0, 0x7E4, 0x7F8, 0x80C, 18 | 0x820, 0x835, 0x849, 0x85D, 0x872, 0x886, 0x89B, 0x8B0, 19 | 0x8C5, 0x8DA, 0x8EF, 0x904, 0x919, 0x92E, 0x943, 0x959, 20 | 0x96F, 0x984, 0x99A, 0x9B0, 0x9C6, 0x9DC, 0x9F2, 0xA08, 21 | 0xA1F, 0xA35, 0xA4C, 0xA63, 0xA7A, 0xA91, 0xAA8, 0xABF, 22 | 0xAD6, 0xAEE, 0xB05, 0xB1D, 0xB35, 0xB4D, 0xB65, 0xB7D, 23 | 0xB96, 0xBAE, 0xBC7, 0xBE0, 0xBF9, 0xC12, 0xC2B, 0xC44, 24 | 0xC5E, 0xC78, 0xC91, 0xCAC, 0xCC6, 0xCE0, 0xCFB, 0xD15, 25 | 0xD30, 0xD4B, 0xD66, 0xD82, 0xD9D, 0xDB9, 0xDD5, 0xDF1, 26 | 0xE0E, 0xE2A, 0xE47, 0xE64, 0xE81, 0xE9E, 0xEBC, 0xED9, 27 | 0xEF7, 0xF16, 0xF34, 0xF53, 0xF72, 0xF91, 0xFB0, 0xFD0, 28 | 0xFEF, 0x1010, 0x1030, 0x1051, 0x1071, 0x1093, 0x10B4, 0x10D6, 29 | 0x10F8, 0x111A, 0x113C, 0x115F, 0x1182, 0x11A6, 0x11C9, 0x11ED, 30 | 0x1212, 0x1236, 0x125B, 0x1281, 0x12A6, 0x12CC, 0x12F3, 0x131A, 31 | 0x1341, 0x1368, 0x1390, 0x13B8, 0x13E1, 0x140A, 0x1433, 0x145D, 32 | 0x1487, 0x14B2, 0x14DD, 0x1509, 0x1535, 0x1561, 0x158E, 0x15BB, 33 | 0x15E9, 0x1618, 0x1647, 0x1676, 0x16A6, 0x16D6, 0x1707, 0x1739, 34 | 0x176B, 0x179E, 0x17D1, 0x1805, 0x183A, 0x186F, 0x18A5, 0x18DB, 35 | 0x1912, 0x194A, 0x1982, 0x19BC, 0x19F6, 0x1A30, 0x1A6C, 0x1AA8, 36 | 0x1AE5, 0x1B23, 0x1B61, 0x1BA1, 0x1BE1, 0x1C22, 0x1C64, 0x1CA7, 37 | 0x1CEB, 0x1D30, 0x1D76, 0x1DBD, 0x1E05, 0x1E4E, 0x1E98, 0x1EE4, 38 | 0x1F30, 0x1F7E, 0x1FCD, 0x201D, 0x206E, 0x20C1, 0x2115, 0x216A, 39 | 0x21C1, 0x2219, 0x2272, 0x22CE, 0x232A, 0x2389, 0x23E9, 0x244B, 40 | 0x24AE, 0x2513, 0x257A, 0x25E4, 0x264F, 0x26BC, 0x272B, 0x279C, 41 | 0x2810, 0x2885, 0x28FD, 0x2978, 0x29F5, 0x2A75, 0x2AF7, 0x2B7C, 42 | 0x2C04, 0x2C8F, 0x2D1D, 0x2DAF, 0x2E43, 0x2EDB, 0x2F77, 0x3016, 43 | 0x30B9, 0x315F, 0x320A, 0x32B9, 0x336D, 0x3425, 0x34E2, 0x35A4, 44 | 0x366B, 0x3737, 0x3809, 0x38E1, 0x39BF, 0x3AA3, 0x3B8E, 0x3C80, 45 | 0x3D79, 0x3E7A, 0x3F83, 0x4094, 0x41AE, 0x42D1, 0x43FE, 0x4535, 46 | 0x4676, 0x47C3, 0x491C, 0x4A82, 0x4BF4, 0x4D75, 0x4F05, 0x50A4, 47 | 0x5254, 0x5416, 0x55EB, 0x57D4, 0x59D3, 0x5BE8, 0x5E16, 0x605F, 48 | 0x62C3, 0x6546, 0x67EA, 0x6AB1, 0x6D9E, 0x70B4, 0x73F8, 0x776C, 49 | 0x7B15, 0x7EF9, 0x831C, 0x8786, 0x8C3D, 0x914A, 0x96B7, 0x9C8D, 50 | 0xA2DC, 0xA9B0, 0xB11B, 0xB933, 0xC210, 0xCBCF, 0xD694, 0xE28B, 51 | 0xEFE9, 0xFEF3, 0x10FFD, 0x12376, 0x139EC, 0x15420, 0x17317, 0x1983F, 52 | 0x1C5A6, 0x1FE66, 0x2475C, 0x2A8A2, 0x330CE, 0x3FD0D, 0x55174, 0x7FA3C, 53 | 0xFF488, 0x7FFFFFFF, 54 | }; 55 | } -------------------------------------------------------------------------------- /fVector2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using UnityEngine; 4 | 5 | [StructLayout(LayoutKind.Explicit)] 6 | public struct fVector2 7 | { 8 | [FieldOffset(0)] 9 | public ffloat x; 10 | [FieldOffset(4)] 11 | public ffloat y; 12 | 13 | #region Defines 14 | static readonly fVector2 zeroVector = new fVector2(0, 0); 15 | static readonly fVector2 oneVector = new fVector2(1, 1); 16 | static readonly fVector2 upVector = new fVector2(0, 1); 17 | static readonly fVector2 downVector = new fVector2(0, -1); 18 | static readonly fVector2 leftVector = new fVector2(-1, 0); 19 | static readonly fVector2 rightVector = new fVector2(1, 0); 20 | 21 | public static fVector2 zero { get { return zeroVector; } } 22 | public static fVector2 one { get { return oneVector; } } 23 | public static fVector2 up { get { return upVector; } } 24 | public static fVector2 down { get { return downVector; } } 25 | public static fVector2 left { get { return leftVector; } } 26 | public static fVector2 right { get { return rightVector; } } 27 | #endregion 28 | 29 | #region Constructors 30 | public fVector2(ffloat x, ffloat y) 31 | { 32 | this.x = x; 33 | this.y = y; 34 | } 35 | 36 | public fVector2(int x, int y) 37 | { 38 | this.x = (ffloat)x; 39 | this.y = (ffloat)y; 40 | } 41 | 42 | public fVector2(float x, float y) 43 | { 44 | this.x = (ffloat)x; 45 | this.y = (ffloat)y; 46 | } 47 | 48 | public fVector2(Vector2 vector) 49 | { 50 | x = (ffloat)vector.x; 51 | y = (ffloat)vector.y; 52 | } 53 | 54 | public static fVector2 CreateFromRawValue(int rawX, int rawY) 55 | { 56 | return new fVector2(ffloat.CreateFromRawValue(rawX), ffloat.CreateFromRawValue(rawY)); 57 | } 58 | #endregion 59 | 60 | public fVector2 normalized { 61 | get { 62 | fVector2 result = this; 63 | result.Normalize(); 64 | return result; 65 | } 66 | } 67 | 68 | public ffloat magnitude { 69 | get { 70 | return ffloat.Sqrt(x * x + y * y); 71 | } 72 | } 73 | 74 | public ffloat sqrMagnitude { 75 | get { 76 | return x * x + y * y; 77 | } 78 | } 79 | 80 | public void Normalize() 81 | { 82 | ffloat mag = magnitude; 83 | if (mag > ffloat.Epsilon) 84 | { 85 | x /= mag; 86 | y /= mag; 87 | } 88 | } 89 | 90 | public void Scale(fVector2 scale) 91 | { 92 | this.x *= scale.x; 93 | this.y *= scale.y; 94 | } 95 | 96 | public void Set(ffloat newX, ffloat newY) 97 | { 98 | this.x = newX; 99 | this.y = newY; 100 | } 101 | 102 | public void Rotate(ffloat angle) 103 | { 104 | ffloat rad = angle * ffloat.Deg2Rad; 105 | ffloat sin = ffloat.Sin(rad); 106 | ffloat cos = ffloat.Cos(rad); 107 | 108 | ffloat tx = x; 109 | ffloat ty = y; 110 | 111 | x = cos * tx - sin * ty; 112 | y = sin * tx + cos * ty; 113 | } 114 | 115 | public override string ToString() 116 | { 117 | return String.Format("({0:F2}, {1:F2})", new object[] { x.ToFloat(), y.ToFloat() }); 118 | } 119 | 120 | #region Arithmetic Operators 121 | public static fVector2 operator +(fVector2 lhs, fVector2 rhs) 122 | { 123 | return new fVector2(lhs.x + rhs.x, lhs.y + rhs.y); 124 | } 125 | 126 | public static fVector2 operator -(fVector2 vec) 127 | { 128 | return new fVector2(-vec.x, -vec.y); 129 | } 130 | 131 | public static fVector2 operator -(fVector2 lhs, fVector2 rhs) 132 | { 133 | return new fVector2(lhs.x - rhs.x, lhs.y - rhs.y); 134 | } 135 | 136 | public static fVector2 operator *(fVector2 lhs, fVector2 rhs) 137 | { 138 | return new fVector2(lhs.x * rhs.x, lhs.y * rhs.y); 139 | } 140 | 141 | public static fVector2 operator *(fVector2 lhs, ffloat rhs) 142 | { 143 | return new fVector2(lhs.x * rhs, lhs.y * rhs); 144 | } 145 | 146 | public static fVector2 operator *(ffloat lhs, fVector2 rhs) 147 | { 148 | return new fVector2(lhs * rhs.x, lhs * rhs.y); 149 | } 150 | 151 | public static fVector2 operator /(fVector2 lhs, fVector2 rhs) 152 | { 153 | return new fVector2(lhs.x / rhs.x, lhs.y / rhs.y); 154 | } 155 | 156 | public static fVector2 operator /(fVector2 lhs, ffloat rhs) 157 | { 158 | return new fVector2(lhs.x / rhs, lhs.y / rhs); 159 | } 160 | #endregion 161 | 162 | #region Comparison Operators 163 | public static bool operator ==(fVector2 lhs, fVector2 rhs) 164 | { 165 | return (lhs - rhs).sqrMagnitude < ffloat.Epsilon; 166 | } 167 | 168 | public static bool operator !=(fVector2 lhs, fVector2 rhs) 169 | { 170 | return !(lhs == rhs); 171 | } 172 | 173 | public override bool Equals(object obj) 174 | { 175 | return obj is fVector2 && (((fVector2)obj).x == x) && (((fVector2)obj).y == y); 176 | } 177 | 178 | public bool Equals(fVector2 other) 179 | { 180 | return (x == other.x) && (y == other.y); 181 | } 182 | 183 | public override int GetHashCode() 184 | { 185 | return x.GetHashCode() ^ y.GetHashCode() << 2; 186 | } 187 | #endregion 188 | 189 | #region Conversion Operators 190 | public static implicit operator fVector2(fVector3 v) 191 | { 192 | return new fVector2(v.x, v.y); 193 | } 194 | public static implicit operator fVector3(fVector2 v) 195 | { 196 | return new fVector3(v.x, v.y, ffloat.Zero); 197 | } 198 | 199 | public Vector2 ToUnityVector2() 200 | { 201 | return new Vector2(x.ToFloat(), y.ToFloat()); 202 | } 203 | #endregion 204 | 205 | #region Math Functions 206 | public static ffloat Angle(fVector2 from, fVector2 to) 207 | { 208 | return ffloat.Acos(ffloat.Clamp(fVector2.Dot(from.normalized, to.normalized), -ffloat.One, ffloat.One)) * new ffloat(57.29578); 209 | } 210 | 211 | public static fVector2 Rotate(fVector2 vector, ffloat angle) 212 | { 213 | ffloat sin = ffloat.Sin(angle); 214 | ffloat cos = ffloat.Cos(angle); 215 | 216 | ffloat tx = vector.x; 217 | ffloat ty = vector.y; 218 | 219 | return new fVector2(cos * tx - sin * ty, sin * tx + cos * ty); 220 | } 221 | 222 | public static ffloat Distance(fVector2 lhs, fVector2 rhs) 223 | { 224 | return (lhs - rhs).magnitude; 225 | } 226 | 227 | public static ffloat Dot(fVector2 lhs, fVector2 rhs) 228 | { 229 | return lhs.x * rhs.x + lhs.y * rhs.y; 230 | } 231 | 232 | public static fVector2 Lerp(fVector2 start, fVector2 end, ffloat t) 233 | { 234 | return new fVector2(start.x + (end.x - start.x) * t, start.y + (end.y - start.y) * t); 235 | } 236 | 237 | public static fVector2 MoveTowards(fVector2 current, fVector2 target, ffloat maxDistance) 238 | { 239 | fVector2 diff = target - current; 240 | ffloat magnitude = diff.magnitude; 241 | 242 | if (magnitude <= maxDistance) 243 | return target; 244 | 245 | return current + diff / magnitude * maxDistance; 246 | } 247 | 248 | public static fVector2 Reflect(fVector2 direction, fVector2 normal) 249 | { 250 | return -2 * fVector2.Dot(normal, direction) * normal + direction; 251 | } 252 | #endregion 253 | } 254 | -------------------------------------------------------------------------------- /fVector3.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | #if FMATH_ENABLE_NATIVE_PLUGIN 4 | #if UNITY_IPHONE 5 | using fMathStaticLib; 6 | #elif UNITY_ANDROID 7 | using fMathDynamicLib; 8 | #endif 9 | #endif 10 | using UnityEngine; 11 | 12 | [StructLayout(LayoutKind.Explicit)] 13 | public struct fVector3 14 | { 15 | [FieldOffset(0)] 16 | public ffloat x; 17 | [FieldOffset(4)] 18 | public ffloat y; 19 | [FieldOffset(8)] 20 | public ffloat z; 21 | 22 | #region Defines 23 | static readonly fVector3 zeroVector = new fVector3(0, 0, 0); 24 | static readonly fVector3 oneVector = new fVector3(1, 1, 1); 25 | static readonly fVector3 upVector = new fVector3(0, 1, 0); 26 | static readonly fVector3 downVector = new fVector3(0, -1, 0); 27 | static readonly fVector3 leftVector = new fVector3(-1, 0, 0); 28 | static readonly fVector3 rightVector = new fVector3(1, 0, 0); 29 | static readonly fVector3 forwardVector = new fVector3(0, 0, 1); 30 | static readonly fVector3 backVector = new fVector3(0, 0, -1); 31 | 32 | public static fVector3 zero { get { return zeroVector; } } 33 | public static fVector3 one { get { return oneVector; } } 34 | public static fVector3 up { get { return upVector; } } 35 | public static fVector3 down { get { return downVector; } } 36 | public static fVector3 left { get { return leftVector; } } 37 | public static fVector3 right { get { return rightVector; } } 38 | public static fVector3 forward { get { return forwardVector; } } 39 | public static fVector3 back { get { return backVector; } } 40 | #endregion 41 | 42 | #region Constructors 43 | public fVector3(ffloat x, ffloat y, ffloat z) 44 | { 45 | this.x = x; 46 | this.y = y; 47 | this.z = z; 48 | } 49 | 50 | public fVector3(int x, int y, int z) 51 | { 52 | this.x = (ffloat)x; 53 | this.y = (ffloat)y; 54 | this.z = (ffloat)z; 55 | } 56 | 57 | public fVector3(float x, float y, float z) 58 | { 59 | this.x = (ffloat)x; 60 | this.y = (ffloat)y; 61 | this.z = (ffloat)z; 62 | } 63 | 64 | public fVector3(Vector3 vector) 65 | { 66 | x = (ffloat)vector.x; 67 | y = (ffloat)vector.y; 68 | z = (ffloat)vector.z; 69 | } 70 | 71 | public static fVector3 CreateFromRawValue(int rawX, int rawY, int rawZ) 72 | { 73 | return new fVector3(ffloat.CreateFromRawValue(rawX), ffloat.CreateFromRawValue(rawY), ffloat.CreateFromRawValue(rawZ)); 74 | } 75 | #endregion 76 | 77 | public fVector3 normalized { 78 | get { 79 | fVector3 result = this; 80 | result.Normalize(); 81 | return result; 82 | } 83 | } 84 | 85 | public ffloat magnitude => ffloat.Sqrt(x * x + y * y + z * z); 86 | public ffloat sqrMagnitude => x * x + y * y + z * z; 87 | 88 | public void Normalize() 89 | { 90 | ffloat mag = magnitude; 91 | if (mag > ffloat.Epsilon) 92 | { 93 | x /= mag; 94 | y /= mag; 95 | z /= mag; 96 | } 97 | } 98 | 99 | public void Scale(fVector3 scale) 100 | { 101 | this.x *= scale.x; 102 | this.y *= scale.y; 103 | this.z *= scale.z; 104 | } 105 | 106 | public void Set(ffloat newX, ffloat newY, ffloat newZ) 107 | { 108 | this.x = newX; 109 | this.y = newY; 110 | this.z = newZ; 111 | } 112 | 113 | public override string ToString() 114 | { 115 | return String.Format("({0:F2}, {1:F2}, {2:F2})", new object[] { x.ToFloat(), y.ToFloat(), z.ToFloat() }); 116 | } 117 | 118 | #region Arithmetic Operators 119 | public static fVector3 operator +(fVector3 lhs, fVector3 rhs) 120 | { 121 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 122 | fVector3 result; 123 | fvector3_add(ref lhs, ref rhs, out result); 124 | return result; 125 | #else 126 | return new fVector3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z); 127 | #endif 128 | } 129 | 130 | public static fVector3 operator -(fVector3 vec) 131 | { 132 | return new fVector3(-vec.x, -vec.y, -vec.z); 133 | } 134 | 135 | public static fVector3 operator -(fVector3 lhs, fVector3 rhs) 136 | { 137 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 138 | fVector3 result; 139 | fvector3_sub(ref lhs, ref rhs, out result); 140 | return result; 141 | #else 142 | return new fVector3(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z); 143 | #endif 144 | } 145 | 146 | public static fVector3 operator *(fVector3 lhs, ffloat rhs) 147 | { 148 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 149 | fVector3 result; 150 | fvector3_mul(ref lhs, rhs, out result); 151 | return result; 152 | #else 153 | return new fVector3(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs); 154 | #endif 155 | } 156 | 157 | public static fVector3 operator *(ffloat lhs, fVector3 rhs) 158 | { 159 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 160 | fVector3 result; 161 | fvector3_mul(ref rhs, lhs, out result); 162 | return result; 163 | #else 164 | return new fVector3(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z); 165 | #endif 166 | } 167 | 168 | public static fVector3 operator *(fVector3 lhs, int rhs) 169 | { 170 | return new fVector3(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs); 171 | } 172 | 173 | public static fVector3 operator *(int lhs, fVector3 rhs) 174 | { 175 | return new fVector3(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z); 176 | } 177 | 178 | public static fVector3 operator *(fVector3 lhs, float rhs) 179 | { 180 | return new fVector3(lhs.x * (ffloat)rhs, lhs.y * (ffloat)rhs, lhs.z * (ffloat)rhs); 181 | } 182 | 183 | public static fVector3 operator *(float lhs, fVector3 rhs) 184 | { 185 | return new fVector3((ffloat)lhs * rhs.x, (ffloat)lhs * rhs.y, (ffloat)lhs * rhs.z); 186 | } 187 | 188 | public static fVector3 operator /(fVector3 lhs, ffloat rhs) 189 | { 190 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 191 | fVector3 result; 192 | fvector3_div(ref lhs, rhs, out result); 193 | return result; 194 | #else 195 | return new fVector3(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs); 196 | #endif 197 | } 198 | #endregion 199 | 200 | #region Comparison Operators 201 | public static bool operator ==(fVector3 lhs, fVector3 rhs) 202 | { 203 | return (lhs - rhs).sqrMagnitude < ffloat.Epsilon; 204 | } 205 | 206 | public static bool operator !=(fVector3 lhs, fVector3 rhs) 207 | { 208 | return !(lhs == rhs); 209 | } 210 | 211 | public override bool Equals(object obj) 212 | { 213 | return obj is fVector3 && 214 | (((fVector3)obj).x == x) && 215 | (((fVector3)obj).y == y) && 216 | (((fVector3)obj).z == z); 217 | } 218 | 219 | public bool Equals(fVector3 other) 220 | { 221 | return (x == other.x) && (y == other.y) && (z == other.z); 222 | } 223 | 224 | public override int GetHashCode() 225 | { 226 | return x.GetHashCode() ^ y.GetHashCode() << 2 ^ z.GetHashCode() >> 2; 227 | } 228 | #endregion 229 | 230 | #region Conversion Operators 231 | public Vector3 ToUnityVector3() 232 | { 233 | return new Vector3(x.ToFloat(), y.ToFloat(), z.ToFloat()); 234 | } 235 | #endregion 236 | 237 | #region Math Functions 238 | public static ffloat Distance(fVector3 lhs, fVector3 rhs) 239 | { 240 | return (lhs - rhs).magnitude; 241 | } 242 | 243 | public static ffloat Angle(fVector3 from, fVector3 to) 244 | { 245 | return ffloat.Acos(ffloat.Clamp(fVector3.Dot(from.normalized, to.normalized), -ffloat.One, ffloat.One)) * ffloat.Rad2Deg; 246 | } 247 | 248 | public static ffloat Dot(fVector3 lhs, fVector3 rhs) 249 | { 250 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 251 | return fvector3_dot(ref lhs, ref rhs); 252 | #else 253 | return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; 254 | #endif 255 | } 256 | 257 | public static fVector3 Cross(fVector3 lhs, fVector3 rhs) 258 | { 259 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 260 | fVector3 result; 261 | fvector3_cross(ref lhs, ref rhs, out result); 262 | return result; 263 | #else 264 | return new fVector3( 265 | lhs.y * rhs.z - lhs.z * rhs.y, 266 | lhs.z * rhs.x - lhs.x * rhs.z, 267 | lhs.x * rhs.y - lhs.y * rhs.x); 268 | #endif 269 | } 270 | 271 | public static fVector3 Lerp(fVector3 start, fVector3 end, ffloat t) 272 | { 273 | return new fVector3( 274 | start.x + (end.x - start.x) * t, 275 | start.y + (end.y - start.y) * t, 276 | start.z + (end.z - start.z) * t); 277 | } 278 | 279 | public static fVector3 MoveTowards(fVector3 current, fVector3 target, ffloat maxDistance) 280 | { 281 | fVector3 diff = target - current; 282 | ffloat magnitude = diff.magnitude; 283 | 284 | if (magnitude <= maxDistance) 285 | return target; 286 | 287 | return current + diff / magnitude * maxDistance; 288 | } 289 | 290 | public static fVector3 Reflect(fVector3 direction, fVector3 normal) 291 | { 292 | return -2 * fVector3.Dot(normal, direction) * normal + direction; 293 | } 294 | #endregion 295 | 296 | #region Native Plugin Interface 297 | #if FMATH_ENABLE_NATIVE_PLUGIN 298 | [DllImport(fMathNativeImport.libName)] 299 | public static extern void fvector3_add(ref fVector3 lhs, ref fVector3 rhs, out fVector3 result); 300 | 301 | [DllImport(fMathNativeImport.libName)] 302 | public static extern void fvector3_sub(ref fVector3 lhs, ref fVector3 rhs, out fVector3 result); 303 | 304 | [DllImport(fMathNativeImport.libName)] 305 | public static extern void fvector3_mul(ref fVector3 lhs, ffloat rhs, out fVector3 result); 306 | 307 | [DllImport(fMathNativeImport.libName)] 308 | public static extern void fvector3_div(ref fVector3 lhs, ffloat rhs, out fVector3 result); 309 | 310 | [DllImport(fMathNativeImport.libName)] 311 | public static extern ffloat fvector3_dot(ref fVector3 lhs, ref fVector3 rhs); 312 | 313 | [DllImport(fMathNativeImport.libName)] 314 | public static extern void fvector3_cross(ref fVector3 lhs, ref fVector3 rhs, out fVector3 result); 315 | #endif 316 | #endregion 317 | } 318 | -------------------------------------------------------------------------------- /fQuaternion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using UnityEngine; 4 | 5 | [StructLayout(LayoutKind.Explicit)] 6 | public struct fQuaternion 7 | { 8 | [FieldOffset(0)] 9 | public ffloat x; 10 | [FieldOffset(4)] 11 | public ffloat y; 12 | [FieldOffset(8)] 13 | public ffloat z; 14 | [FieldOffset(12)] 15 | public ffloat w; 16 | 17 | #region Defines 18 | static readonly fQuaternion identityQuaternion = new fQuaternion(0, 0, 0, 1); 19 | 20 | public static fQuaternion identity { get { return identityQuaternion; } } 21 | #endregion 22 | 23 | #region Construtor 24 | public fQuaternion(ffloat x, ffloat y, ffloat z, ffloat w) 25 | { 26 | this.x = x; 27 | this.y = y; 28 | this.z = z; 29 | this.w = w; 30 | } 31 | 32 | public fQuaternion(int x, int y, int z, int w) 33 | { 34 | this.x = (ffloat)x; 35 | this.y = (ffloat)y; 36 | this.z = (ffloat)z; 37 | this.w = (ffloat)w; 38 | } 39 | 40 | public fQuaternion(float x, float y, float z, float w) 41 | { 42 | this.x = (ffloat)x; 43 | this.y = (ffloat)y; 44 | this.z = (ffloat)z; 45 | this.w = (ffloat)w; 46 | } 47 | 48 | public fQuaternion(fQuaternion other) 49 | { 50 | x = other.x; 51 | y = other.y; 52 | z = other.z; 53 | w = other.w; 54 | } 55 | 56 | public fQuaternion(Quaternion other) 57 | { 58 | x = (ffloat)other.x; 59 | y = (ffloat)other.y; 60 | z = (ffloat)other.z; 61 | w = (ffloat)other.w; 62 | } 63 | 64 | public fQuaternion(fVector3 vec, ffloat f) 65 | { 66 | x = vec.x; 67 | y = vec.y; 68 | z = vec.z; 69 | w = f; 70 | } 71 | #endregion 72 | 73 | fVector3 xyz { 74 | get { 75 | return new fVector3(x, y, z); 76 | } 77 | } 78 | 79 | ffloat length { 80 | get { 81 | return ffloat.Sqrt(sqrLength); 82 | } 83 | } 84 | 85 | ffloat sqrLength { 86 | get { 87 | return x * x + y * y + z * z + w * w; 88 | } 89 | } 90 | 91 | public fVector3 eulerAngles { 92 | get { 93 | return ToEuler(this); 94 | } 95 | set { 96 | this = Euler(value); 97 | } 98 | } 99 | 100 | public void Set(ffloat newX, ffloat newY, ffloat newZ, ffloat newW) 101 | { 102 | this.x = newX; 103 | this.y = newY; 104 | this.z = newZ; 105 | this.w = newW; 106 | } 107 | 108 | public void Scale(ffloat scale) 109 | { 110 | this.x *= scale; 111 | this.y *= scale; 112 | this.z *= scale; 113 | this.w *= scale; 114 | } 115 | 116 | void Normalize() 117 | { 118 | ffloat scale = ffloat.One / length; 119 | x *= scale; 120 | y *= scale; 121 | z *= scale; 122 | w *= scale; 123 | } 124 | 125 | void Conjugate() 126 | { 127 | x = -x; 128 | y = -y; 129 | z = -z; 130 | } 131 | 132 | public override string ToString() 133 | { 134 | return String.Format("({0:F2}, {1:F2}, {2:F2}, {3:F2})", new object[] { x.ToFloat(), y.ToFloat(), z.ToFloat(), w.ToFloat() }); 135 | } 136 | 137 | #region Arithmetic Operators 138 | public static fQuaternion operator *(fQuaternion lhs, fQuaternion rhs) 139 | { 140 | return new fQuaternion( 141 | lhs.w * rhs.x + lhs.x * rhs.w + lhs.y * rhs.z - lhs.z * rhs.y, 142 | lhs.w * rhs.y + lhs.y * rhs.w + lhs.z * rhs.x - lhs.x * rhs.z, 143 | lhs.w * rhs.z + lhs.z * rhs.w + lhs.x * rhs.y - lhs.y * rhs.x, 144 | lhs.w * rhs.w - lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z); 145 | } 146 | 147 | public static fVector3 operator *(fQuaternion rotation, fVector3 point) 148 | { 149 | // nVidia SDK implementation 150 | fVector3 qvec = new fVector3(rotation.x, rotation.y, rotation.z); 151 | fVector3 uv = fVector3.Cross(qvec, point); 152 | fVector3 uuv = fVector3.Cross(qvec, uv); 153 | uv *= 2 * rotation.w; 154 | uuv *= 2; 155 | 156 | return point + uv + uuv; 157 | } 158 | #endregion 159 | 160 | #region Comparison Operators 161 | static bool IsEqualUsingDot(ffloat dot) 162 | { 163 | return dot > (ffloat.One - ffloat.Epsilon); 164 | } 165 | 166 | public static bool operator ==(fQuaternion lhs, fQuaternion rhs) 167 | { 168 | return IsEqualUsingDot(fQuaternion.Dot(lhs, rhs)); 169 | } 170 | 171 | public static bool operator !=(fQuaternion lhs, fQuaternion rhs) 172 | { 173 | return !(lhs == rhs); 174 | } 175 | 176 | public override bool Equals(object obj) 177 | { 178 | if (!(obj is fQuaternion)) 179 | return false; 180 | 181 | fQuaternion other = (fQuaternion)obj; 182 | return x.Equals(other.x) && 183 | y.Equals(other.y) && 184 | z.Equals(other.z) && 185 | w.Equals(other.w); 186 | } 187 | 188 | public override int GetHashCode() 189 | { 190 | return x.GetHashCode() ^ y.GetHashCode() << 2 ^ z.GetHashCode() >> 2 ^ w.GetHashCode() >> 1; 191 | } 192 | #endregion 193 | 194 | #region Conversion Operators 195 | public static fQuaternion Euler(ffloat x, ffloat y, ffloat z) 196 | { 197 | ffloat halfRad = ffloat.Deg2Rad * ffloat.Half; 198 | x *= halfRad; 199 | y *= halfRad; 200 | z *= halfRad; 201 | ffloat cx = ffloat.Cos(x); 202 | ffloat cy = ffloat.Cos(y); 203 | ffloat cz = ffloat.Cos(z); 204 | ffloat sx = ffloat.Sin(x); 205 | ffloat sy = ffloat.Sin(y); 206 | ffloat sz = ffloat.Sin(z); 207 | 208 | return new fQuaternion( 209 | sx * cy * cz + cx * sy * sz, 210 | cx * sy * cz - sx * cy * sz, 211 | cx * cy * sz - sx * sy * cz, 212 | cx * cy * cz + sx * sy * sz); 213 | } 214 | 215 | public static fQuaternion Euler(fVector3 euler) 216 | { 217 | return Euler(euler.x, euler.y, euler.z); 218 | } 219 | 220 | public static fQuaternion Euler(float x, float y, float z) 221 | { 222 | return Euler((ffloat)x, (ffloat)y, (ffloat)z); 223 | } 224 | 225 | public static fQuaternion AngleAxis(ffloat angle, fVector3 axis) 226 | { 227 | ffloat halfRad = angle * ffloat.Deg2Rad * ffloat.Half; 228 | 229 | return new fQuaternion((axis * ffloat.Sin(halfRad)).normalized, ffloat.Cos(halfRad)); 230 | } 231 | 232 | public static fQuaternion LookRotation(fVector3 position) 233 | { 234 | return LookRotation(position, fVector3.up); 235 | } 236 | 237 | public static fQuaternion LookRotation(fVector3 position, fVector3 upVector) 238 | { 239 | fVector3 forward = position.normalized; 240 | fVector3 right = fVector3.Cross(upVector, forward); 241 | upVector = fVector3.Cross(forward, right); 242 | 243 | ffloat m00 = right.x; 244 | ffloat m01 = right.y; 245 | ffloat m02 = right.z; 246 | ffloat m10 = upVector.x; 247 | ffloat m11 = upVector.y; 248 | ffloat m12 = upVector.z; 249 | ffloat m20 = forward.x; 250 | ffloat m21 = forward.y; 251 | ffloat m22 = forward.z; 252 | 253 | ffloat diag = m00 + m11 + m22; 254 | 255 | fQuaternion result = new fQuaternion(); 256 | if (diag > ffloat.Zero) { 257 | ffloat scale = ffloat.Sqrt(diag + ffloat.One); 258 | result.w = scale * ffloat.Half; 259 | scale = ffloat.Half / scale; 260 | 261 | result.x = (m12 - m21) * scale; 262 | result.y = (m20 - m02) * scale; 263 | result.z = (m01 - m10) * scale; 264 | } 265 | else if ((m00 >= m11) && (m00 >= m22)) { 266 | ffloat scale = ffloat.Sqrt(((ffloat.One + m00) - m11) - m22); 267 | result.x = ffloat.Half * scale; 268 | scale = ffloat.Half / scale; 269 | result.y = (m01 + m10) * scale; 270 | result.z = (m02 + m20) * scale; 271 | result.w = (m12 - m21) * scale; 272 | } 273 | else if (m11 > m22) { 274 | ffloat scale = ffloat.Sqrt(((ffloat.One + m11) - m00) - m22); 275 | result.y = ffloat.Half * scale; 276 | scale = ffloat.Half / scale; 277 | result.x = (m10 + m01) * scale; 278 | result.z = (m21 + m12) * scale; 279 | result.w = (m20 - m02) * scale; 280 | } 281 | else { 282 | ffloat scale = ffloat.Sqrt(((ffloat.One + m22) - m00) - m11); 283 | result.z = ffloat.Half * scale; 284 | scale = ffloat.Half / scale; 285 | result.x = (m20 + m02) * scale; 286 | result.y = (m21 + m12) * scale; 287 | result.w = (m01 - m10) * scale; 288 | } 289 | 290 | return result; 291 | } 292 | 293 | public static fQuaternion FromToRotation(fVector3 from, fVector3 to) 294 | { 295 | fVector3 fn = from.normalized; 296 | fVector3 tn = to.normalized; 297 | 298 | ffloat dot = fVector3.Dot(fn, tn); 299 | if (dot <= -ffloat.One) { 300 | fVector3 tmp = fVector3.Cross(fVector3.right, fn); 301 | if (tmp.magnitude < ffloat.Epsilon) 302 | tmp = fVector3.Cross(fVector3.up, fn); 303 | tmp.Normalize(); 304 | 305 | return AngleAxis(ffloat.Pi, tmp); 306 | } 307 | else if (dot >= ffloat.One) { 308 | return identity; 309 | } 310 | ffloat scale = ffloat.Sqrt((ffloat.One + dot) * 2); 311 | ffloat inv = ffloat.One / scale; 312 | fVector3 cross = fVector3.Cross(fn, tn); 313 | fQuaternion result = new fQuaternion(cross * inv, scale * ffloat.Half); 314 | result.Normalize(); 315 | 316 | return result; 317 | } 318 | 319 | public static fVector3 ToEuler(fQuaternion rot) 320 | { 321 | ffloat x2 = rot.x * rot.x; 322 | ffloat y2 = rot.y * rot.y; 323 | ffloat z2 = rot.z * rot.z; 324 | ffloat w2 = rot.w * rot.w; 325 | ffloat unit = x2 + y2 + z2 + w2; 326 | ffloat test = rot.x * rot.w - rot.y * rot.z; 327 | 328 | if (test >= ffloat.Half * unit) { 329 | return new fVector3(2 * ffloat.Atan2(rot.x, rot.w), ffloat.HalfPi, ffloat.Zero); 330 | } 331 | if (test <= -ffloat.Half * unit) { 332 | return new fVector3(-2 * ffloat.Atan2(rot.x, rot.w), -ffloat.HalfPi, ffloat.Zero); 333 | } 334 | 335 | return NormalizedAngleVector( 336 | ffloat.Asin(2 * test), 337 | ffloat.Atan2(2 * rot.w * rot.y + 2 * rot.z * rot.x, ffloat.One - 2 * (x2 + y2)), 338 | ffloat.Atan2(2 * rot.w * rot.z + 2 * rot.x * rot.y, ffloat.One - 2 * (z2 + x2))); 339 | } 340 | 341 | static fVector3 NormalizedAngleVector(ffloat x, ffloat y, ffloat z) 342 | { 343 | return new fVector3(NormalizeAngle(x), NormalizeAngle(y), NormalizeAngle(z)); 344 | } 345 | 346 | static ffloat NormalizeAngle(ffloat rad) 347 | { 348 | ffloat oneRound = new ffloat(360); 349 | ffloat degree = rad * ffloat.Rad2Deg; 350 | 351 | while (degree > oneRound) 352 | degree -= oneRound; 353 | while (degree < ffloat.Zero) 354 | degree += oneRound; 355 | 356 | return degree; 357 | } 358 | 359 | public static fQuaternion Inverse(fQuaternion rotation) 360 | { 361 | ffloat inv = ffloat.One / (rotation.x * rotation.x + rotation.y * rotation.y + rotation.z * rotation.z + rotation.w * rotation.w); 362 | return new fQuaternion(-rotation.x * inv, -rotation.y * inv, -rotation.z * inv, rotation.w * inv); 363 | } 364 | 365 | public Quaternion ToUnityQuaternion() 366 | { 367 | return new Quaternion(x.ToFloat(), y.ToFloat(), z.ToFloat(), w.ToFloat()); 368 | } 369 | #endregion 370 | 371 | #region MathFunctions 372 | public static ffloat Angle(fQuaternion lhs, fQuaternion rhs) 373 | { 374 | ffloat dot = Dot(lhs, rhs); 375 | return (!IsEqualUsingDot(dot)) ? ffloat.Acos(ffloat.Min(ffloat.Abs(dot), ffloat.One)) * 2 * ffloat.Rad2Deg : ffloat.Zero; 376 | } 377 | 378 | public static ffloat Dot(fQuaternion lhs, fQuaternion rhs) 379 | { 380 | return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z + lhs.w * rhs.w; 381 | } 382 | 383 | public static fQuaternion Lerp(fQuaternion from, fQuaternion to, ffloat t) 384 | { 385 | fQuaternion result; 386 | ffloat inv = ffloat.One - t; 387 | if (Dot(from, to) > ffloat.Zero) { 388 | result = new fQuaternion( 389 | inv * from.x + t * to.x, 390 | inv * from.y + t * to.y, 391 | inv * from.z + t * to.z, 392 | inv * from.w + t * to.w); 393 | } 394 | else { 395 | result = new fQuaternion( 396 | inv * from.x - t * to.x, 397 | inv * from.y - t * to.y, 398 | inv * from.z - t * to.z, 399 | inv * from.w - t * to.w); 400 | } 401 | 402 | return result; 403 | } 404 | 405 | public static fQuaternion Slerp(fQuaternion from, fQuaternion to, ffloat t) 406 | { 407 | if (from.sqrLength == ffloat.Zero) { 408 | if (to.sqrLength == ffloat.Zero) 409 | return identity; 410 | return to; 411 | } 412 | else if (to.sqrLength == ffloat.Zero) 413 | return from; 414 | 415 | ffloat cosHalf = from.w * to.w + fVector3.Dot(from.xyz, to.xyz); 416 | if (cosHalf >= ffloat.One || cosHalf <= -ffloat.One) 417 | return from; 418 | else if (cosHalf <= ffloat.Zero) { 419 | to.Conjugate(); 420 | cosHalf = -cosHalf; 421 | } 422 | 423 | ffloat fromRate = ffloat.One - t; 424 | ffloat toRate = t; 425 | if (cosHalf <= ffloat.One) { 426 | ffloat half = ffloat.Acos(cosHalf); 427 | ffloat sinHalf = ffloat.Sin(half); 428 | ffloat overSinHalf = ffloat.One / sinHalf; 429 | 430 | fromRate = ffloat.Sin(half * fromRate) * overSinHalf; 431 | toRate = ffloat.Sin(half * toRate) * overSinHalf; 432 | } 433 | 434 | return new fQuaternion(fromRate * from.xyz + toRate * to.xyz, fromRate * from.w + toRate * to.w); 435 | } 436 | 437 | public static fQuaternion RotateTowards(fQuaternion from, fQuaternion to, ffloat maxDegree) 438 | { 439 | ffloat angle = Angle(from, to); 440 | if (angle == ffloat.Zero) 441 | return to; 442 | 443 | return Slerp(from, to, ffloat.Min(ffloat.One, maxDegree / angle)); 444 | } 445 | #endregion 446 | } 447 | -------------------------------------------------------------------------------- /fMathTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using UnityEngine; 4 | 5 | partial struct ffloat 6 | { 7 | const int TestCase = 10; 8 | 9 | public static void TestAll() 10 | { 11 | Test_ffloat(); 12 | Test_fVector2(); 13 | Test_fVector3(); 14 | Test_fQuaternion(); 15 | } 16 | 17 | #region Test ffloat 18 | public static void Test_ffloat() 19 | { 20 | Test_ffloatTypeConversion(); 21 | Test_ffloatPi(); 22 | Test_ffloatSqrt(); 23 | Test_ffloatTrigonFunctions(); 24 | } 25 | 26 | public static void Test_ffloatTypeConversion() 27 | { 28 | StringBuilder sb = new StringBuilder(); 29 | sb.AppendLine("ffloat TypeConversion Test"); 30 | 31 | for (int i = 1; i <= 200; i++) 32 | { 33 | float origin = (float)i / 100.0f; 34 | ffloat f = new ffloat(origin); 35 | sb.AppendLine("origin: " + origin + ", ffloat: " + f + ", ffloat ToFloat: " + f); 36 | } 37 | 38 | Debug.Log(sb); 39 | } 40 | 41 | public static void Test_ffloatPi() 42 | { 43 | StringBuilder sb = new StringBuilder(); 44 | sb.AppendLine("ffloat PI Test"); 45 | 46 | sb.AppendLine("PI -> ffloat: " + Pi + ", Math: " + Math.PI + ", Diff: " + (Math.PI - Pi.ToDouble())); 47 | sb.AppendLine("PI to Degree: " + (Pi * Rad2Deg)); 48 | 49 | Debug.Log(sb); 50 | } 51 | 52 | public static void Test_ffloatSqrt() 53 | { 54 | StringBuilder sb = new StringBuilder(); 55 | sb.AppendLine("ffloat Sqrt Test"); 56 | 57 | for (int i = 2; i < TestCase; i++) 58 | { 59 | sb.AppendLine(i + " Sqrt -> ffloat: " + Sqrt(new ffloat(i)) + ", Math: " + Math.Sqrt(i)); 60 | } 61 | 62 | Debug.Log(sb); 63 | } 64 | 65 | public static void Test_ffloatTrigonFunctions() 66 | { 67 | StringBuilder sb = new StringBuilder(); 68 | sb.AppendLine("ffloat TrigonFunctions Test"); 69 | const double degreeToRadian = Math.PI / 180.0; 70 | 71 | for (int i = 0; i < 15; i++) 72 | { 73 | double radian = i * 15.0 * degreeToRadian; 74 | sb.AppendLine("Sin " + i * 15 + 75 | "-> ffloat: " + Sin(new ffloat(radian)) + 76 | ", Math: " + Math.Sin(radian)); 77 | } 78 | 79 | for (int i = 0; i < 15; i++) 80 | { 81 | double radian = i * 15.0 * degreeToRadian; 82 | sb.AppendLine("Cos " + i * 15 + 83 | "-> ffloat: " + Cos(new ffloat(radian)) + 84 | ", Math: " + Math.Cos(radian)); 85 | } 86 | 87 | for (int i = 0; i < 15; i++) 88 | { 89 | double radian = i * 15.0 * degreeToRadian; 90 | sb.AppendLine("Tan " + i * 15 + 91 | "-> ffloat: " + Tan(new ffloat(radian)) + 92 | ", Math: " + Math.Tan(radian)); 93 | } 94 | 95 | for (int i = 0; i < 10; i++) 96 | { 97 | double radian = i / 10.0; 98 | sb.AppendLine("Asin " + radian + 99 | "-> ffloat: " + Asin(new ffloat(radian)) + 100 | ", Math: " + Math.Asin(radian)); 101 | } 102 | 103 | for (int i = 0; i < 10; i++) 104 | { 105 | double radian = i / 10.0; 106 | sb.AppendLine("Acos " + radian + 107 | "-> ffloat: " + Acos(new ffloat(radian)) + 108 | ", Math: " + Math.Acos(radian)); 109 | } 110 | 111 | for (int i = 0; i < 10; i++) 112 | { 113 | double radian = i / 10.0; 114 | sb.AppendLine("Atan " + radian + 115 | "-> ffloat: " + Atan(new ffloat(radian)) + 116 | ", Math: " + Math.Atan(radian)); 117 | } 118 | 119 | float[] inputx = new float[TestCase]; 120 | for (int i = 0; i < TestCase; i++) 121 | { 122 | inputx[i] = UnityEngine.Random.Range(-10.0f, 10.0f); 123 | } 124 | float[] inputy = new float[TestCase]; 125 | for (int i = 0; i < TestCase; i++) 126 | { 127 | inputy[i] = UnityEngine.Random.Range(-10.0f, 10.0f); 128 | } 129 | 130 | for (int i = 0; i < 10; i++) 131 | { 132 | sb.AppendLine("Atan2 " + 133 | "-> ffloat: " + Atan2((ffloat)inputx[i], (ffloat)inputy[i]) + 134 | ", Math: " + Math.Atan2(inputx[i], inputy[i])); 135 | } 136 | 137 | Debug.Log(sb); 138 | } 139 | #endregion 140 | 141 | #region Test fVector2 142 | public static void Test_fVector2() 143 | { 144 | Test_fVector2Normalize(); 145 | Test_fVector2Rotation(); 146 | } 147 | 148 | public static void Test_fVector2Normalize() 149 | { 150 | StringBuilder sb = new StringBuilder(); 151 | sb.AppendLine("fVector2 Normalize Test"); 152 | 153 | Vector2[] inputs = new Vector2[TestCase]; 154 | for (int i = 0; i < TestCase; i++) 155 | { 156 | inputs[i] = new Vector2(UnityEngine.Random.Range(-10.0f, 10.0f), UnityEngine.Random.Range(-10.0f, 10.0f)); 157 | } 158 | 159 | for (int i = 0; i < TestCase; i++) 160 | { 161 | sb.AppendLine(inputs[i] + 162 | " -> fVector2: " + (new fVector2(inputs[i])).normalized + 163 | ", Vector2: " + inputs[i].normalized); 164 | } 165 | 166 | Debug.Log(sb); 167 | } 168 | 169 | public static void Test_fVector2Rotation() 170 | { 171 | StringBuilder sb = new StringBuilder(); 172 | sb.AppendLine("fVector2 Rotation Test"); 173 | 174 | Vector2[] inputs = new Vector2[TestCase]; 175 | for (int i = 0; i < TestCase; i++) 176 | { 177 | inputs[i] = new Vector2(UnityEngine.Random.Range(-10.0f, 10.0f), UnityEngine.Random.Range(-10.0f, 10.0f)); 178 | } 179 | float[] rots = new float[TestCase]; 180 | for (int i = 0; i < TestCase; i++) 181 | rots[i] = UnityEngine.Random.Range(-320.0f, 320.0f); 182 | 183 | for (int i = 0; i < inputs.Length; i++) 184 | { 185 | fVector2 rotfVec = new fVector2(inputs[i]); 186 | rotfVec.Rotate((ffloat)rots[i]); 187 | 188 | sb.AppendLine(inputs[i] + "&" + rots[i] + 189 | " Rotation -> fVector2: " + rotfVec); 190 | } 191 | 192 | Debug.Log(sb); 193 | } 194 | 195 | #endregion 196 | 197 | #region Test fVector3 198 | public static void Test_fVector3() 199 | { 200 | Test_fVector3Normalize(); 201 | Test_fVector3Distance(); 202 | Test_fVector3Rotation(); 203 | Test_fVector3Angle(); 204 | Test_fVector3Dot(); 205 | Test_fVector3Cross(); 206 | } 207 | 208 | public static void Test_fVector3Normalize() 209 | { 210 | StringBuilder sb = new StringBuilder(); 211 | sb.AppendLine("fVector3 Normalize Test"); 212 | 213 | Vector3[] inputs = new Vector3[TestCase]; 214 | for (int i = 0; i < inputs.Length; i++) 215 | { 216 | inputs[i] = new UnityEngine.Vector3( 217 | UnityEngine.Random.Range(-10.0f, 10.0f), 218 | UnityEngine.Random.Range(-10.0f, 10.0f), 219 | UnityEngine.Random.Range(-10.0f, 10.0f)); 220 | } 221 | 222 | for (int i = 0; i < inputs.Length; i++) 223 | { 224 | sb.AppendLine(inputs[i] + 225 | " -> fVector3: " + (new fVector3(inputs[i])).normalized + 226 | ", Vector3: " + inputs[i].normalized); 227 | } 228 | 229 | Debug.Log(sb); 230 | } 231 | 232 | public static void Test_fVector3Distance() 233 | { 234 | StringBuilder sb = new StringBuilder(); 235 | sb.AppendLine("fVector3 Distance Test"); 236 | 237 | Vector3[] fromInputs = new Vector3[TestCase]; 238 | for (int i = 0; i < fromInputs.Length; i++) 239 | { 240 | fromInputs[i] = new Vector3( 241 | UnityEngine.Random.Range(-10.0f, 10.0f), 242 | UnityEngine.Random.Range(-10.0f, 10.0f), 243 | UnityEngine.Random.Range(-10.0f, 10.0f)); 244 | } 245 | 246 | Vector3[] toInputs = new Vector3[TestCase]; 247 | for (int i = 0; i < toInputs.Length; i++) 248 | { 249 | toInputs[i] = new Vector3( 250 | UnityEngine.Random.Range(-10.0f, 10.0f), 251 | UnityEngine.Random.Range(-10.0f, 10.0f), 252 | UnityEngine.Random.Range(-10.0f, 10.0f)); 253 | } 254 | 255 | for (int i = 0; i < fromInputs.Length; i++) 256 | { 257 | sb.AppendLine(fromInputs[i] + "&" + toInputs[i] + 258 | " Distance -> fVector3: " + fVector3.Distance(new fVector3(fromInputs[i]), (new fVector3(toInputs[i]))) + 259 | ", Vector3: " + Vector3.Distance(fromInputs[i], toInputs[i])); 260 | } 261 | 262 | Debug.Log(sb); 263 | } 264 | 265 | public static void Test_fVector3Rotation() 266 | { 267 | StringBuilder sb = new StringBuilder(); 268 | sb.AppendLine("fVector3 Rotation Test"); 269 | 270 | Vector3[] inputs = new Vector3[TestCase]; 271 | for (int i = 0; i < inputs.Length; i++) 272 | { 273 | inputs[i] = new Vector3( 274 | UnityEngine.Random.Range(-10.0f, 10.0f), 275 | UnityEngine.Random.Range(-10.0f, 10.0f), 276 | UnityEngine.Random.Range(-10.0f, 10.0f)); 277 | } 278 | 279 | Vector3[] rotInputs = new Vector3[TestCase]; 280 | for (int i = 0; i < rotInputs.Length; i++) 281 | { 282 | rotInputs[i] = new Vector3( 283 | UnityEngine.Random.Range(-150.0f, 150.0f), 284 | UnityEngine.Random.Range(-150.0f, 150.0f), 285 | UnityEngine.Random.Range(-150.0f, 150.0f)); 286 | } 287 | 288 | Quaternion[] rots = new Quaternion[TestCase]; 289 | for (int i = 0; i < inputs.Length; i++) 290 | { 291 | rots[i] = Quaternion.Euler(rotInputs[i].x, rotInputs[i].y, rotInputs[i].z); 292 | } 293 | 294 | for (int i = 0; i < inputs.Length; i++) 295 | { 296 | sb.AppendLine(inputs[i] + 297 | " Rotation -> fVector3: " + (new fQuaternion(rots[i])) * (new fVector3(inputs[i])) + 298 | ", Vector3: " + rots[i] * inputs[i]); 299 | } 300 | 301 | Debug.Log(sb); 302 | } 303 | 304 | public static void Test_fVector3Angle() 305 | { 306 | StringBuilder sb = new StringBuilder(); 307 | sb.AppendLine("fVector3 Angle Test"); 308 | 309 | Vector3[] fromInputs = new Vector3[TestCase]; 310 | for (int i = 0; i < fromInputs.Length; i++) 311 | { 312 | fromInputs[i] = new Vector3( 313 | UnityEngine.Random.Range(-10.0f, 10.0f), 314 | UnityEngine.Random.Range(-10.0f, 10.0f), 315 | UnityEngine.Random.Range(-10.0f, 10.0f)); 316 | } 317 | 318 | Vector3[] toInputs = new Vector3[TestCase]; 319 | for (int i = 0; i < toInputs.Length; i++) 320 | { 321 | toInputs[i] = new Vector3( 322 | UnityEngine.Random.Range(-10.0f, 10.0f), 323 | UnityEngine.Random.Range(-10.0f, 10.0f), 324 | UnityEngine.Random.Range(-10.0f, 10.0f)); 325 | } 326 | 327 | for (int i = 0; i < fromInputs.Length; i++) 328 | { 329 | sb.AppendLine(fromInputs[i] + "&" + toInputs[i] + 330 | " Angle -> fVector3: " + fVector3.Angle(new fVector3(fromInputs[i]), (new fVector3(toInputs[i]))) + 331 | ", Vector3: " + Vector3.Angle(fromInputs[i], toInputs[i])); 332 | } 333 | 334 | Debug.Log(sb); 335 | } 336 | 337 | public static void Test_fVector3Dot() 338 | { 339 | StringBuilder sb = new StringBuilder(); 340 | sb.AppendLine("fVector3 Dot Test"); 341 | 342 | Vector3[] fromInputs = new Vector3[TestCase]; 343 | for (int i = 0; i < fromInputs.Length; i++) 344 | { 345 | fromInputs[i] = new Vector3( 346 | UnityEngine.Random.Range(-10.0f, 10.0f), 347 | UnityEngine.Random.Range(-10.0f, 10.0f), 348 | UnityEngine.Random.Range(-10.0f, 10.0f)); 349 | } 350 | 351 | Vector3[] toInputs = new Vector3[TestCase]; 352 | for (int i = 0; i < toInputs.Length; i++) 353 | { 354 | toInputs[i] = new Vector3( 355 | UnityEngine.Random.Range(-10.0f, 10.0f), 356 | UnityEngine.Random.Range(-10.0f, 10.0f), 357 | UnityEngine.Random.Range(-10.0f, 10.0f)); 358 | } 359 | 360 | for (int i = 0; i < fromInputs.Length; i++) 361 | { 362 | sb.AppendLine(fromInputs[i] + "&" + toInputs[i] + 363 | " Dot -> fVector3: " + fVector3.Dot(new fVector3(fromInputs[i]), (new fVector3(toInputs[i]))) + 364 | ", Vector3: " + Vector3.Dot(fromInputs[i], toInputs[i])); 365 | } 366 | 367 | Debug.Log(sb); 368 | } 369 | 370 | public static void Test_fVector3Cross() 371 | { 372 | StringBuilder sb = new StringBuilder(); 373 | sb.AppendLine("fVector3 Cross Test"); 374 | 375 | Vector3[] fromInputs = new Vector3[TestCase]; 376 | for (int i = 0; i < fromInputs.Length; i++) 377 | { 378 | fromInputs[i] = new Vector3( 379 | UnityEngine.Random.Range(-10.0f, 10.0f), 380 | UnityEngine.Random.Range(-10.0f, 10.0f), 381 | UnityEngine.Random.Range(-10.0f, 10.0f)); 382 | } 383 | 384 | Vector3[] toInputs = new Vector3[TestCase]; 385 | for (int i = 0; i < toInputs.Length; i++) 386 | { 387 | toInputs[i] = new Vector3( 388 | UnityEngine.Random.Range(-10.0f, 10.0f), 389 | UnityEngine.Random.Range(-10.0f, 10.0f), 390 | UnityEngine.Random.Range(-10.0f, 10.0f)); 391 | } 392 | 393 | for (int i = 0; i < fromInputs.Length; i++) 394 | { 395 | sb.AppendLine(fromInputs[i] + "&" + toInputs[i] + 396 | " Cross -> fVector3: " + fVector3.Cross(new fVector3(fromInputs[i]), (new fVector3(toInputs[i]))) + 397 | ", Vector3: " + Vector3.Cross(fromInputs[i], toInputs[i])); 398 | } 399 | 400 | Debug.Log(sb); 401 | } 402 | #endregion 403 | 404 | #region Test fQuaternion 405 | public static void Test_fQuaternion() 406 | { 407 | Test_fQuaternionEuler(); 408 | Test_fQuaternionLookRotation(); 409 | Test_fQuaternionFromToRotation(); 410 | Test_fQuaternionAngle(); 411 | Test_fQuaternionSlerp(); 412 | } 413 | 414 | public static void Test_fQuaternionEuler() 415 | { 416 | StringBuilder sb = new StringBuilder(); 417 | sb.AppendLine("fQuaternion Euler Test"); 418 | 419 | Vector3[] inputs = new Vector3[TestCase]; 420 | for (int i = 0; i < inputs.Length; i++) 421 | { 422 | inputs[i] = new Vector3( 423 | UnityEngine.Random.Range(-180.0f, 180.0f), 424 | UnityEngine.Random.Range(-180.0f, 180.0f), 425 | UnityEngine.Random.Range(-180.0f, 180.0f)); 426 | } 427 | 428 | for (int i = 0; i < TestCase; i++) 429 | { 430 | fQuaternion frot = fQuaternion.Euler(inputs[i].x, inputs[i].y, inputs[i].z); 431 | Quaternion rot = Quaternion.Euler(inputs[i].x, inputs[i].y, inputs[i].z); 432 | sb.AppendLine(inputs[i] + 433 | " FromEuler -> fQuaternion: " + frot + 434 | ", Quaternion: " + rot); 435 | sb.AppendLine(inputs[i] + 436 | " ToEuler -> fQuaternion: " + frot.eulerAngles + 437 | ", Quaternion: " + rot.eulerAngles); 438 | sb.AppendLine(inputs[i] + 439 | " Restore -> fQuaternion: " + fQuaternion.Euler(frot.eulerAngles) + 440 | ", Quaternion: " + Quaternion.Euler(rot.eulerAngles)); 441 | } 442 | 443 | Debug.Log(sb); 444 | } 445 | 446 | public static void Test_fQuaternionLookRotation() 447 | { 448 | StringBuilder sb = new StringBuilder(); 449 | sb.AppendLine("fQuaternion LookRotation Test"); 450 | 451 | Vector3[] inputs = new Vector3[TestCase]; 452 | for (int i = 0; i < inputs.Length; i++) 453 | { 454 | inputs[i] = new Vector3( 455 | UnityEngine.Random.Range(-10.0f, 10.0f), 456 | UnityEngine.Random.Range(-10.0f, 10.0f), 457 | UnityEngine.Random.Range(-10.0f, 10.0f)); 458 | } 459 | 460 | for (int i = 0; i < TestCase; i++) 461 | { 462 | sb.AppendLine(inputs[i] + 463 | " LookRotation -> fQuaternion: " + fQuaternion.LookRotation(new fVector3(inputs[i])) + 464 | ", Quaternion: " + Quaternion.LookRotation(inputs[i])); 465 | } 466 | 467 | Debug.Log(sb); 468 | } 469 | 470 | public static void Test_fQuaternionFromToRotation() 471 | { 472 | StringBuilder sb = new StringBuilder(); 473 | sb.AppendLine("fQuaternion FromToRotation Test"); 474 | 475 | Vector3[] fromInputs = new Vector3[TestCase]; 476 | for (int i = 0; i < fromInputs.Length; i++) 477 | { 478 | fromInputs[i] = new Vector3( 479 | UnityEngine.Random.Range(-10.0f, 10.0f), 480 | UnityEngine.Random.Range(-10.0f, 10.0f), 481 | UnityEngine.Random.Range(-10.0f, 10.0f)); 482 | fromInputs[i].Normalize(); 483 | } 484 | 485 | Vector3[] toInputs = new Vector3[TestCase]; 486 | for (int i = 0; i < toInputs.Length; i++) 487 | { 488 | toInputs[i] = new Vector3( 489 | UnityEngine.Random.Range(-10.0f, 10.0f), 490 | UnityEngine.Random.Range(-10.0f, 10.0f), 491 | UnityEngine.Random.Range(-10.0f, 10.0f)); 492 | toInputs[i].Normalize(); 493 | } 494 | 495 | for (int i = 0; i < fromInputs.Length; i++) 496 | { 497 | fVector3 from = new fVector3(fromInputs[i]); 498 | fVector3 to = new fVector3(toInputs[i]); 499 | fQuaternion fromto = fQuaternion.FromToRotation(from, to); 500 | Quaternion fromtou = Quaternion.FromToRotation(fromInputs[i], toInputs[i]); 501 | sb.AppendLine(fromInputs[i] + "&" + toInputs[i] + 502 | " FromTo -> fQuaternion: " + fromto + "[" + fromto * from + "]" + 503 | ", Quaternion: " + fromtou); 504 | } 505 | 506 | Debug.Log(sb); 507 | } 508 | 509 | public static void Test_fQuaternionAngle() 510 | { 511 | StringBuilder sb = new StringBuilder(); 512 | sb.AppendLine("fQuaternion Angle Test"); 513 | 514 | Vector3[] fromInputs = new Vector3[TestCase]; 515 | for (int i = 0; i < fromInputs.Length; i++) 516 | { 517 | fromInputs[i] = new Vector3( 518 | UnityEngine.Random.Range(-10.0f, 10.0f), 519 | UnityEngine.Random.Range(-10.0f, 10.0f), 520 | UnityEngine.Random.Range(-10.0f, 10.0f)); 521 | } 522 | 523 | Vector3[] toInputs = new Vector3[TestCase]; 524 | for (int i = 0; i < toInputs.Length; i++) 525 | { 526 | toInputs[i] = new Vector3( 527 | UnityEngine.Random.Range(-10.0f, 10.0f), 528 | UnityEngine.Random.Range(-10.0f, 10.0f), 529 | UnityEngine.Random.Range(-10.0f, 10.0f)); 530 | } 531 | 532 | for (int i = 0; i < fromInputs.Length; i++) 533 | { 534 | sb.AppendLine(fromInputs[i] + "&" + toInputs[i] + 535 | " Angle -> fQuaternion: " + fQuaternion.Angle(fQuaternion.Euler(new fVector3(fromInputs[i])), fQuaternion.Euler(new fVector3(toInputs[i]))) + 536 | ", Quaternion: " + Quaternion.Angle(Quaternion.Euler(fromInputs[i]), Quaternion.Euler(toInputs[i]))); 537 | } 538 | 539 | Debug.Log(sb); 540 | } 541 | 542 | public static void Test_fQuaternionSlerp() 543 | { 544 | StringBuilder sb = new StringBuilder(); 545 | sb.AppendLine("fQuaternion Slerp Test"); 546 | 547 | Vector3[] fromInputs = new Vector3[TestCase]; 548 | for (int i = 0; i < fromInputs.Length; i++) 549 | { 550 | fromInputs[i] = new Vector3( 551 | UnityEngine.Random.Range(-10.0f, 10.0f), 552 | UnityEngine.Random.Range(-10.0f, 10.0f), 553 | UnityEngine.Random.Range(-10.0f, 10.0f)); 554 | } 555 | 556 | Vector3[] toInputs = new Vector3[TestCase]; 557 | for (int i = 0; i < toInputs.Length; i++) 558 | { 559 | toInputs[i] = new Vector3( 560 | UnityEngine.Random.Range(-10.0f, 10.0f), 561 | UnityEngine.Random.Range(-10.0f, 10.0f), 562 | UnityEngine.Random.Range(-10.0f, 10.0f)); 563 | } 564 | 565 | float[] rate = new float[TestCase]; 566 | for (int i = 0; i < TestCase; i++) 567 | { 568 | rate[i] = UnityEngine.Random.Range(0.0f, 1.0f); 569 | } 570 | 571 | for (int i = 0; i < fromInputs.Length; i++) 572 | { 573 | sb.AppendLine(fromInputs[i] + "&" + toInputs[i] + 574 | " Slerp -> fQuaternion: " + fQuaternion.Slerp(fQuaternion.Euler(new fVector3(fromInputs[i])), fQuaternion.Euler(new fVector3(toInputs[i])), (ffloat)rate[i]) + 575 | ", Quaternion: " + Quaternion.Slerp(Quaternion.Euler(fromInputs[i]), Quaternion.Euler(toInputs[i]), rate[i])); 576 | } 577 | 578 | Debug.Log(sb); 579 | } 580 | #endregion 581 | } 582 | -------------------------------------------------------------------------------- /ffloat.cs: -------------------------------------------------------------------------------- 1 | #define FFLOAT_LUT_CREATED 2 | //#define FFLOAT_FRACTION_8BIT 3 | #define FFLOAT_FRACTION_12BIT 4 | //#define FFLOAT_FRACTION_16BIT 5 | 6 | using System; 7 | using System.Runtime.InteropServices; 8 | 9 | #if FMATH_ENABLE_NATIVE_PLUGIN 10 | #if UNITY_IPHONE 11 | using fMathStaticLib; 12 | #elif UNITY_ANDROID 13 | using fMathDynamicLib; 14 | #endif 15 | #endif 16 | 17 | // 32bit Q23.8 fixed-point float 18 | public partial struct ffloat : IEquatable, IComparable 19 | { 20 | int _rawValue; 21 | public int RawValue { get { return _rawValue; } } 22 | 23 | #region Defines 24 | const int MAX_VALUE = int.MaxValue; 25 | const int MIN_VALUE = int.MinValue; 26 | const int NUM_BITS = 32; 27 | 28 | #if FFLOAT_FRACTION_8BIT 29 | const int FRACTIONAL_PLACES = 8; 30 | const uint UPPER_MASK = 0xFFFFFF00; 31 | const uint LOWER_MASK = 0x000000FF; 32 | 33 | const int PI = 0x0000032F; // 3.14159274 << 8 = 804.24774144 34 | const int PI_DOUBLE = 0x00000648; // 6.28318531 << 8 = 1608.49543936 35 | const int PI_HALF = 0x00000192; // 1.57079633 << 8 = 402.12386048 36 | const int DEG2RAD = 0x00000004; // 0.0174532924 << 8 = 4.4680428544 37 | const int RAD2DEG = 0x0000394B; // 57.29578 << 8 = 14667.71968 38 | #elif FFLOAT_FRACTION_12BIT 39 | const int FRACTIONAL_PLACES = 12; 40 | const uint UPPER_MASK = 0xFFFFF000; 41 | const uint LOWER_MASK = 0x00000FFF; 42 | 43 | const int PI = 0x00003244; // 3.14159274 << 12 = 12867.96386304 44 | const int PI_DOUBLE = 0x00006488; // 6.28318531 << 12 = 25735.92702976 45 | const int PI_HALF = 0x00001922; // 1.57079633 << 12 = 6433.98176768 46 | const int DEG2RAD = 0x00000047; // 0.0174532924 << 12 = 71.4886856704 47 | const int RAD2DEG = 0x000394BC; // 57.29578 << 12 = 234683.51488 48 | #elif FFLOAT_FRACTION_16BIT 49 | const int FRACTIONAL_PLACES = 16; 50 | const uint UPPER_MASK = 0xFFFF0000; 51 | const uint LOWER_MASK = 0x0000FFFF; 52 | 53 | const int PI = 0x0003243F; // 3.14159274 << 16 = 205887.42180864 54 | const int PI_DOUBLE = 0x0006487F; // 6.28318531 << 16 = 411774.83247616 55 | const int PI_HALF = 0x00019220; // 1.57079633 << 16 = 102943.70828288 56 | const int DEG2RAD = 0x00000478; // 0.0174532924 << 16 = 1143.8189707264 57 | const int RAD2DEG = 0x00394BB8; // 57.29578 << 16 = 3754936.23808 58 | #endif 59 | const int ONE = 1 << FRACTIONAL_PLACES; 60 | const int HALF = ONE >> 1; 61 | 62 | public static readonly ffloat MaxValue = CreateFromRawValue(MAX_VALUE); 63 | public static readonly ffloat MinValue = CreateFromRawValue(MIN_VALUE); 64 | public static readonly ffloat Zero = new ffloat(); 65 | public static readonly ffloat One = CreateFromRawValue(ONE); 66 | public static readonly ffloat Half = CreateFromRawValue(HALF); 67 | public static readonly ffloat Epsilon = CreateFromRawValue(1); 68 | 69 | public static readonly ffloat Pi = CreateFromRawValue(PI); 70 | public static readonly ffloat DoublePi = CreateFromRawValue(PI_DOUBLE); 71 | public static readonly ffloat HalfPi = CreateFromRawValue(PI_HALF); 72 | public static readonly ffloat Deg2Rad = CreateFromRawValue(DEG2RAD); 73 | public static readonly ffloat Rad2Deg = CreateFromRawValue(RAD2DEG); 74 | #endregion 75 | 76 | #region Sin, Tangent LookUpTable 77 | const int LUT_SCALE = 4; 78 | const int LUT_SIZE = (int)(PI_HALF >> LUT_SCALE); 79 | static readonly ffloat LutInterval = new ffloat(LUT_SIZE - 1) / HalfPi; 80 | 81 | #if !FFLOAT_LUT_CREATED 82 | static int[] _sinLut; 83 | static int[] _tanLut; 84 | 85 | public static void CreateLookUpTable() 86 | { 87 | _sinLut = new int[LUT_SIZE]; 88 | _tanLut = new int[LUT_SIZE]; 89 | 90 | double interval = Math.PI * 0.5 / (LUT_SIZE - 1); 91 | for (int i = 0; i < LUT_SIZE; i++) { 92 | double angle = i * interval; 93 | 94 | // sin 95 | double sin = Math.Sin(angle); 96 | int sinValue = ((ffloat)sin)._rawValue; 97 | _sinLut[i] = sinValue; 98 | 99 | // tan 100 | double tan = Math.Tan(angle); 101 | if (tan < (double)MaxValue || tan >= 0.0) { 102 | _tanLut[i] = ((ffloat)tan)._rawValue; 103 | } 104 | else { 105 | _tanLut[i] = MAX_VALUE - 1; 106 | } 107 | } 108 | } 109 | #endif 110 | 111 | internal static void GenerateLookUpTable() 112 | { 113 | using (var writer = new System.IO.StreamWriter("ffloat_sinLut.cs")) 114 | { 115 | double interval = Math.PI * 0.5 / (LUT_SIZE - 1); 116 | 117 | writer.Write( 118 | @"partial struct ffloat { 119 | public static readonly int[] _sinLut = new int[] {"); 120 | int lineCounter = 0; 121 | for (int i = 0; i < LUT_SIZE; ++i) 122 | { 123 | double angle = i * interval; 124 | if (lineCounter++ % 8 == 0) 125 | { 126 | writer.WriteLine(); 127 | writer.Write(" "); 128 | } 129 | 130 | double sin = Math.Sin(angle); 131 | int rawValue = ((ffloat)sin)._rawValue; 132 | writer.Write(string.Format("0x{0:X}, ", rawValue)); 133 | } 134 | writer.Write( 135 | @" 136 | }; 137 | }"); 138 | } 139 | 140 | using (var writer = new System.IO.StreamWriter("ffloat_tanLut.cs")) 141 | { 142 | double interval = Math.PI * 0.5 / (LUT_SIZE - 1); 143 | 144 | writer.Write( 145 | @"partial struct ffloat { 146 | public static readonly int[] _tanLut = new int[] {"); 147 | int lineCounter = 0; 148 | for (int i = 0; i < LUT_SIZE; ++i) 149 | { 150 | double angle = i * interval; 151 | if (lineCounter++ % 8 == 0) 152 | { 153 | writer.WriteLine(); 154 | writer.Write(" "); 155 | } 156 | 157 | double tan = Math.Tan(angle); 158 | int rawValue = MAX_VALUE; 159 | if (tan < (double)MaxValue || tan >= 0.0) 160 | { 161 | rawValue = ((ffloat)tan)._rawValue; 162 | } 163 | writer.Write(string.Format("0x{0:X}, ", rawValue)); 164 | } 165 | writer.Write( 166 | @" 167 | }; 168 | }"); 169 | } 170 | } 171 | #endregion 172 | 173 | #region Constructors 174 | public ffloat(int intValue) 175 | { 176 | _rawValue = intValue << FRACTIONAL_PLACES; 177 | } 178 | 179 | public ffloat(float floatValue) 180 | { 181 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 182 | _rawValue = ffloat_fromFloat(floatValue); 183 | #else 184 | int sign = (floatValue < 0) ? -1 : 1; 185 | if (floatValue < 0) 186 | floatValue *= -1; 187 | 188 | floatValue *= ONE; 189 | int integralPart = (int)floatValue; 190 | integralPart = (int)((uint)integralPart & UPPER_MASK); 191 | 192 | floatValue -= integralPart; 193 | floatValue = floatValue * 4; 194 | int choppedValue = (int)floatValue; 195 | choppedValue = choppedValue >> 2; 196 | choppedValue = (int)((uint)choppedValue & LOWER_MASK); 197 | 198 | _rawValue = (integralPart + choppedValue) * sign; 199 | #endif 200 | } 201 | 202 | public ffloat(double doubleValue) 203 | { 204 | int sign = (doubleValue < 0) ? -1 : 1; 205 | if (doubleValue < 0) 206 | doubleValue *= -1; 207 | 208 | doubleValue *= ONE; 209 | int integralPart = (int)doubleValue; 210 | integralPart = (int)((uint)integralPart & UPPER_MASK); 211 | 212 | doubleValue -= integralPart; 213 | doubleValue = doubleValue * 4; 214 | int choppedValue = (int)doubleValue; 215 | choppedValue = choppedValue >> 2; 216 | choppedValue = (int)((uint)choppedValue & LOWER_MASK); 217 | 218 | _rawValue = (integralPart + choppedValue) * sign; 219 | } 220 | 221 | public static ffloat CreateFromRawValue(int rawValue) 222 | { 223 | ffloat result = new ffloat 224 | { 225 | _rawValue = rawValue 226 | }; 227 | 228 | return result; 229 | } 230 | #endregion 231 | 232 | public override string ToString() 233 | { 234 | return String.Format("{0:F3}", new object[] { ToFloat() }); 235 | } 236 | 237 | #region Arithmetic Operators 238 | public static ffloat operator +(ffloat lhs, ffloat rhs) => CreateFromRawValue(lhs._rawValue + rhs._rawValue); 239 | public static ffloat operator +(ffloat lhs, int rhs) => lhs + (ffloat)rhs; 240 | public static ffloat operator +(int lhs, ffloat rhs) => (ffloat)lhs + rhs; 241 | public static ffloat operator +(ffloat lhs, float rhs) => lhs + (ffloat)rhs; 242 | public static ffloat operator +(float lhs, ffloat rhs) => (ffloat)lhs + rhs; 243 | public static ffloat operator -(ffloat x) => x._rawValue == MIN_VALUE ? MaxValue : CreateFromRawValue(-x._rawValue); 244 | public static ffloat operator -(ffloat lhs, ffloat rhs) => CreateFromRawValue(lhs._rawValue - rhs._rawValue); 245 | public static ffloat operator -(ffloat lhs, int rhs) => lhs - (ffloat)rhs; 246 | public static ffloat operator -(int lhs, ffloat rhs) => (ffloat)lhs - rhs; 247 | public static ffloat operator -(ffloat lhs, float rhs) => lhs - (ffloat)rhs; 248 | public static ffloat operator -(float lhs, ffloat rhs) => (ffloat)lhs - rhs; 249 | public static ffloat operator *(ffloat lhs, ffloat rhs) 250 | { 251 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 252 | return ffloat_mul(lhs._rawValue, rhs._rawValue); 253 | #else 254 | int xi = lhs._rawValue; 255 | int yi = rhs._rawValue; 256 | 257 | uint xlo = (uint)(xi & LOWER_MASK); 258 | int xhi = xi >> FRACTIONAL_PLACES; 259 | uint ylo = (uint)(yi & LOWER_MASK); 260 | int yhi = yi >> FRACTIONAL_PLACES; 261 | 262 | int lolo = (int)((xlo * ylo) >> FRACTIONAL_PLACES); 263 | int lohi = (int)xlo * yhi; 264 | int hilo = xhi * (int)ylo; 265 | int hihi = (xhi * yhi) << FRACTIONAL_PLACES; 266 | 267 | int sum = lolo + lohi + hilo + hihi; 268 | 269 | return CreateFromRawValue(sum); 270 | #endif 271 | } 272 | public static ffloat operator *(ffloat lhs, int rhs) => lhs * (ffloat)rhs; 273 | public static ffloat operator *(int lhs, ffloat rhs) => (ffloat)lhs * rhs; 274 | public static ffloat operator *(ffloat lhs, float rhs) => lhs * (ffloat)rhs; 275 | public static ffloat operator *(float lhs, ffloat rhs) => (ffloat)lhs * rhs; 276 | 277 | static int CountLeadingZeroes(uint x) 278 | { 279 | int result = 0; 280 | 281 | while ((x & 0xF0000000) == 0) 282 | { 283 | result += 4; 284 | x <<= 4; 285 | } 286 | while ((x & 0x80000000) == 0) 287 | { 288 | result++; 289 | x <<= 1; 290 | } 291 | 292 | return result; 293 | } 294 | 295 | public static ffloat operator /(ffloat lhs, ffloat rhs) 296 | { 297 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 298 | return ffloat_div(lhs._rawValue, rhs._rawValue); 299 | #else 300 | int xi = lhs._rawValue; 301 | int yi = rhs._rawValue; 302 | 303 | if (yi == 0) 304 | return MaxValue; 305 | 306 | uint remainder = (uint)(xi >= 0 ? xi : -xi); 307 | uint divider = (uint)(yi >= 0 ? yi : -yi); 308 | uint quotient = 0; 309 | int bitPos = (FRACTIONAL_PLACES) + 1; 310 | 311 | while ((divider & 0xF) == 0 && bitPos >= 4) 312 | { 313 | divider >>= 4; 314 | bitPos -= 4; 315 | } 316 | 317 | while (remainder != 0 && bitPos >= 0) 318 | { 319 | int shift = CountLeadingZeroes(remainder); 320 | if (shift > bitPos) 321 | shift = bitPos; 322 | 323 | remainder <<= shift; 324 | bitPos -= shift; 325 | 326 | uint div = remainder / divider; 327 | remainder = remainder % divider; 328 | quotient += div << bitPos; 329 | 330 | if ((div & ~(0xFFFFFFFF >> bitPos)) != 0) 331 | { 332 | return ((xi ^ yi) & MIN_VALUE) == 0 ? MaxValue : MinValue; 333 | } 334 | 335 | remainder <<= 1; 336 | bitPos--; 337 | } 338 | 339 | quotient++; 340 | 341 | int result = (int)(quotient >> 1); 342 | if (((xi ^ yi) & MIN_VALUE) != 0) 343 | { 344 | result = -result; 345 | } 346 | 347 | return CreateFromRawValue(result); 348 | #endif 349 | } 350 | public static ffloat operator /(ffloat lhs, int rhs) => lhs / (ffloat)rhs; 351 | public static ffloat operator /(int lhs, ffloat rhs) => (ffloat)lhs / rhs; 352 | public static ffloat operator %(ffloat lhs, ffloat rhs) => CreateFromRawValue(lhs._rawValue % rhs._rawValue); 353 | public static ffloat operator <<(ffloat x, int amount) => CreateFromRawValue(x._rawValue << amount); 354 | public static ffloat operator >>(ffloat x, int amount) => CreateFromRawValue(x._rawValue >> amount); 355 | #endregion 356 | 357 | #region Comparison Operators 358 | public static bool operator ==(ffloat lhs, ffloat rhs) => lhs._rawValue == rhs._rawValue; 359 | public static bool operator !=(ffloat lhs, ffloat rhs) => lhs._rawValue != rhs._rawValue; 360 | public static bool operator >(ffloat lhs, ffloat rhs) => lhs._rawValue > rhs._rawValue; 361 | public static bool operator >(ffloat lhs, int rhs) => lhs._rawValue > (rhs * ONE); 362 | public static bool operator >(int lhs, ffloat rhs) => (lhs * ONE) > rhs._rawValue; 363 | public static bool operator <(ffloat lhs, ffloat rhs) => lhs._rawValue < rhs._rawValue; 364 | public static bool operator <(ffloat lhs, int rhs) => lhs._rawValue < (rhs * ONE); 365 | public static bool operator <(int lhs, ffloat rhs) => (lhs * ONE) < rhs._rawValue; 366 | public static bool operator >=(ffloat lhs, ffloat rhs) => lhs._rawValue >= rhs._rawValue; 367 | public static bool operator >=(ffloat lhs, int rhs) => lhs._rawValue >= (rhs * ONE); 368 | public static bool operator >=(int lhs, ffloat rhs) => (lhs * ONE) >= rhs._rawValue; 369 | public static bool operator <=(ffloat lhs, ffloat rhs) => lhs._rawValue <= rhs._rawValue; 370 | public static bool operator <=(ffloat lhs, int rhs) => lhs._rawValue <= (rhs * ONE); 371 | public static bool operator <=(int lhs, ffloat rhs) => (lhs * ONE) <= rhs._rawValue; 372 | public override bool Equals(object obj) => obj is ffloat && ((ffloat)obj)._rawValue == _rawValue; 373 | public bool Equals(ffloat other) => _rawValue == other._rawValue; 374 | public override int GetHashCode() => _rawValue.GetHashCode(); 375 | public int CompareTo(ffloat other) => _rawValue.CompareTo(other._rawValue); 376 | #endregion 377 | 378 | #region Conversion Operators 379 | public static explicit operator int(ffloat source) => source.ToInt(); 380 | public static explicit operator float(ffloat source) => source.ToFloat(); 381 | public static explicit operator double(ffloat source) => source.ToDouble(); 382 | public static explicit operator ffloat(int source) => new ffloat(source); 383 | public static explicit operator ffloat(float source) => new ffloat(source); 384 | public static explicit operator ffloat(double source) => new ffloat(source); 385 | public int ToInt() => (int)(_rawValue >> FRACTIONAL_PLACES); 386 | public float ToFloat() => (float)_rawValue / (float)ONE; 387 | public double ToDouble() => (double)_rawValue / (double)ONE; 388 | #endregion 389 | 390 | #region Math Functions 391 | public static int Sign(ffloat x) => x._rawValue < 0 ? -1 : x._rawValue > 0 ? 1 : 0; 392 | 393 | public static ffloat Abs(ffloat x) 394 | { 395 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 396 | return ffloat_abs(x._rawValue); 397 | #else 398 | int mask = x._rawValue >> (NUM_BITS - 1); 399 | return CreateFromRawValue((x._rawValue + mask) ^ mask); 400 | #endif 401 | } 402 | 403 | public static ffloat Floor(ffloat x) 404 | { 405 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 406 | return ffloat_floor(x._rawValue); 407 | #else 408 | return CreateFromRawValue((int)((uint)x._rawValue & UPPER_MASK)); 409 | #endif 410 | } 411 | 412 | public static ffloat Ceiling(ffloat x) 413 | { 414 | bool hasFractionalPart = (x.RawValue & LOWER_MASK) != 0; 415 | return hasFractionalPart ? Floor(x) + One : x; 416 | } 417 | 418 | public static ffloat Round(ffloat x) 419 | { 420 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 421 | return ffloat_round(x._rawValue); 422 | #else 423 | uint fractionalPart = (uint)x._rawValue & LOWER_MASK; 424 | ffloat integralPart = Floor(x); 425 | if (fractionalPart < HALF) 426 | return integralPart; 427 | if (fractionalPart > HALF) 428 | return integralPart + One; 429 | 430 | return (integralPart._rawValue & ONE) == 0 ? integralPart : integralPart + One; 431 | #endif 432 | } 433 | 434 | public static ffloat Clamp(ffloat value, ffloat min, ffloat max) 435 | { 436 | if (value < min) 437 | value = min; 438 | else if (value > max) 439 | value = max; 440 | 441 | return value; 442 | } 443 | 444 | public static ffloat Min(ffloat lhs, ffloat rhs) 445 | { 446 | return lhs < rhs ? lhs : rhs; 447 | } 448 | 449 | public static ffloat Max(ffloat lhs, ffloat rhs) 450 | { 451 | return lhs > rhs ? lhs : rhs; 452 | } 453 | 454 | public static ffloat Sqrt(ffloat x) 455 | { 456 | int xi = x._rawValue; 457 | if (xi < 0) 458 | return Zero; 459 | 460 | uint num = (uint)xi; 461 | uint result = 0U; 462 | 463 | uint bit = 1U << (NUM_BITS - 2); 464 | while (bit > num) 465 | bit >>= 2; 466 | 467 | for (int i = 0; i < 2; i++) 468 | { 469 | while (bit != 0) 470 | { 471 | if (num >= result + bit) 472 | { 473 | num -= result + bit; 474 | result = (result >> 1) + bit; 475 | } 476 | else 477 | { 478 | result = result >> 1; 479 | } 480 | bit >>= 2; 481 | } 482 | 483 | if (i == 0) 484 | { 485 | if (num > (1U << (FRACTIONAL_PLACES)) - 1) 486 | { 487 | num -= result; 488 | num = (num << (FRACTIONAL_PLACES)) - HALF; 489 | result = (result << (FRACTIONAL_PLACES)) + HALF; 490 | } 491 | else 492 | { 493 | num <<= (FRACTIONAL_PLACES); 494 | result <<= (FRACTIONAL_PLACES); 495 | } 496 | 497 | bit = 1U << ((FRACTIONAL_PLACES) - 2); 498 | } 499 | } 500 | 501 | if (num > result) 502 | { 503 | result++; 504 | } 505 | 506 | return CreateFromRawValue((int)result); 507 | } 508 | 509 | // angle in radian, result is clamped to half Pi 510 | static int ClampSinValue(int rad, out bool flipHorizontal, out bool flipVertical) 511 | { 512 | int clamped2Pi = rad % PI_DOUBLE; 513 | if (rad < 0) 514 | clamped2Pi += PI_DOUBLE; 515 | flipVertical = clamped2Pi >= PI; 516 | 517 | int clampedPi = clamped2Pi; 518 | while (clampedPi >= PI) 519 | clampedPi -= PI; 520 | flipHorizontal = clampedPi >= PI_HALF; 521 | 522 | int clampedHalfPi = clampedPi; 523 | if (clampedHalfPi >= PI_HALF) 524 | clampedHalfPi -= PI_HALF; 525 | 526 | return clampedHalfPi; 527 | } 528 | 529 | static int _Sin(int rad) 530 | { 531 | bool flipHorizontal, flipVertical; 532 | int clamped = ClampSinValue(rad, out flipHorizontal, out flipVertical); 533 | 534 | int index = clamped >> LUT_SCALE; 535 | if (index >= LUT_SIZE) 536 | index = LUT_SIZE - 1; 537 | 538 | int nearestValue = _sinLut[flipHorizontal ? LUT_SIZE - 1 - index : index]; 539 | return flipVertical ? -nearestValue : nearestValue; 540 | } 541 | 542 | public static ffloat Sin(ffloat rad) 543 | { 544 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 545 | return ffloat_sin(rad._rawValue); 546 | #else 547 | return CreateFromRawValue(_Sin(rad._rawValue)); 548 | #endif 549 | } 550 | 551 | public static ffloat Cos(ffloat rad) 552 | { 553 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 554 | return ffloat_cos(rad._rawValue); 555 | #else 556 | int xi = rad._rawValue; 557 | int angle = xi + (xi > 0 ? -PI - PI_HALF : PI_HALF); 558 | return CreateFromRawValue(_Sin(angle)); 559 | #endif 560 | } 561 | 562 | public static ffloat Tan(ffloat rad) 563 | { 564 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 565 | return ffloat_tan(rad._rawValue); 566 | #else 567 | int clampedPi = rad._rawValue % PI; 568 | bool flip = false; 569 | if (clampedPi < 0) 570 | { 571 | clampedPi = -clampedPi; 572 | flip = true; 573 | } 574 | 575 | if (clampedPi > PI_HALF) 576 | { 577 | clampedPi = PI_HALF - (clampedPi - PI_HALF); 578 | flip = !flip; 579 | } 580 | 581 | clampedPi = clampedPi >> LUT_SCALE; 582 | 583 | ffloat rawIndex = CreateFromRawValue(clampedPi) * LutInterval; 584 | ffloat roundedIndex = Round(rawIndex); 585 | ffloat indexError = rawIndex - roundedIndex; 586 | 587 | if (_tanLut.Length <= roundedIndex.ToInt()) 588 | { 589 | 590 | } 591 | ffloat nearestValue = CreateFromRawValue(_tanLut[(int)roundedIndex]); 592 | ffloat nearValue = CreateFromRawValue(_tanLut[(int)roundedIndex + Sign(indexError)]); 593 | 594 | int delta = (indexError * Abs(nearestValue - nearValue))._rawValue; 595 | int interpolatedValue = nearestValue._rawValue + delta; 596 | int finalValue = flip ? -interpolatedValue : interpolatedValue; 597 | 598 | return CreateFromRawValue(finalValue); 599 | #endif 600 | } 601 | 602 | public static ffloat Asin(ffloat x) 603 | { 604 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 605 | return ffloat_asin(x._rawValue); 606 | #else 607 | if (x > One) 608 | return ffloat.Zero; 609 | 610 | ffloat dx = x * x; 611 | return -(new ffloat(-0.9391155) * x + new ffloat(0.92178415) * x * dx) 612 | / (1 + new ffloat(-1.28459062) * dx + new ffloat(0.29562414) * dx * dx); 613 | #endif 614 | } 615 | 616 | public static ffloat Acos(ffloat x) 617 | { 618 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 619 | return ffloat_acos(x._rawValue); 620 | #else 621 | if (x > One) 622 | return ffloat.Zero; 623 | 624 | ffloat dx = x * x; 625 | 626 | return HalfPi + (new ffloat(-0.9391155) * x + new ffloat(0.92178415) * x * dx) 627 | / (1 + new ffloat(-1.28459062) * dx + new ffloat(0.29562414) * dx * dx); 628 | #endif 629 | } 630 | 631 | public static ffloat Atan(ffloat x) 632 | { 633 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 634 | return ffloat_atan(x._rawValue); 635 | #else 636 | return Asin(x / Sqrt(One + (x * x))); 637 | #endif 638 | } 639 | 640 | public static ffloat Atan2(ffloat y, ffloat x) 641 | { 642 | #if FMATH_ENABLE_NATIVE_PLUGIN && !UNITY_EDITOR 643 | return ffloat_tan2(y._rawValue, x._rawValue); 644 | #else 645 | if (x == Zero) 646 | { 647 | if (y > Zero) 648 | { 649 | return HalfPi; 650 | } 651 | else 652 | { 653 | return -HalfPi; 654 | } 655 | } 656 | 657 | if (Abs(x) > Abs(y)) 658 | { 659 | ffloat z = y / x; 660 | 661 | if (x > Zero) 662 | { 663 | return ApproxAtan(z); 664 | } 665 | else if (y >= Zero) 666 | { 667 | return ApproxAtan(z) + Pi; 668 | } 669 | else 670 | { 671 | return ApproxAtan(z) - Pi; 672 | } 673 | } 674 | else 675 | { 676 | ffloat z = x / y; 677 | 678 | if (y > Zero) 679 | { 680 | return -ApproxAtan(z) + HalfPi; 681 | } 682 | else 683 | { 684 | return -ApproxAtan(z) - HalfPi; 685 | } 686 | } 687 | #endif 688 | } 689 | 690 | static ffloat ApproxAtan(ffloat z) 691 | { 692 | ffloat n1 = new ffloat(0.97239411); 693 | ffloat n2 = new ffloat(-0.19194795); 694 | 695 | return (n1 + n2 * z * z) * z; 696 | } 697 | 698 | #endregion 699 | 700 | #region Native Plugin Interface 701 | #if FMATH_ENABLE_NATIVE_PLUGIN 702 | [DllImport(fMathNativeImport.libName)] 703 | public static extern int ffloat_fromInt(int value); 704 | 705 | [DllImport(fMathNativeImport.libName)] 706 | public static extern int ffloat_fromFloat(float value); 707 | 708 | [DllImport(fMathNativeImport.libName)] 709 | public static extern int ffloat_mul(int lhs, int rhs); 710 | 711 | [DllImport(fMathNativeImport.libName)] 712 | public static extern int ffloat_div(int lhs, int rhs); 713 | 714 | [DllImport(fMathNativeImport.libName)] 715 | public static extern int ffloat_abs(int value); 716 | 717 | [DllImport(fMathNativeImport.libName)] 718 | public static extern int ffloat_floor(int value); 719 | 720 | [DllImport(fMathNativeImport.libName)] 721 | public static extern int ffloat_round(int value); 722 | 723 | [DllImport(fMathNativeImport.libName)] 724 | public static extern int ffloat_sqrt(int value); 725 | 726 | [DllImport(fMathNativeImport.libName)] 727 | public static extern int ffloat_sin(int value); 728 | 729 | [DllImport(fMathNativeImport.libName)] 730 | public static extern int ffloat_cos(int value); 731 | 732 | [DllImport(fMathNativeImport.libName)] 733 | public static extern int ffloat_tan(int value); 734 | 735 | [DllImport(fMathNativeImport.libName)] 736 | public static extern int ffloat_asin(int value); 737 | 738 | [DllImport(fMathNativeImport.libName)] 739 | public static extern int ffloat_acos(int value); 740 | 741 | [DllImport(fMathNativeImport.libName)] 742 | public static extern int ffloat_atan(int value); 743 | 744 | [DllImport(fMathNativeImport.libName)] 745 | public static extern int ffloat_atan2(int y, int x); 746 | #endif 747 | #endregion 748 | } 749 | --------------------------------------------------------------------------------