Back to Main Page


Saw a comment in the description of the Kitsune Trains mod about trains not animating, specifically steam trains. Decided to investigate...

Page 2

Kitsune Trains - A one-way ticket to prop attachment hell.


So I was browsing *that* mod site and I noticed an update for Kitsune Trains. The modder (Foxunitone) mentioned that the wheels don't turn and that they would need a scripter to do that. I hadn't really thought about wheels on the trains not rotating and you generally don't notice. But when a Mallard goes past, everything locked into place, then you notice. So I found the modder's YouTube channel, found their latest video and asked if they wanted to make some changes to a model for me to investigate this animation thing. They said yes and so it began.

First task was to see if my theory about rotating the wheels at the correct speed worked. This involved using the speed of the train and the circumference of the wheel to work out how many rotations would happen per second. Once I had that, I multiplied it by the delta time and that got me the rotation amount per frame. Because the trains don't have wheels defined like cars, I couldn't get the size from memory, so at first it was a manual configuration. I then switched to using the model dimensions when the props were being created, meaning that process now became automatic. So having worked out the rotation, I then had to work out the attachment. My first thought was that I could just attach the wheels and then rotate them but that didn't seem to work. The only solution that did work, was calculating the rotation and then attaching them again with the new rotation... that got things turning nicely.

This is the first test using the wheels from one of the small flatbed freight cars found around La Mesa.

 

Trains can have up to 3 main bones to attach to, chassis_dummy, bogie_f and bogie_r. The wheels in that video are attached to bogie_f. So as you can see, the wheels rotating seemed to match up to the speed pretty well, so that confirmed that part was doable.

So I asked the modder to break apart the Bluebell as the test model. All the wheels were the same size and the con rods connecting the wheels was a simple single-beam. Attaching the wheels was straightforward because they just aligned with the centre of the chassis. The con rods were a bit more tricky because they attached to a wheel at an offset and then had to rotate in the opposite direction to the wheels, to keep them straight.

The end result of all that, was this:

 

Unlike previous mods, where this would have been a few hours work, this had already taken several days because I just can't code for long periods of time now.

So anyway, by this time the **proof of concept was in place and it seemed this idea was more than doable but I had only tackled a simple train at this point.

Note: Proof of concept - a rough and brief demonstration confirming that the technology behind the idea is feasible. I thought I would post that as 99% of the GTAV community has no idea what those 3 words mean.

Up until now, my plan was to build a configuration tool that runs alongside the mod, much like the AVE Config Builder. However, this idea was about to come crumbling down, as the mighty Mallard entered the arena. Where the Bluebell was a simple little thing, the Mallard is a masterpiece in complex engineering and the many moving parts that made this train so magnificent are testament to that fact. A plethora of shifting, rotating and undulating components all moved in perfect harmony... and I wanted to recreate as much of that as possible.

Some say I have had the word STUPID tattooed on the inside of my eyelids so I don't forget, some say... but anyway.

The main rod was basically the same as the Bluebell, attached to the three centre wheels at connection points, meaning I could do this part in the same way. It was interesting with the Mallard though because it confirmed an error in my calculations that I had noticed earlier, purely because the rod was so long. I hadn't taken into account the angle of the train when it was going uphill or downhill. This meant that when the train was at an angle, the rods were still straight, making them out of position at the back end. A small piece of compensation code fixed that problem.

The most critical problem the Mallard exposed, was the need to have multiple bones in a prop, that could be detected and things located onto. My efforts at putting bones in props failed miserably, no matter what I did, trying to get the index of a named bone failed every time. It was getting close to breaking point, because without this ability, building complex chains of moving objects would be painful to say the least. So I reached for the Native DB file I use for reference and found a native that returns the bone count in an entity. I created a small test prop, added three bones and then created a small test mod that simply called that native on the prop. The result was 3 but still every named bone returned an index of -1, meaning it didn't exist as far as the game was concerned. So I ran a loop based on the bone count and tried to get the coordinate of each bone by index value... it returned the correct coordinate. So I couldn't name bones but they could all be accessed by index value, this was all I needed, this opened the door.

To Be Continued... for now, I will just add this image to show the current progress with the Mallard.

And yes, you're all probably thinking, that's just the original model with parts that don't move. Emily, please demonstrate.

 

Update 21st January 2021: So I asked myself today "How much is too much?".

What that refers to is the small animating pieces that are part of the whole system. What caused me to ask that question, is the main con rod on the Mallard. There is a rotating piece that is attached to the rear of the con rod, that connects to a floating connection aligned with the centre of the back main wheel. It's a simple fix, detach the mesh, create it as an object, align the bones, attach it to the rear con rod bone in an orientation that matches the rear wheel rotation.

These are the times when trying to draw a line that you don't go beyond, become increasingly difficult. Everything is doable, it all just takes time and effort but how much reward does that effort return? For me, every extra piece that does what it should do, is essential... but what about everyone else, would they notice, would they care? What that piece does at the moment doesn't make sense and things that don't make sense annoy me.

Update 22nd January 2021: Today proved the necessity for good groundwork in this type of thing. I woke up and there was an email with all the parts for a new train. By 2pm the new train was in, working and with all intended moving parts moving correctly.

 

Update 23rd January 2021: The other thing that steam trains have that are unique to them, is the steam they blow out as they move. The creator of the trains had made some vfx changes to make them use exhaust effects but they seemed to be really limited in how far they would be visible from. I'm not sure if it was even 100m, it was really close.

So I dragged out my particle effect class from my previous mods and added looped particle spawning based on the exhaust position in the train. After trying a couple of different smoke types, I found ent_amb_smoke_factory in the Core library. It's not perfect, it has a secondary layer that spawns above the main smoke but the dispersal pattern and multiple self-spawns (i.e. the whole sequence includes multiple spawns of the smoke) make it ideal. More importantly, it is still visible over 400m away. For some reason it spawns at an offset from the emitter position but a small corrective offset sorted that out.

I think it looks okay.

 

Update 26th January 2021: Today saw the addition of the Union Pacific 4014 (Big Boy) to the collection.

Update 29th January 2021: There was another train added to the mod since the last update but there wasn't much point in another video to show it. Foxunitone has uploaded a public video on his channel to show the new functionality, which seems to be getting a good response.

Code-wise, the con rod management system was rewritten to be more generic, meaning instead of conditional code per-train, I can use the same code for multiple trains with a configuration struct telling that code how to work. It should make adding new trains as simple as defining the control struct, as long as the trains are configured to match the code requirements.

On that note I made a useful discovery with the OpenFormats exported props. When you export a prop with bones, GIMS may export them in an undesirable order. All you need to do, is to change the order in the .skel file, then re-import them into your dlc and the indexes will change based on the .skel order.

One problem that still exists with the mod is the smoke effect that is used. It's intended for the factory and it creates a dual-layer effect, one for the direct smoke from the chimney and a second higher layer for wispy smoke. I tried to remove the wispy part by using a blank texture in the core.ypt file but it negatively affected other effects, so I had to restore the original texture. I exported the core.ypt as XML from Codewalker but I don't understand how that file works.

The other part of the effect problem is that it uses looped particle effects... I've been here before with those particles, the R* Editor doesn't like them at all.

The final problem running around my head today is cinematic cameras. They obviously don't intend that you should drive the trains and the result of that is the cinematic camera doesn't work. Not sure if that's fixable through a script or not.

Update 30th January 2021: There's a saying in the UK about waiting for a bus and then three come at once. The same thing applies to trains although when you are trying to test things, it can sometimes be even worse. I was trying to see a specific train but unless you force the train to be the only one by editing the trains.xml file, you are at the mercy of RNG (Randum Number Generation). I wanted to see the Mallard go past last night but the game decided that what I really wanted to see, was the Mikado. So after 9 trains spawned and 8 of them were the Mikado, I gave in and changed the xml file. The problem is, that means quitting the game, changing the file, then reloading the game... which is a complete pain. I tried another train spawner mod today but that just seemed to quit after a couple of spawns, so a solution was needed.

In most circumstances, when you spawn a vehicle, you can't apply a velocity and keep it in place... or at least that seems to be the case with trains. All I got was crash after crash when trying to do precisely that. Fortunately, because I made the decision to drive all the animation from the speed of the train, it was easy enough to feed it a fake speed instead. So a bit of cheat code was added and I now have the ability to spawn a train in front of me and control the fake speed with some key presses. Any train, any time, any speed... the ideal test solution.

 

Update 7th February 2021: So it was getting a bit frustrating that everything that happened in the animation was so restrictively tied to values I had manually entered in for motion ranges etc... There was no way I was going to write a rigid-body constraints system for this, it is so far beyond my capabilities that it would be easier for me to walk around the edge of the UK in 3 days. So I sat thinking about the properties of the parts that needed to move and their relationships to other moving parts and one word kept popping into my head... intersections. Not those type you drive through but the type that represent a point where two factors cross... or intersect.

Take for example the pistons, they travel back and forth on a fixed horizontal axis but their motion is determined by the rotation of a rod that is on a rotating connection point. What they had in common, was the point where the piston attached to the rod and this point was determined by the length of the rod.

So imagine the connection point of the rod as the centre of a circle and the length of the rod represents the radius of that circle. The line that the piston travels along at some point intersects that circle and this is where they should connect. My maths is dreadful when it comes to this kind of thing but thankfully, the internet is full of code written by people whose maths isn't dreadful... in fact to them, this kind of thing doesn't even need thinking about because it's just that simple to them.

So consider this video...

 

You can see the start and end point of the line the piston might travel along marked by the green and red spheres. You can also see a magenta circle that has an origin on the connecting rod attach point and has a radius that matches that rod's length. As the rod moves based on its rotating origin, you can see the circle move accordingly. You can also see that as the intersect point changes position on the piston line, the position of the piston changes to match.

What this means is that the motion of the piston is now completely controlled by the rotation and length of the rod... change the rod and the piston will change its movement range to match. This removes my need to try and estimate the piston movement range by applying a modifier to a Math.Cos value based on the wheel rotation and simply lets the wheels control the piston. It's far more precise and far more flexible.

Update: Code is now in and the circular detection is working for all the relevant parts... now I just need to place the relevant parts in the right place and point them in the right direction.

I like to see this kind of image when my debug code shows signs of success.

The orange circle is what the bone on the end of the rocker would create if it was rotated 360 degrees, the yellow circle is for the rod that connects to that rocker. The two spheres on the orange circle are the intersection points of those two circles, with the lower blue one being the one I need to use. I have to make the rocker point at it and place the connecting rod at that position and point it at the centre of the yellow circle. Just as with the piston, if I decide I need a longer rod connector, the code will read the length from the prop and adjust itself automatically.

Update 8th February 2021: After a minor hiccup caused by me checking for bone and prop positions at the wrong time, the rest of the intersection code is now in and working properly. Had to make a couple of changes to existing prop parts on some trains but this should now be a stable platform to add trains to and for them to animate with this system.

Video of the Mallard animations in more detail added to YouTube

 

Update 12th February 2021: Two new trains got added in the last week, one slid in and has sat pretty much unnoticed, the other has been dragged in kicking and screaming and raising all kinds of hell on the way. The quiet giant was the UP844, which still needs some additional parts to get fully working. The hell-raiser was the German Class 01, a magnificent train with a hefty mental cost.

Source models are always going to be a bit of a lucky-dip, if you get one designed for animation, then you know it's going to animate. If however you get one designed for rendering purposes, then you're at the mercy of the creator's awareness of practical working limitations and that's where this Class 01 model suffered.

For the connecting rods to work, they have to be exactly the same length between connection points as the distance from wheel-centre to wheel-centre.

If they're too short or too long, then the wheels will never be able to complete a full revolution and in real-life, would probably destroy the wheels of the train... if it could even get moving that is. This source model had been built with the rods needing the wheels to be in an unnatural position for it to fit. That meant when the animation tried to drive it through natural cycles, it failed. My first instinct when something fails, is that I have done something wrong... so after chasing the bug for quite a while, I decided to look at the source model and that's when I spotted the error.

So the wheels were modified to sit in a completely neutral position, with the weight and connection point central at zero degrees. The wheels were then positioned based on the axle spacing and the rod connections were modified to precisely fit those spacings. Having done that, we got this as the end result.

 

I think the colour of this train is brilliant and the unusual shapes of the moving parts makes it a definite plus to be included.

As well as this train being added, the continued push for a generic, configurable system has moved onwards. An additional bunch of offsets have been added to the configuration struct meaning that each attachment can be finely tuned into their optimum position. Config file loading and saving has been tested and that seems to work fine.

I did run into a couple of issues where certain connection positions were lagging behind under speed. One of them was a case of me checking the position before it had been set, so it was getting the position from the previous frame. At 30m/s, that's a half metre lag at 60fps, so it was very noticeable.

The other issue was what I call a catch-22 bug. I needed a position for getting the attachment offset but I needed the attachment to be added, to get the position... you can see the problem. I simply switched to using another point of reference and now things all stay in sync.

So as it stands, the system now caters for three main configurations:

1) Single connecting rod across a set of wheels.

2) As 1 but with an additional secondary con rod and piston.

3) As 2 but with a rocker system driven by attachments on the secondary con rod.

The Mallard is a slight exception in that it is like a 3+ because it has the additional rear-wheel connection that others don't seem to have. The other exception is the UP4014 BigBoy, that definitely needs custom code to drive that beast.

Update 13th February 2021: Today was a slow day and I suspected it was going to end with zero progress. Then out of the blue I decided to search for steam train sound effects and found a page with quite a few in mp3 format. So I played a few of them and picked one that had a good consistent volume level, nice clear regular chuffs and a steady increase in speed but not to anything too fast.

So I downloaded the mp3 and dropped it into Soundforge, which is my goto sound editor. I quickly picked out a reasonable sound section of a single slow chuff. I applied a short fade to either end, just to make it a clean in and out. I then created 3 more quick time-stretches of the same sample, tightening up the end to allow for sharper stop and start of repeat playbacks. The final sounds will be handled with more care but the main purpose was to answer the "Will this work?" question.

I did a search for free sound libraries for .Net and the irrKlang site caught my eye. It was a name I rememebered from a question on GTAF by IKT about the 3D sound settings. So I downloaded the library, checked over a few things in the API and moved the samples into a sounds folder. I added some rough code that fires off a sounds when the wheel has gone through a 180 degree rotation, meaning you get two chuffs per revolution. I think you are supposed to get 2 per piston but I am compromising to keep a handle on performance for now. I don't know the limitations of the sound library, so I am proceeding with extreme caution until I learn more. I set 4 speed related change-overs that switch to different samples based on the speed of the train. After doing that, I got it to play a 2D sound, using the distance between player and train to control the volume of the sound. That all seemed to work okay, so I looked at the 3D aspect of it.

Some of the property names are confusing and the descriptions don't seem to reflect the behaviour when you change them. After encountering the same inverted direction problem that IKT encountered and remembering my explanation about different handedness, I changed the UP vector and that got things coming from the correct side. The sound direction is currently based on the player's direction, not the camera so with the player facing directly at the track, the train appeared from the left and panned across to the right as it passed by. I have an issue where it is playing too loud when it starts, which is confusing because it fades out correctly when it's going away... so maybe there are more parameters I need to find to deal with that.

The sound library also handles doppler effects according to the API docs, so that will be interesting if I can get it working.

But the end result of a few hours research and testing is promising for sure...

 

Update 14th February 2021: *a tumbleweed rolls past*

Update 15th February 2021: Major refactoring of all of the train classes today due to the increased functionality that has been added beyond the initial requirements. The mod was supposed to be for steam trains but we now have diesel trains emitting smoke and playing sounds and all that checking involved to filter those out was causing things to be messy. So there's now the abstract KitsuneTrain class and then several classes that derive from that depending on what functionality they require.

Prop creation was reworked to refine the initialisation process, that cleaned out a large amount of duplicated code and made thing much easier to deal with.

The mod is rapidly heading towards the finish line and I have given myself just this week to finalise everything and then move on from it. It's been about a month in total, which is both longer and shorter than expected. It's longer because there isn't that much code but I code so slowly now. It's shorter because I expected my mental health problems to get in the way and they have... but fortunately not quite as much as I anticipated.

But I have reached the stage where I am just staring at the screen, without the will to engage more with the code and that tells me that I am getting to the critical point where I start to resent the code instead and that's when I really turn destructive.

Looking at the positive comments on the video Foxunitone posted on their channel tells me that this mod is generating a high level of expectation... I do wonder what is going to happen though when that expectation is met with the fact that this mod does not and never will work with the latest versions of ScriptHookVDotNet. It is built against 2.10.10 because as far as I am concerned, along with some other script modders, that is the last version worth using. I refuse to jump through hoops to cater for their screw-ups, I am sick of telling them about defaulting parameters that should be optional. But they're a bunch of egoists, convinced they can do no wrong and they clearly don't give a damn about the mod authors or the users.

This mod is already going to be released without any form of support because I just won't deal with it... so I am not sure what the consequences of all that are going to be. It works on my machine, it works on Foxunitone's machine... beyond that is not my problem I am afraid. Once it leaves my hands in the final state, it's gone...

Update: Last task of the day... decided to investigate the Doppler effects in the sound engine, got them working with surprisingly very little resistance. If you drive fast towards an oncoming steam train, the sound reacts as you would expect with it dropping in pitch as you go past it.

Update 16th February 2021: The mod now works with the crappy SHVDN versions beyond v2.10.10. Why, you might ask, after everything I have said? Because I am better than them at what I do. I might not be as technically minded, or clever at coding... but I am better at putting the user front and centre when I put my mind to it. That's the defining difference between a Professional doing things for the passion of doing them and an Amateur doing it for the glory and ego-boosting and I have spent almost 40 years being a professional developer.

Update 17th February 2021: I have now added an adjustment menu option to the mod to assist with fine tuning certain offsets with the moving parts. Because of the inter-dependancy of some of the parts, I couldn't do a progressive build as some parts are created during train initialisation, so it has to be aware of all parts it can use at that point. This means some level of manual configuration to the XML file but it shouldn't be a problem.

What it means though is that getting the bones in exactly the right place isn't quite so critical and if you need to adjust a rod length to alter the animation slightly, you can do without having to change a file and then reset the scripts to reload everything.

A video might follow shortly...

Update 20th February 2021: It's funny how my mentality has shifted so far over the past few years. I was asked to add something to the mod today and my immediate instinct was a defensive "No, that can't be done" without any consideration of whether it could be done or not. A few years back, it would have been the complete opposite because I was convinced anything could be done. As it turns out, not only has it been almost done but it was easier than I thought to do it.

Latest major changes to have been added to the mod are the addition of whistle/horn effects and the potential for people to be able to add custom sounds.

The whistle uses a look-ahead area and if it detects the player within that area, the whistle will sound. Once that is done, it will wait a random period of time, between 30 and 45 seconds before it will check again. This was mainly to avoid it constantly blowing the whistle if you were driving or riding alongside the train. It works pretty well and it's a nice interaction between the train and player that wasn't there before. I plan to extend the whistle functionality to better represent the regulations regarding whistle usage at grade crossings. I'd like to add the bell as well if possible.

As for custom sounds, the train "chuff" system is based on 5 sounds being present to represent several speed segments. Within the sounds folder, there is the potential to add a folder based on the name of the model. If the sound system finds a matching folder for the train it is using, then it will use the sounds in that folder instead of the default ones. If any of the sounds are missing, then it will default that particular sound back to the generic version instead.

Continued to Part 2