ソースを参照

Supporting suspension that is at an angle with the vehicle up direction (#483)

* Improved drawing of wheeled vehicle constraint
* Ability to configure suspension angle in the vehicle constraint test
* Added a race track to the flat world test to get a better idea of car handling
Jorrit Rouwe 2 年 前
コミット
172a99c718

+ 165 - 0
Assets/Racetracks/LICENSE.txt

@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.

+ 3 - 0
Assets/Racetracks/README.md

@@ -0,0 +1,3 @@
+Race tracks taken from: https://github.com/TUMFTM/racetrack-database
+
+See LICENSE.txt for license for these race tracks.

+ 865 - 0
Assets/Racetracks/Zandvoort.csv

@@ -0,0 +1,865 @@
+# x_m,y_m,w_tr_right_m,w_tr_left_m
+-1.683339,-1.878198,5.074,5.271
+0.151452,2.772507,5.099,5.295
+1.986397,7.423128,5.125,5.319
+3.821455,12.073677,5.150,5.343
+5.656587,16.724166,5.175,5.367
+7.491752,21.374607,5.200,5.391
+9.326909,26.025014,5.226,5.415
+11.162019,30.675397,5.251,5.439
+12.997042,35.325770,5.276,5.462
+14.831936,39.976144,5.301,5.486
+16.666662,44.626532,5.327,5.510
+18.501180,49.276946,5.352,5.534
+20.335449,53.927398,5.377,5.558
+22.169429,58.577901,5.402,5.582
+24.003080,63.228467,5.428,5.606
+25.836362,67.879107,5.453,5.630
+27.669244,72.529833,5.478,5.654
+29.501738,77.180644,5.503,5.678
+31.333865,81.831537,5.529,5.702
+33.165645,86.482511,5.554,5.726
+34.997100,91.133563,5.579,5.750
+36.828251,95.784691,5.604,5.774
+38.659117,100.435894,5.630,5.798
+40.489721,105.087167,5.655,5.822
+42.320083,109.738510,5.680,5.845
+44.150224,114.389921,5.706,5.869
+45.980165,119.041396,5.731,5.893
+47.809927,123.692934,5.756,5.917
+49.639531,128.344532,5.781,5.941
+51.468997,132.996189,5.807,5.965
+53.298346,137.647901,5.832,5.989
+55.127600,142.299668,5.857,6.013
+56.956780,146.951486,5.882,6.037
+58.785905,151.603353,5.908,6.061
+60.614998,156.255268,5.933,6.085
+62.444078,160.907227,5.958,6.109
+64.273168,165.559229,5.983,6.133
+66.102287,170.211271,6.009,6.157
+67.931458,174.863352,6.034,6.181
+69.760699,179.515468,6.059,6.205
+71.590034,184.167619,6.084,6.229
+73.419481,188.819800,6.110,6.252
+75.249063,193.472011,6.135,6.276
+77.078801,198.124249,6.160,6.300
+78.908714,202.776512,6.185,6.324
+80.738825,207.428798,6.211,6.348
+82.569153,212.081104,6.236,6.372
+84.399720,216.733428,6.261,6.396
+86.230547,221.385768,6.286,6.420
+88.061655,226.038121,6.312,6.444
+89.893064,230.690486,6.337,6.468
+91.724795,235.342861,6.362,6.492
+93.556870,239.995242,6.388,6.516
+95.389309,244.647628,6.413,6.540
+97.222133,249.300016,6.438,6.564
+99.055363,253.952405,6.463,6.588
+100.889021,258.604792,6.489,6.612
+102.723126,263.257175,6.514,6.635
+104.557699,267.909552,6.539,6.659
+106.392763,272.561920,6.564,6.683
+108.228337,277.214277,6.590,6.707
+110.064442,281.866621,6.615,6.731
+111.901100,286.518949,6.640,6.755
+113.738331,291.171261,6.665,6.779
+115.576156,295.823552,6.691,6.803
+117.414596,300.475821,6.716,6.827
+119.253672,305.128067,6.741,6.851
+121.093405,309.780286,6.766,6.875
+122.933816,314.432476,6.792,6.899
+124.774925,319.084635,6.816,6.923
+126.651699,323.714334,6.669,7.006
+128.738887,328.209430,6.522,7.089
+131.246183,332.435357,6.522,6.226
+134.379060,336.257639,6.092,6.062
+138.182494,339.545392,5.677,6.289
+142.495039,342.172347,5.848,6.230
+147.141834,344.013044,6.038,6.065
+151.981474,345.037089,5.885,6.029
+156.945280,345.420747,6.132,5.821
+161.974127,345.367418,6.058,5.924
+167.008204,345.065211,5.807,6.178
+171.977944,344.482774,5.789,6.230
+176.806475,343.424482,5.869,6.193
+181.417183,341.694720,5.987,6.121
+185.749365,339.242190,5.914,6.218
+189.762356,336.197440,5.948,6.207
+193.416775,332.702620,6.070,6.105
+196.666967,328.885936,6.207,5.965
+199.423062,324.777280,5.742,6.233
+201.576184,320.364273,5.591,6.084
+203.023502,315.640256,5.646,5.825
+203.748491,310.681493,5.785,5.710
+203.798453,305.625580,5.801,5.643
+203.222189,300.611552,5.651,5.577
+202.092240,295.744077,5.363,5.608
+200.571389,290.997188,5.182,5.600
+198.843858,286.313886,5.142,5.539
+197.076965,281.642398,5.102,5.479
+195.319515,276.967602,5.062,5.418
+193.569611,272.290054,5.022,5.358
+191.825166,267.610371,4.982,5.297
+190.084090,262.929171,4.942,5.236
+188.344295,258.247069,4.903,5.176
+186.603693,253.564683,4.863,5.115
+184.860195,248.882630,4.823,5.055
+183.111713,244.201525,4.783,4.994
+181.356158,239.521985,4.743,4.934
+179.591443,234.844628,4.703,4.873
+177.815478,230.170070,4.664,4.813
+176.026175,225.498928,4.624,4.752
+174.221466,220.831812,4.584,4.692
+172.402865,216.168291,4.544,4.631
+170.579569,211.505702,4.504,4.571
+168.761766,206.841091,4.464,4.510
+166.959647,202.171505,4.424,4.450
+165.183399,197.493991,4.385,4.389
+163.443211,192.805596,4.345,4.329
+161.749274,188.103367,4.305,4.268
+160.111775,183.384351,4.190,4.272
+158.540904,178.645594,4.021,4.323
+157.046851,173.884144,4.128,4.318
+155.639803,169.097048,4.247,4.300
+154.329950,164.281352,4.406,4.212
+153.127482,159.434104,4.493,4.107
+152.040841,154.553794,4.555,3.996
+151.071739,149.644481,4.460,4.003
+150.220267,144.711567,4.332,4.045
+149.486513,139.760453,4.374,4.113
+148.870566,134.796540,4.291,4.230
+148.372516,129.825230,4.183,4.336
+147.992434,124.851911,4.140,4.294
+147.726990,119.879465,4.200,4.285
+147.565511,114.905358,4.313,4.294
+147.496360,109.926350,4.273,4.313
+147.507905,104.939200,4.175,4.335
+147.588509,99.940667,4.077,4.357
+147.726539,94.927512,4.132,4.316
+147.909661,89.896706,4.206,4.266
+148.099958,84.853004,4.281,4.217
+148.227166,79.811000,4.355,4.167
+148.218945,74.785923,4.352,4.137
+148.002954,69.792997,4.281,4.126
+147.506854,64.847451,4.275,4.132
+146.658303,59.964509,4.310,4.205
+145.388472,55.160670,4.205,4.498
+143.678977,50.470695,4.146,4.624
+141.549258,45.943039,4.172,4.747
+139.019670,41.626490,4.262,4.889
+136.110565,37.569834,4.418,4.983
+132.842300,33.821858,4.451,4.599
+129.235227,30.431348,4.451,4.398
+125.312766,27.436881,4.418,4.409
+121.120144,24.804331,4.355,4.278
+116.712056,22.468034,4.270,4.348
+112.143230,20.362198,4.184,4.427
+107.468396,18.421033,4.099,4.507
+102.742286,16.578745,4.048,4.545
+98.019629,14.769545,4.095,4.463
+93.342513,12.942394,4.142,4.381
+88.704216,11.103219,4.189,4.300
+84.086235,9.271696,4.236,4.218
+79.470070,7.467499,4.284,4.136
+74.837217,5.710302,4.331,4.055
+70.169176,4.019780,4.278,4.105
+65.447529,2.415476,4.160,4.240
+60.669630,0.892412,4.042,4.375
+55.866984,-0.607489,4.038,4.423
+51.075564,-2.149247,4.236,4.314
+46.331341,-3.797882,4.434,4.205
+41.670287,-5.618416,4.632,4.096
+37.128373,-7.675868,4.635,4.242
+32.743211,-10.035645,4.623,4.392
+28.612354,-12.777269,4.606,4.509
+24.909111,-15.998105,4.888,4.185
+21.811325,-19.796169,4.673,4.211
+19.439269,-24.181707,4.222,4.435
+17.789594,-28.976489,4.444,4.545
+16.842948,-33.977887,4.564,4.573
+16.579252,-39.007227,4.818,4.705
+16.975636,-43.977492,5.060,4.754
+18.008569,-48.823586,5.137,4.584
+19.654514,-53.480415,4.896,4.452
+21.889938,-57.882883,4.802,4.511
+24.691308,-61.965894,4.744,4.612
+28.034938,-65.664418,4.670,4.685
+31.872160,-68.924115,4.762,4.498
+36.101648,-71.713185,4.797,4.469
+40.615402,-74.002680,4.672,4.788
+45.309127,-75.747824,4.864,4.612
+50.128975,-76.688113,5.130,4.321
+55.040283,-76.579563,4.952,4.341
+59.946477,-75.810565,4.752,4.290
+64.789826,-74.708054,4.543,4.150
+69.577538,-73.344400,4.460,4.132
+74.320695,-71.776786,4.424,4.159
+79.030380,-70.062396,4.388,4.186
+83.717677,-68.258412,4.352,4.213
+88.393668,-66.422017,4.316,4.240
+93.068996,-64.607786,4.280,4.268
+97.748404,-62.835178,4.244,4.295
+102.432408,-61.098522,4.208,4.322
+107.121432,-59.391600,4.172,4.349
+111.815902,-57.708192,4.136,4.377
+116.516242,-56.042081,4.100,4.404
+121.222878,-54.387047,4.064,4.431
+125.936245,-52.737906,4.028,4.458
+130.656867,-51.096499,3.992,4.485
+135.385298,-49.467589,3.956,4.513
+140.122098,-47.855949,4.007,4.456
+144.867821,-46.266353,4.087,4.372
+149.623025,-44.703573,4.167,4.288
+154.388268,-43.172382,4.247,4.203
+159.164104,-41.677554,4.298,4.120
+163.951092,-40.223862,4.334,4.036
+168.749789,-38.816079,4.370,3.953
+173.560750,-37.458978,4.405,3.869
+178.384534,-36.157332,4.254,4.031
+183.221696,-34.915914,4.064,4.244
+188.072792,-33.739486,3.874,4.456
+192.938144,-32.630868,3.876,4.512
+197.817589,-31.588837,3.951,4.510
+202.710900,-30.611665,4.026,4.507
+207.617853,-29.697620,4.115,4.497
+212.538223,-28.844975,4.216,4.481
+217.471784,-28.052000,4.345,4.438
+222.418294,-27.317078,4.570,4.297
+227.376885,-26.642445,4.794,4.157
+232.345927,-26.035039,4.806,4.423
+237.323747,-25.502088,4.795,4.733
+242.308667,-25.050816,4.804,4.968
+247.299013,-24.688449,4.870,4.985
+252.293109,-24.422213,4.936,5.003
+257.289037,-24.259478,4.961,5.020
+262.283974,-24.208158,4.971,5.038
+267.274885,-24.276290,4.800,5.101
+272.258736,-24.471913,4.628,5.164
+277.232491,-24.803068,4.505,5.156
+282.193116,-25.277793,4.486,5.000
+287.137588,-25.904062,4.466,4.845
+292.064716,-26.679188,4.474,4.579
+296.977157,-27.578124,4.488,4.286
+301.878054,-28.573005,4.503,3.993
+306.770548,-29.635967,4.404,3.982
+311.657780,-30.739145,4.285,4.017
+316.542892,-31.854674,4.167,4.051
+321.428992,-32.955036,4.096,4.095
+326.318026,-34.024611,4.133,4.158
+331.210506,-35.062414,4.169,4.222
+336.106862,-36.068357,4.205,4.286
+341.007518,-37.042351,4.225,4.300
+345.912902,-37.984309,4.219,4.240
+350.823442,-38.894142,4.214,4.181
+355.739563,-39.771763,4.209,4.121
+360.661693,-40.617082,4.204,4.062
+365.590258,-41.430014,4.198,4.003
+370.525686,-42.210468,4.181,3.995
+375.468403,-42.958358,4.141,4.092
+380.418836,-43.673594,4.100,4.190
+385.377412,-44.356090,4.194,4.121
+390.344264,-45.001791,4.371,3.948
+395.317475,-45.579028,4.443,3.916
+400.294259,-46.044395,4.418,4.015
+405.271826,-46.354443,4.385,4.118
+410.247385,-46.465723,4.282,4.258
+415.218148,-46.334784,4.178,4.398
+420.181323,-45.918177,4.224,4.298
+425.133987,-45.197781,4.323,4.108
+430.072713,-44.251288,4.344,4.070
+434.993954,-43.179046,4.316,4.125
+439.894009,-42.078959,4.281,4.177
+444.763823,-40.963691,4.231,4.223
+449.587719,-39.740469,4.201,4.249
+454.349640,-38.310236,4.266,4.175
+459.038639,-36.617037,4.326,4.089
+463.654604,-34.696295,4.371,3.972
+468.198805,-32.595092,4.383,3.886
+472.673103,-30.359601,3.976,4.184
+477.087549,-28.023365,3.990,4.182
+481.458197,-25.610672,4.119,4.098
+485.800998,-23.145258,4.249,4.014
+490.123304,-20.638911,4.379,3.930
+494.421825,-18.088632,4.371,3.952
+498.692612,-15.490497,4.249,4.062
+502.931711,-12.840586,4.126,4.173
+507.135172,-10.134977,4.004,4.283
+511.299044,-7.369750,4.089,4.144
+515.423163,-4.546296,4.176,4.003
+519.521744,-1.686182,4.263,3.862
+523.612415,1.184239,4.166,3.903
+527.712803,4.038614,3.966,4.047
+531.840536,6.850589,3.819,4.161
+536.013241,9.593812,3.869,4.167
+540.248511,12.241964,4.004,4.106
+544.557752,14.774904,4.127,4.031
+548.939186,17.185655,4.102,4.026
+553.389343,19.468929,4.048,4.004
+557.904756,21.619437,3.997,4.001
+562.481955,23.631891,3.949,4.032
+567.117471,25.501002,3.951,4.119
+571.807805,27.221672,3.978,4.235
+576.548384,28.795486,4.095,4.320
+581.333297,30.232374,4.258,4.389
+586.156546,31.542788,4.369,4.434
+591.012137,32.737181,4.398,4.439
+595.894073,33.826004,4.428,4.443
+600.796359,34.819711,4.342,4.407
+605.713335,35.726460,4.185,4.346
+610.644123,36.522011,4.027,4.284
+615.591378,37.158137,4.053,4.379
+620.557840,37.586044,4.089,4.483
+625.544317,37.776802,4.175,4.408
+630.544207,37.777603,4.261,4.331
+635.549133,37.653867,4.305,4.272
+640.551532,37.465775,4.266,4.246
+645.549590,37.236469,4.228,4.220
+650.543968,36.973127,4.189,4.195
+655.535339,36.682867,4.151,4.169
+660.524373,36.372805,4.113,4.143
+665.511744,36.050057,4.205,4.209
+670.498122,35.721741,4.298,4.277
+675.484179,35.394973,4.392,4.344
+680.470588,35.076869,4.485,4.411
+685.458019,34.774546,4.578,4.478
+690.447146,34.495121,4.672,4.545
+695.438639,34.245711,4.765,4.612
+700.433170,34.033432,4.859,4.679
+705.431408,33.865362,4.912,4.711
+710.433423,33.741608,4.827,4.620
+715.437997,33.647216,4.743,4.530
+720.443740,33.565270,4.658,4.439
+725.449265,33.478855,4.574,4.348
+730.453184,33.371056,4.489,4.258
+735.454108,33.224956,4.405,4.167
+740.450587,33.023502,4.286,4.211
+745.438840,32.744590,4.134,4.392
+750.412147,32.359732,3.981,4.572
+755.363599,31.840032,3.878,4.650
+760.286286,31.156595,3.899,4.616
+765.173300,30.280525,4.058,4.459
+770.017731,29.182924,4.231,4.252
+774.811717,27.841028,4.260,4.180
+779.543702,26.255866,4.236,4.158
+784.201230,24.434244,4.119,4.268
+788.771846,22.382966,4.080,4.251
+793.243097,20.108838,4.203,3.975
+797.602526,17.618665,4.216,4.021
+801.837680,14.919252,4.222,4.103
+805.936103,12.017403,4.226,4.225
+809.885342,8.919924,4.235,4.237
+813.672940,5.633620,4.249,4.191
+817.286445,2.165295,4.283,4.160
+820.713399,-1.478244,4.332,4.094
+823.941350,-5.290194,4.351,4.032
+826.957948,-9.263686,3.868,4.384
+829.754717,-13.389548,3.924,4.341
+832.328095,-17.655687,4.092,4.272
+834.674835,-22.049819,4.085,4.457
+836.791692,-26.559662,4.044,4.341
+838.675419,-31.172934,3.951,4.294
+840.322770,-35.877353,3.873,4.297
+841.730499,-40.660635,3.942,4.140
+842.895359,-45.510499,4.032,4.063
+843.814104,-50.414663,4.113,4.038
+844.483488,-55.360842,4.079,4.147
+844.900265,-60.336757,4.221,4.148
+845.061188,-65.330123,4.272,4.158
+844.963011,-70.328658,4.222,4.184
+844.603481,-75.319993,4.123,4.224
+843.987444,-80.291123,4.229,4.096
+843.122839,-85.228773,4.214,4.084
+842.017620,-90.119662,4.126,4.175
+840.679739,-94.950513,4.017,4.390
+839.117149,-99.708047,4.070,4.387
+837.337803,-104.378985,4.045,4.431
+835.352725,-108.954255,4.002,4.435
+833.184895,-113.441146,3.951,4.377
+830.860195,-117.850926,3.900,4.319
+828.404511,-122.194866,3.849,4.261
+825.843726,-126.484231,3.798,4.204
+823.203724,-130.730292,3.811,4.173
+820.510358,-134.944303,3.852,4.154
+817.783350,-139.135229,3.894,4.135
+815.029104,-143.307047,3.935,4.115
+812.252270,-147.463082,3.976,4.096
+809.457503,-151.606658,4.018,4.077
+806.649452,-155.741097,4.043,4.067
+803.832772,-159.869725,4.035,4.073
+801.012113,-163.995864,4.028,4.078
+798.192128,-168.122838,4.021,4.084
+795.377469,-172.253972,4.014,4.090
+792.572788,-176.392589,4.006,4.096
+789.782738,-180.542012,4.016,4.084
+787.011970,-184.705566,4.062,4.035
+784.265137,-188.886574,4.108,3.985
+781.548155,-193.088638,4.154,3.936
+778.871892,-197.316448,4.187,3.912
+776.248431,-201.574960,4.213,3.905
+773.689850,-205.869131,4.226,3.963
+771.208232,-210.203916,4.218,4.136
+768.815657,-214.584272,4.326,4.338
+766.524154,-219.015133,4.587,4.577
+764.335653,-223.497073,4.847,4.816
+762.229923,-228.021102,5.174,5.141
+760.183793,-232.576960,5.514,5.484
+758.174091,-237.154385,5.854,5.826
+756.177647,-241.743118,6.194,6.169
+754.171288,-246.332898,6.534,6.512
+752.132210,-250.913573,6.875,6.854
+750.051474,-255.479086,6.929,6.928
+747.937913,-260.028628,6.935,6.955
+745.801525,-264.561735,6.941,6.983
+743.652310,-269.077941,6.947,7.011
+741.500266,-273.576782,6.953,7.038
+739.355392,-278.057794,6.959,7.062
+737.227192,-282.522008,6.957,6.995
+735.117889,-286.992512,6.954,6.927
+733.024156,-291.509202,6.952,6.860
+730.942527,-296.112395,6.949,6.793
+728.838033,-300.796250,6.947,6.725
+726.550918,-305.372085,6.816,6.838
+723.890534,-309.605955,6.657,7.124
+720.690584,-313.287789,6.919,7.008
+716.962948,-316.382204,6.936,6.563
+712.798664,-318.931422,6.330,7.057
+708.289129,-320.978019,6.497,7.072
+703.525739,-322.564569,6.586,7.140
+698.599890,-323.733646,6.635,7.236
+693.602980,-324.527826,7.056,6.882
+688.609823,-324.991490,7.328,6.647
+683.631014,-325.176014,7.024,6.932
+678.661600,-325.134471,6.721,7.218
+673.696625,-324.919930,6.832,7.143
+668.731136,-324.585464,6.953,7.059
+663.760179,-324.184143,7.018,7.031
+658.778821,-323.768904,6.947,7.138
+653.786008,-323.367672,6.875,7.244
+648.789087,-322.954242,6.929,7.170
+643.796500,-322.495333,7.011,7.054
+638.816689,-321.957669,7.093,6.939
+633.858098,-321.307969,7.122,6.893
+628.929168,-320.512955,7.079,6.942
+624.038261,-319.539882,7.030,6.996
+619.190736,-318.375604,6.783,7.274
+614.388155,-317.031783,6.537,7.552
+609.631833,-315.521672,6.759,7.267
+604.923088,-313.858527,7.030,6.923
+600.263236,-312.055602,7.123,6.884
+595.653594,-310.126153,7.190,6.891
+591.095298,-308.081827,7.140,6.938
+586.588781,-305.928036,7.021,7.008
+582.134305,-303.668679,6.893,7.134
+577.732134,-301.307655,6.749,7.356
+573.382530,-298.848864,6.804,7.293
+569.085755,-296.296203,6.932,7.126
+564.842074,-293.653573,7.060,6.958
+560.651748,-290.924873,7.039,6.927
+556.515040,-288.114002,6.945,6.961
+552.432213,-285.224858,6.856,6.995
+548.403530,-282.261342,6.963,7.018
+544.429254,-279.227351,7.071,7.042
+540.509648,-276.126786,7.045,7.143
+536.646058,-272.962511,6.921,7.301
+532.879958,-269.699105,6.796,7.459
+529.303798,-266.252512,6.800,7.424
+526.013321,-262.535532,6.982,7.125
+523.106293,-258.472868,7.046,6.955
+520.695052,-254.074915,6.962,6.947
+518.898317,-249.389602,6.882,7.008
+517.832775,-244.470660,6.727,7.126
+517.585351,-239.453276,6.760,7.127
+518.220595,-234.533878,6.960,6.976
+519.802514,-229.910386,6.644,7.012
+522.334949,-225.730525,6.697,7.030
+525.588832,-221.947703,6.723,7.076
+529.278745,-218.468315,6.755,7.116
+533.145068,-215.207132,7.006,6.938
+537.111602,-212.138479,7.135,6.844
+541.181603,-209.262478,6.882,7.016
+545.358650,-206.579354,6.630,7.189
+549.646318,-204.089333,6.583,7.245
+554.048185,-201.792641,6.776,7.166
+558.567826,-199.689504,6.962,7.066
+563.205028,-197.787576,7.142,6.952
+567.944951,-196.123167,7.104,6.998
+572.769226,-194.739502,6.943,7.135
+577.659441,-193.677704,6.782,7.272
+582.595565,-192.902588,6.832,7.157
+587.555526,-192.282824,6.891,7.032
+592.517155,-191.681168,6.950,6.907
+597.464671,-191.000804,7.008,6.782
+602.396071,-190.232186,7.067,6.656
+607.311150,-189.377138,7.078,6.584
+612.209702,-188.437485,6.754,6.880
+617.091520,-187.415051,6.736,6.909
+621.956399,-186.311660,6.885,6.792
+626.804133,-185.129135,6.995,6.731
+631.634516,-183.869300,7.081,6.705
+636.447341,-182.533981,7.031,6.778
+641.242404,-181.125000,6.926,6.889
+646.019497,-179.644181,6.822,7.001
+650.778415,-178.093350,6.911,6.915
+655.518952,-176.474329,7.032,6.797
+660.238753,-174.785985,7.152,6.679
+664.927143,-173.015721,7.144,6.677
+669.571431,-171.148166,6.832,6.948
+674.158924,-169.167949,6.519,7.220
+678.676932,-167.059698,6.652,7.059
+683.112764,-164.808044,7.010,6.679
+687.453615,-162.397497,7.367,6.300
+691.665455,-159.790647,7.318,6.459
+695.668265,-156.902579,7.235,6.631
+699.376004,-153.642165,7.092,6.768
+702.706093,-149.926475,6.881,6.943
+705.625359,-145.789734,6.597,7.155
+708.137497,-141.353584,6.711,7.081
+710.246759,-136.740516,6.913,6.949
+711.945681,-132.028298,7.275,6.712
+713.212100,-127.238602,7.271,6.600
+714.022926,-122.389546,7.233,6.584
+714.355066,-117.499248,7.183,6.721
+714.185428,-112.585823,7.151,6.829
+713.490920,-107.667391,7.131,6.916
+712.250145,-102.766987,6.929,6.984
+710.465448,-97.976495,6.713,7.023
+708.156646,-93.438475,7.013,6.892
+705.343960,-89.296659,7.275,6.739
+702.055119,-85.657540,7.465,6.546
+698.346209,-82.486891,7.379,6.678
+694.280014,-79.717258,6.996,7.159
+689.919273,-77.291912,7.046,7.064
+685.326428,-75.228159,7.189,6.846
+680.563791,-73.574557,7.133,6.834
+675.693600,-72.379388,7.037,6.865
+670.765309,-71.627906,7.088,6.839
+665.801465,-71.172685,7.209,6.787
+660.821215,-70.849523,7.175,6.820
+655.840166,-70.524248,6.962,6.952
+650.860639,-70.175383,6.793,6.998
+645.881844,-69.807882,6.707,6.881
+640.902988,-69.426694,6.621,6.763
+635.923278,-69.036773,6.535,6.646
+630.941924,-68.643070,6.449,6.528
+625.958135,-68.250537,6.363,6.411
+620.971472,-67.864144,6.277,6.293
+615.982233,-67.488901,6.192,6.175
+610.990811,-67.129823,6.093,6.032
+605.997598,-66.791923,5.973,5.842
+601.002986,-66.480216,5.852,5.652
+596.007369,-66.199717,5.791,5.575
+591.011138,-65.955440,5.734,5.551
+586.014686,-65.752400,5.650,5.580
+581.018405,-65.595610,5.565,5.609
+576.022688,-65.490086,5.481,5.638
+571.027927,-65.440841,5.534,5.590
+566.034515,-65.452890,5.705,5.477
+561.042844,-65.531248,5.876,5.364
+556.053237,-65.679211,5.782,5.483
+551.065759,-65.893563,5.623,5.659
+546.080411,-66.169548,6.015,6.094
+541.097195,-66.502409,6.506,6.577
+536.116113,-66.887389,6.677,6.700
+531.137167,-67.319731,6.553,6.493
+526.160360,-67.794679,6.430,6.286
+521.185692,-68.307476,6.306,6.079
+516.213166,-68.853365,6.182,5.872
+511.242784,-69.427590,6.058,5.664
+506.274548,-70.025392,5.935,5.457
+501.308460,-70.642017,5.896,5.416
+496.344521,-71.272706,6.002,5.654
+491.382740,-71.912804,6.107,5.892
+486.423329,-72.561174,6.212,6.130
+481.466756,-73.221067,6.318,6.368
+476.513507,-73.896006,6.381,6.522
+471.564064,-74.589515,6.270,6.332
+466.618913,-75.305119,6.158,6.142
+461.678537,-76.046340,6.047,5.952
+456.743421,-76.816702,5.936,5.762
+451.814048,-77.619730,5.824,5.571
+446.890904,-78.458948,5.713,5.381
+441.974472,-79.337878,5.621,5.239
+437.065236,-80.260046,5.595,5.267
+432.163681,-81.228974,5.570,5.295
+427.270291,-82.248187,5.544,5.323
+422.385725,-83.321083,5.518,5.351
+417.511874,-84.450176,5.515,5.365
+412.651163,-85.637598,5.517,5.377
+407.806015,-86.885480,5.518,5.389
+402.978857,-88.195952,5.519,5.401
+398.172113,-89.571147,5.520,5.432
+393.388210,-91.013195,5.520,5.463
+388.624995,-92.511917,5.520,5.495
+383.862725,-94.009802,5.520,5.526
+379.077425,-95.437961,5.521,5.557
+374.245857,-96.729642,5.517,5.554
+369.371439,-97.895329,5.513,5.549
+364.491077,-99.042554,5.509,5.545
+359.643725,-100.284782,5.510,5.531
+354.853551,-101.692947,5.546,5.452
+350.112834,-103.246245,5.582,5.374
+345.409702,-104.911934,5.649,5.341
+340.732282,-106.657272,6.154,5.936
+336.068704,-108.449520,6.658,6.530
+331.407095,-110.255934,7.163,7.125
+326.735781,-112.044283,7.177,7.169
+322.050266,-113.800844,6.985,6.979
+317.355113,-115.535242,6.792,6.790
+312.655461,-117.258596,6.599,6.601
+307.956451,-118.982024,6.406,6.412
+303.263223,-120.716643,6.214,6.223
+298.580917,-122.473571,6.021,6.034
+293.913754,-124.262073,5.828,5.844
+289.262383,-126.084219,5.816,5.779
+284.626590,-127.940337,6.002,5.849
+280.006160,-129.830753,6.188,5.920
+275.400879,-131.755795,6.375,5.990
+270.810530,-133.715789,6.561,6.061
+266.234900,-135.711061,6.747,6.131
+261.673772,-137.741939,6.916,6.212
+257.126933,-139.808750,6.896,6.400
+252.594167,-141.911820,6.877,6.588
+248.075260,-144.051476,6.857,6.776
+243.569995,-146.228045,6.837,6.964
+239.078159,-148.441854,6.817,7.152
+234.599579,-150.693176,6.982,7.327
+230.135655,-152.980290,7.413,7.481
+225.689788,-155.298942,7.844,7.636
+221.265508,-157.644713,8.276,7.791
+216.866345,-160.013182,8.296,7.724
+212.495831,-162.399931,8.123,7.553
+208.157494,-164.800540,7.950,7.382
+203.853512,-167.212860,7.777,7.211
+199.566372,-169.667776,7.604,7.039
+195.263682,-172.221124,7.431,6.868
+190.912688,-174.929357,7.258,6.697
+186.487983,-177.724071,7.078,6.551
+181.992983,-180.047120,6.789,6.764
+177.438175,-181.220304,6.203,6.945
+172.870986,-180.656336,6.339,7.243
+168.602825,-178.417542,6.636,6.911
+165.010059,-174.878403,7.071,6.883
+162.122071,-170.618791,7.181,6.805
+159.750492,-166.150514,7.152,6.715
+157.558157,-161.658507,7.123,6.624
+155.189121,-157.286255,7.093,6.533
+152.359584,-153.167518,7.064,6.443
+149.057687,-149.399398,6.723,6.016
+145.335628,-146.070364,6.197,5.369
+141.245605,-143.268882,5.813,5.483
+136.839819,-141.083420,5.615,5.650
+132.170467,-139.602446,5.607,5.556
+127.289888,-138.914002,5.642,5.330
+122.272466,-139.038412,5.431,5.067
+117.238594,-139.854676,5.131,4.831
+112.314429,-141.224096,4.822,4.830
+107.623328,-143.018285,4.742,4.689
+103.250890,-145.248063,4.859,4.429
+99.255600,-148.024195,4.934,4.321
+95.696653,-151.455576,4.649,4.520
+92.675502,-155.519437,4.528,4.649
+90.344115,-160.035622,4.667,4.596
+88.856582,-164.815027,4.703,4.596
+88.243937,-169.712396,4.812,4.552
+88.286263,-174.671888,4.788,4.429
+88.733063,-179.648558,4.874,4.484
+89.374668,-184.606576,4.960,4.568
+90.149191,-189.543100,4.955,4.566
+91.028177,-194.462751,4.950,4.564
+91.983170,-199.370149,4.945,4.563
+92.985716,-204.269917,4.940,4.561
+94.007360,-209.166676,4.934,4.559
+95.019718,-214.065034,4.929,4.558
+96.004832,-218.967894,4.924,4.556
+96.965993,-223.874674,4.919,4.555
+97.909081,-228.784371,4.914,4.553
+98.839976,-233.695981,4.909,4.551
+99.764556,-238.608499,4.904,4.550
+100.688701,-243.520920,4.898,4.548
+101.618197,-248.432257,4.893,4.546
+102.555794,-253.342056,4.888,4.545
+103.500606,-258.250500,4.883,4.543
+104.451536,-263.157814,4.878,4.542
+105.407485,-268.064218,4.873,4.540
+106.367355,-272.969935,4.868,4.538
+107.330048,-277.875186,4.862,4.537
+108.294464,-282.780196,4.857,4.535
+109.259505,-287.685184,4.852,4.534
+110.224074,-292.590374,4.847,4.532
+111.187071,-297.495988,4.842,4.530
+112.147399,-302.402248,4.837,4.529
+113.103958,-307.309376,4.832,4.527
+114.055651,-312.217594,4.826,4.525
+115.001615,-317.127073,4.821,4.524
+115.942575,-322.037631,4.816,4.522
+116.879915,-326.948942,4.811,4.521
+117.815019,-331.860680,4.806,4.519
+118.749270,-336.772517,4.801,4.517
+119.684054,-341.684127,4.795,4.516
+120.620755,-346.595184,4.790,4.514
+121.560757,-351.505360,4.785,4.512
+122.505444,-356.414329,4.780,4.511
+123.456202,-361.321764,4.775,4.509
+124.414414,-366.227339,4.770,4.508
+125.381466,-371.130726,4.765,4.506
+126.358740,-376.031599,4.759,4.504
+127.347598,-380.929639,4.754,4.503
+128.345619,-385.825549,4.749,4.501
+129.342556,-390.722158,4.744,4.499
+130.327190,-395.622556,4.739,4.498
+131.288302,-400.529834,4.734,4.496
+132.214673,-405.447083,4.729,4.495
+133.095083,-410.377394,4.683,4.590
+133.917404,-415.323672,4.623,4.720
+134.638923,-420.282615,4.457,4.923
+135.179778,-425.243373,4.219,5.176
+135.457865,-430.194646,4.345,5.035
+135.391083,-435.125129,4.520,4.798
+134.897330,-440.023522,4.740,4.485
+133.894503,-444.878522,4.971,4.557
+132.336811,-449.670650,5.116,4.317
+130.312854,-454.350171,4.238,4.713
+127.942320,-458.860348,4.493,4.717
+125.341900,-463.144788,4.832,4.446
+122.528502,-467.158487,4.407,5.135
+119.398290,-470.870226,4.540,5.028
+115.840666,-474.249571,4.692,4.847
+111.815858,-477.260106,4.796,4.742
+107.429983,-479.853095,4.993,4.883
+102.807143,-481.978282,5.144,4.671
+98.066603,-483.592601,4.997,4.496
+93.262909,-484.749189,4.346,5.074
+88.404440,-485.569807,4.216,5.375
+83.498594,-486.176411,4.600,5.017
+78.553298,-486.648841,4.913,4.723
+73.577110,-487.016376,4.873,4.744
+68.578624,-487.305298,4.832,4.766
+63.566436,-487.541894,4.792,4.787
+58.549142,-487.752447,4.752,4.809
+53.535335,-487.963242,4.712,4.830
+48.533082,-488.198747,4.671,4.852
+43.543349,-488.459106,4.631,4.874
+38.562040,-488.727119,4.591,4.895
+33.584950,-488.985207,4.550,4.917
+28.607876,-489.215796,4.510,4.938
+23.626611,-489.401309,4.494,4.943
+18.636952,-489.524170,4.592,4.871
+13.635726,-489.567169,4.690,4.799
+8.626745,-489.515580,4.791,4.707
+3.616713,-489.355702,4.905,4.545
+-1.387655,-489.073840,4.987,4.419
+-6.379646,-488.656298,4.833,4.555
+-11.352545,-488.089378,4.681,4.685
+-16.299639,-487.359387,4.536,4.799
+-21.214299,-486.455141,4.463,4.853
+-26.090210,-485.374739,4.576,4.755
+-30.921130,-484.118421,4.582,4.768
+-35.700815,-482.686423,4.522,4.848
+-40.423024,-481.078986,4.498,4.905
+-45.081514,-479.296347,4.487,4.948
+-49.670044,-477.338752,4.568,4.879
+-54.182501,-475.207651,4.658,4.797
+-58.613044,-472.906981,4.765,4.683
+-62.955864,-470.440990,4.830,4.597
+-67.205151,-467.813926,4.720,4.624
+-71.355097,-465.030037,4.629,4.657
+-75.399894,-462.093570,4.584,4.703
+-79.333732,-459.008773,4.692,4.591
+-83.150802,-455.779894,4.790,4.495
+-86.845296,-452.411181,4.700,4.618
+-90.411405,-448.906882,4.501,4.828
+-93.843320,-445.271244,4.212,5.105
+-97.135232,-441.508515,4.502,4.728
+-100.281332,-437.622943,4.692,4.503
+-103.276256,-433.619225,4.728,4.508
+-106.116287,-429.503720,4.668,4.614
+-108.798092,-425.283176,4.513,4.820
+-111.318336,-420.964340,4.574,4.784
+-113.673685,-416.553957,4.674,4.702
+-115.860805,-412.058774,4.708,4.695
+-117.876362,-407.485538,4.717,4.717
+-119.717022,-402.840995,4.673,4.745
+-121.379450,-398.131892,4.621,4.775
+-122.860313,-393.364976,4.599,4.922
+-124.156277,-388.546993,4.577,5.032
+-125.264006,-383.684690,4.597,5.061
+-126.180169,-378.784813,4.804,4.887
+-126.901541,-373.854095,4.655,4.719
+-127.428676,-368.898797,4.516,4.593
+-127.766739,-363.924608,4.611,4.602
+-127.921173,-358.937177,4.759,4.548
+-127.897419,-353.942159,4.812,4.530
+-127.700923,-348.945204,4.747,4.569
+-127.337126,-343.951966,4.658,4.569
+-126.811472,-338.968095,4.573,4.609
+-126.129405,-333.999244,4.504,4.716
+-125.296367,-329.051065,4.458,4.741
+-124.317801,-324.129210,4.449,4.630
+-123.199151,-319.239331,4.425,4.558
+-121.945861,-314.387080,4.321,4.705
+-120.563372,-309.578109,4.473,4.754
+-119.058509,-304.816715,4.680,4.769
+-117.447588,-300.097872,4.829,4.771
+-115.750915,-295.412637,4.896,4.761
+-113.988811,-290.752052,4.861,4.736
+-112.181595,-286.107159,4.826,4.711
+-110.349589,-281.469002,4.791,4.686
+-108.513113,-276.828622,4.777,4.683
+-106.688459,-272.178978,4.764,4.680
+-104.876827,-267.520207,4.750,4.677
+-103.075877,-262.854130,4.737,4.675
+-101.283269,-258.182568,4.724,4.672
+-99.496665,-253.507342,4.711,4.669
+-97.713725,-248.830270,4.697,4.667
+-95.932114,-244.153172,4.684,4.664
+-94.150161,-239.477307,4.671,4.662
+-92.367593,-234.802761,4.658,4.659
+-90.584313,-230.129473,4.645,4.656
+-88.800227,-225.457379,4.631,4.654
+-87.015237,-220.786419,4.618,4.651
+-85.229248,-216.116530,4.605,4.648
+-83.442165,-211.447649,4.592,4.646
+-81.653892,-206.779715,4.578,4.643
+-79.864331,-202.112665,4.565,4.640
+-78.073389,-197.446438,4.552,4.638
+-76.280968,-192.780971,4.539,4.635
+-74.486973,-188.116201,4.526,4.632
+-72.691308,-183.452068,4.512,4.630
+-70.893901,-178.788517,4.499,4.627
+-69.094771,-174.125528,4.486,4.624
+-67.293958,-169.463089,4.473,4.622
+-65.491502,-164.801187,4.459,4.619
+-63.687444,-160.139812,4.446,4.617
+-61.881823,-155.478949,4.433,4.614
+-60.074681,-150.818588,4.420,4.611
+-58.266057,-146.158715,4.407,4.609
+-56.455991,-141.499319,4.393,4.606
+-54.644524,-136.840387,4.380,4.603
+-52.831697,-132.181907,4.367,4.601
+-51.017548,-127.523866,4.392,4.625
+-49.202119,-122.866254,4.417,4.649
+-47.385450,-118.209056,4.443,4.672
+-45.567581,-113.552261,4.468,4.696
+-43.748552,-108.895857,4.493,4.720
+-41.928403,-104.239831,4.518,4.744
+-40.107175,-99.584172,4.544,4.768
+-38.284908,-94.928866,4.569,4.792
+-36.461642,-90.273902,4.594,4.816
+-34.637418,-85.619267,4.619,4.840
+-32.812275,-80.964950,4.645,4.864
+-30.986254,-76.310937,4.670,4.888
+-29.159396,-71.657217,4.695,4.912
+-27.331740,-67.003778,4.720,4.936
+-25.503326,-62.350606,4.746,4.960
+-23.674195,-57.697691,4.771,4.984
+-21.844388,-53.045019,4.796,5.008
+-20.013943,-48.392578,4.821,5.032
+-18.182903,-43.740356,4.847,5.055
+-16.351306,-39.088341,4.872,5.079
+-14.519193,-34.436521,4.897,5.103
+-12.686605,-29.784883,4.923,5.127
+-10.853581,-25.133415,4.948,5.151
+-9.020162,-20.482105,4.973,5.175
+-7.186388,-15.830941,4.998,5.199
+-5.352300,-11.179909,5.024,5.223
+-3.517937,-6.528999,5.049,5.247

+ 1 - 1
Jolt/Physics/Constraints/ConstraintPart/SpringPart.h

@@ -47,7 +47,7 @@ public:
 
 
 			// Calculate bias factor (baumgarte stabilization):
 			// Calculate bias factor (baumgarte stabilization):
 			// beta = dt * k / (c + dt * k) = dt * k^2 * softness
 			// beta = dt * k / (c + dt * k) = dt * k^2 * softness
-			// b = beta / dt * C = dt * k * softness * C;
+			// b = beta / dt * C = dt * k * softness * C
 			mBias = inBias + inDeltaTime * k * mSoftness * inC;
 			mBias = inBias + inDeltaTime * k * mSoftness * inC;
 			
 			
 			// Update the effective mass, see post by Erin Catto: http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=4&t=1354
 			// Update the effective mass, see post by Erin Catto: http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=4&t=1354

+ 81 - 20
Jolt/Physics/Vehicle/VehicleConstraint.cpp

@@ -165,9 +165,9 @@ void VehicleConstraint::OnStep(float inDeltaTime, PhysicsSystem &inPhysicsSystem
 		w->mSuspensionLength = settings->mSuspensionMaxLength;
 		w->mSuspensionLength = settings->mSuspensionMaxLength;
 
 
 		// Test collision to find the floor
 		// Test collision to find the floor
-		RVec3 origin = mBody->GetCenterOfMassPosition() + mBody->GetRotation() * (settings->mPosition - mBody->GetShape()->GetCenterOfMass());
-		w->mWSDirection = mBody->GetRotation() * settings->mDirection;
-		if (mVehicleCollisionTester->Collide(inPhysicsSystem, *this, wheel_index, origin, w->mWSDirection, mBody->GetID(), w->mContactBody, w->mContactSubShapeID, w->mContactPosition, w->mContactNormal, w->mSuspensionLength))
+		RVec3 ws_origin = mBody->GetCenterOfMassPosition() + mBody->GetRotation() * (settings->mPosition - mBody->GetShape()->GetCenterOfMass());
+		Vec3 ws_direction = mBody->GetRotation() * settings->mDirection;
+		if (mVehicleCollisionTester->Collide(inPhysicsSystem, *this, wheel_index, ws_origin, ws_direction, mBody->GetID(), w->mContactBody, w->mContactSubShapeID, w->mContactPosition, w->mContactNormal, w->mSuspensionLength))
 		{
 		{
 			// Store ID (pointer is not valid outside of the simulation step)
 			// Store ID (pointer is not valid outside of the simulation step)
 			w->mContactBodyID = w->mContactBody->GetID();
 			w->mContactBodyID = w->mContactBody->GetID();
@@ -175,6 +175,9 @@ void VehicleConstraint::OnStep(float inDeltaTime, PhysicsSystem &inPhysicsSystem
 			// Store contact velocity, cache this as the contact body may be removed
 			// Store contact velocity, cache this as the contact body may be removed
 			w->mContactPointVelocity = w->mContactBody->GetPointVelocity(w->mContactPosition);
 			w->mContactPointVelocity = w->mContactBody->GetPointVelocity(w->mContactPosition);
 
 
+			// Determine plane constant for axle contact plane
+			w->mAxlePlaneConstant = RVec3(w->mContactNormal).Dot(ws_origin + w->mSuspensionLength * ws_direction);
+
 			// Check if body is active, if so the entire vehicle should be active
 			// Check if body is active, if so the entire vehicle should be active
 			mIsActive |= w->mContactBody->IsActive();
 			mIsActive |= w->mContactBody->IsActive();
 
 
@@ -324,19 +327,71 @@ void VehicleConstraint::SetupVelocityConstraint(float inDeltaTime)
 		{
 		{
 			const WheelSettings *settings = w->mSettings;
 			const WheelSettings *settings = w->mSettings;
 
 
+			Vec3 neg_contact_normal = -w->mContactNormal;
+
 			Vec3 r1_plus_u, r2;
 			Vec3 r1_plus_u, r2;
 			CalculateWheelContactPoint(*w, r1_plus_u, r2);
 			CalculateWheelContactPoint(*w, r1_plus_u, r2);
 
 
 			// Suspension spring
 			// Suspension spring
 			if (settings->mSuspensionMaxLength > settings->mSuspensionMinLength)
 			if (settings->mSuspensionMaxLength > settings->mSuspensionMinLength)
-				w->mSuspensionPart.CalculateConstraintProperties(inDeltaTime, *mBody, r1_plus_u, *w->mContactBody, r2, w->mWSDirection, w->mAntiRollBarImpulse, w->mSuspensionLength - settings->mSuspensionMaxLength - settings->mSuspensionPreloadLength, settings->mSuspensionFrequency, settings->mSuspensionDamping);
+			{
+				// Calculate the damping and frequency of the suspension spring given the angle between the suspension direction and the contact normal
+				// If the angle between the suspension direction and the inverse of the contact normal is alpha then the force on the spring relates to the force along the contact normal as:
+				// 
+				// Fspring = Fnormal * cos(alpha)
+				//
+				// The spring force is:
+				//
+				// Fspring = -k * x
+				//
+				// where k is the spring constant and x is the displacement of the spring. So we have:
+				//
+				// Fnormal * cos(alpha) = -k * x <=> Fnormal = -k / cos(alpha) * x
+				//
+				// So we can see this as a spring with spring constant:
+				//
+				// k' = k / cos(alpha)
+				// 
+				// In the same way the velocity relates like:
+				// 
+				// Vspring = Vnormal * cos(alpha)
+				//
+				// Which results in the modified damping constant c:
+				//
+				// c' = c / cos(alpha)
+				//
+				// Since we're not supplying k and c directly but rather the frequency and damping we can calculate the spring constant and damping constant as:
+				//
+				// w = 2 * pi * f
+				// k = m * w^2
+				// c = 2 * m * w * d
+				//
+				// where m is the mass of the spring, f is the frequency and d is the damping factor (see SpringPart::CalculateSpringProperties). So we have:
+				//
+				// w' = w * pi * f'
+				// k' = m * w'^2
+				// c' = 2 * m * w' * d'
+				//
+				// where f' = f / sqrt(cos(alpha)) and d' = d / sqrt(cos(alpha))
+				//
+				// Note that we clamp 1 / cos(alpha) to the range [0.1, 1] in order not to increase the stiffness / damping by too much.
+				// We also ensure that the frequency doesn't go over half the simulation frequency to prevent the spring from getting unstable.
+				Vec3 ws_direction = body_transform.Multiply3x3(settings->mDirection);
+				float sqrt_cos_angle = sqrt(max(0.1f, ws_direction.Dot(neg_contact_normal)));
+				float damping = settings->mSuspensionDamping / sqrt_cos_angle;
+				float frequency = min(0.5f / inDeltaTime, settings->mSuspensionFrequency / sqrt_cos_angle);
+
+				// Get the value of the constraint equation
+				float c = w->mSuspensionLength - settings->mSuspensionMaxLength - settings->mSuspensionPreloadLength;
+
+				w->mSuspensionPart.CalculateConstraintProperties(inDeltaTime, *mBody, r1_plus_u, *w->mContactBody, r2, neg_contact_normal, w->mAntiRollBarImpulse, c, frequency, damping);
+			}
 			else
 			else
 				w->mSuspensionPart.Deactivate();
 				w->mSuspensionPart.Deactivate();
 
 
-			// Check if we reached the 'max up' position
-			float max_up_error = w->mSuspensionLength - settings->mSuspensionMinLength;
-			if (max_up_error < 0.0f)
-				w->mSuspensionMaxUpPart.CalculateConstraintProperties(inDeltaTime, *mBody, r1_plus_u, *w->mContactBody, r2, w->mWSDirection, 0.0f, max_up_error);
+			// Check if we reached the 'max up' position and if so add a hard velocity constraint that stops any further movement in the normal direction
+			if (w->mSuspensionLength < settings->mSuspensionMinLength)
+				w->mSuspensionMaxUpPart.CalculateConstraintProperties(inDeltaTime, *mBody, r1_plus_u, *w->mContactBody, r2, neg_contact_normal);
 			else
 			else
 				w->mSuspensionMaxUpPart.Deactivate();
 				w->mSuspensionMaxUpPart.Deactivate();
 			
 			
@@ -361,8 +416,10 @@ void VehicleConstraint::WarmStartVelocityConstraint(float inWarmStartImpulseRati
 	for (Wheel *w : mWheels)
 	for (Wheel *w : mWheels)
 		if (w->mContactBody != nullptr)
 		if (w->mContactBody != nullptr)
 		{
 		{
-			w->mSuspensionPart.WarmStart(*mBody, *w->mContactBody, w->mWSDirection, inWarmStartImpulseRatio);
-			w->mSuspensionMaxUpPart.WarmStart(*mBody, *w->mContactBody, w->mWSDirection, inWarmStartImpulseRatio);
+			Vec3 neg_contact_normal = -w->mContactNormal;
+
+			w->mSuspensionPart.WarmStart(*mBody, *w->mContactBody, neg_contact_normal, inWarmStartImpulseRatio);
+			w->mSuspensionMaxUpPart.WarmStart(*mBody, *w->mContactBody, neg_contact_normal, inWarmStartImpulseRatio);
 			w->mLongitudinalPart.WarmStart(*mBody, *w->mContactBody, -w->mContactLongitudinal, 0.0f); // Don't warm start the longitudinal part (the engine/brake force, we don't want to preserve anything from the last frame)
 			w->mLongitudinalPart.WarmStart(*mBody, *w->mContactBody, -w->mContactLongitudinal, 0.0f); // Don't warm start the longitudinal part (the engine/brake force, we don't want to preserve anything from the last frame)
 			w->mLateralPart.WarmStart(*mBody, *w->mContactBody, -w->mContactLateral, inWarmStartImpulseRatio);	
 			w->mLateralPart.WarmStart(*mBody, *w->mContactBody, -w->mContactLateral, inWarmStartImpulseRatio);	
 		}
 		}
@@ -378,13 +435,15 @@ bool VehicleConstraint::SolveVelocityConstraint(float inDeltaTime)
 	for (Wheel *w : mWheels)
 	for (Wheel *w : mWheels)
 		if (w->mContactBody != nullptr)
 		if (w->mContactBody != nullptr)
 		{
 		{
+			Vec3 neg_contact_normal = -w->mContactNormal;
+
 			// Suspension spring, note that it can only push and not pull
 			// Suspension spring, note that it can only push and not pull
 			if (w->mSuspensionPart.IsActive())
 			if (w->mSuspensionPart.IsActive())
-				impulse |= w->mSuspensionPart.SolveVelocityConstraint(*mBody, *w->mContactBody, w->mWSDirection, 0.0f, FLT_MAX);
+				impulse |= w->mSuspensionPart.SolveVelocityConstraint(*mBody, *w->mContactBody, neg_contact_normal, 0.0f, FLT_MAX);
 
 
 			// When reaching the minimal suspension length only allow forces pushing the bodies away
 			// When reaching the minimal suspension length only allow forces pushing the bodies away
 			if (w->mSuspensionMaxUpPart.IsActive())
 			if (w->mSuspensionMaxUpPart.IsActive())
-				impulse |= w->mSuspensionMaxUpPart.SolveVelocityConstraint(*mBody, *w->mContactBody, w->mWSDirection, 0.0f, FLT_MAX);
+				impulse |= w->mSuspensionMaxUpPart.SolveVelocityConstraint(*mBody, *w->mContactBody, neg_contact_normal, 0.0f, FLT_MAX);
 		}
 		}
 
 
 	// Solve the horizontal movement of the vehicle
 	// Solve the horizontal movement of the vehicle
@@ -408,22 +467,24 @@ bool VehicleConstraint::SolvePositionConstraint(float inDeltaTime, float inBaumg
 		{
 		{
 			const WheelSettings *settings = w->mSettings;
 			const WheelSettings *settings = w->mSettings;
 
 
-			// Calculate new contact length as the body may have moved
-			// TODO: This assumes that only the vehicle moved and not the ground (contact point/normal is stored in world space)
+			// Check if we reached the 'max up' position now that the body has possibly moved
+			// We do this by calculating the axle position at minimum suspension length and making sure it does not go through the
+			// plane defined by the contact normal and the axle position when the contact happened
+			// TODO: This assumes that only the vehicle moved and not the ground as we kept the axle contact plane in world space
 			Vec3 ws_direction = body_transform.Multiply3x3(settings->mDirection);
 			Vec3 ws_direction = body_transform.Multiply3x3(settings->mDirection);
 			RVec3 ws_position = body_transform * settings->mPosition;
 			RVec3 ws_position = body_transform * settings->mPosition;
-			float contact_length = Vec3(w->mContactPosition - ws_position).Dot(ws_direction);
-
-			// Check if we reached the 'max up' position
-			float max_up_error = contact_length - settings->mRadius - settings->mSuspensionMinLength;
+			RVec3 min_suspension_pos = ws_position + settings->mSuspensionMinLength * ws_direction;
+			float max_up_error = float(RVec3(w->mContactNormal).Dot(min_suspension_pos) - w->mAxlePlaneConstant);
 			if (max_up_error < 0.0f)
 			if (max_up_error < 0.0f)
 			{
 			{
+				Vec3 neg_contact_normal = -w->mContactNormal;
+
 				// Recalculate constraint properties since the body may have moved
 				// Recalculate constraint properties since the body may have moved
 				Vec3 r1_plus_u, r2;
 				Vec3 r1_plus_u, r2;
 				CalculateWheelContactPoint(*w, r1_plus_u, r2);
 				CalculateWheelContactPoint(*w, r1_plus_u, r2);
-				w->mSuspensionMaxUpPart.CalculateConstraintProperties(inDeltaTime, *mBody, r1_plus_u, *w->mContactBody, r2, ws_direction, 0.0f, max_up_error);
+				w->mSuspensionMaxUpPart.CalculateConstraintProperties(inDeltaTime, *mBody, r1_plus_u, *w->mContactBody, r2, neg_contact_normal, 0.0f, max_up_error);
 
 
-				impulse |= w->mSuspensionMaxUpPart.SolvePositionConstraint(*mBody, *w->mContactBody, ws_direction, max_up_error, inBaumgarte);
+				impulse |= w->mSuspensionMaxUpPart.SolvePositionConstraint(*mBody, *w->mContactBody, neg_contact_normal, max_up_error, inBaumgarte);
 			}
 			}
 		}
 		}
 
 

+ 2 - 2
Jolt/Physics/Vehicle/Wheel.h

@@ -126,14 +126,14 @@ protected:
 	Vec3					mContactNormal;								///< Normal of the contact point between wheel and ground
 	Vec3					mContactNormal;								///< Normal of the contact point between wheel and ground
 	Vec3					mContactLongitudinal;						///< Vector perpendicular to normal in the forward direction
 	Vec3					mContactLongitudinal;						///< Vector perpendicular to normal in the forward direction
 	Vec3					mContactLateral;							///< Vector perpendicular to normal and longitudinal direction in the right direction
 	Vec3					mContactLateral;							///< Vector perpendicular to normal and longitudinal direction in the right direction
-	Vec3					mWSDirection;								///< Suspension spring direction in world space
+	Real					mAxlePlaneConstant;							///< Constant for the contact plane of the axle, defined as ContactNormal . (WorldSpaceSuspensionPoint + SuspensionLength * WorldSpaceSuspensionDirection)
 	float					mAntiRollBarImpulse = 0.0f;					///< Amount of impulse applied to the suspension from the anti-rollbars
 	float					mAntiRollBarImpulse = 0.0f;					///< Amount of impulse applied to the suspension from the anti-rollbars
 
 
 	float					mSteerAngle = 0.0f;							///< Rotation around the suspension direction, positive is to the left
 	float					mSteerAngle = 0.0f;							///< Rotation around the suspension direction, positive is to the left
 	float					mAngularVelocity = 0.0f;					///< Rotation speed of wheel, positive when the wheels cause the vehicle to move forwards (rad/s)
 	float					mAngularVelocity = 0.0f;					///< Rotation speed of wheel, positive when the wheels cause the vehicle to move forwards (rad/s)
 	float					mAngle = 0.0f;								///< Current rotation of the wheel (rad, [0, 2 pi])
 	float					mAngle = 0.0f;								///< Current rotation of the wheel (rad, [0, 2 pi])
 
 
-	AxisConstraintPart		mSuspensionPart;							///< Controls movement up/down
+	AxisConstraintPart		mSuspensionPart;							///< Controls movement up/down along the contact normal
 	AxisConstraintPart		mSuspensionMaxUpPart;						///< Adds a hard limit when reaching the minimal suspension length
 	AxisConstraintPart		mSuspensionMaxUpPart;						///< Adds a hard limit when reaching the minimal suspension length
 	AxisConstraintPart		mLongitudinalPart;							///< Controls movement forward/backward
 	AxisConstraintPart		mLongitudinalPart;							///< Controls movement forward/backward
 	AxisConstraintPart		mLateralPart;								///< Controls movement sideways (slip)
 	AxisConstraintPart		mLateralPart;								///< Controls movement sideways (slip)

+ 16 - 7
Jolt/Physics/Vehicle/WheeledVehicleController.cpp

@@ -638,6 +638,8 @@ bool WheeledVehicleController::SolveLongitudinalAndLateralConstraints(float inDe
 
 
 void WheeledVehicleController::Draw(DebugRenderer *inRenderer) const 
 void WheeledVehicleController::Draw(DebugRenderer *inRenderer) const 
 {
 {
+	float constraint_size = mConstraint.GetDrawConstraintSize();
+
 	// Draw RPM
 	// Draw RPM
 	Body *body = mConstraint.GetVehicleBody();
 	Body *body = mConstraint.GetVehicleBody();
 	Vec3 rpm_meter_up = body->GetRotation() * mConstraint.GetLocalUp();
 	Vec3 rpm_meter_up = body->GetRotation() * mConstraint.GetLocalUp();
@@ -650,7 +652,7 @@ void WheeledVehicleController::Draw(DebugRenderer *inRenderer) const
 								 "Gear: %d, Clutch: %.1f\nEngineRPM: %.0f, V: %.1f km/h", 
 								 "Gear: %d, Clutch: %.1f\nEngineRPM: %.0f, V: %.1f km/h", 
 								 (double)mForwardInput, (double)mRightInput, (double)mBrakeInput, (double)mHandBrakeInput, 
 								 (double)mForwardInput, (double)mRightInput, (double)mBrakeInput, (double)mHandBrakeInput, 
 								 mTransmission.GetCurrentGear(), (double)mTransmission.GetClutchFriction(), (double)mEngine.GetCurrentRPM(), (double)body->GetLinearVelocity().Length() * 3.6);
 								 mTransmission.GetCurrentGear(), (double)mTransmission.GetClutchFriction(), (double)mEngine.GetCurrentRPM(), (double)body->GetLinearVelocity().Length() * 3.6);
-	inRenderer->DrawText3D(body->GetPosition(), status, Color::sWhite, mConstraint.GetDrawConstraintSize());
+	inRenderer->DrawText3D(body->GetPosition(), status, Color::sWhite, constraint_size);
 
 
 	for (const Wheel *w_base : mConstraint.GetWheels())
 	for (const Wheel *w_base : mConstraint.GetWheels())
 	{
 	{
@@ -659,24 +661,31 @@ void WheeledVehicleController::Draw(DebugRenderer *inRenderer) const
 
 
 		// Calculate where the suspension attaches to the body in world space
 		// Calculate where the suspension attaches to the body in world space
 		RVec3 ws_position = body->GetCenterOfMassPosition() + body->GetRotation() * (settings->mPosition - body->GetShape()->GetCenterOfMass());
 		RVec3 ws_position = body->GetCenterOfMassPosition() + body->GetRotation() * (settings->mPosition - body->GetShape()->GetCenterOfMass());
+		Vec3 ws_direction = body->GetRotation() * settings->mDirection;
+
+		// Draw suspension
+		RVec3 min_suspension_pos = ws_position + ws_direction * settings->mSuspensionMinLength;
+		RVec3 max_suspension_pos = ws_position + ws_direction * settings->mSuspensionMaxLength;
+		inRenderer->DrawLine(ws_position, min_suspension_pos, Color::sRed);
+		inRenderer->DrawLine(min_suspension_pos, max_suspension_pos, Color::sGreen);
+
+		// Draw current length
+		RVec3 cur_suspension_pos = ws_position + ws_direction * w->GetSuspensionLength();
+		inRenderer->DrawMarker(cur_suspension_pos, w->GetSuspensionLength() < settings->mSuspensionMinLength? Color::sRed : Color::sGreen, constraint_size);
 
 
 		if (w->HasContact())
 		if (w->HasContact())
 		{
 		{
 			// Draw contact
 			// Draw contact
-			inRenderer->DrawLine(ws_position, w->GetContactPosition(), w->HasHitHardPoint()? Color::sRed : Color::sGreen); // Red if we hit the 'max up' limit
 			inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactNormal(), Color::sYellow);
 			inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactNormal(), Color::sYellow);
 			inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLongitudinal(), Color::sRed);
 			inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLongitudinal(), Color::sRed);
 			inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLateral(), Color::sBlue);
 			inRenderer->DrawLine(w->GetContactPosition(), w->GetContactPosition() + w->GetContactLateral(), Color::sBlue);
 
 
-			DebugRenderer::sInstance->DrawText3D(w->GetContactPosition(), StringFormat("W: %.1f, S: %.2f\nSlip: %.2f, FrLateral: %.1f, FrLong: %.1f", (double)w->GetAngularVelocity(), (double)w->GetSuspensionLength(), (double)w->mLongitudinalSlip, (double)w->mCombinedLateralFriction, (double)w->mCombinedLongitudinalFriction), Color::sWhite, 0.1f);
+			DebugRenderer::sInstance->DrawText3D(cur_suspension_pos, StringFormat("W: %.1f, S: %.2f\nSlip: %.2f, FrLateral: %.1f, FrLong: %.1f", (double)w->GetAngularVelocity(), (double)w->GetSuspensionLength(), (double)w->mLongitudinalSlip, (double)w->mCombinedLateralFriction, (double)w->mCombinedLongitudinalFriction), Color::sWhite, constraint_size);
 		}
 		}
 		else
 		else
 		{
 		{
 			// Draw 'no hit'
 			// Draw 'no hit'
-			Vec3 max_droop = body->GetRotation() * settings->mDirection * (settings->mSuspensionMaxLength + settings->mRadius);
-			inRenderer->DrawLine(ws_position, ws_position + max_droop, Color::sYellow);
-
-			DebugRenderer::sInstance->DrawText3D(ws_position + max_droop, StringFormat("W: %.1f", (double)w->GetAngularVelocity()), Color::sRed, 0.1f);
+			DebugRenderer::sInstance->DrawText3D(cur_suspension_pos, StringFormat("W: %.1f", (double)w->GetAngularVelocity()), Color::sRed, constraint_size);
 		}
 		}
 	}
 	}
 }
 }

+ 2 - 0
Samples/Tests/Vehicle/TankTest.cpp

@@ -164,6 +164,8 @@ void TankTest::Initialize()
 
 
 void TankTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
 void TankTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
 {
 {
+	VehicleTest::PrePhysicsUpdate(inParams);
+
 	const float min_velocity_pivot_turn = 1.0f;
 	const float min_velocity_pivot_turn = 1.0f;
 
 
 	const float bullet_radius = 0.061f; // 120 mm
 	const float bullet_radius = 0.061f; // 120 mm

+ 48 - 11
Samples/Tests/Vehicle/VehicleConstraintTest.cpp

@@ -32,9 +32,6 @@ void VehicleConstraintTest::Initialize()
 	const float half_vehicle_length = 2.0f;
 	const float half_vehicle_length = 2.0f;
 	const float half_vehicle_width = 0.9f;
 	const float half_vehicle_width = 0.9f;
 	const float half_vehicle_height = 0.2f;
 	const float half_vehicle_height = 0.2f;
-	const float suspension_min_length = 0.3f;
-	const float suspension_max_length = 0.5f;
-	const float max_steering_angle = DegreesToRadians(30);
 
 
 	// Create collision testers
 	// Create collision testers
 	mTesters[0] = new VehicleCollisionTesterRay(Layers::MOVING);
 	mTesters[0] = new VehicleCollisionTesterRay(Layers::MOVING);
@@ -44,7 +41,7 @@ void VehicleConstraintTest::Initialize()
 	// Create vehicle body
 	// Create vehicle body
 	RVec3 position(0, 2, 0);
 	RVec3 position(0, 2, 0);
 	RefConst<Shape> car_shape = OffsetCenterOfMassShapeSettings(Vec3(0, -half_vehicle_height, 0), new BoxShape(Vec3(half_vehicle_width, half_vehicle_height, half_vehicle_length))).Create().Get();
 	RefConst<Shape> car_shape = OffsetCenterOfMassShapeSettings(Vec3(0, -half_vehicle_height, 0), new BoxShape(Vec3(half_vehicle_width, half_vehicle_height, half_vehicle_length))).Create().Get();
-	BodyCreationSettings car_body_settings(car_shape, position, Quat::sIdentity(), EMotionType::Dynamic, Layers::MOVING);
+	BodyCreationSettings car_body_settings(car_shape, position, Quat::sRotation(Vec3::sAxisZ(), sInitialRollAngle), EMotionType::Dynamic, Layers::MOVING);
 	car_body_settings.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
 	car_body_settings.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
 	car_body_settings.mMassPropertiesOverride.mMass = 1500.0f;
 	car_body_settings.mMassPropertiesOverride.mMass = 1500.0f;
 	mCarBody = mBodyInterface->CreateBody(car_body_settings);
 	mCarBody = mBodyInterface->CreateBody(car_body_settings);
@@ -53,25 +50,49 @@ void VehicleConstraintTest::Initialize()
 	// Create vehicle constraint
 	// Create vehicle constraint
 	VehicleConstraintSettings vehicle;
 	VehicleConstraintSettings vehicle;
 	vehicle.mDrawConstraintSize = 0.1f;
 	vehicle.mDrawConstraintSize = 0.1f;
-	vehicle.mMaxPitchRollAngle = DegreesToRadians(60.0f);
+	vehicle.mMaxPitchRollAngle = sMaxRollAngle;
+
+	// Suspension direction
+	Vec3 front_dir = Vec3(Tan(sFrontSuspensionKinPinAngle), -1, Tan(sFrontSuspensionCasterAngle)).Normalized();
+	Vec3 rear_dir = Vec3(Tan(sRearSuspensionKingPinAngle), -1, Tan(sRearSuspensionCasterAngle)).Normalized();
 
 
 	// Wheels
 	// Wheels
 	WheelSettingsWV *w1 = new WheelSettingsWV;
 	WheelSettingsWV *w1 = new WheelSettingsWV;
 	w1->mPosition = Vec3(half_vehicle_width, -0.9f * half_vehicle_height, half_vehicle_length - 2.0f * wheel_radius);
 	w1->mPosition = Vec3(half_vehicle_width, -0.9f * half_vehicle_height, half_vehicle_length - 2.0f * wheel_radius);
-	w1->mMaxSteerAngle = max_steering_angle;
+	w1->mDirection = front_dir;
+	w1->mSuspensionMinLength = sFrontSuspensionMinLength;
+	w1->mSuspensionMaxLength = sFrontSuspensionMaxLength;
+	w1->mSuspensionFrequency = sFrontSuspensionFrequency;
+	w1->mSuspensionDamping = sFrontSuspensionDamping;
+	w1->mMaxSteerAngle = sMaxSteeringAngle;
 	w1->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
 	w1->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
 
 
 	WheelSettingsWV *w2 = new WheelSettingsWV;
 	WheelSettingsWV *w2 = new WheelSettingsWV;
 	w2->mPosition = Vec3(-half_vehicle_width, -0.9f * half_vehicle_height, half_vehicle_length - 2.0f * wheel_radius);
 	w2->mPosition = Vec3(-half_vehicle_width, -0.9f * half_vehicle_height, half_vehicle_length - 2.0f * wheel_radius);
-	w2->mMaxSteerAngle = max_steering_angle;
+	w2->mDirection = Vec3(-1, 1, 1) * front_dir;
+	w2->mSuspensionMinLength = sFrontSuspensionMinLength;
+	w2->mSuspensionMaxLength = sFrontSuspensionMaxLength;
+	w2->mSuspensionFrequency = sFrontSuspensionFrequency;
+	w2->mSuspensionDamping = sFrontSuspensionDamping;
+	w2->mMaxSteerAngle = sMaxSteeringAngle;
 	w2->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
 	w2->mMaxHandBrakeTorque = 0.0f; // Front wheel doesn't have hand brake
 
 
 	WheelSettingsWV *w3 = new WheelSettingsWV;
 	WheelSettingsWV *w3 = new WheelSettingsWV;
 	w3->mPosition = Vec3(half_vehicle_width, -0.9f * half_vehicle_height, -half_vehicle_length + 2.0f * wheel_radius);
 	w3->mPosition = Vec3(half_vehicle_width, -0.9f * half_vehicle_height, -half_vehicle_length + 2.0f * wheel_radius);
+	w3->mDirection = rear_dir;
+	w3->mSuspensionMinLength = sRearSuspensionMinLength;
+	w3->mSuspensionMaxLength = sRearSuspensionMaxLength;
+	w3->mSuspensionFrequency = sRearSuspensionFrequency;
+	w3->mSuspensionDamping = sRearSuspensionDamping;
 	w3->mMaxSteerAngle = 0.0f;
 	w3->mMaxSteerAngle = 0.0f;
 
 
 	WheelSettingsWV *w4 = new WheelSettingsWV;
 	WheelSettingsWV *w4 = new WheelSettingsWV;
 	w4->mPosition = Vec3(-half_vehicle_width, -0.9f * half_vehicle_height, -half_vehicle_length + 2.0f * wheel_radius);
 	w4->mPosition = Vec3(-half_vehicle_width, -0.9f * half_vehicle_height, -half_vehicle_length + 2.0f * wheel_radius);
+	w4->mDirection = Vec3(-1, 1, 1) * rear_dir;
+	w4->mSuspensionMinLength = sRearSuspensionMinLength;
+	w4->mSuspensionMaxLength = sRearSuspensionMaxLength;
+	w4->mSuspensionFrequency = sRearSuspensionFrequency;
+	w4->mSuspensionDamping = sRearSuspensionDamping;
 	w4->mMaxSteerAngle = 0.0f;
 	w4->mMaxSteerAngle = 0.0f;
 
 
 	vehicle.mWheels = { w1, w2, w3, w4 };
 	vehicle.mWheels = { w1, w2, w3, w4 };
@@ -80,8 +101,6 @@ void VehicleConstraintTest::Initialize()
 	{
 	{
 		w->mRadius = wheel_radius;
 		w->mRadius = wheel_radius;
 		w->mWidth = wheel_width;
 		w->mWidth = wheel_width;
-		w->mSuspensionMinLength = suspension_min_length;
-		w->mSuspensionMaxLength = suspension_max_length;
 	}
 	}
 
 
 	WheeledVehicleControllerSettings *controller = new WheeledVehicleControllerSettings;
 	WheeledVehicleControllerSettings *controller = new WheeledVehicleControllerSettings;
@@ -117,6 +136,8 @@ void VehicleConstraintTest::Initialize()
 
 
 void VehicleConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
 void VehicleConstraintTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
 {
 {
+	VehicleTest::PrePhysicsUpdate(inParams);
+
 	// Determine acceleration and brake
 	// Determine acceleration and brake
 	float forward = 0.0f, right = 0.0f, brake = 0.0f, hand_brake = 0.0f;
 	float forward = 0.0f, right = 0.0f, brake = 0.0f, hand_brake = 0.0f;
 	if (inParams.mKeyboard->IsKeyPressed(DIK_UP))
 	if (inParams.mKeyboard->IsKeyPressed(DIK_UP))
@@ -213,10 +234,26 @@ void VehicleConstraintTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMe
 {
 {
 	VehicleTest::CreateSettingsMenu(inUI, inSubMenu);
 	VehicleTest::CreateSettingsMenu(inUI, inSubMenu);
 
 
+	inUI->CreateSlider(inSubMenu, "Initial Roll Angle", RadiansToDegrees(sInitialRollAngle), 0.0f, 90.0f, 0.1f, [](float inValue) { sInitialRollAngle = DegreesToRadians(inValue); });
+	inUI->CreateSlider(inSubMenu, "Max Roll Angle", RadiansToDegrees(sMaxRollAngle), 0.0f, 90.0f, 0.1f, [](float inValue) { sMaxRollAngle = DegreesToRadians(inValue); });
+	inUI->CreateSlider(inSubMenu, "Max Steering Angle", RadiansToDegrees(sMaxSteeringAngle), 0.0f, 90.0f, 0.1f, [](float inValue) { sMaxSteeringAngle = DegreesToRadians(inValue); });
 	inUI->CreateComboBox(inSubMenu, "Collision Mode", { "Ray", "Cast Sphere", "Cast Cylinder" }, sCollisionMode, [](int inItem) { sCollisionMode = inItem; });
 	inUI->CreateComboBox(inSubMenu, "Collision Mode", { "Ray", "Cast Sphere", "Cast Cylinder" }, sCollisionMode, [](int inItem) { sCollisionMode = inItem; });
-	inUI->CreateCheckBox(inSubMenu, "4 Wheel Drive", sFourWheelDrive, [this](UICheckBox::EState inState) { sFourWheelDrive = inState == UICheckBox::STATE_CHECKED; RestartTest(); });
-	inUI->CreateCheckBox(inSubMenu, "Anti Rollbars", sAntiRollbar, [this](UICheckBox::EState inState) { sAntiRollbar = inState == UICheckBox::STATE_CHECKED; RestartTest(); });
+	inUI->CreateCheckBox(inSubMenu, "4 Wheel Drive", sFourWheelDrive, [](UICheckBox::EState inState) { sFourWheelDrive = inState == UICheckBox::STATE_CHECKED; });
+	inUI->CreateCheckBox(inSubMenu, "Anti Rollbars", sAntiRollbar, [](UICheckBox::EState inState) { sAntiRollbar = inState == UICheckBox::STATE_CHECKED; });
 	inUI->CreateCheckBox(inSubMenu, "Limited Slip Differentials", sLimitedSlipDifferentials, [](UICheckBox::EState inState) { sLimitedSlipDifferentials = inState == UICheckBox::STATE_CHECKED; });
 	inUI->CreateCheckBox(inSubMenu, "Limited Slip Differentials", sLimitedSlipDifferentials, [](UICheckBox::EState inState) { sLimitedSlipDifferentials = inState == UICheckBox::STATE_CHECKED; });
 	inUI->CreateSlider(inSubMenu, "Max Engine Torque", float(sMaxEngineTorque), 100.0f, 2000.0f, 10.0f, [](float inValue) { sMaxEngineTorque = inValue; });
 	inUI->CreateSlider(inSubMenu, "Max Engine Torque", float(sMaxEngineTorque), 100.0f, 2000.0f, 10.0f, [](float inValue) { sMaxEngineTorque = inValue; });
 	inUI->CreateSlider(inSubMenu, "Clutch Strength", float(sClutchStrength), 1.0f, 40.0f, 1.0f, [](float inValue) { sClutchStrength = inValue; });
 	inUI->CreateSlider(inSubMenu, "Clutch Strength", float(sClutchStrength), 1.0f, 40.0f, 1.0f, [](float inValue) { sClutchStrength = inValue; });
+	inUI->CreateSlider(inSubMenu, "Front Suspension Caster Angle", RadiansToDegrees(sFrontSuspensionCasterAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sFrontSuspensionCasterAngle = DegreesToRadians(inValue); });
+	inUI->CreateSlider(inSubMenu, "Front Suspension King Pin Angle", RadiansToDegrees(sFrontSuspensionKinPinAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sFrontSuspensionKinPinAngle = DegreesToRadians(inValue); });
+	inUI->CreateSlider(inSubMenu, "Front Suspension Min Length", sFrontSuspensionMinLength, 0.0f, 3.0f, 0.01f, [](float inValue) { sFrontSuspensionMinLength = inValue; });
+	inUI->CreateSlider(inSubMenu, "Front Suspension Max Length", sFrontSuspensionMaxLength, 0.0f, 3.0f, 0.01f, [](float inValue) { sFrontSuspensionMaxLength = inValue; });
+	inUI->CreateSlider(inSubMenu, "Front Suspension Frequency", sFrontSuspensionFrequency, 0.1f, 5.0f, 0.01f, [](float inValue) { sFrontSuspensionFrequency = inValue; });
+	inUI->CreateSlider(inSubMenu, "Front Suspension Damping", sFrontSuspensionDamping, 0.0f, 2.0f, 0.01f, [](float inValue) { sFrontSuspensionDamping = inValue; });
+	inUI->CreateSlider(inSubMenu, "Rear Suspension Caster Angle", RadiansToDegrees(sRearSuspensionCasterAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sRearSuspensionCasterAngle = DegreesToRadians(inValue); });
+	inUI->CreateSlider(inSubMenu, "Rear Suspension King Pin Angle", RadiansToDegrees(sRearSuspensionKingPinAngle), -89.0f, 89.0f, 1.0f, [](float inValue) { sRearSuspensionKingPinAngle = DegreesToRadians(inValue); });
+	inUI->CreateSlider(inSubMenu, "Rear Suspension Min Length", sRearSuspensionMinLength, 0.0f, 3.0f, 0.01f, [](float inValue) { sRearSuspensionMinLength = inValue; });
+	inUI->CreateSlider(inSubMenu, "Rear Suspension Max Length", sRearSuspensionMaxLength, 0.0f, 3.0f, 0.01f, [](float inValue) { sRearSuspensionMaxLength = inValue; });
+	inUI->CreateSlider(inSubMenu, "Rear Suspension Frequency", sRearSuspensionFrequency, 0.1f, 5.0f, 0.01f, [](float inValue) { sRearSuspensionFrequency = inValue; });
+	inUI->CreateSlider(inSubMenu, "Rear Suspension Damping", sRearSuspensionDamping, 0.0f, 2.0f, 0.01f, [](float inValue) { sRearSuspensionDamping = inValue; });
+	inUI->CreateTextButton(inSubMenu, "Accept", [this]() { RestartTest(); });
 }
 }

+ 15 - 0
Samples/Tests/Vehicle/VehicleConstraintTest.h

@@ -26,12 +26,27 @@ public:
 	virtual void				CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
 	virtual void				CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu) override;
 
 
 private:
 private:
+	static inline float			sInitialRollAngle = 0;
+	static inline float			sMaxRollAngle = DegreesToRadians(60.0f);
+	static inline float			sMaxSteeringAngle = DegreesToRadians(30.0f);
 	static inline int			sCollisionMode = 2;
 	static inline int			sCollisionMode = 2;
 	static inline bool			sFourWheelDrive = false;
 	static inline bool			sFourWheelDrive = false;
 	static inline bool			sAntiRollbar = true;
 	static inline bool			sAntiRollbar = true;
 	static inline bool			sLimitedSlipDifferentials = true;
 	static inline bool			sLimitedSlipDifferentials = true;
 	static inline float			sMaxEngineTorque = 500.0f;
 	static inline float			sMaxEngineTorque = 500.0f;
 	static inline float			sClutchStrength = 10.0f;
 	static inline float			sClutchStrength = 10.0f;
+	static inline float			sFrontSuspensionCasterAngle = 0.0f;
+	static inline float 		sFrontSuspensionKinPinAngle = 0.0f;
+	static inline float			sFrontSuspensionMinLength = 0.3f;
+	static inline float			sFrontSuspensionMaxLength = 0.5f;
+	static inline float			sFrontSuspensionFrequency = 1.5f;
+	static inline float			sFrontSuspensionDamping = 0.5f;
+	static inline float 		sRearSuspensionCasterAngle = 0.0f;
+	static inline float 		sRearSuspensionKingPinAngle = 0.0f;
+	static inline float			sRearSuspensionMinLength = 0.3f;
+	static inline float			sRearSuspensionMaxLength = 0.5f;
+	static inline float			sRearSuspensionFrequency = 1.5f;
+	static inline float			sRearSuspensionDamping = 0.5f;
 
 
 	Body *						mCarBody;									///< The vehicle
 	Body *						mCarBody;									///< The vehicle
 	Ref<VehicleConstraint>		mVehicleConstraint;							///< The vehicle constraint
 	Ref<VehicleConstraint>		mVehicleConstraint;							///< The vehicle constraint

+ 2 - 0
Samples/Tests/Vehicle/VehicleSixDOFTest.cpp

@@ -120,6 +120,8 @@ void VehicleSixDOFTest::Initialize()
 
 
 void VehicleSixDOFTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
 void VehicleSixDOFTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
 {
 {
+	VehicleTest::PrePhysicsUpdate(inParams);
+
 	const float max_rotation_speed = 10.0f * JPH_PI;
 	const float max_rotation_speed = 10.0f * JPH_PI;
 
 
 	// Determine steering and speed
 	// Determine steering and speed

+ 71 - 0
Samples/Tests/Vehicle/VehicleTest.cpp

@@ -15,6 +15,7 @@
 #include <Layers.h>
 #include <Layers.h>
 #include <Application/DebugUI.h>
 #include <Application/DebugUI.h>
 #include <Utils/Log.h>
 #include <Utils/Log.h>
+#include <Renderer/DebugRendererImp.h>
 
 
 JPH_IMPLEMENT_RTTI_VIRTUAL(VehicleTest) 
 JPH_IMPLEMENT_RTTI_VIRTUAL(VehicleTest) 
 { 
 { 
@@ -39,6 +40,9 @@ void VehicleTest::Initialize()
 		Body &floor = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(1000.0f, 1.0f, 1000.0f), 0.0f), RVec3(0.0f, -1.0f, 0.0f), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
 		Body &floor = *mBodyInterface->CreateBody(BodyCreationSettings(new BoxShape(Vec3(1000.0f, 1.0f, 1000.0f), 0.0f), RVec3(0.0f, -1.0f, 0.0f), Quat::sIdentity(), EMotionType::Static, Layers::NON_MOVING));
 		floor.SetFriction(1.0f);
 		floor.SetFriction(1.0f);
 		mBodyInterface->AddBody(floor.GetID(), EActivation::DontActivate);
 		mBodyInterface->AddBody(floor.GetID(), EActivation::DontActivate);
+
+		// Load a race track to have something to assess speed and steering behavior
+		LoadRaceTrack("Assets/Racetracks/Zandvoort.csv");
 	}
 	}
 	else if (strcmp(sSceneName, "Steep Slope") == 0)
 	else if (strcmp(sSceneName, "Steep Slope") == 0)
 	{
 	{
@@ -157,6 +161,73 @@ void VehicleTest::CreateRubble()
 		}
 		}
 }
 }
 
 
+void VehicleTest::LoadRaceTrack(const char *inFileName)
+{
+	// Open the track file
+	std::ifstream stream;
+	stream.open(inFileName, std::ifstream::in);
+	if (!stream.is_open()) 
+		return;
+
+	// Ignore header line
+	String line;
+	std::getline(stream, line);
+
+	// Read coordinates
+	struct Segment
+	{
+		RVec3				mCenter;
+		float				mWidthLeft;
+		float				mWidthRight;
+	};
+	Array<Segment> segments;
+	Real x, y;
+	float wl, wr;
+	char c;
+	RVec3 track_center = RVec3::sZero();
+	while (stream >> x >> c >> y >> c >> wl >> c >> wr)
+	{
+		RVec3 center(x, 0, y);
+		segments.push_back({ center, wl, wr });
+		track_center += center;
+	}
+	if (!segments.empty())
+		track_center /= (float)segments.size();
+
+	// Convert to line segments
+	RVec3 prev_tleft = RVec3::sZero(), prev_tright = RVec3::sZero();
+	for (size_t i = 0; i < segments.size(); ++i)
+	{
+		const Segment &segment = segments[i];
+		const Segment &next_segment = segments[(i + 1) % segments.size()];
+
+		// Calculate left and right point of the track
+		Vec3 fwd = Vec3(next_segment.mCenter - segment.mCenter);
+		Vec3 right = fwd.Cross(Vec3::sAxisY()).Normalized();
+		RVec3 tcenter = segment.mCenter - track_center + Vec3(0, 0.1f, 0); // Put a bit above the floor to avoid z fighting
+		RVec3 tleft = tcenter - right * segment.mWidthLeft;
+		RVec3 tright = tcenter + right * segment.mWidthRight;
+		mTrackData.push_back({ tleft, tright });
+
+		// Connect left and right point with the previous left and right point
+		if (i > 0)
+		{
+			mTrackData.push_back({ prev_tleft, tleft });
+			mTrackData.push_back({ prev_tright, tright });
+		}
+
+		prev_tleft = tleft;
+		prev_tright = tright;
+	}
+}
+
+void VehicleTest::PrePhysicsUpdate(const PreUpdateParams &inParams)
+{
+	// Render the track
+	for (const Line &l : mTrackData)
+		mDebugRenderer->DrawLine(l.mStart, l.mEnd, Color::sBlack);
+}
+
 void VehicleTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
 void VehicleTest::CreateSettingsMenu(DebugUI *inUI, UIElement *inSubMenu)
 {
 {
 	inUI->CreateTextButton(inSubMenu, "Select Scene", [this, inUI]() { 
 	inUI->CreateTextButton(inSubMenu, "Select Scene", [this, inUI]() { 

+ 10 - 0
Samples/Tests/Vehicle/VehicleTest.h

@@ -13,6 +13,7 @@ public:
 	JPH_DECLARE_RTTI_VIRTUAL(VehicleTest)
 	JPH_DECLARE_RTTI_VIRTUAL(VehicleTest)
 
 
 	// See: Test
 	// See: Test
+	virtual void			PrePhysicsUpdate(const PreUpdateParams &inParams) override;
 	virtual void			Initialize() override;
 	virtual void			Initialize() override;
  
  
 	// Optional settings menu
 	// Optional settings menu
@@ -29,4 +30,13 @@ private:
 	void					CreateBridge();
 	void					CreateBridge();
 	void					CreateWall();
 	void					CreateWall();
 	void					CreateRubble();
 	void					CreateRubble();
+	void					LoadRaceTrack(const char *inFileName);
+
+	// A set of line segments to render a race track
+	struct Line
+	{
+		RVec3				mStart;
+		RVec3				mEnd;
+	};
+	Array<Line>				mTrackData;
 };
 };