Where are the Course Save Files? - Android Tablet/Phone

  • Thread starter Imari
  • 119 comments
  • 17,031 views
Just out of curiosity @Outspacer what decompiler did you use to decompile the .meta and .bin files?

Mostly just manually looking for patterns and distinctive values in the hex code. I used 010 Editor to record what I found using its template feature, which also lets me edit the values more easily. Then refining by making changes to tracks and seeing what changed in the files. So what you see posted was generated by me :D As file formats go, it's about as basic as they come, TBH.

I've got the rest of the .bin file worked out, and there's just one unknown value in the .meta - can't think what it is yet, but it's often very roughly the same as the number of anchors (sometimes less, sometimes more). Some statistic perhaps. (It's also followed by a value that I've only ever seen set to '0', but might be something). Everything else in the .meta either ties the 3 files together, or is used to display info on the tracks screen (e.g. last edited date/time), or is a statistic (e.g. track length).
 
Oh yeah... and some of it is just making a guess and seeing if it's wrong :lol:

I mean, I've no idea if what I've called "version" is definitely that - there's only been one version so far!

.meta - possibly the same as the last one I posted. Still an unknown int.
Code:
//--------------------------------------
//--- 010 Editor v3.1 Binary Template
//
// File: GT6 Track Path Editor .meta
// Author: Outspacer
// Revision: 1.0.0
// Purpose:
// Unknowns: an int
//--------------------------------------

BigEndian();

enum <int> theme_t
{
    Death_Valley = 1,
    Eifel = 2,
    Andalusia = 3,
    //Death_Valley_Flat = 4,
    Eifel_Flat = 5,
    //Andalusia_Flat = 6,
};

char    file_id[6]; // ="GT6TEM"
int     version; // =103 version???
byte    reserved[6]; // ={0,0,0,0,0,0}
int     file_name_start;
int     file_name_end;
char    file_name[file_name_end - file_name_start];
theme_t theme;
double  track_length;
int     unknown; // ??? roughly the same as the number of anchors (sometimes more, sometimes less)
int     reserved; // =0
time_t  timestamp;
short   display_name_length;
char    display_name[display_name_length];

.bin - added object type values for all tracks, worked out the timestamp in INFO, and the length in OBJECT (I think). No more unknowns :)
Code:
//--------------------------------------
//--- 010 Editor v3.1 Binary Template
//
// File: GT6 Track Path Editor .bin
// Author: Outspacer
// Revision: 1.0.0
// Purpose:
//--------------------------------------

BigEndian();

// HEADER

struct HEADER
{
    char file_id[6]; // ="GT6TER"
    int  version; // =105 version ???
    byte reserved[6]; // ={0,0,0,0,0,0}
    int  name_start; // offset from start of file
    int  name_count; // =1
    int  info_start;
    int  info_count; // =1
    int  path_start;
    int  path_count; // = number of user anchors + 2 for the home straight
    int  objects_start;
    int  objects_count; // = number of trackside objects and curbs
    int  section5_start;
    int  section5_count;
};

// INFO

enum <int> theme_t
{
    Death_Valley = 1,
    Eifel        = 2,
    Andalusia    = 3,
    //Death_Valley_Flat = 4,
    Eifel_Flat   = 5,
    //Andalusia_Flat = 6,
};

struct timestamp_t
{
    uint year   : 6; // since 1970
    uint month  : 4;
    uint mday   : 5;
    uint hour   : 5;
    uint minute : 6;
    uint second : 6;
};

struct INFO
{
    theme_t     theme; // 1=death valley, 2=Eifel, 5=Eifel Flat, 3=Andalusia
    double      track_width;
    byte        connected; // 0=open, 1=connected
    timestamp_t timestamp;
    byte        bank_angle; // 0=very gentle, ..., 4=very steep
};

// PATH

enum <byte> segment_type_t
{
    straight = 0,
    curve    = 1,
};

enum <byte> radius_t
{
    increased = 0,
    decreased = 1,
};

struct HOME_STRAIGHT
{
    segment_type_t  start_type; // =0, straight
    double          start_x;
    double          start_y;
    double          start_a;
                                // (missing byte here breaks the pattern of the path_section)
    segment_type_t  end_type;   // =0, straight
    double          end_x;
    double          end_y;
    double          end_a;
    byte            reserved;   // =0
};

struct ANCHOR
{
    segment_type_t  type;   // for preceding segment
    double          x;
    double          y;
    double          a;
    radius_t        radius; // for preceding segment
};

// OBJECTS

enum <byte> side_t
{
    right = 0,
    left  = 1,
};

enum <byte> dist_from_track_t
{
    edge  = 1, // used for curbing
    close = 2,
    far   = 3,
    deep  = 4, // both close and far regions
};

enum <byte> object_type_t
{
                                // dist_from_track, length
// eifel / eifel flat:
    curbing             =  6,   // edge, 25
    trees               =  1,   // far, 100
    woods               =  2,   // far, 100
    field_of_rapeseed   =  3,   // deep, 100
    billboard           = 20,   // far, 100
    marshall_post       = 14,   // close, 100
    tent                = 21,   // deep, 100
    grandstand          = 13,   // deep, 100
    circuit_gate        = 12,   // deep, 100
    raised_bank         =  9,   // deep, 200
// death valley:
    grass               =  0,   // far, 90
    cactus              =  4,   // close, 40
    cactus_cluster      =  5,   // far, 60
    banner              = 16,   // close, 100
    electrical_wire     = 10,   // far, 350
    //tent              = 21,   // far, 50
    rocks               =  8,   // far, 60
// andalusia:
    //curbing           =  6,   // edge, 100
    //trees             =  1,   // close, 20
    //woods             =  2,   // far, 100
    //sign              = billboard, // close, 30
    //marshall_post     = 14,   // close, 20
    building            =  7,   // deep, 40
    spectators          = 19,   // close, 100
    //grandstand        = 13,   // close, 20
    //circuit_gate      = 12,   // close, 20
    gate                = 22,   // close, 20
    rally_gate          = 18,   // close, 20
    //rocks             =  8    // deep, 100
// 11, 15, 17, 23...
};

struct OBJECT
{
    side_t              side;
    dist_from_track_t   dist_from_track;
    object_type_t       type;
    double              length; // ??? seen 20, 25, 30, 40, 50, 60, 90, 100, 200, 350
    double              position; // distance around the track (measured from the start of the home straight?)
};

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

HEADER          header;
char            name[header.info_start - header.name_start];
INFO            info;
HOME_STRAIGHT   home_straight;
ANCHOR          anchors[header.path_count - 2];
OBJECT          objects[header.objects_count];

Unfortunately I have too much pesky Real Life getting in the way right now to make us an App :(
 
Great job to all the programmers out there finding stuff in the app and whatnot! Every single one of you are god sends for someone who's wondering if the limits can be broken :) I just remembered something though. @con360 mentioned on the last page that two more themes were found. Although I do believe that the last two themes could be Andalusia flat and Death Valley flat (I'm still highly skeptical of the Andalusia theme), it got the gears in my head turning and I remember PD mentioning that there would be a Ronda and GT Arena open layout theme. Is it possible that these could be the last two themes found in the files? If I missed something and the themes that @con360 found are confirmed then I apologize but I figured I'd ask the people who can actually find the files lol

Edit:Nevermind. Just looked at the last post and saw the tracks are indeed flat variations of Andalusia and Death Valley. Still part of me is still wondering if the Ronda and GT arena themes will make an appearance in a future CM update
 
Edit:Nevermind. Just looked at the last post and saw the tracks are indeed flat variations of Andalusia and Death Valley.

Oh, I haven't confirmed that, I just added the info con360 found. Whatever they might've been, they aren't implemented anyway. If PD do add more themes it will be easy to see which numbers they use for them (as long as they don't completely change the file format).
 
The unknown value in the .meta seems to be the number of (what I'm calling) automatic objects, like run-offs etc, maybe road-side cafes.

.meta
Code:
//--------------------------------------
//--- 010 Editor v3.1 Binary Template
//
// File: GT6 Track Path Editor .meta
// Author: Outspacer
// Revision: 1.0.0
// Purpose:
// Unknowns: an int
//--------------------------------------

BigEndian();

enum <int> theme_t
{
    Death_Valley = 1,
    Eifel = 2,
    Andalusia = 3,
    //Death_Valley_Flat = 4,
    Eifel_Flat = 5,
    //Andalusia_Flat = 6,
};

char    file_id[6]; // ="GT6TEM"
int     version; // =103 version???
byte    reserved[6]; // ={0,0,0,0,0,0}
int     file_name_start;
int     file_name_end;
char    file_name[file_name_end - file_name_start];
theme_t theme;
double  track_length;
int     auto_objects_count; // ??? grey areas where trackside objects can't be placed. Not sure exactly what it counts, or why!
int     reserved; // =0
time_t  timestamp;
short   display_name_length;
char    display_name[display_name_length];

There's a definite correlation anyway. I started with a big circle using about 8 anchors, and the value was 0. Then added some anchors making kinks (causing some grey areas) and the value went up. Then removed the kinks and got it back down to 1 - at that point, there were no grey areas though! Then removed some anchors and it went to 0. Adding more anchors into segments but not moving them at all kept it at 0.

But... why? Why would the editor need to pass this value along to the track-checking bit in GT6? I'd have thought it could just do its algorithms to decide on run-offs, cafes, etc and remove any user-placed objects in those areas. A simple count doesn't have enough information in it to do anything useful with.

Kinda related thought... the 'reserved' value just after that one, and the 5th section in the .bin file that doesn't appear to be used, could get used to store the automatic objects once the track has passed through GT6 and been saved. (Assuming the file format is the same at that point). Dunno how we could test that theory though.
 
I'd be interested to see how densely you can pack anchors with this method, considering how chaotic the terrain can get with standard permissions...
 
@Outspacer You've done a wonderful job. I just wonder if there's a way to remove barriers on the roads to make it actually able to cross roads up. It could seriously open up a lot of things in the course maker. Monza would be a feasible feat and so would be a TGTT replica of some sort.
 
@Outspacer You've done a wonderful job. I just wonder if there's a way to remove barriers on the roads to make it actually able to cross roads up. It could seriously open up a lot of things in the course maker. Monza would be a feasible feat and so would be a TGTT replica of some sort.

Thanks! I can't see that being possible :( I wish it was, removing the stupidly close invisible wall from Death Valley would be great. I chanced upon this fun little layout while making a test track to explore the edges of the allowed area, but those walls ruin the final left/right/left onto the home straight - got right, you can really blast out of there, and it would be fun to explore how much dirt you could take before it slowed you down :(

Screenshot_2015-10-18-21-16-59.png
 
Thanks! I can't see that being possible :( I wish it was, removing the stupidly close invisible wall from Death Valley would be great. I chanced upon this fun little layout while making a test track to explore the edges of the allowed area, but those walls ruin the final left/right/left onto the home straight - got right, you can really blast out of there, and it would be fun to explore how much dirt you could take before it slowed you down :(

View attachment 464931
Well that's sad honestly. Imagine the possibilities. I just want to be able to do Rally courses like those in the X Games where you have cars jumping over the track and having extremely dynamic setups for rallycross. And I really was hoping to recreate things like monza completely open, make a Special Rtage Route track with all variations connected together like a city block etc. I just hope they keep adding things to the course maker by GT7 if they decide to not update the course maker anymore in GT6.

I would love to help but my programming knowledge is very basic. If somehow I can still be of help despite all that I'm more than happy to be able to lend a hand.
 
Some :D

View attachment 461665 View attachment 461666

Theme can be switched. Home straight can be anywhere, any angle (but still fixed at 600m). Anchors can be moved, and there seems to be no error checking as long as you don't edit them the app afterwards. Figure-of-8 just results in barriers across the track. Tighter corners are possible, but only to a certain extent since otherwise the barriers or skirtings poke out onto the tarmac. Reversing a track should be possible, as should scaling it, flipping/mirroring it, or moving it to another bit of terrain. Shrinking it might cause overlap issues though, of course. Track can be put in the grey bits of terrain, and work ok, but terrain/scenery has gaps out there (could be great for jump tracks...).

Latest file format info... may well contain errors!...

.meta
Code:
//--------------------------------------
//--- 010 Editor v3.1 Binary Template
//
// File: GT6 Track Path Editor .meta
// Author: Outspacer
// Revision: 1.0.0
// Purpose:
// Unknowns: an int
//--------------------------------------

enum <int> theme_t
{
  Death_Valley = 1,
  Eifel = 2,
  Andalusia = 3,
  Eifel_Flat = 5,
};

char file_id[6]; // ="GT6TEM"
int version; // =103 version???
byte reserved[6]; // ={0,0,0,0,0,0}
int file_name_start;
int file_name_end;
char file_name[file_name_end - file_name_start];
theme_t theme;
double track_length;
int unknown;
int reserved;
time_t timestamp;
short display_name_length;
char display_name[display_name_length];

.bin
Code:
//--------------------------------------
//--- 010 Editor v3.1 Binary Template
//
// File: GT6 Track Path Editor .bin
// Author: Outspacer
// Revision: 1.0.0
// Purpose:
// Unknowns: 4 bytes in INFO, 2 bytes in OBJECT
//--------------------------------------

typedef unsigned byte u_byte;

// HEADER

struct HEADER
{
  char file_id[6]; // ="GT6TER"
  int version; // =105 version ???
  byte reserved[6]; // ={0,0,0,0,0,0}
  int name_start; // offset from start of file
  int name_count; // =1
  int info_start;
  int info_count; // =1
  int path_start;
  int path_count; // = number of user anchors + 2 for the home straight
  int objects_start;
  int objects_count; // = number of trackside objects and curbs
  int section5_start;
  int section5_count;
};

// INFO

enum <int> theme_t
{
  Death_Valley = 1,
  Eifel = 2,
  Andalusia = 3,
  Eifel_Flat = 5,
};

struct INFO
{
  theme_t theme; // 1=death valley, 2=Eifel, 5=Eifel Flat, 3=Andalusia
  double track_width;
  byte connected; // 0=open, 1=connected
  u_byte unknown[4]; // timestamp???
  byte bank_angle; // 0=very gentle, ..., 4=very steep
};

// PATH

enum <byte> segment_type_t
{
  straight = 0,
  curve = 1,
};

enum <byte> radius_t
{
  increased = 0,
  decreased = 1,
};

struct HOME_STRAIGHT
{
  segment_type_t start_type; // =1, straight
  double start_x;
  double start_y;
  double start_a;
  // (missing byte here breaks the pattern of the path_section)
  segment_type_t end_type; // =1, straight
  double end_x;
  double end_y;
  double end_a;
  byte reserved; // =0
};

struct ANCHOR
{
  segment_type_t type; // 0=curve, 1=straight
  double x;
  double y;
  double a;
  radius_t radius; // for preceding segment
};

// OBJECTS

enum <byte> side_t
{
  right = 0,
  left = 1,
};

enum <byte> dist_from_track_t
{
  edge = 1, // used for curbing
  close = 2,
  far = 3,
  deep = 4, // both close and far regions
};

enum <byte> object_type_t
{
  curbing = 6,
  trees = 1,
  woods = 2,
  rapeseed = 3,
  billboard = 20,
  marshal = 14,
  tent = 21,
  grandstand = 13,
  gate = 12,
  bank = 9,
};

struct OBJECT
{
  side_t side;
  dist_from_track_t dist_from_track;
  object_type_t type;
  u_byte unknown1; // seen 40h (64)
  u_byte unknown2; // seen 39h (57) for curbing, 69h (105) for bank, 59h (89) for trees, woods, rapeseed, billboard, marshal, tent, grandstand, gate
  byte reserved[6]; // ={0,0,0,0,0,0}
  double position;
};

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

struct HEADER header;
char name[header.info_start - header.name_start];
struct INFO info;
struct PATH
{
  struct HOME_STRAIGHT home_straight;
  struct ANCHOR anchors[header.path_count - 2];
} path;
struct OBJECT objects[header.objects_count];

Nice work modelling these binaries Outspacer ! ,

What possible advantages do you see without the constraints of the Path Editor App ?
Tighter Bends ? Shorter Home straight ?
 
Nice work modelling these binaries Outspacer ! ,

What possible advantages do you see without the constraints of the Path Editor App ?
Tighter Bends ? Shorter Home straight ?

Thanks! There's a triage we could do, between 'can', '(possibly) glitchy' and 'cannot', but there's some overlap...

Can
Change Theme (with track still inside valid area for theme)
Move/rotate a track around (with track still inside valid area) - home straight anywhere, but might glitch
Scale a track uniformly larger (but not home straight)
Decrease track width
Rename

Possibly Glitchy
Move track outside valid area (probably missing terrain around track, but track itself ok)
Change Theme (with track going outside valid area for theme)
Increase track width (can set more than 15m)
Scale a track uniformly smaller (but not home straight)
Edit anchors beyond limits, e.g. tighter turns
Create track direct from GPS trace (not easy!)
Longer tracks (although, a 19km one crashed my PS3 after testing it)

Cannot
Figure-of-8, crossovers, etc. (well, you can, but always has graphical glitches, often undrivable)
Bridges
Control elevation changes (apart from using more/fewer anchors to pin the track differently)
Vary track width per segment
Vary banking per segment
Change length of home straight
Place cafes, run-offs, etc.


The glitchy category most often results in track-side 'skirt' overlapping itself (or the track itself in extreme cases), and barriers can stick out on tight corners. Once a track has an invalid layout you probably need to leave it alone in the editor (only use the editor to transfer). As long as barriers aren't sticking out over the track, these are just graphical glitches - the skirt doesn't block you even if on track ;)

Any alternate editor for anchors would first have to figure out PD's algorithm for the curves between anchors. It doesn't appear to be a spline, more like one or two arcs joined together. And it's 'jumpy'... try this: new track, create a big semi-circle turn after the home straight, then spin the angle of that anchor around slowly. OK, at some point it's got to jump from one side of the anchor to the other, but the weird jumps happen when the anchor is pointing roughly 90 degrees away from the HS (so if HS runs south to north, then roughly east).
 
Last edited:
Code:
struct ANCHOR
{
segment_type_t type; // for preceding segment
double x;
double y;
double a;
radius_t radius; // for preceding segment
};
Great job! :D:tup:
Do you know what the coordinate a is? Maybe the orientation of the path at the anchor? Is it an angle?
I'd speculate they add Euler Spiral segments and circular segments in the curved paths between anchors. That way you get a linear increasing curvature, then a constant curvature (normal circle segment) and then a linear decreasing curvature. Maybe it's possible to find a unique path with these segments between anchors...
 
Great job! :D:tup:
Do you know what the coordinate a is? Maybe the orientation of the path at the anchor? Is it an angle?
I'd speculate they add Euler Spiral segments and circular segments in the curved paths between anchors. That way you get a linear increasing curvature, then a constant curvature (normal circle segment) and then a linear decreasing curvature. Maybe it's possible to find a unique path with these segments between anchors...

It's an angle in radians, often kept within -pi to pi but not always (not sure yet if it makes any difference).

I don't think it's using spirals; it doesn't look like there's any easing in to the arcs. Certainly when adding a new anchor to the end of a path, it's creating a single constant radius arc. Assuming it's using two arcs when the angle of that anchor is changed... The points where it jumps are where the second arc's origin changes sides (when the second arc is almost straight). It seems to be indecisive about which solution to pick and there isn't a single changover point. Such an algo can't easily do a straight line because its radius would be infinity, so they'd have to add code to avoid that. But even approaching a straight line the radius gets very large, so the calculations become innacurate. That's my theory so far, anyway :) Replicating that, with avoiding infinty and the jumpy changover, would be a challenge!
 
I don't think it's using spirals; it doesn't look like there's any easing in to the arcs. Certainly when adding a new anchor to the end of a path, it's creating a single constant radius arc. Assuming it's using two arcs when the angle of that anchor is changed... The points where it jumps are where the second arc's origin changes sides (when the second arc is almost straight). It seems to be indecisive about which solution to pick and there isn't a single changover point. Such an algo can't easily do a straight line because its radius would be infinity, so they'd have to add code to avoid that. But even approaching a straight line the radius gets very large, so the calculations become innacurate. That's my theory so far, anyway :) Replicating that, with avoiding infinty and the jumpy changover, would be a challenge!

I think they are using the inverse of radius -- curvature -- internally. No infinities involved here, straight lines have curvature 0. The Euler Spirals only enter the calculation at the end, when you want to smoothly interpolate the curvatures of the adjacent circular segments.

So basically we have to find a way to determine a circular segment going through two points (the two anchors) and having tangents at that points like the angles at the anchors. Will try and report back later when i'm back home.
 
I think they are using the inverse of radius -- curvature -- internally. No infinities involved here, straight lines have curvature 0. The Euler Spirals only enter the calculation at the end, when you want to smoothly interpolate the curvatures of the adjacent circular segments.

So basically we have to find a way to determine a circular segment going through two points (the two anchors) and having tangents at that points like the angles at the anchors. Will try and report back later when i'm back home.

Pair of circular segments (when editing an anchor). It's like there's a virtual anchor placed on the line between anchors where the segments join, which moves around to suit. An algorithm to find the position and angle of that virtual anchor is the tricky bit ( (p, a) = f(p1, a1, p2, a2) ). Once found, the center of each circle could be found by intersecting the pairs of lines perpendicular to the tangents and passing through the points at each end of the segment. No idea yet if that's the order an algo would actually do it in.

Appending a new anchor is much simpler, since the angle of the anchor is changed to suit a single radius ( (r, a2) = f(p1, a1, p2) ).

I don't know if they use curvature. That they avoid straight lines hints at not though!


edit: OK, here's a thing: 'biarcs'. Intro and many links here: http://www.redblobgames.com/articles/curved-paths/
 
Last edited:
edit: OK, here's a thing: 'biarcs'. Intro and many links here: http://www.redblobgames.com/articles/curved-paths/

This could be it! See here for an extensive discussion of biarcs with an interactive demo and code:

http://www.ryanjuckett.com/programming/biarc-interpolation/

This is really a nice riddle, tried to fiddle with the anchors a bit, some observations are that most of the time the lengths of the two arcs are almost 1:2, i.e. one is twice as long as the other -- all read up from the radius/length annotations on the path when moving an anchor. It seems there is some radius constraints, and the biarcs described above have one degree of freedom that can be used to alter their shape -- so PD might use this in a special way. The brain-teasing continues... :cool:
 
This could be it! See here for an extensive discussion of biarcs with an interactive demo and code:

http://www.ryanjuckett.com/programming/biarc-interpolation/

This is really a nice riddle, tried to fiddle with the anchors a bit, some observations are that most of the time the lengths of the two arcs are almost 1:2, i.e. one is twice as long as the other -- all read up from the radius/length annotations on the path when moving an anchor. It seems there is some radius constraints, and the biarcs described above have one degree of freedom that can be used to alter their shape -- so PD might use this in a special way. The brain-teasing continues... :cool:

Hehe, now we have a little knowledge (and google, ahem), we are dangerous things :D That page you've found will save a lot of time, I suspect :) PD presumably copy-pasted code from somewhere...

I've been reading http://www.ag.jku.at/pubs/2006sfj.pdf which describes how to find the circle that transition points lie on and the solutions. But, like you say, then there's a decision to make on which solution to choose, and by default the middle one is typically chosen. That would certainly explain the jumpiness I was talking about (and in a much more satisfactory way than running out of numerical precision). It also follows from that how the double semi-circle is arrived at (drag a new anchor out to a semi-circle, then flip its angle 180 so the angles are parallel - there are an infinite number of solutions along the line between the two anchors, but it picks the central one). Anyway, it's well worth a (very slow) read of the first 2 or 3 pages of that PDF.
 
Is there always 74 bytes of padding on the end of the bin file ?

Yeah. Well, I haven't bothered checking that size, but there is always a big block of zeros at the end. No idea why. Possibly something to do with the mysterious 'section 5' (although I'd expect that to be variable-length defined by section5_count in the header, maybe section5_count ends up being 1 and it's fixed-length).
 
Yeah. Well, I haven't bothered checking that size, but there is always a big block of zeros at the end. No idea why. Possibly something to do with the mysterious 'section 5' (although I'd expect that to be variable-length defined by section5_count in the header, maybe section5_count ends up being 1 and it's fixed-length).

74 bytes seems to be accepted by the path editor for all my tests (so far).

I just managed to chance a crossover (bridge) on Andalucia while trying to drive over un-driveable areas of the map.

https://www.gran-turismo.com/us/gt6/user/#!/friend/PR1VATEJ0KER/course/1539198
 
Last edited:
some glitchy crossovers

You are too modest :P

I did manage a 'lap' in the end... in 11:08... having repeated big chunks of the track a few times. It's that third to last cross that's difficult to get under - the AI all jump up and get stuck. Managed a 'clean' lap 3:20, but I had to punt some AI out of the way!
 
Hehe, now we have a little knowledge (and google, ahem), we are dangerous things :D That page you've found will save a lot of time, I suspect :) PD presumably copy-pasted code from somewhere...

I've been reading http://www.ag.jku.at/pubs/2006sfj.pdf which describes how to find the circle that transition points lie on and the solutions. But, like you say, then there's a decision to make on which solution to choose, and by default the middle one is typically chosen. That would certainly explain the jumpiness I was talking about (and in a much more satisfactory way than running out of numerical precision). It also follows from that how the double semi-circle is arrived at (drag a new anchor out to a semi-circle, then flip its angle 180 so the angles are parallel - there are an infinite number of solutions along the line between the two anchors, but it picks the central one). Anyway, it's well worth a (very slow) read of the first 2 or 3 pages of that PDF.

I misread some of that PDF - it doesn't explain the jumpiness. They are mostly talking about fitting biarcs to an already defined curve. In fact there's an infinite number of solutions, so there needn't be any jumps. I played around with working out the circle that transition points lie on, but can't see yet how PD would then pick a solution, in a way that would cause the jumpiness.

"Find the center S of the circle C, e.g., by intersecting the bisectors of P0P1 and of (P0+U0)(P1+U1)."

This works great but has an edge case where both tangents are pointing straight in or out of the circle, since then the bisectors are coincident. And it just gives all possible solutions without hints at how to choose one.


See here for an extensive discussion of biarcs with an interactive demo and code:

http://www.ryanjuckett.com/programming/biarc-interpolation/

With this method, I can imagine how they might decide that the default solution (with d1 = d2) gives too tight a radius for an arc and then modify d1, say, by doubling it. That would give the jumpiness.
 
I plugged in the data from the last turn on my crossover track into the bi-arc demo on ryanjuckett.
Looks exactly like what's in-game just from eyeballing it.

Quite a few cases do - it's the ones that don't that are the riddle. After all, he was using it for sword trails, where having a very tight radius, or going the long way round, just adds to the drama.

With the blue angle just left of vertical, GT6 takes a path between the anchors instead of:
biarc_rj1.png


GT6 would avoid the tight radius by making the red arc larger than:
biarc_rj4.png


Getting more subtle, GT6 bumps up the red arc even in this case:
biarc_rj3.png


And this one (even though it's very close):
biarc_rj2.png

The last two are in the region where GT6 doesn't change the curve smoothly as you change the angle of the blue anchor.
 
Back