Outerra forum

User mods, screenshots & videos => Vehicles => Topic started by: giucam on May 03, 2013, 10:19:43 am

Title: Vehicle script [version 1.4.0]
Post by: giucam on May 03, 2013, 10:19:43 am
Lately many vehicles have been imported in Outerra, but, while many use the same script as a base, there is not a shared effort toward a good vehicle script, and improvements can get lost in some vehicle's thread without them being applied to other vehicles.

I cannot model nor i understand how to import a model in OT, but i can develop, so i though it was time to start the ball running.
I took my script from my BMW thread (http://www.outerra.com/forum/index.php?topic=1352.0) and i updated it to support and use the new keys:

See here (http://www.outerra.com/forum/index.php?topic=1721.msg21821#msg21821) for the latest version!
You can freely use, modify and redistribute it for your project, the only requirement is to keep the license header, with the copyright assignment.


Code: [Select]
/*
    Vehicle script file
    Copyright (C) 2012 - 2013  Giulio Camuffo <giuliocamuffo@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 3.0 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//see [url]http://xtrac.outerra.com/index.fcgi/wiki/vehicle[/url] for example and documentation

// VERSION 1.1

//invoked only the first time the model is loaded, or upon reload
function init_chassis(){
    var wheelparam = {
        radius: 0.30,
        width: 0.20,
        suspension_max: 0.1,
        suspension_min: -0.05,
        suspension_stiffness: 50.0,
        damping_compression: 0.06,
        damping_relaxation: 0.05,
        slip: 5.0,
        roll_influence: 0.1,
        rotation: -1
    };
    this.add_wheel('wheel_FL', wheelparam);
    this.add_wheel('wheel_FR', wheelparam);
    this.add_wheel('wheel_RL', wheelparam);
    this.add_wheel('wheel_RR', wheelparam);

    this.load_sound("e90_startup.ogg"); //startup
    this.load_sound("e90_onidle.ogg"); //idle
    this.load_sound("e90_offlow.ogg");    //off
    this.load_sound("e90_onlow.ogg"); //on
    this.basepitch = [ 1, 1, 0.4, 0.4 ];             //base pitch for start, idle, off, on
    this.add_sound_emitter("wheel_FL");

    //engine properties -- modify these to change the engine behaviour
    this.wheelRadius = 0.24;

    this.torquePoints = [ { rpm: 0,    torque: 180},
                          { rpm: 1000, torque: 250},
                          { rpm: 2000, torque: 335},
                          { rpm: 3000, torque: 380},
                          { rpm: 4000, torque: 400},
                          { rpm: 5000, torque: 395},
                          { rpm: 6000, torque: 400},
                          { rpm: 7000, torque: 390},
                          { rpm: 8000, torque: 365},
                          { rpm: 9000, torque: 0  } ];

    this.forwardGears = [ { ratio: 4.06, shiftUp: 8000, shiftDown: -1   },
                          { ratio: 2.40, shiftUp: 8000, shiftDown: 4500 },
                          { ratio: 1.58, shiftUp: 8000, shiftDown: 5000 },
                          { ratio: 1.19, shiftUp: 8000, shiftDown: 5500 },
                          { ratio: 1.00, shiftUp: 8000, shiftDown: 6000 },
                          { ratio: 0.87, shiftUp: -1,   shiftDown: 6000 } ];
    this.reverseGears = [ { ratio: 4.68, shiftUp: -1, shiftDown: -1 } ];

    this.differentialRatio = 3.85;
    this.efficiency = 0.7;
    this.speedLimiter = -1; // speed in m/s. a value < 0 means there's no limiter
    this.engineBrakeCoefficient = 0.1;
    this.breakingForce = 5000.0;
    this.pitchMultiplier = 1.0;
    this.automaticTransmission = false;
    //end of engine properties

    this.maxPowerTP = this.torquePoints[0];
    this.maxRPM = 0;
    var maxPw = 0;
    for (var i = 1; i < this.torquePoints.length; ++i) {
        var tp = this.torquePoints[i];
        if (tp.rpm * tp.torque > maxPw) {
            this.maxPowerTP = tp;
        }
        if (tp.rpm > this.maxRPM) {
            this.maxRPM = tp.rpm;
        }
    }

    this.neutroGear = { ratio: 0.0, shiftUp: -1, shiftDown: -1, index: 0 };
    var prev = null;
    for (var i = 0; i < this.forwardGears.length; ++i) {
        var gear = this.forwardGears[i];
        if (prev)
            prev.next = gear;
        gear.prev = prev;
        gear.index = i + 1;
        prev = gear;
    }

    prev = null;
    for (var i = 0; i < this.reverseGears.length; ++i) {
        var gear = this.reverseGears[i];
        if (prev)
            prev.prev = gear;
        gear.next = prev;
        gear.index = -i - 1;
        prev = gear;
    }

    this.torque = engineTorque;
    this.shiftUp = shift_up;
    this.shiftDown = shift_down;

    return {mass:1570, steering:1.0, steering_ecf:10000, centering: 2.0, centering_ecf: 10};
}

//invoked for each new instance of the vehicle
function init_vehicle() {
    this.set_fps_camera_pos({x:-0.33,y:-0.25,z:1.15});

    this.snd = this.sound();
    this.snd.set_ref_distance(0, 9.0);

    this.started = false;
    this.gear = this.neutroGear;
    this.direction = 0;
}

//invoked when engine starts or stops
function engine(start) {
    if (start) {
        this.started = true;
        this.sound = 0;
        this.snd.play(0, 0, false, false);
    }
}

function engineTorque(rpm) {
    var min = 0;
    var max = this.torquePoints.length - 1;

    if (rpm > this.torquePoints[max].rpm || rpm < this.torquePoints[min].rpm)
        return 0;

    while (max - min > 1) {
        var mid = Math.floor(min + (max - min) / 2);
        var tp = this.torquePoints[mid];
        if (tp.rpm == rpm) {
            return tp.torque;
        } else if (tp.rpm > rpm) {
            max = mid;
        } else {
            min = mid;
        }
    }

    var minTp = this.torquePoints[min];
    var maxTp = this.torquePoints[max];
    var TM = maxTp.torque;
    var Tm = minTp.torque;
    var RM = maxTp.rpm;
    var Rm = minTp.rpm;

    var a = (TM - Tm) / (RM - Rm);

    return rpm * a + Tm - Rm * a;
}

function sign(x) {
    return (x > 0.0 ? 1 : (x < 0.0 ? -1 : 0));
}

function action(key, value, dt)
{
    if (key == AAuxb1 && value == 0) {
        this.automaticTransmission = !this.automaticTransmission;
        this.log_inf("Automatic transmission " + (this.automaticTransmission ? "enabled" : "disabled"));
    }

    if (value == 1 && !this.automaticTransmission) {
        switch(key) {
            case AShiftUp:
                this.shiftUp();
                break;
            case AShiftDown:
                this.shiftDown();
                break;
            default:
                break;
        }
    }
}

function shift_up()
{
    var i = this.gear.index;
    if (this.gear.next) {
        this.gear = this.gear.next;
    } else if (i == 0) {
        this.gear = this.forwardGears[0];
    } else if (i == -1) {
        this.gear = this.neutroGear;
    } else {
        return;
    }

    this.log_inf("gear " + i + " => " + this.gear.index);
}

function shift_down()
{
    var i = this.gear.index;
    if (this.gear.prev) {
        this.gear = this.gear.prev;
    } else if (i == 0) {
        this.gear = this.reverseGears[0];
    } else if (i == 1) {
        this.gear = this.neutroGear;
    } else {
        return;
    }

    this.log_inf("gear " + i + " => " + this.gear.index);
}

//invoked each frame to handle the inputs and animate the model
function update_frame(dt, engine, brake, steering) {
    steering *= 0.6;
    this.steer(-2, steering);

    var wheelsRpm = this.max_rpm();

    var rpm = wheelsRpm * this.gear.ratio * this.differentialRatio;
    if (this.automaticTransmission) {
        if (rpm < 10 && this.gear.index != 0) {
            this.gear = this.neutroGear;
            this.direction = 0;
            this.log_inf("neutro");
        }
        if (engine != 0.0) {
            if (sign(engine) != this.direction) {
                this.direction = sign(engine);
                if (this.direction == 1.0) {
                    this.shiftUp();
                } else if (this.direction == -1.0) {
                    this.shiftDown();
                }
                rpm = wheelsRpm * this.gear.ratio * this.differentialRatio;
            }

            if (rpm > this.gear.shiftUp && this.gear.next) {
                this.shiftUp();
            } else if (rpm < this.gear.shiftDown && this.gear.prev) {
                this.shiftDown();
            }
        }
    } else if (engine < 0) {
        engine = -engine;
    }

    rpm = wheelsRpm * this.gear.ratio * this.differentialRatio;

    var engineStarted = this.started && (this.sound > 0 || (this.sound == 0 && !this.snd.is_playing(0)));

    if (engineStarted && (this.speedLimiter < 0 || this.speed < this.speedLimiter)) {
        var force = engine * this.torque(rpm) * this.gear.ratio * this.differentialRatio * this.efficiency / this.wheelRadius;
        if (!this.automaticTransmission && this.gear.index < 0)
            force = -force;
        this.wheel_force(2, force);
        this.wheel_force(3, force);
    }

    //From Torcs engine code
    var static_friction = 0.1;
    var engineBrake = this.maxPowerTP.torque * this.engineBrakeCoefficient * this.differentialRatio * this.gear.ratio *
                      (static_friction + (1.0 - static_friction) * rpm / this.maxRPM) / this.wheelRadius;

    brake *= this.breakingForce;
    this.wheel_brake(-1, brake + engineBrake);

    this.animate_wheels();

    if (engineStarted) {
        if (this.gear.index == 0 && (!this.snd.is_playing(0) || this.sound != 1)) {
            this.snd.stop(0);
            this.
Title: Re: Vehicle script [version 1.1]
Post by: cameni on May 03, 2013, 10:27:29 am
P.S. cameni, can we have some way to write on screen? I'd like to write the current gear.
That fading log? .. yep, will add it.
Title: Re: Vehicle script [version 1.1]
Post by: giucam on May 03, 2013, 10:34:35 am
Yeah, but also a thing like that but not fading.
Title: Re: Vehicle script [version 1.1]
Post by: ZeosPantera on May 03, 2013, 11:50:17 am
I think me means more like at the very bottom center a permanent

P  R  N  D  3  2  1

And I always envisioned a highlight box around whatever gear you are in.. So "[P] R N D 3 2 1" The contents would be variable per car also.. Some cars only have 3 gears.. some have 12.
Title: Re: Vehicle script [version 1.1]
Post by: Chaoz on May 03, 2013, 01:44:37 pm
a way to change the ui on script basis should be possible
Title: Re: Vehicle script [version 1.1]
Post by: ZeosPantera on May 03, 2013, 02:10:37 pm
A few comment on your script. Why does everyone put the slip values so high? Only on the Tiger2 Tank have I gone to 1.1 for the forward slip because.. its a tank. Between 0.6 and 0.8 is a pretty normal value in my testing for realism on the road and on the grass. The BMW getting a bit of tire spin off the line, etc. Obviously when the ground helps determine grip there will need to be a few tweaks to all scripts.

You also didn't add the lateral slip value. "slip_lateral_coef: 1.0" which is important for making cars turn more realistically while keeping the normal slip value down.
Title: Re: Vehicle script [version 1.1]
Post by: cameni on May 03, 2013, 02:39:28 pm
Do you know that slip_lateral_coef: 1.0 actually gets you back to the time when there was no distinction between the lateral and longitudinal slip, since this coef is a multiplier of the normal (longitudinal) slip value?
Title: Re: Vehicle script [version 1.1]
Post by: ZeosPantera on May 03, 2013, 02:53:36 pm
The hell you say.. Well on some vehicles it works. Usually the lateral grip needs to be higher than forward due to the bending of the tire's edge, digging in to the road surface as the weight shifts and she rolls.
Title: Re: Vehicle script [version 1.1]
Post by: mLichy on May 03, 2013, 04:00:56 pm
A few comment on your script. Why does everyone put the slip values so high? Only on the Tiger2 Tank have I gone to 1.1 for the forward slip because.. its a tank. Between 0.6 and 0.8 is a pretty normal value in my testing for realism on the road and on the grass. The BMW getting a bit of tire spin off the line, etc. Obviously when the ground helps determine grip there will need to be a few tweaks to all scripts.

You also didn't add the lateral slip value. "slip_lateral_coef: 1.0" which is important for making cars turn more realistically while keeping the normal slip value down.


Well, I might have to turn mine down more personally, but I want the car to slip/start to slide a little, going 45MPH or so around a corner.  I feel that is pretty realistic, or even too fast for some cars to maintain grip.  Maybe for a lower riding car with thin walled street tires and hard suspension, it could still grip well at 45+MPH.   

But, I don't think it's as fun to have a car that always grips super well.  Becomes more boring to drive, since it's pretty predictable.  Also, it's fun to drift around corners.  But actually drift, and not slide the entire car like you're on ice. 

Anyways, great Idea giucam, thanks for posting this. 
Title: Re: Vehicle script [version 1.1]
Post by: Sunnyyello on May 03, 2013, 04:38:23 pm
A few comment on your script. Why does everyone put the slip values so high? Only on the Tiger2 Tank have I gone to 1.1 for the forward slip because.. its a tank. Between 0.6 and 0.8 is a pretty normal value in my testing for realism on the road and on the grass. The BMW getting a bit of tire spin off the line, etc. Obviously when the ground helps determine grip there will need to be a few tweaks to all scripts.

You also didn't add the lateral slip value. "slip_lateral_coef: 1.0" which is important for making cars turn more realistically while keeping the normal slip value down.


Well, I might have to turn mine down more personally, but I want the car to slip/start to slide a little, going 45MPH or so around a corner.  I feel that is pretty realistic, or even too fast for some cars to maintain grip.  Maybe for a lower riding car with thin walled street tires and hard suspension, it could still grip well at 45 MPH.   

But, I don't think it's as fun to have a car that always grips super well.  Becomes more boring to drive, since it's pretty predictable.  Also, it's fun to drift around corners.  But actually drift, and not slide the entire car like you're on ice. 

Anyways, great Idea giucam, thanks for posting this. 


[youtube]HQ7R_buZPSo[/youtube]

I think Mr. Ken Block would agree. I know it's old but any excuse to post it is a good one!  8)

Title: Re: Vehicle script [version 1.1]
Post by: giucam on May 03, 2013, 06:45:17 pm
A few comment on your script. Why does everyone put the slip values so high? Only on the Tiger2 Tank have I gone to 1.1 for the forward slip because.. its a tank. Between 0.6 and 0.8 is a pretty normal value in my testing for realism on the road and on the grass. The BMW getting a bit of tire spin off the line, etc. Obviously when the ground helps determine grip there will need to be a few tweaks to all scripts.

You also didn't add the lateral slip value. "slip_lateral_coef: 1.0" which is important for making cars turn more realistically while keeping the normal slip value down.

I cannot test the car behaviour at all, so that is just a dummy value at the moment.
Title: Re: Vehicle script [version 1.1]
Post by: mLichy on May 04, 2013, 10:20:19 am

Edit:  Forget this post.
Title: Re: Vehicle script [version 1.2]
Post by: giucam on May 08, 2013, 04:36:16 pm
Version 1.2.

The main change is the addition of the clutch. You can now rev up the engine in neutral, and releasing the clutch when still would stall the engine (the code makes sure that doesn't happen though. still, you can disable the automatic clutch by setting this.automaticClutch to false).
The automatic clutch may still feel a little slow though, it's not trivial to do an efficient algorithm.

The engine provides now 0 torque at 0 RPM, so there is a minimum RPM and a minimum quantity of "fuel" the engine takes to make sure it doesn't go below that limit.

I'd like to implement manual clutch, but unfortunately i don't have a wheel with one, and using a key is just wrong. :/

Code: [Select]
/*
    Vehicle script file
    Copyright (C) 2012 - 2013  Giulio Camuffo <giuliocamuffo@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 3.0 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//see http://xtrac.outerra.com/index.fcgi/wiki/vehicle for example and documentation

// VERSION 1.2

//invoked only the first time the model is loaded, or upon reload
function init_chassis(){
    var wheelparam = {
        radius: 0.30,
        width: 0.20,
        suspension_max: 0.1,
        suspension_min: -0.05,
        suspension_stiffness: 50.0,
        damping_compression: 0.06,
        damping_relaxation: 0.05,
        slip: 5.0,
        roll_influence: 0.1,
        rotation: -1
    };

    this.add_wheel('wheel_FL', wheelparam);
    this.add_wheel('wheel_FR', wheelparam);
    this.add_wheel('wheel_RL', wheelparam);
    this.add_wheel('wheel_RR', wheelparam);

    this.load_sound("e90_startup.ogg"); //startup
    this.load_sound("e90_onidle.ogg"); //idle
    this.load_sound("e90_offlow.ogg"); //off
    this.load_sound("e90_onlow.ogg"); //on
    this.basepitch = [ 1, 1, 0.4, 0.4 ];             //base pitch for start, idle, off, on
    this.add_sound_emitter("wheel_FL");

    //engine properties -- modify these to change the engine behaviour
    this.wheelRadius = 0.24;

    this.torquePoints = [ { rpm: 200,    torque: 0},
                          { rpm: 1000, torque: 250},
                          { rpm: 2000, torque: 335},
                          { rpm: 3000, torque: 380},
                          { rpm: 4000, torque: 400},
                          { rpm: 5000, torque: 395},
                          { rpm: 6000, torque: 400},
                          { rpm: 7000, torque: 390},
                          { rpm: 8000, torque: 365},
                          { rpm: 9000, torque: 0  } ];

    this.forwardGears = [ { ratio: 4.06, shiftUp: 8000, shiftDown: -1   },
                          { ratio: 2.40, shiftUp: 8000, shiftDown: 4500 },
                          { ratio: 1.58, shiftUp: 8000, shiftDown: 5000 },
                          { ratio: 1.19, shiftUp: 8000, shiftDown: 5500 },
                          { ratio: 1.00, shiftUp: 8000, shiftDown: 6000 },
                          { ratio: 0.87, shiftUp: -1,   shiftDown: 6000 } ];
    this.reverseGears = [ { ratio: 4.68, shiftUp: -1, shiftDown: -1 } ];

    this.differentialRatio = 3.85;
    this.efficiency = 0.9;
    this.speedLimiter = 5; // maximum speed in m/s. a value < 0 means there's no limiter
    this.engineBrakeCoefficient = 0.4;
    this.breakingForce = 5000.0;
    this.pitchMultiplier = 1.5;
    this.automaticTransmission = false;
    this.automaticClutch = true;
    this.engineInertia = 0.05;
    this.minimumRPM = 1000;
    this.minimum = 0.2;
    //end of engine properties

    this.maxPowerTP = this.torquePoints[0];
    this.maxRPM = 0;
    var maxPw = 0;
    for (var i = 1; i < this.torquePoints.length; ++i) {
        var tp = this.torquePoints[i];
        if (tp.rpm * tp.torque > maxPw) {
            this.maxPowerTP = tp;
        }
        if (tp.rpm > this.maxRPM) {
            this.maxRPM = tp.rpm;
        }
    }

    this.neutroGear = { ratio: 0.0, shiftUp: -1, shiftDown: -1, index: 0 };
    var prev = null;
    for (var i = 0; i < this.forwardGears.length; ++i) {
        var gear = this.forwardGears[i];
        if (prev)
            prev.next = gear;
        gear.prev = prev;
        gear.index = i + 1;
        prev = gear;
    }

    prev = null;
    for (var i = 0; i < this.reverseGears.length; ++i) {
        var gear = this.reverseGears[i];
        if (prev)
            prev.prev = gear;
        gear.next = prev;
        gear.index = -i - 1;
        prev = gear;
    }

    this.torque = engineTorque;
    this.shiftUp = shift_up;
    this.shiftDown = shift_down;
    this.selectGear = select_gear;
    this.die = die;
    this.updateAutomaticTransmission = automatic_transmission;
    this.animate = animate;

    return {mass:1570, steering:1.0, steering_ecf:10000, centering: 2.0, centering_ecf: 10};
}

//invoked for each new instance of the vehicle
function init_vehicle() {
    this.set_fps_camera_pos({x:-0.33,y:-0.25,z:1.15});

    this.snd = this.sound();
    this.snd.set_ref_distance(0, 9.0);

    this.started = false;
    this.gear = this.neutroGear;
    this.direction = 0;
    this.engineRPM = 0;
    this.appliedTorque = 0;
    this.starting = false;
    this.clutch = 0.0;
    this.clutchState = 1;
}

//invoked when engine starts or stops
function engine(start) {
    if (start) {
        this.started = false;
        this.soundId = 0;
        this.starting = true;
    } else {
        this.started = false;
    }
}

function engineTorque(rpm) {
    var min = 0;
    var max = this.torquePoints.length - 1;

    if (rpm > this.torquePoints[max].rpm || rpm < this.torquePoints[min].rpm)
        return 0;

    while (max - min > 1) {
        var mid = Math.floor(min + (max - min) / 2);
        var tp = this.torquePoints[mid];
        if (tp.rpm == rpm) {
            return tp.torque;
        } else if (tp.rpm > rpm) {
            max = mid;
        } else {
            min = mid;
        }
    }

    var minTp = this.torquePoints[min];
    var maxTp = this.torquePoints[max];
    var TM = maxTp.torque;
    var Tm = minTp.torque;
    var RM = maxTp.rpm;
    var Rm = minTp.rpm;

    var a = (TM - Tm) / (RM - Rm);

    return rpm * a + Tm - Rm * a;
}

function sign(x) {
    return (x > 0.0 ? 1 : (x < 0.0 ? -1 : 0));
}

function action(key, value, dt)
{
    if (key == AAuxb1 && value == 0) {
        this.automaticTransmission = !this.automaticTransmission;
        this.log_inf("Automatic transmission " + (this.automaticTransmission ? "enabled" : "disabled"));
    }

    if (value == 1 && !this.automaticTransmission) {
        switch(key) {
            case AShiftUp:
                this.shiftUp();
                break;
            case AShiftDown:
                this.shiftDown();
                break;
            default:
                break;
        }
    }
}

function select_gear(gear)
{
    var i = this.gear.index;
    this.gear = gear;
    this.log_inf("gear " + i + " => " + this.gear.index);
}

function shift_up()
{
    var i = this.gear.index;
    if (this.gear.next) {
        this.selectGear(this.gear.next);
    } else if (i == 0) {
        this.selectGear(this.forwardGears[0]);
    } else if (i == -1) {
        this.selectGear(this.neutroGear);
    }
}

function shift_down()
{
    var i = this.gear.index;
    if (this.gear.prev) {
        this.selectGear(this.gear.prev);
    } else if (i == 0) {
        this.selectGear(this.reverseGears[0]);
    } else if (i == 1) {
        this.selectGear(this.neutroGear);
    }
}

function die()
{
    this.started = false;
}

function automatic_transmission(engine)
{
    if (this.engineRPM < 10 && this.gear.index != 0) {
        this.gear = this.neutroGear;
        this.direction = 0;
        this.selectGear(this.neutroGear);
    }
    if (engine != 0.0) {
        if (sign(engine) != this.direction) {
            this.direction = sign(engine);
            if (this.direction == 1.0) {
                this.selectGear(this.forwardGears[0]);
            } else if (this.direction == -1.0) {
                this.selectGear(this.reverseGears[0]);
            }
        }

        if (this.engineRPM > this.gear.shiftUp && this.gear.next) {
            this.shiftUp();
        } else if (this.engineRPM < this.gear.shiftDown && this.gear.prev) {
            this.shiftDown();
        }
    }
}

function animate()
{
    this.animate_wheels();
}

//invoked each frame to handle the inputs and animate the model
function update_frame(dt, engine, brake, steering) {
    steering *= 0.6;
    this.steer(-2, steering);

    var wheelsRpm = this.max_rpm();
    if (sign(this.speed()) != sign(this.gear.index) && this.gear.index != 0) {
        wheelsRpm = 0;
    }

    if (this.automaticTransmission) {
        this.updateAutomaticTransmission(engine);
    }
    if (engine < 0) {
        engine = -engine;
    }

    var clutched = this.clutchState == 1 && this.gear.index != 0;

    var clutchRPM = this.minimumRPM + 2000;
    if (this.automaticClutch) {
        if (clutched && this.engineRPM < this.minimumRPM) {
            this.clutchState = 0;
        } else if (!clutched && this.engineRPM > clutchRPM) {
            this.clutchState = 1;
        }
        clutched = this.clutchState == 1 && this.gear.index != 0;
    }

    var w = wheelsRpm * this.gear.ratio * this.differentialRatio;
    if (clutched) {
        this.clutch = (w > this.minimumRPM ? this.minimumRPM : w) / this.minimumRPM;

        // This makes an horizontal S-like shape. (mirrored, like Z)
        var f = this.clutch;
        if (f < 0.5) {
            f = f * 2 - 1;
            this.clutch = -(f * f * f * f - 1) / 5;
        } else {
            f = 2 * f - 1;
            this.clutch = f * f * f * f + 0.2;
        }
        this.clutch += 0.2;

        var r = this.engineRPM / clutchRPM;
        this.clutch *= (r > 1 ? 1 : (r < 0 ? 0 : r));

        if (this.clutch > 1) {
            this.clutch = 1;
        }
    } else if (!clutched) {
        this.clutch = 0;
    }

    this.appliedTorque = 0;
    if (this.starting) {
        if (this.engineRPM > this.minimumRPM) {
            this.starting = false;
            this.started = true;
        } else {
            this.appliedTorque = 100 * (1 - this.engineRPM / this.minimumRPM);
        }
    }

    var oldRPM = this.engineRPM;

    if (this.gear.index != 0) {
        this.engineRPM = this.engineRPM * (1 - this.clutch) + wheelsRpm * this.clutch * this.gear.ratio * this.differentialRatio;
    }

    var engineStarted = this.started;
    if (engineStarted || this.starting) {
        if (this.engineRPM < this.minimumRPM && engine < this.minimum) {
            engine = this.minimum;
        }
        if (Math.abs(this.speed()) < this.speedLimiter) {
            this.appliedTorque += engine * this.torque(this.engineRPM);
        }
    }

    var friction = 0.1;
    this.appliedTorque -= sign(this.engineRPM) * this.maxPowerTP.torque * this.engineBrakeCoefficient *
                          (friction + (1.0 - friction) * this.engineRPM / this.maxRPM);

    this.engineRPM = this.engineRPM + this.appliedTorque * dt / this.engineInertia;
    if (oldRPM < 300) {
        this.die();
    }

    var tq = this.appliedTorque + this.engineInertia * (oldRPM - this.engineRPM) / dt;
    var force = tq * this.clutch * this.gear.ratio * this.differentialRatio * this.efficiency / this.wheelRadius;
    if (this.gear.index < 0)
        force = -force;

    this.wheel_force(2, force / 2);
    this.wheel_force(3, force / 2);

    brake *= this.breakingForce;
    this.wheel_brake(-1, brake);

    this.animate();

    if (this.gear.index == 0 && (!this.snd.is_playing(0) || this.soundId != 1)) {
        this.snd.stop(0);
        this.snd.play(0, 1, true, false);
        this.soundId = 1;
    } else if (this.gear.index != 0 && engine != 1 && (!this.snd.is_playing(0) || this.soundId != 2)) { //idle
        this.snd.stop(0);
        this.snd.play(0, 2, true, false);
        this.soundId = 2;
    } else if (this.gear.index != 0 && engine == 1 && (!this.snd.is_playing(0) || this.soundId != 3)) { //throttle
        this.snd.stop(0);
        this.snd.play(0, 3, true, false);
        this.soundId = 3;
    }

    var pitch = this.pitchMultiplier * (this.engineRPM - this.minimumRPM) / this.maxRPM;
    this.snd.set_pitch(0, pitch + this.basepitch[this.soundId]);
    var g = this.engineRPM / this.minimumRPM;
    this.snd.set_gain(0, Math.min(g * g * g, 1.0));
}
Title: Re: Vehicle script [version 1.2]
Post by: M7 on May 08, 2013, 10:43:10 pm
Just tried it, didn't figured the clutch yet so i just put it to automatic clutch to quickly testdrive,  but in either mode, the car moves like  molass  ???   

Also i'm wondering if you could reimport the BMW and include another bone other than the wheels. I'd like to test other sounds using a manual transmission and the BMW being the only vehicle with  manual transmission. The problem is that you set the sound emiter to one of the wheel . Doesn't seems to be problematic with the default sound but when  replace it , it sound really weird  because when the wheel turn, the sound kind of woble.
Title: Re: Vehicle script [version 1.2]
Post by: mLichy on May 08, 2013, 11:06:32 pm
Yeah. I don't think we want to attach the engine audio to the wheel?
I'd had the same issue. Mines attached to the cars hood for now.

Even for tire slip audio, we might want to attach to the suspension or chassis maybe. Rotating audio like that sounds wrong.
Title: Re: Vehicle script [version 1.2]
Post by: M7 on May 09, 2013, 12:03:38 am
Well i just tried giucam manual transmission script (without clutch) with mLichy's Belair so i can test sounds on a manual transmission script  without the wobling. Only problem i can load the Belair only once, if i get off a cliff and have to reload a new one i get this error message.
(http://imageshack.us/a/img254/9472/13133442.jpg)

But so far i just love the manual transmission script. I made some windy road in the mountains and the driving is great. Can wait to test the updated clutch version.
Title: Re: Vehicle script [version 1.2]
Post by: giucam on May 09, 2013, 06:00:32 am
Just tried it, didn't figured the clutch yet so i just put it to automatic clutch to quickly testdrive,  but in either mode, the car moves like  molass  ???
That is, the engine doesn't rev up? Only when starting or always? Are you starting in first gear?

Also i'm wondering if you could reimport the BMW and include another bone other than the wheels. I'd like to test other sounds using a manual transmission and the BMW being the only vehicle with  manual transmission. The problem is that you set the sound emiter to one of the wheel . Doesn't seems to be problematic with the default sound but when  replace it , it sound really weird  because when the wheel turn, the sound kind of woble.
That totally makes sense, but i don't know how to import the BMW, and i'd rather like not to learn to :P. I took the BMW imported by cameni, so if someone can import it again i'd be very thankful!

Quote
Well i just tried giucam manual transmission script (without clutch) with mLichy's Belair so i can test sounds on a manual transmission script  without the wobling. Only problem i can load the Belair only once, if i get off a cliff and have to reload a new one i get this error message.
Does that happen with the 1.2 script too?
Title: Re: Vehicle script [version 1.2.1]
Post by: giucam on May 09, 2013, 06:32:38 am
Just tried it, didn't figured the clutch yet so i just put it to automatic clutch to quickly testdrive,  but in either mode, the car moves like  molass  ???   

I made a silly mistake when cleaning up the script, fixed now.
Code: [Select]
/*
    Vehicle script file
    Copyright (C) 2012 - 2013  Giulio Camuffo <giuliocamuffo@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 3.0 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//see http://xtrac.outerra.com/index.fcgi/wiki/vehicle for example and documentation

// VERSION 1.2.1

//invoked only the first time the model is loaded, or upon reload
function init_chassis(){
    var wheelparam = {
        radius: 0.30,
        width: 0.20,
        suspension_max: 0.1,
        suspension_min: -0.05,
        suspension_stiffness: 50.0,
        damping_compression: 0.06,
        damping_relaxation: 0.05,
        slip: 5.0,
        roll_influence: 0.1,
        rotation: -1
    };

    this.add_wheel('wheel_FL', wheelparam);
    this.add_wheel('wheel_FR', wheelparam);
    this.add_wheel('wheel_RL', wheelparam);
    this.add_wheel('wheel_RR', wheelparam);

    this.load_sound("e90_startup.ogg"); //startup
    this.load_sound("e90_onidle.ogg"); //idle
    this.load_sound("e90_offlow.ogg"); //off
    this.load_sound("e90_onlow.ogg"); //on
    this.basepitch = [ 1, 1, 0.4, 0.4 ];             //base pitch for start, idle, off, on
    this.add_sound_emitter("wheel_FL");

    //engine properties -- modify these to change the engine behaviour
    this.wheelRadius = 0.24;

    this.torquePoints = [ { rpm: 200,    torque: 0},
                          { rpm: 1000, torque: 250},
                          { rpm: 2000, torque: 335},
                          { rpm: 3000, torque: 380},
                          { rpm: 4000, torque: 400},
                          { rpm: 5000, torque: 395},
                          { rpm: 6000, torque: 400},
                          { rpm: 7000, torque: 390},
                          { rpm: 8000, torque: 365},
                          { rpm: 9000, torque: 0  } ];

    this.forwardGears = [ { ratio: 4.06, shiftUp: 8000, shiftDown: -1   },
                          { ratio: 2.40, shiftUp: 8000, shiftDown: 4500 },
                          { ratio: 1.58, shiftUp: 8000, shiftDown: 5000 },
                          { ratio: 1.19, shiftUp: 8000, shiftDown: 5500 },
                          { ratio: 1.00, shiftUp: 8000, shiftDown: 6000 },
                          { ratio: 0.87, shiftUp: -1,   shiftDown: 6000 } ];
    this.reverseGears = [ { ratio: 4.68, shiftUp: -1, shiftDown: -1 } ];

    this.differentialRatio = 3.85;
    this.efficiency = 0.9;
    this.speedLimiter = -1; // maximum speed in m/s. a value < 0 means there's no limiter
    this.engineBrakeCoefficient = 0.4;
    this.breakingForce = 5000.0;
    this.pitchMultiplier = 1.5;
    this.automaticTransmission = false;
    this.automaticClutch = true;
    this.engineInertia = 0.05;
    this.minimumRPM = 1000;
    this.minimum = 0.2;
    //end of engine properties

    this.maxPowerTP = this.torquePoints[0];
    this.maxRPM = 0;
    var maxPw = 0;
    for (var i = 1; i < this.torquePoints.length; ++i) {
        var tp = this.torquePoints[i];
        if (tp.rpm * tp.torque > maxPw) {
            this.maxPowerTP = tp;
        }
        if (tp.rpm > this.maxRPM) {
            this.maxRPM = tp.rpm;
        }
    }

    this.neutroGear = { ratio: 0.0, shiftUp: -1, shiftDown: -1, index: 0 };
    var prev = null;
    for (var i = 0; i < this.forwardGears.length; ++i) {
        var gear = this.forwardGears[i];
        if (prev)
            prev.next = gear;
        gear.prev = prev;
        gear.index = i + 1;
        prev = gear;
    }

    prev = null;
    for (var i = 0; i < this.reverseGears.length; ++i) {
        var gear = this.reverseGears[i];
        if (prev)
            prev.prev = gear;
        gear.next = prev;
        gear.index = -i - 1;
        prev = gear;
    }

    this.torque = engineTorque;
    this.shiftUp = shift_up;
    this.shiftDown = shift_down;
    this.selectGear = select_gear;
    this.die = die;
    this.updateAutomaticTransmission = automatic_transmission;
    this.animate = animate;

    return {mass:1570, steering:1.0, steering_ecf:10000, centering: 2.0, centering_ecf: 10};
}

//invoked for each new instance of the vehicle
function init_vehicle() {
    this.set_fps_camera_pos({x:-0.33,y:-0.25,z:1.15});

    this.snd = this.sound();
    this.snd.set_ref_distance(0, 9.0);

    this.started = false;
    this.gear = this.neutroGear;
    this.direction = 0;
    this.engineRPM = 0;
    this.appliedTorque = 0;
    this.starting = false;
    this.clutch = 0.0;
    this.clutchState = 1;
}

//invoked when engine starts or stops
function engine(start) {
    if (start) {
        this.started = false;
        this.soundId = 0;
        this.starting = true;
    } else {
        this.started = false;
    }
}

function engineTorque(rpm) {
    var min = 0;
    var max = this.torquePoints.length - 1;

    if (rpm > this.torquePoints[max].rpm || rpm < this.torquePoints[min].rpm)
        return 0;

    while (max - min > 1) {
        var mid = Math.floor(min + (max - min) / 2);
        var tp = this.torquePoints[mid];
        if (tp.rpm == rpm) {
            return tp.torque;
        } else if (tp.rpm > rpm) {
            max = mid;
        } else {
            min = mid;
        }
    }

    var minTp = this.torquePoints[min];
    var maxTp = this.torquePoints[max];
    var TM = maxTp.torque;
    var Tm = minTp.torque;
    var RM = maxTp.rpm;
    var Rm = minTp.rpm;

    var a = (TM - Tm) / (RM - Rm);

    return rpm * a + Tm - Rm * a;
}

function sign(x) {
    return (x > 0.0 ? 1 : (x < 0.0 ? -1 : 0));
}

function action(key, value, dt)
{
    if (key == AAuxb1 && value == 0) {
        this.automaticTransmission = !this.automaticTransmission;
        this.log_inf("Automatic transmission " + (this.automaticTransmission ? "enabled" : "disabled"));
    }

    if (value == 1 && !this.automaticTransmission) {
        switch(key) {
            case AShiftUp:
                this.shiftUp();
                break;
            case AShiftDown:
                this.shiftDown();
                break;
            default:
                break;
        }
    }
}

function select_gear(gear)
{
    var i = this.gear.index;
    this.gear = gear;
    this.log_inf("gear " + i + " => " + this.gear.index);
}

function shift_up()
{
    var i = this.gear.index;
    if (this.gear.next) {
        this.selectGear(this.gear.next);
    } else if (i == 0) {
        this.selectGear(this.forwardGears[0]);
    } else if (i == -1) {
        this.selectGear(this.neutroGear);
    }
}

function shift_down()
{
    var i = this.gear.index;
    if (this.gear.prev) {
        this.selectGear(this.gear.prev);
    } else if (i == 0) {
        this.selectGear(this.reverseGears[0]);
    } else if (i == 1) {
        this.selectGear(this.neutroGear);
    }
}

function die()
{
    this.started = false;
}

function automatic_transmission(engine)
{
    if (this.engineRPM < 10 && this.gear.index != 0) {
        this.gear = this.neutroGear;
        this.direction = 0;
        this.selectGear(this.neutroGear);
    }
    if (engine != 0.0) {
        if (sign(engine) != this.direction) {
            this.direction = sign(engine);
            if (this.direction == 1.0) {
                this.selectGear(this.forwardGears[0]);
            } else if (this.direction == -1.0) {
                this.selectGear(this.reverseGears[0]);
            }
        }

        if (this.engineRPM > this.gear.shiftUp && this.gear.next) {
            this.shiftUp();
        } else if (this.engineRPM < this.gear.shiftDown && this.gear.prev) {
            this.shiftDown();
        }
    }
}

function animate()
{
    this.animate_wheels();
}

//invoked each frame to handle the inputs and animate the model
function update_frame(dt, engine, brake, steering) {
    steering *= 0.6;
    this.steer(-2, steering);

    var wheelsRpm = this.max_rpm();
    if (sign(this.speed()) != sign(this.gear.index) && this.gear.index != 0) {
        wheelsRpm = 0;
    }

    if (this.automaticTransmission) {
        this.updateAutomaticTransmission(engine);
    }
    if (engine < 0) {
        engine = -engine;
    }

    var clutched = this.clutchState == 1 && this.gear.index != 0;

    var clutchRPM = this.minimumRPM + 2000;
    if (this.automaticClutch) {
        if (clutched && this.engineRPM < this.minimumRPM) {
            this.clutchState = 0;
        } else if (!clutched && this.engineRPM > clutchRPM) {
            this.clutchState = 1;
        }
        clutched = this.clutchState == 1 && this.gear.index != 0;
    }

    var w = wheelsRpm * this.gear.ratio * this.differentialRatio;
    if (clutched) {
        this.clutch = (w > this.minimumRPM ? this.minimumRPM : w) / this.minimumRPM;

        // This makes an horizontal S-like shape. (mirrored, like Z)
        var f = this.clutch * 2;
        if (f < 0.5) {
            this.clutch = -(f * f * f * f - 1) / 5;
        } else {
            f = f - 1;
            this.clutch = (f * f * f * f)  + 0.2;
        }
        this.clutch += 0.2;

        var r = this.engineRPM / clutchRPM;
        this.clutch *= (r > 1 ? 1 : (r < 0 ? 0 : r));

        if (this.clutch > 1) {
            this.clutch = 1;
        }
    } else if (!clutched) {
        this.clutch = 0;
    }

    this.appliedTorque = 0;
    if (this.starting) {
        if (this.engineRPM > this.minimumRPM) {
            this.starting = false;
            this.started = true;
        } else {
            this.appliedTorque = 100 * (1 - this.engineRPM / this.minimumRPM);
        }
    }

    var oldRPM = this.engineRPM;

    if (this.gear.index != 0) {
        this.engineRPM = this.engineRPM * (1 - this.clutch) + wheelsRpm * this.clutch * this.gear.ratio * this.differentialRatio;
    }

    var engineStarted = this.started;
    if (engineStarted || this.starting) {
        if (this.engineRPM < this.minimumRPM && engine < this.minimum) {
            engine = this.minimum;
        }
        if (this.speedLimiter < 0 || Math.abs(this.speed()) < this.speedLimiter) {
            this.appliedTorque += engine * this.torque(this.engineRPM);
        }
    }

    var friction = 0.1;
    this.appliedTorque -= sign(this.engineRPM) * this.maxPowerTP.torque * this.engineBrakeCoefficient *
                          (friction + (1.0 - friction) * this.engineRPM / this.maxRPM);

    this.engineRPM = this.engineRPM + this.appliedTorque * dt / this.engineInertia;
    if (oldRPM < 300) {
        this.die();
    }

    var tq = this.appliedTorque + this.engineInertia * (oldRPM - this.engineRPM) / dt;
    var force = tq * this.clutch * this.gear.ratio * this.differentialRatio * this.efficiency / this.wheelRadius;
    if (this.gear.index < 0) {
        force = -force;
    }

    this.wheel_force(2, force / 2);
    this.wheel_force(3, force / 2);

    brake *= this.breakingForce;
    this.wheel_brake(-1, brake);

    this.animate();

    if (this.gear.index == 0 && (!this.snd.is_playing(0) || this.soundId != 1)) {
        this.snd.stop(0);
        this.snd.play(0, 1, true, false);
        this.soundId = 1;
    } else if (this.gear.index != 0 && engine != 1 && (!this.snd.is_playing(0) || this.soundId != 2)) { //idle
        this.snd.stop(0);
        this.snd.play(0, 2, true, false);
        this.soundId = 2;
    } else if (this.gear.index != 0 && engine == 1 && (!this.snd.is_playing(0) || this.soundId != 3)) { //throttle
        this.snd.stop(0);
        this.snd.play(0, 3, true, false);
        this.soundId = 3;
    }

    var pitch = this.pitchMultiplier * (this.engineRPM - this.minimumRPM) / this.maxRPM;
    this.snd.set_pitch(0, pitch + this.basepitch[this.soundId]);
    var g = this.engineRPM / this.minimumRPM;
    this.snd.set_gain(0, Math.min(g * g * g, 1.0));
}
Title: Re: Vehicle script [version 1.2.1]
Post by: M7 on May 09, 2013, 09:40:55 am
Yep works perferctly now. Thanks!



Title: Re: Vehicle script [version 1.2.1]
Post by: ZeosPantera on May 09, 2013, 01:19:42 pm
I think a test vehicle needs to be modeled that has an external wheel or fan that represents the "Engine RPM" and then you could watch how it reacts to clutching and power loss etc.

Maybe go full-retard and import something like this::

(http://i.imgur.com/HLy1avH.jpg)


NO FREAKIN WAY.. http://sketchup.google.com/3dwarehouse/cldetails?mid=29422801b7c529217109178e924a3a02&ct=mdcc&prevstart=0 (http://sketchup.google.com/3dwarehouse/cldetails?mid=29422801b7c529217109178e924a3a02&ct=mdcc&prevstart=0)  Anyone feel like helping me with a little project?

Damn.. colz::mesh::optimize_mesh_indices@colz_mesh.cpp(641) Mesh "@0"cannot have more than 65535 vertices! (src mesh vertex count: 23077)
Title: Re: Vehicle script [version 1.2.1]
Post by: M7 on May 09, 2013, 04:00:39 pm
Ill make a quick import of the super car. It does look very cool, so much stuff to animate. it would look bassass if every moving part were animated.

wow this thing is quite complex, sketchup takes forever just to explode all groups/componants
Title: Re: Vehicle script [version 1.2.1]
Post by: M7 on May 09, 2013, 10:29:12 pm
Sorry the model has too many vertices , more than 65535 says the error message. which seems to be the limit  :'(

edit: Ah ok you beat me to it
Title: Re: Vehicle script [version 1.2.1]
Post by: ZeosPantera on May 09, 2013, 11:41:54 pm
It would need some work done to fix that. All those damn crown nuts.
Title: Re: Vehicle script [version 1.2.1]
Post by: krz9000 on May 10, 2013, 07:45:55 am
lego_technic_8880_supercar.fbx

https://docs.google.com/file/d/0ByUs2QvLmkw4VTZrX2Nvc3JPM1E/edit?usp=sharing


i cleaned up the lego car a bit. normals are all nice now, made some basic uvs and started to group dynamic parts. im going to update the file every now and then
Title: Re: Vehicle script [version 1.2.1]
Post by: ZeosPantera on May 10, 2013, 09:33:38 am
Quick question. What is an FBX? (Cancel that I looked it up)

Soooooo I need that program to get it converted to work in OT?

Title: Re: Vehicle script [version 1.2.1]
Post by: mLichy on May 10, 2013, 09:59:21 am
Auto desk products will open it. Maybe others.
Title: Re: Vehicle script [version 1.2.1]
Post by: PytonPago on May 10, 2013, 04:45:42 pm
I think a test vehicle needs to be modeled that has an external wheel or fan that represents the "Engine RPM" and then you could watch how it reacts to clutching and power loss etc.

 ... give me time to Monday and i give you the engine fan  ;D  And as for the Lego - i hope you dont plan to implement that in a in-game building fashion, hard to build in a carpet, no need to fight some mouse for it.  ;D :D

Quote from: M7
Sorry the model has too many vertices , more than 65535 says the error message. which seems to be the limit

Just found this line :o  ...  dont like that number  :-\
Title: Re: Vehicle script [version 1.2.1]
Post by: giucam on May 10, 2013, 04:52:33 pm
That Lego model is wonderful, but i think a HUD showing the RPM may be more effective ;)

Quote
Quote from: M7
Sorry the model has too many vertices , more than 65535 says the error message. which seems to be the limit

Just found this line   ...  dont like that number 

Actually, meshes have the 65535 limit, not models. Models can have more than one mesh.
Title: Re: Vehicle script [version 1.2.1]
Post by: M7 on May 10, 2013, 07:43:18 pm
Well i gave the lego car another go. This time i didn't explode the groups/components and it did import fine.

https://docs.google.com/file/d/0B96RrTcNJsI2ZFgxMDczZVU5Vnc/edit?usp=sharing
Title: Re: Vehicle script [version 1.2.1]
Post by: ZeosPantera on May 10, 2013, 11:30:19 pm
Well i gave the lego car another go. This time i didn't explode the groups/components and it did import fine.

https://docs.google.com/file/d/0B96RrTcNJsI2ZFgxMDczZVU5Vnc/edit?usp=sharing

Love is a over used concept. But I love you man.
Title: Re: Vehicle script [version 1.2.1]
Post by: M7 on May 11, 2013, 12:42:44 am
Hehe! Are you going to animate all those gears? I can post the sketchup file if you want.

Don't know but Lego should pay someone to have that car animated. It could  make them a good promo to have it on youtube.
Title: Re: Vehicle script [version 1.2.1]
Post by: ZeosPantera on May 11, 2013, 12:58:47 am
Move all the gears.
Title: Re: Vehicle script [version 1.2.1]
Post by: ZeosPantera on May 11, 2013, 01:35:33 am
(http://i7.minus.com/iWB4SYz21yfNh.jpg)

Joy.
Title: Re: Vehicle script [version 1.2.1]
Post by: PytonPago on May 11, 2013, 04:15:36 am
That Lego model is wonderful, but i think a HUD showing the RPM may be more effective ;)

Quote
Quote from: M7
Sorry the model has too many vertices , more than 65535 says the error message. which seems to be the limit

Just found this line   ...  dont like that number 

Actually, meshes have the 65535 limit, not models. Models can have more than one mesh.

... yes, i know the difference - but still made me check my meshes trough ... at the end, i found out, that Blender makes fun of me, showing the overall number of the model even if i select a single mesh - till edit mode. :D
Title: Re: Vehicle script [version 1.2.1]
Post by: M7 on May 11, 2013, 11:48:16 am
Move all the gears.

After the gears animation , i'm just thinking of the possibilities for crash animation on this thing, kind of like Keva planks destruction animation with bulltet physics. The meshes would not need to twist but every piece could come apart depending on the force of the impact. I don't know, that would be so cool to crash in a lego car.
Title: Re: Vehicle script [version 1.2.1]
Post by: GHAO on May 11, 2013, 04:13:07 pm
Reminds of a game on the Lego site itself where you drive that car around a course full of jumps - you have to do enough tricks to pass the level. Anyone want to make a video of that car barrelling round the mountains?
Title: Re: Vehicle script [version 1.2.1]
Post by: M7 on May 15, 2013, 12:46:22 am
Am i the only who getting this with the latest BMW version? First spawn, everything is fine, car drive perfectly. But if i try to spawn another one, it will just hang in the air (just like when you first import a vehicle).

Same kind of thing happen when loading a Belair. First spawn, everything is fine, second one, the engine will start but then go silent and it wont move.



(http://imageshack.us/a/img845/8187/bugbmw.jpg)
Title: Re: Vehicle script [version 1.2.1]
Post by: cameni on May 15, 2013, 01:07:49 am
That is a bug in the vehicle script, some global properties attached only to 'this' of the first vehicle in init_chassis. I've posted a fixed version somewhere, but every vehicle that was derived from the original script needs to be fixed.
Title: Re: Vehicle script [version 1.2.1]
Post by: Chaoz on May 15, 2013, 01:35:50 am
//vehicle script file
//see http://xtrac.outerra.com/index.fcgi/wiki/vehicle (http://xtrac.outerra.com/index.fcgi/wiki/vehicle) for example and documentation

var chassis={};

//invoked only the first time the model is loaded, or upon reload
function init_chassis(){
    var wheelparam = {
        radius: 0.42,
        width: 10,
        suspension_max: 0.1,
        suspension_min: -0.1,
        suspension_stiffness: 20.0,
        damping_compression: 0.5,
        damping_relaxation: 0.2,
        slip: 5,
        roll_influence: 0.01,
        rotation: -1
             };
   var wheelparam2 = {
        radius: 0.64,
        width: 100,
        suspension_max: 0.1,
        suspension_min: -0.3,
        suspension_stiffness: 15.0,
        damping_compression: 0.5,
        damping_relaxation: 0.2,
        slip: 5,
        roll_influence: 0.01,
        rotation: 1
             };
  this.add_wheel('AV_G', wheelparam);
  this.add_wheel('AV_D', wheelparam);
  this.add_wheel('AR_G', wheelparam2);
  this.add_wheel('AR_D', wheelparam2);
 
    this.load_sound("EngineStart.ogg"); //startup
    this.load_sound("EngineIdle.ogg"); //idle
    this.load_sound("EngineOff.ogg");    //off
    this.load_sound("EngineLow.ogg"); //on
   
    chassis.basepitch = [ 0.2, 0.6, 0.6, 0.4 ];             //base pitch for start, idle, throttle
  this.add_sound_emitter("AR_G");
    //engine properties -- modify these to change the engine behaviour
    chassis.wheelRadius = 0.53;

    chassis.torquePoints = [ { rpm: 0,    torque: 180},
                          { rpm: 1000, torque: 120},
                          { rpm: 2000, torque: 80},
                          { rpm: 3000, torque: 60},
                          { rpm: 4000, torque: 20},
                          { rpm: 5000, torque: 0},
                          { rpm: 6000, torque: 0},
                          { rpm: 7000, torque: 0},
                          { rpm: 8000, torque: 0},
                          { rpm: 9000, torque: 0  } ];
   
    chassis.forwardGears = [ { ratio: 4.20, shiftUp: 8000, shiftDown: -1   },
                          { ratio: 2.49, shiftUp: 8000, shiftDown: 4500 },
                          { ratio: 1.66, shiftUp: 8000, shiftDown: 5000 },
                          { ratio: 1.24, shiftUp: 8000, shiftDown: 6000 },
                          { ratio: 1.00, shiftUp: -1,   shiftDown: 6000 } ];
    chassis.reverseGears = [ { ratio: 4.20, shiftUp: -1, shiftDown: -1 } ];
   
    chassis.finalRatio = 1;
    chassis.torqueMultiplier = 40;
    chassis.maximumSpeed = -1; // speed in m/s. a value < 0 means there's no maximum speed
    chassis.engineBrakeCoefficient = 0.2;
    chassis.breakingForce = 50000.0;
    chassis.pitchMultiplier = 1;
    //end of engine properties

    chassis.maxPowerTP = chassis.torquePoints[0];
    chassis.maxRPM = 0;
    var maxPw = 0;
    for (var i = 1; i < chassis.torquePoints.length; ++i) {
        var tp = chassis.torquePoints;
        if (tp.rpm * tp.torque > maxPw) {
            chassis.maxPowerTP = tp;
        }
        if (tp.rpm > chassis.maxRPM) {
            chassis.maxRPM = tp.rpm;
        }
    }

    chassis.neutroGear = { ratio: 0.0, shiftUp: -1, shiftDown: -1, index: 0 };
    var prev = null;
    for (var i = 0; i < chassis.forwardGears.length; ++i) {
        var gear = chassis.forwardGears;
        if (prev)
            prev.next = gear;
        gear.prev = prev;
        gear.index = i + 1;
        prev = gear;
    }

    prev = null;
    for (var i = 0; i < chassis.reverseGears.length; ++i) {
        var gear = chassis.reverseGears;
        if (prev)
            prev.next = gear;
        gear.prev = prev;
        gear.index = -i - 1;
        prev = gear;
    }


    chassis.torque = engineTorque;

    return {mass:1500, com:{y:0, z:-0.82}, steering:10, steering_ecf:60, centering: 30.6, centering_ecf: 40};
}

//invoked for each new instance of the vehicle
function init_vehicle() {
    this.set_fps_camera_pos({x:-0.33,y:-0.25,z:1.15});

    this.snd = this.sound();
    this.snd.set_ref_distance(0, 9.0);

    this.started = false;
    this.gear = chassis.neutroGear;
    this.direction = 0;
}

//invoked when engine starts or stops
function engine(start) {
    if (start) {
        this.started = true;
        this.sound = 0;
        this.snd.play(0, 0, false, false);
    }
}

function engineTorque(rpm) {
    var min = 0;
    var max = chassis.torquePoints.length - 1;

    if (rpm > chassis.torquePoints[max].rpm || rpm < chassis.torquePoints[min].rpm)
        return 0;

    while (max - min > 1) {
        var mid = Math.floor(min + (max - min) / 2);
        var tp = chassis.torquePoints[mid];
        if (tp.rpm == rpm) {
            return tp.torque;
        } else if (tp.rpm > rpm) {
            max = mid;
        } else {
            min = mid;
        }
    }

    var minTp = chassis.torquePoints[min];
    var maxTp = chassis.torquePoints[max];
    var TM = maxTp.torque;
    var Tm = minTp.torque;
    var RM = maxTp.rpm;
    var Rm = minTp.rpm;

    var a = (TM - Tm) / (RM - Rm);

    return rpm * a + Tm - Rm * a;
}

function sign(x) {
    return (x > 0.0 ? 1 : (x < 0.0 ? -1 : 0));
}

//invoked each frame to handle the inputs and animate the model
function update_frame(dt, engine, brake, steering) {
    if (!this.started)
        return;

    if (this.sound == 0 && this.snd.is_playing(0)) { // still starting up
        return;
    }

  steering *= 0.2;
  this.steer(0, steering);
  this.steer(1, steering);

    var absSpeed = Math.abs(this.speed());
    var khm = absSpeed * 3.6;
    var wheels = absSpeed / (chassis.wheelRadius * (2 * Math.PI));

    //automatic gear shifting
    var rpm = wheels * this.gear.ratio * chassis.finalRatio * 60;
    if (rpm < 10 && this.gear.index != 0) {
        this.gear = chassis.neutroGear;
        this.direction = 0;
//         this.log_inf("neutro");
    }
    if (engine != 0.0 && sign(engine) != this.direction) {
        this.direction = sign(engine);
        if (this.direction == 1.0) {
            this.gear = chassis.forwardGears[0];
//             this.log_inf("forward, gear " + this.gear.index);
        } else if (this.direction == -1.0) {
            this.gear = chassis.reverseGears[0];
//             this.log_inf("reverse, gear " + this.gear.index);
        }
        rpm = wheels * this.gear.ratio * chassis.finalRatio * 60;
    }

    if (rpm > this.gear.shiftUp && this.gear.next) {
        this.gear = this.gear.next;
//         this.log_inf("gear up " + this.gear.index);
    } else if (rpm < this.gear.shiftDown && this.gear.prev) {
        this.gear = this.gear.prev;
//         this.log_inf("gear down " + this.gear.index);
    }

    rpm = wheels * this.gear.ratio * chassis.finalRatio * 60;

    if (chassis.maximumSpeed < 0 || this.speed < chassis.maximumSpeed) {
        var force = engine * chassis.torque(rpm) * chassis.torqueMultiplier * this.gear.ratio;
        this.wheel_force(0, force*0.5);
        this.wheel_force(1, force*0.5);
        this.wheel_force(2, force);
        this.wheel_force(3, force);
    }

    //From Torcs engine code
    var static_friction = 0.1;
    var engineBrake = chassis.maxPowerTP.torque * chassis.engineBrakeCoefficient * chassis.torqueMultiplier * this.gear.ratio *
                      (static_friction + (1.0 - static_friction) * rpm / chassis.maxRPM);

    brake *= chassis.breakingForce;
    this.wheel_brake(-1, brake + engineBrake);

    this.animate_wheels();

    if (this.gear.index == 0 && (!this.snd.is_playing(0) || this.sound != 1)) {
        this.snd.stop(0);
        this.snd.play(0, 1, true, false);
        this.sound = 1;
    } else if (this.gear.index != 0 && engine == 0 && (!this.snd.is_playing(0) || this.sound != 2)) { //idle
        this.snd.stop(0);
        this.snd.play(0, 2, true, false);
        this.sound = 2;
    } else if (this.gear.index != 0 && engine != 0 && (!this.snd.is_playing(0) || this.sound != 3)) { //throttle
        this.snd.stop(0);
        this.snd.play(0, 3, true, false);
        this.sound = 3;
    }

    var pitch = chassis.pitchMultiplier * rpm / chassis.maxRPM;
    this.snd.set_pitch(0, pitch + chassis.basepitch[this.sound]);
}


This is a fixed script from another vehicle, notice the var chassis at the beginning...
Title: Re: Vehicle script [version 1.2.1]
Post by: giucam on May 15, 2013, 11:35:34 am
That doesn't happen to me, and i can't read the line number of the error in the screenshot, i guess due of high compression.
The index property is of gears though, i don't see what the "var chassis={};" would do. I guess it is using a null gear variable for some reason.
Title: Re: Vehicle script [version 1.2.1]
Post by: Chaoz on May 15, 2013, 11:40:24 am
"chassis" is an array that is global and not like "this" locally, so if you put something in "chassis" then every car can access it, like the gear tables. With "this" it's only possible to have one car functioning at the same time.
Title: Re: Vehicle script [version 1.2.1]
Post by: cameni on May 15, 2013, 11:41:26 am
'this' in init_chassis is for the first vehicle, so whatever custom property you attach there is available only to the first vehicle instance. The chassis global object was added instead, to have these accessible from every vehicle instance.
Title: Re: Vehicle script [version 1.2.1]
Post by: giucam on May 15, 2013, 11:51:38 am
Ah i see, thanks. I'll work on it
Title: Re: Vehicle script [version 1.2.2]
Post by: giucam on May 21, 2013, 03:43:13 pm
Version 1.2.2.

Code: [Select]
/*
    Vehicle script file
    Copyright (C) 2012 - 2013  Giulio Camuffo <giuliocamuffo@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 3.0 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//see http://xtrac.outerraworld.com/trac.fcgi/wiki/vehicle for example and documentation

// VERSION 1.2.2

var chassis = {};

//invoked only the first time the model is loaded, or upon reload
function init_chassis(){
    var wheelparam = {
        radius: 0.30,
        width: 0.20,
        suspension_max: 0.1,
        suspension_min: -0.05,
        suspension_stiffness: 50.0,
        damping_compression: 0.06,
        damping_relaxation: 0.05,
        slip: 5.0,
        roll_influence: 0.1,
        rotation: -1
    };

    this.add_wheel('wheel_FL', wheelparam);
    this.add_wheel('wheel_FR', wheelparam);
    this.add_wheel('wheel_RL', wheelparam);
    this.add_wheel('wheel_RR', wheelparam);

    this.load_sound("e90_startup.ogg"); //startup
    this.load_sound("e90_onidle.ogg"); //idle
    this.load_sound("e90_offlow.ogg"); //off
    this.load_sound("e90_onlow.ogg"); //on
    chassis.basepitch = [ 1, 1, 0.4, 0.4 ];             //base pitch for start, idle, off, on
    this.add_sound_emitter("wheel_FL");

    //engine properties -- modify these to change the engine behaviour
    chassis.wheelRadius = 0.24;

    chassis.torquePoints = [ { rpm: 200,  torque: 0},
                             { rpm: 1000, torque: 250},
                             { rpm: 2000, torque: 335},
                             { rpm: 3000, torque: 380},
                             { rpm: 4000, torque: 400},
                             { rpm: 5000, torque: 395},
                             { rpm: 6000, torque: 400},
                             { rpm: 7000, torque: 390},
                             { rpm: 8000, torque: 365},
                             { rpm: 9000, torque: 0  } ];

    chassis.forwardGears = [ { ratio: 4.06, shiftUp: 8000, shiftDown: -1   },
                             { ratio: 2.40, shiftUp: 8000, shiftDown: 4500 },
                             { ratio: 1.58, shiftUp: 8000, shiftDown: 5000 },
                             { ratio: 1.19, shiftUp: 8000, shiftDown: 5500 },
                             { ratio: 1.00, shiftUp: 8000, shiftDown: 6000 },
                             { ratio: 0.87, shiftUp: -1,   shiftDown: 6000 } ];
    chassis.reverseGears = [ { ratio: 4.68, shiftUp: -1, shiftDown: -1 } ];

    chassis.differentialRatio = 3.85;
    chassis.efficiency = 0.9;
    chassis.speedLimiter = -1; // maximum speed in m/s. a value < 0 means there's no limiter
    chassis.engineBrakeCoefficient = 0.4;
    chassis.breakingForce = 5000.0;
    chassis.pitchMultiplier = 1.5;
    chassis.engineInertia = 0.05;
    chassis.minimumRPM = 1500;
    chassis.minimum = 0.3;
    //end of engine properties

    chassis.maxPowerTP = chassis.torquePoints[0];
    chassis.maxRPM = 0;
    var maxPw = 0;
    for (var i = 1; i < chassis.torquePoints.length; ++i) {
        var tp = chassis.torquePoints[i];
        if (tp.rpm * tp.torque > maxPw) {
            chassis.maxPowerTP = tp;
        }
        if (tp.rpm > chassis.maxRPM) {
            chassis.maxRPM = tp.rpm;
        }
    }

    chassis.neutroGear = { ratio: 0.0, shiftUp: -1, shiftDown: -1, index: 0 };
    var prev = null;
    for (var i = 0; i < chassis.forwardGears.length; ++i) {
        var gear = chassis.forwardGears[i];
        if (prev)
            prev.next = gear;
        gear.prev = prev;
        gear.index = i + 1;
        prev = gear;
    }

    prev = null;
    for (var i = 0; i < chassis.reverseGears.length; ++i) {
        var gear = chassis.reverseGears[i];
        if (prev)
            prev.prev = gear;
        gear.next = prev;
        gear.index = -i - 1;
        prev = gear;
    }

    return {mass:1570, steering:1.0, steering_ecf:10000, centering: 2.0, centering_ecf: 10};
}

var ClutchMode = { Manual: 0, Automatic: 1, Mixed: 2 };
var ClutchState = { Engaged: 0, Disengaged: 1, Engaging: 2, Disengaging: 3 };

//invoked for each new instance of the vehicle
function init_vehicle() {
    this.set_fps_camera_pos({x:-0.33,y:-0.25,z:1.15});

    this.snd = this.sound();
    this.snd.set_ref_distance(0, 9.0);

    this.automaticTransmission = false;
    this.clutchMode = ClutchMode.Automatic;

    this.started = false;
    this.gear = chassis.neutroGear;
    this.direction = 0;
    this.engineRPM = 0;
    this.starting = false;
    this.clutch = 1.0;
    this.clutchState = ClutchState.Disengaged;
    this.clutchOverride = false;

    this.torque = engineTorque;
    this.shiftUp = shift_up;
    this.shiftDown = shift_down;
    this.selectGear = select_gear;
    this.die = die;
    this.updateAutomaticTransmission = automatic_transmission;
    this.automaticClutch = automatic_clutch;
    this.animate = animate;
}

//invoked when engine starts or stops
function engine(start) {
    if (start) {
        this.started = false;
        this.soundId = 0;
        this.starting = true;
    } else {
        this.started = false;
    }
}

function engineTorque(rpm) {
    var min = 0;
    var max = chassis.torquePoints.length - 1;

    if (rpm > chassis.torquePoints[max].rpm || rpm < chassis.torquePoints[min].rpm)
        return 0;

    while (max - min > 1) {
        var mid = Math.floor(min + (max - min) / 2);
        var tp = chassis.torquePoints[mid];
        if (tp.rpm == rpm) {
            return tp.torque;
        } else if (tp.rpm > rpm) {
            max = mid;
        } else {
            min = mid;
        }
    }

    var minTp = chassis.torquePoints[min];
    var maxTp = chassis.torquePoints[max];
    var TM = maxTp.torque;
    var Tm = minTp.torque;
    var RM = maxTp.rpm;
    var Rm = minTp.rpm;

    var a = (TM - Tm) / (RM - Rm);

    return rpm * a + Tm - Rm * a;
}

function sign(x) {
    return (x > 0.0 ? 1 : (x < 0.0 ? -1 : 0));
}

function action(key, value, dt)
{
    if (key == AAuxb1 && value == 0) {
        this.automaticTransmission = !this.automaticTransmission;
        this.log_inf("Automatic transmission " + (this.automaticTransmission ? "enabled" : "disabled"));
    }

    if (key == AAuxb2) {
        if (this.clutchMode != ClutchMode.Manual) {
            this.clutchState = (value == 0 ? ClutchState.Disengaging : ClutchState.Engaging);
        } else {
            this.clutchState = (value == 0 ? ClutchState.Disengaged : ClutchState.Engaged);
            this.clutch = 1 - value;
        }
        this.clutchOverride = value;
    }

    if (value == 1 && !this.automaticTransmission) {
        switch(key) {
            case AShiftUp:
                this.shiftUp();
                break;
            case AShiftDown:
                this.shiftDown();
                break;
            default:
                break;
        }
    }
}

function select_gear(gear)
{
    var i = this.gear.index;
    this.gear = gear;
    if (i == 0 && this.clutchMode == ClutchMode.Mixed && this.clutchState != ClutchState.Engaged) {
        this.clutchState = ClutchState.Disengaging;
    }
    this.log_inf("gear " + i + " => " + this.gear.index);
}

function shift_up()
{
    var i = this.gear.index;
    if (this.gear.next) {
        this.selectGear(this.gear.next);
    } else if (i == 0) {
        this.selectGear(chassis.forwardGears[0]);
    } else if (i == -1) {
        this.selectGear(chassis.neutroGear);
    }
}

function shift_down()
{
    var i = this.gear.index;
    if (this.gear.prev) {
        this.selectGear(this.gear.prev);
    } else if (i == 0) {
        this.selectGear(chassis.reverseGears[0]);
    } else if (i == 1) {
        this.selectGear(chassis.neutroGear);
    }
}

function die()
{
    this.started = false;
}

function automatic_transmission(engine)
{
    if (this.engineRPM < 10 && this.gear.index != 0) {
        this.gear = chassis.neutroGear;
        this.direction = 0;
        this.selectGear(chassis.neutroGear);
    }
    if (engine != 0.0) {
        if (sign(engine) != this.direction) {
            this.direction = sign(engine);
            if (this.direction == 1.0) {
                this.selectGear(chassis.forwardGears[0]);
            } else if (this.direction == -1.0) {
                this.selectGear(chassis.reverseGears[0]);
            }
        }

        if (this.engineRPM > this.gear.shiftUp && this.gear.next) {
            this.shiftUp();
        } else if (this.engineRPM < this.gear.shiftDown && this.gear.prev) {
            this.shiftDown();
        }
    }
}

function automatic_clutch(wheelsRpm)
{
    var clutched = (this.clutchState == ClutchState.Disengaged  || this.clutchState == ClutchState.Disengaging) && this.gear.index != 0;
    var clutchRPM = chassis.minimumRPM + 2000;

    if (this.clutchMode == ClutchMode.Automatic && !this.clutchOverride) {
        if (clutched && this.engineRPM < chassis.minimumRPM) {
            this.clutchState = ClutchState.Engaging;
        } else if (!clutched && this.engineRPM > clutchRPM) {
            this.clutchState = ClutchState.Disengaging;
        }
    }

    if (this.clutchState == ClutchState.Disengaging) {
        if (this.gear.index != 0) {
            var w = wheelsRpm * this.gear.ratio * chassis.differentialRatio;
            this.clutch = (w > chassis.minimumRPM ? chassis.minimumRPM : w) / chassis.minimumRPM;

            // This makes an horizontal S-like shape. (mirrored, like Z)
            var f = this.clutch * 2;
            if (f < 0.5) {
                this.clutch = -(f * f * f * f - 1) / 5;
            } else {
                f = f - 1;
                this.clutch = (f * f * f * f)  + 0.2;
            }
            this.clutch += 0.2;

            var r = this.engineRPM / clutchRPM;
            this.clutch *= (r > 1 ? 1 : (r < 0 ? 0 : r));
        } else {
            this.clutch = 1;
        }

        if (this.clutch >= 1) {
            this.clutch = 1;
            this.clutchState = ClutchState.Disengaged;
        }
    } else if (this.clutchState == ClutchState.Engaging) {
        this.clutch = 0;
        this.clutchState = ClutchState.Engaged;
    }
    return this.clutch;
}

function animate()
{
    this.animate_wheels();
}

//invoked each frame to handle the inputs and animate the model
function update_frame(dt, engine, brake, steering) {
    steering *= 0.6;
    this.steer(-2, steering);

    var wheelsRpm = this.max_rpm();
    if (sign(this.speed()) != sign(this.gear.index) && this.gear.index != 0) {
        wheelsRpm = 0;
    }

    if (this.automaticTransmission) {
        this.updateAutomaticTransmission(engine);
    }
    if (engine < 0) {
        engine = -engine;
    }

    var clutch = this.clutch;
    if (this.gear.index == 0) {
        clutch = 0;
    }
    if (this.clutchMode != ClutchMode.Manual) {
        clutch = this.automaticClutch(wheelsRpm);
    }

    var appliedTorque = 0;
    if (this.starting) {
        if (this.engineRPM > chassis.minimumRPM) {
            this.starting = false;
            this.started = true;
        } else {
            appliedTorque = 100 * (1 - this.engineRPM / chassis.minimumRPM);
        }
    }

    var oldRPM = this.engineRPM;

    if (this.gear.index != 0) {
        this.engineRPM = this.engineRPM * (1 - clutch) + wheelsRpm * clutch * this.gear.ratio * chassis.differentialRatio;
    }
this.log_inf(this.engineRPM);
    var engineStarted = this.started;
    if (engineStarted || this.starting) {
        if (this.engineRPM < chassis.minimumRPM && engine < chassis.minimum) {
            engine = chassis.minimum;
        }
        if (chassis.speedLimiter < 0 || Math.abs(this.speed()) < chassis.speedLimiter) {
            appliedTorque += engine * this.torque(this.engineRPM);
        }
    }

    var friction = 0.2;
    appliedTorque -= sign(this.engineRPM) * chassis.maxPowerTP.torque * chassis.engineBrakeCoefficient *
                          (friction + (1.0 - friction) * this.engineRPM / chassis.maxRPM);

    this.engineRPM += 2 * appliedTorque * dt / chassis.engineInertia; // torque*2 because of the mean below
    this.engineRPM = (this.engineRPM + oldRPM) / 2.0; // stabilize the fluctuations when the wheelsRpm and engineRPM
                                                      // are very different
    if (this.engineRPM < 300) {
        this.die();
    }

    var tq = appliedTorque + chassis.engineInertia * (oldRPM - this.engineRPM) / dt;
    var force = tq * clutch * this.gear.ratio * chassis.differentialRatio * chassis.efficiency / chassis.wheelRadius;
    if (this.gear.index < 0) {
        force = -force;
    }

    this.wheel_force(2, force / 2);
    this.wheel_force(3, force / 2);

    brake *= chassis.breakingForce;
    this.wheel_brake(-1, brake);

    this.animate();

    if (this.gear.index == 0 && (!this.snd.is_playing(0) || this.soundId != 1)) {
        this.snd.stop(0);
        this.snd.play(0, 1, true, false);
        this.soundId = 1;
    } else if (this.gear.index != 0 && engine != 1 && (!this.snd.is_playing(0) || this.soundId != 2)) { //idle
        this.snd.stop(0);
        this.snd.play(0, 2, true, false);
        this.soundId = 2;
    } else if (this.gear.index != 0 && engine == 1 && (!this.snd.is_playing(0) || this.soundId != 3)) { //throttle
        this.snd.stop(0);
        this.snd.play(0, 3, true, false);
        this.soundId = 3;
    }

    var pitch = chassis.pitchMultiplier * (this.engineRPM - chassis.minimumRPM) / chassis.maxRPM;
    this.snd.set_pitch(0, pitch + chassis.basepitch[this.soundId]);
    var g = this.engineRPM / chassis.minimumRPM * 2.0;
    this.snd.set_gain(0, Math.min(g * g * g, 1.0));
}

Changelog:


cameni, I tried to use AAuxa[1,2] with the clutch, using my wheel's brake or accelerator, but i didn't succed. It seems that when i set it as primary for AAuxa1 it behaves like a button, increasing or decreasing the value i get in the action handler, but i couldn't manage to make it work as an axis. Setting it as secondary didn't work at all, i didn't receive anything for it.
Title: Re: Vehicle script [version 1.2.2]
Post by: cameni on May 21, 2013, 03:56:31 pm
Axis handling will be changed because currently it's a bit limited, I already modified it in the dev version.
Title: Re: Vehicle script [version 1.2.2]
Post by: M7 on May 22, 2013, 01:31:22 am
Almost perfect!!!! i just love the manual transmission. But now i'm getting something new in the log window. Instead of seing the gear changes, i get 'info with fast scrolling numbers '. Not a big deal though.

(http://imageshack.us/a/img191/4541/error2nf.jpg)
Title: Re: Vehicle script [version 1.2.2]
Post by: Chaoz on May 22, 2013, 02:35:29 am
you have to remove "this.log_inf(this.engineRPM);" from the script to get rid of the numbers, probably a left over from debugging ;)
Title: Re: Vehicle script [version 1.2.2]
Post by: M7 on May 22, 2013, 09:48:42 am
Good that did it!

Next i think i'll try to bring that manual transmission script to the monster truck. It sure is one of my favorite vehicle. I already found a better sound for the engine (taken from the batman tumbler engineoff.ogg) and made some offroad circuit with unpaved road and big bumps. All is missing is the mud and some mesh trashing  ;D
Title: Re: Vehicle script [version 1.3.0]
Post by: giucam on May 27, 2013, 12:35:57 pm
Version 1.3.0

Code: [Select]
/*
    Vehicle script file
    Copyright (C) 2012 - 2013  Giulio Camuffo <giuliocamuffo@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 3.0 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//see http://xtrac.outerraworld.com/trac.fcgi/wiki/vehicle for example and documentation

// VERSION 1.3.0

var chassis = {};

// --- Vehicle specific stuff ---
function chassis_setup() {
    var wheelparam = {
        radius: 0.30,
        width: 0.20,
        suspension_max: 0.1,
        suspension_min: -0.05,
        suspension_stiffness: 50.0,
        damping_compression: 0.06,
        damping_relaxation: 0.05,
        slip: 3.0,
        roll_influence: 0.1,
        rotation: -1
    };

    this.add_wheel('wheel_FL', wheelparam);
    this.add_wheel('wheel_FR', wheelparam);
    this.add_wheel('wheel_RL', wheelparam);
    this.add_wheel('wheel_RR', wheelparam);

    this.load_sound("e90_onidle.ogg"); //idle
    this.load_sound("e90_offlow.ogg"); //off
    this.load_sound("e90_onlow.ogg"); //on
    chassis.basePitch = [ 1, 0.4, 0.4 ];             //base pitch for idle, off, on
    chassis.relVolumes = [ 0.8, 1, 1 ];
    this.add_sound_emitter("hood");

    //engine properties -- modify these to change the engine behaviour
    chassis.wheelRadius = 0.24;

    chassis.torquePoints = [ { rpm: 500,  torque: 0},
                             { rpm: 1000, torque: 250},
                             { rpm: 2000, torque: 335},
                             { rpm: 3000, torque: 380},
                             { rpm: 4000, torque: 400},
                             { rpm: 5000, torque: 395},
                             { rpm: 6000, torque: 400},
                             { rpm: 7000, torque: 390},
                             { rpm: 8000, torque: 365},
                             { rpm: 9000, torque: 0  } ];

    chassis.forwardGears = [ { ratio: 4.06, shiftUp: 8000, shiftDown: -1   },
                             { ratio: 2.40, shiftUp: 8000, shiftDown: 4500 },
                             { ratio: 1.58, shiftUp: 8000, shiftDown: 5000 },
                             { ratio: 1.19, shiftUp: 8000, shiftDown: 5500 },
                             { ratio: 1.00, shiftUp: 8000, shiftDown: 6000 },
                             { ratio: 0.87, shiftUp: -1,   shiftDown: 6000 } ];
    chassis.reverseGears = [ { ratio: 4.68, shiftUp: -1, shiftDown: -1 } ];

    chassis.differentialRatio = 3.85;
    chassis.efficiency = 0.9;
    chassis.speedLimiter = -1; // maximum speed in m/s. a value < 0 means there's no limiter
    chassis.engineBrakeCoefficient = 0.4;
    chassis.breakingForce = 3000.0;
    chassis.pitchMultiplier = 1.5;
    chassis.engineInertia = 0.015;
    chassis.minimumRPM = 1500;
    chassis.minimum = 0.2;
    //end of engine properties

    return { mass:1570, steering:1.2, steering_ecf:70, centering: 5.0, centering_ecf: 40, com: { z:0.1 } };
}

function vehicle_setup() {
    this.set_fps_camera_pos( {x: -0.33,y: -0.20, z: 1.15});
    this.soundRefDistance = 5;
    this.automaticTransmission = false;
    this.clutchMode = ClutchMode.Automatic;
}

// --- End of vehicle specific stuff ---



//invoked only the first time the model is loaded, or upon reload
function init_chassis() {
    this.setup = chassis_setup;
    var ret = this.setup();

    chassis.maxPowerTP = chassis.torquePoints[0];
    chassis.maxRPM = 0;
    var maxPw = 0;
    for (var i = 1; i < chassis.torquePoints.length; ++i) {
        var tp = chassis.torquePoints[i];
        if (tp.rpm * tp.torque > maxPw) {
            chassis.maxPowerTP = tp;
        }
        if (tp.rpm > chassis.maxRPM) {
            chassis.maxRPM = tp.rpm;
        }
    }

    chassis.neutroGear = { ratio: 0.0, shiftUp: -1, shiftDown: -1, index: 0 };
    var prev = null;
    for (var i = 0; i < chassis.forwardGears.length; ++i) {
        var gear = chassis.forwardGears[i];
        if (prev)
            prev.next = gear;
        gear.prev = prev;
        gear.index = i + 1;
        prev = gear;
    }

    prev = null;
    for (var i = 0; i < chassis.reverseGears.length; ++i) {
        var gear = chassis.reverseGears[i];
        if (prev)
            prev.prev = gear;
        gear.next = prev;
        gear.index = -i - 1;
        prev = gear;
    }

    return ret;
}

var ClutchMode = { Manual: 0, Automatic: 1, Mixed: 2 };
var ClutchState = { Engaged: 0, Disengaged: 1, Engaging: 2, Disengaging: 3 };

//invoked for each new instance of the vehicle
function init_vehicle() {
    this.snd = this.sound();

    this.torque = engineTorque;
    this.shiftUp = shift_up;
    this.shiftDown = shift_down;
    this.selectGear = select_gear;
    this.updateAutomaticTransmission = automatic_transmission;
    this.automaticClutch = automatic_clutch;
    this.clutchValue = clutch_value;
    this.animate = animate;
    this.setup = vehicle_setup;

    this.setup();

    this.idleSource = this.snd.create_source(0);
    this.onSource = this.snd.create_source(0);
    this.offSource = this.snd.create_source(0);
    this.idleSound = 1;
    this.snd.set_ref_distance(this.idleSource, this.soundRefDistance);
    this.snd.set_ref_distance(this.onSource, this.soundRefDistance);
    this.snd.set_ref_distance(this.offSource, this.soundRefDistance);
    this.snd.play(this.idleSource, 0, true, false);
    this.snd.play(this.offSource, 1, true, false);
    this.snd.play(this.onSource, 2, true, false);

    this.clutch = 1.0;
    this.clutchState = ClutchState.Disengaged;
    this.started = false;
    this.gear = chassis.neutroGear;
    this.direction = 0;
    this.engineRPM = 0;
    this.starting = false;
    this.clutchOverride = false;
}

//invoked when engine starts or stops
function engine(start) {
    this.started = false;
    this.idleSound = 1.0;
    if (start) {
        this.starting = true;
    }
}

function engineTorque(rpm) {
    var min = 0;
    var max = chassis.torquePoints.length - 1;

    if (rpm > chassis.torquePoints[max].rpm || rpm < chassis.torquePoints[min].rpm)
        return 0;

    while (max - min > 1) {
        var mid = Math.floor(min + (max - min) / 2);
        var tp = chassis.torquePoints[mid];
        if (tp.rpm == rpm) {
            return tp.torque;
        } else if (tp.rpm > rpm) {
            max = mid;
        } else {
            min = mid;
        }
    }

    var minTp = chassis.torquePoints[min];
    var maxTp = chassis.torquePoints[max];
    var TM = maxTp.torque;
    var Tm = minTp.torque;
    var RM = maxTp.rpm;
    var Rm = minTp.rpm;

    var a = (TM - Tm) / (RM - Rm);

    return rpm * a + Tm - Rm * a;
}

function sign(x) {
    return (x > 0.0 ? 1 : (x < 0.0 ? -1 : 0));
}

function action(key, value, dt)
{
    if (key == AAuxb1 && value == 0) {
        this.automaticTransmission = !this.automaticTransmission;
        this.log_inf("Automatic transmission " + (this.automaticTransmission ? "enabled" : "disabled"));
    }

    if (key == AAuxb2) {
        if (this.clutchMode != ClutchMode.Manual) {
            this.clutchState = (value == 0 ? ClutchState.Disengaging : ClutchState.Engaging);
        } else {
            this.clutchState = (value == 0 ? ClutchState.Disengaged : ClutchState.Engaged);
            this.clutch = 1 - value;
        }
        this.clutchOverride = value;
    }

    if (value == 1 && !this.automaticTransmission) {
        switch(key) {
            case AShiftUp:
                this.shiftUp();
                break;
            case AShiftDown:
                this.shiftDown();
                break;
            default:
                break;
        }
    }
}

function select_gear(gear)
{
    if (this.gear == gear) {
        return;
    }

    var i = this.gear.index;
    this.gear = gear;
    if (i == 0 && this.clutchMode == ClutchMode.Automatic && (this.started || this.starting)) {
        this.clutchState = ClutchState.Engaging;
    }
    this.log_inf("gear " + i + " => " + this.gear.index);
}

function shift_up()
{
    var i = this.gear.index;
    if (this.gear.next) {
        this.selectGear(this.gear.next);
    } else if (i == 0) {
        this.selectGear(chassis.forwardGears[0]);
    } else if (i == -1) {
        this.selectGear(chassis.neutroGear);
    }
}

function shift_down()
{
    var i = this.gear.index;
    if (this.gear.prev) {
        this.selectGear(this.gear.prev);
    } else if (i == 0) {
        this.selectGear(chassis.reverseGears[0]);
    } else if (i == 1) {
        this.selectGear(chassis.neutroGear);
    }
}

function automatic_transmission(engine)
{
    if (this.engineRPM < 10 && this.gear.index != 0) {
        this.gear = chassis.neutroGear;
        this.direction = 0;
        this.selectGear(chassis.neutroGear);
    }
    if (engine != 0.0) {
        if (sign(engine) != this.direction) {
            this.direction = sign(engine);
            if (this.direction == 1.0) {
                this.selectGear(chassis.forwardGears[0]);
            } else if (this.direction == -1.0) {
                this.selectGear(chassis.reverseGears[0]);
            }
        }

        if (this.engineRPM > this.gear.shiftUp && this.gear.next) {
            this.shiftUp();
        } else if (this.engineRPM < this.gear.shiftDown && this.gear.prev) {
            this.shiftDown();
        }
    }
}

function automatic_clutch(wheelsRpm)
{
    var clutched = (this.clutchState == ClutchState.Disengaged  || this.clutchState == ClutchState.Disengaging) && this.gear.index != 0;
    var clutchRPM = chassis.minimumRPM + 2000;
    var w = wheelsRpm * this.gear.ratio * chassis.differentialRatio;

    if (this.clutchMode == ClutchMode.Automatic && !this.clutchOverride && (this.started || this.starting)) {
        if (clutched && this.engineRPM < chassis.minimumRPM && w < chassis.minimumRPM) {
            this.clutchState = ClutchState.Engaging;
        } else if (!clutched && (this.engineRPM > clutchRPM || w > chassis.minimumRPM)) {
            this.clutchState = ClutchState.Disengaging;
        }
    }

    if (this.clutchState == ClutchState.Disengaging) {
        if (this.gear.index != 0) {
            this.clutch = (w > chassis.minimumRPM ? chassis.minimumRPM : w) / chassis.minimumRPM;

            // This makes an horizontal S-like shape. (mirrored, like Z)
            var f = this.clutch * 2;
            if (f < 0.5) {
                this.clutch = -(f * f * f * f - 1) / 5;
            } else {
                f = f - 1;
                this.clutch = (f * f * f * f) + 0.2;
            }
            this.clutch += 0.4;
            var r = (this.engineRPM - chassis.minimumRPM) / clutchRPM;
            r = (r > 1 ? 1 : (r < 0 ? 0 : r));
            r = Math.sqrt(r);
            this.clutch *= r
        } else {
            this.clutch = 1;
        }

        if (this.clutch >= 1) {
            this.clutch = 1;
            this.clutchState = ClutchState.Disengaged;
        }
    } else if (this.clutchState == ClutchState.Engaging) {
        this.clutch = 0;
        this.clutchState = ClutchState.Engaged;
    }
    return this.clutch;
}

function clutch_value(wheelsRpm)
{
    var clutch = this.clutch;
    if (this.clutchMode != ClutchMode.Manual) {
        clutch = this.automaticClutch(wheelsRpm);
    }
    if (this.gear.index == 0) {
        clutch = 0;
    }
    return clutch;
}

function animate()
{
    this.animate_wheels();
}

//invoked each frame to handle the inputs and animate the model
function update_frame(dt, engine, brake, steering) {
    steering *= 0.6;
    this.steer(-2, steering);

    var wheelsRpm = this.max_rpm();
    if (sign(this.speed()) != sign(this.gear.index) && this.gear.index != 0) {
        wheelsRpm = 0;
    }

    if (this.automaticTransmission) {
        this.updateAutomaticTransmission(engine);
    }
    if (engine < 0) {
        engine = -engine;
    }

    var appliedTorque = 0;
    if (this.starting) {
        if (this.engineRPM > chassis.minimumRPM) {
            this.starting = false;
            this.started = true;
        } else {
            appliedTorque = 200 * (1 - this.engineRPM / chassis.minimumRPM);
        }
    }

    var oldRPM = this.engineRPM;
    var clutch = this.clutchValue(wheelsRpm);
    this.engineRPM = this.engineRPM * (1 - clutch) + wheelsRpm * clutch * this.gear.ratio * chassis.differentialRatio;

    var running = this.started || this.starting;
    if (running) {
        if (this.engineRPM < chassis.minimumRPM && engine < chassis.minimum) {
            engine = chassis.minimum;
        }
        if (chassis.speedLimiter < 0 || Math.abs(this.speed()) < chassis.speedLimiter) {
            appliedTorque += engine * this.torque(this.engineRPM);
        }
    }

    this.engineRPM += appliedTorque * dt / chassis.engineInertia;

    var s = sign(this.engineRPM);
    var friction = 0.2;
    var eb = -sign(this.engineRPM) * chassis.maxPowerTP.torque * chassis.engineBrakeCoefficient *
                  (friction + (1.0 - friction) * this.engineRPM / chassis.maxRPM);

    this.engineRPM += eb * dt / chassis.engineInertia;
    if (s != sign(this.engineRPM)) {
        this.engineRPM = 0;
    } else {
        appliedTorque += eb;
    }

    var tq = appliedTorque + chassis.engineInertia * (oldRPM - this.engineRPM) / dt;
    var force = tq * clutch * this.gear.ratio * chassis.differentialRatio * chassis.efficiency / chassis.wheelRadius;
    if (this.gear.index < 0) {
        force = -force;
    }

    this.wheel_force(2, force / 2);
    this.wheel_force(3, force / 2);

    brake *= chassis.breakingForce;
    this.wheel_brake(-1, brake);

    this.animate();

    var rpm = (this.engineRPM + oldRPM) / 2.0; // stabilize the fluctuations when the wheelsRpm and engineRPM
                                               // are very different
    var pitch = chassis.pitchMultiplier * (rpm - chassis.minimumRPM) / chassis.maxRPM;
    var vol = rpm / chassis.minimumRPM * 2.0;
    vol = (vol > 1 ? 1 : (vol < 0 ? 0 : vol));

    this.snd.set_pitch(this.onSource, pitch + chassis.basePitch[2]);
    this.snd.set_gain(this.onSource, chassis.relVolumes[2] * engine * vol);

    var speed = Math.abs(this.speed());
    this.snd.set_pitch(this.offSource, pitch + chassis.basePitch[1]);
    var v = speed / 5;
    this.snd.set_gain(this.offSource, chassis.relVolumes[1] * (1 - engine * vol) * Math.min(v, 1.0));

    var v = speed / 20;
    if (!clutch) {
        v = rpm / chassis.maxRPM;
    }
    if (!running && this.idleSound > 0) {
        this.idleSound -= dt;
        this.idleSound = (this.idleSound < 0 ? 0 : this.idleSound);
    }
    this.snd.set_pitch(this.idleSource, pitch + chassis.basePitch[0]);
    this.snd.set_gain(this.idleSource, this.idleSound * chassis.relVolumes[0] * (1 - engine) * Math.max(vol - v, 0));

    if (rpm < 100) { //dead
        if (!this.clutchOverride) {
            this.clutch = 1.0;
            this.clutchState = ClutchState.Disengaged;
        }
        this.started = false;
    }
}

Changelog:
Title: Re: Vehicle script [version 1.3.0]
Post by: ZeosPantera on May 27, 2013, 12:44:14 pm
I still think you should have a separate front and rear wheel description and you don't have lateral slip in there.
Title: Re: Vehicle script [version 1.3.0]
Post by: giucam on May 27, 2013, 12:51:02 pm
Those are all vehicle specific things. While this script works with the BMW out od the box (actually not anymore, an update of it is coming), it's not the focus of my work here.
Besides, I wouldn't know what to use as the lateral slip and in what the front and rear wheels would differ.

P.S. I just noticed 1.3.0 has a bug with automatic transmission. 1.3.1 coming. Testing is never enough...
Title: Re: Vehicle script [version 1.3.1]
Post by: giucam on May 27, 2013, 01:15:59 pm
1.3.1:

Code: [Select]
/*
    Vehicle script file
    Copyright (C) 2012 - 2013  Giulio Camuffo <giuliocamuffo@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 3.0 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//see http://xtrac.outerraworld.com/trac.fcgi/wiki/vehicle for example and documentation

// VERSION 1.3.1

var chassis = {};

// --- Vehicle specific stuff ---
function chassis_setup() {
    var wheelparam = {
        radius: 0.30,
        width: 0.20,
        suspension_max: 0.1,
        suspension_min: -0.05,
        suspension_stiffness: 50.0,
        damping_compression: 0.06,
        damping_relaxation: 0.05,
        slip: 3.0,
        roll_influence: 0.1,
        rotation: -1
    };

    this.add_wheel('wheel_FL', wheelparam);
    this.add_wheel('wheel_FR', wheelparam);
    this.add_wheel('wheel_RL', wheelparam);
    this.add_wheel('wheel_RR', wheelparam);

    this.load_sound("e90_onidle.ogg"); //idle
    this.load_sound("e90_offlow.ogg"); //off
    this.load_sound("e90_onlow.ogg"); //on
    chassis.basePitch = [ 1, 0.4, 0.4 ];             //base pitch for idle, off, on
    chassis.relVolumes = [ 0.8, 1, 1 ];
    this.add_sound_emitter("hood");

    //engine properties -- modify these to change the engine behaviour
    chassis.wheelRadius = 0.24;

    chassis.torquePoints = [ { rpm: 500,  torque: 0},
                             { rpm: 1000, torque: 250},
                             { rpm: 2000, torque: 335},
                             { rpm: 3000, torque: 380},
                             { rpm: 4000, torque: 400},
                             { rpm: 5000, torque: 395},
                             { rpm: 6000, torque: 400},
                             { rpm: 7000, torque: 390},
                             { rpm: 8000, torque: 365},
                             { rpm: 9000, torque: 0  } ];

    chassis.forwardGears = [ { ratio: 4.06, shiftUp: 8000, shiftDown: -1   },
                             { ratio: 2.40, shiftUp: 8000, shiftDown: 4500 },
                             { ratio: 1.58, shiftUp: 8000, shiftDown: 5000 },
                             { ratio: 1.19, shiftUp: 8000, shiftDown: 5500 },
                             { ratio: 1.00, shiftUp: 8000, shiftDown: 6000 },
                             { ratio: 0.87, shiftUp: -1,   shiftDown: 6000 } ];
    chassis.reverseGears = [ { ratio: 4.68, shiftUp: -1, shiftDown: -1 } ];

    chassis.differentialRatio = 3.85;
    chassis.efficiency = 0.9;
    chassis.speedLimiter = -1; // maximum speed in m/s. a value < 0 means there's no limiter
    chassis.engineBrakeCoefficient = 0.4;
    chassis.breakingForce = 3000.0;
    chassis.pitchMultiplier = 1.5;
    chassis.engineInertia = 0.015;
    chassis.minimumRPM = 1500;
    chassis.minimum = 0.2;
    //end of engine properties

    return { mass:1570, steering:1.2, steering_ecf:70, centering: 5.0, centering_ecf: 40, com: { z:0.1 } };
}

function vehicle_setup() {
    this.set_fps_camera_pos( {x: -0.33,y: -0.20, z: 1.15});
    this.soundRefDistance = 5;
    this.automaticTransmission = false;
    this.clutchMode = ClutchMode.Automatic;
}

// --- End of vehicle specific stuff ---



//invoked only the first time the model is loaded, or upon reload
function init_chassis() {
    this.setup = chassis_setup;
    var ret = this.setup();

    chassis.maxPowerTP = chassis.torquePoints[0];
    chassis.maxRPM = 0;
    var maxPw = 0;
    for (var i = 1; i < chassis.torquePoints.length; ++i) {
        var tp = chassis.torquePoints[i];
        if (tp.rpm * tp.torque > maxPw) {
            chassis.maxPowerTP = tp;
        }
        if (tp.rpm > chassis.maxRPM) {
            chassis.maxRPM = tp.rpm;
        }
    }

    chassis.neutroGear = { ratio: 0.0, shiftUp: -1, shiftDown: -1, index: 0 };
    var prev = null;
    for (var i = 0; i < chassis.forwardGears.length; ++i) {
        var gear = chassis.forwardGears[i];
        if (prev)
            prev.next = gear;
        gear.prev = prev;
        gear.index = i + 1;
        prev = gear;
    }

    prev = null;
    for (var i = 0; i < chassis.reverseGears.length; ++i) {
        var gear = chassis.reverseGears[i];
        if (prev)
            prev.prev = gear;
        gear.next = prev;
        gear.index = -i - 1;
        prev = gear;
    }

    return ret;
}

var ClutchMode = { Manual: 0, Automatic: 1, Mixed: 2 };
var ClutchState = { Engaged: 0, Disengaged: 1, Engaging: 2, Disengaging: 3 };

//invoked for each new instance of the vehicle
function init_vehicle() {
    this.snd = this.sound();

    this.torque = engineTorque;
    this.shiftUp = shift_up;
    this.shiftDown = shift_down;
    this.selectGear = select_gear;
    this.updateAutomaticTransmission = automatic_transmission;
    this.automaticClutch = automatic_clutch;
    this.clutchValue = clutch_value;
    this.animate = animate;
    this.setup = vehicle_setup;

    this.setup();

    this.idleSource = this.snd.create_source(0);
    this.onSource = this.snd.create_source(0);
    this.offSource = this.snd.create_source(0);
    this.idleSound = 1;
    this.snd.set_ref_distance(this.idleSource, this.soundRefDistance);
    this.snd.set_ref_distance(this.onSource, this.soundRefDistance);
    this.snd.set_ref_distance(this.offSource, this.soundRefDistance);
    this.snd.play(this.idleSource, 0, true, false);
    this.snd.play(this.offSource, 1, true, false);
    this.snd.play(this.onSource, 2, true, false);

    this.clutch = 1.0;
    this.clutchState = ClutchState.Disengaged;
    this.started = false;
    this.gear = chassis.neutroGear;
    this.direction = 0;
    this.engineRPM = 0;
    this.starting = false;
    this.clutchOverride = false;
}

//invoked when engine starts or stops
function engine(start) {
    this.started = false;
    this.idleSound = 1.0;
    if (start) {
        this.starting = true;
    }
}

function engineTorque(rpm) {
    var min = 0;
    var max = chassis.torquePoints.length - 1;

    if (rpm > chassis.torquePoints[max].rpm || rpm < chassis.torquePoints[min].rpm)
        return 0;

    while (max - min > 1) {
        var mid = Math.floor(min + (max - min) / 2);
        var tp = chassis.torquePoints[mid];
        if (tp.rpm == rpm) {
            return tp.torque;
        } else if (tp.rpm > rpm) {
            max = mid;
        } else {
            min = mid;
        }
    }

    var minTp = chassis.torquePoints[min];
    var maxTp = chassis.torquePoints[max];
    var TM = maxTp.torque;
    var Tm = minTp.torque;
    var RM = maxTp.rpm;
    var Rm = minTp.rpm;

    var a = (TM - Tm) / (RM - Rm);

    return rpm * a + Tm - Rm * a;
}

function sign(x) {
    return (x > 0.0 ? 1 : (x < 0.0 ? -1 : 0));
}

function action(key, value, dt)
{
    if (key == AAuxb1 && value == 0) {
        this.automaticTransmission = !this.automaticTransmission;
        this.log_inf("Automatic transmission " + (this.automaticTransmission ? "enabled" : "disabled"));
    }

    if (key == AAuxb2) {
        if (this.clutchMode != ClutchMode.Manual) {
            this.clutchState = (value == 0 ? ClutchState.Disengaging : ClutchState.Engaging);
        } else {
            this.clutchState = (value == 0 ? ClutchState.Disengaged : ClutchState.Engaged);
            this.clutch = 1 - value;
        }
        this.clutchOverride = value;
    }

    if (value == 1 && !this.automaticTransmission) {
        switch(key) {
            case AShiftUp:
                this.shiftUp();
                break;
            case AShiftDown:
                this.shiftDown();
                break;
            default:
                break;
        }
    }
}

function select_gear(gear)
{
    if (this.gear == gear) {
        return;
    }

    var i = this.gear.index;
    this.gear = gear;
    if (i == 0 && this.clutchMode == ClutchMode.Automatic && (this.started || this.starting)) {
        this.clutchState = ClutchState.Engaging;
    }
    this.log_inf("gear " + i + " => " + this.gear.index);
}

function shift_up()
{
    var i = this.gear.index;
    if (this.gear.next) {
        this.selectGear(this.gear.next);
    } else if (i == 0) {
        this.selectGear(chassis.forwardGears[0]);
    } else if (i == -1) {
        this.selectGear(chassis.neutroGear);
    }
}

function shift_down()
{
    var i = this.gear.index;
    if (this.gear.prev) {
        this.selectGear(this.gear.prev);
    } else if (i == 0) {
        this.selectGear(chassis.reverseGears[0]);
    } else if (i == 1) {
        this.selectGear(chassis.neutroGear);
    }
}

function automatic_transmission(engine)
{
    if (this.engineRPM < 10 && this.gear.index != 0) {
        this.direction = 0;
        this.selectGear(chassis.neutroGear);
    }
    if (engine != 0.0) {
        if (sign(engine) != this.direction) {
            this.direction = sign(engine);
            this.selectGear(chassis.neutroGear);
            if (this.direction == 1.0) {
                this.selectGear(chassis.forwardGears[0]);
            } else if (this.direction == -1.0) {
                this.selectGear(chassis.reverseGears[0]);
            }
        }
    }

    if (this.clutch != 0) {
        if (this.engineRPM > this.gear.shiftUp && this.gear.next) {
            this.shiftUp();
        } else if (this.engineRPM < this.gear.shiftDown && this.gear.prev) {
            this.shiftDown();
        }
    }
}

function automatic_clutch(wheelsRpm)
{
    var clutched = (this.clutchState == ClutchState.Disengaged  || this.clutchState == ClutchState.Disengaging) && this.gear.index != 0;
    var clutchRPM = chassis.minimumRPM * 7 / 4;
    var w = wheelsRpm * this.gear.ratio * chassis.differentialRatio;

    if (this.clutchMode == ClutchMode.Automatic && !this.clutchOverride && (this.started || this.starting)) {
        if (clutched && this.engineRPM < chassis.minimumRPM && w < chassis.minimumRPM) {
            this.clutchState = ClutchState.Engaging;
        } else if (!clutched && (this.engineRPM > clutchRPM || w > chassis.minimumRPM)) {
            this.clutchState = ClutchState.Disengaging;
        }
    }

    if (this.clutchState == ClutchState.Disengaging) {
        if (this.gear.index != 0) {
            this.clutch = (w > chassis.minimumRPM ? chassis.minimumRPM : w) / chassis.minimumRPM;

            // This makes an horizontal S-like shape. (mirrored, like Z)
            var f = this.clutch * 2;
            if (f < 0.5) {
                this.clutch = -(f * f * f * f - 1) / 5;
            } else {
                f = f - 1;
                this.clutch = (f * f * f * f) + 0.2;
            }
            this.clutch += 0.4;
            var r = (this.engineRPM - chassis.minimumRPM) / clutchRPM;
            r = (r > 1 ? 1 : (r < 0 ? 0 : r));
            r = Math.sqrt(r);
            this.clutch *= r
        } else {
            this.clutch = 1;
        }

        if (this.clutch >= 1) {
            this.clutch = 1;
            this.clutchState = ClutchState.Disengaged;
        }
    } else if (this.clutchState == ClutchState.Engaging) {
        this.clutch = 0;
        this.clutchState = ClutchState.Engaged;
    }
    return this.clutch;
}

function clutch_value(wheelsRpm)
{
    var clutch = this.clutch;
    if (this.clutchMode != ClutchMode.Manual) {
        clutch = this.automaticClutch(wheelsRpm);
    }
    if (this.gear.index == 0) {
        clutch = 0;
    }
    return clutch;
}

function animate()
{
    this.animate_wheels();
}

//invoked each frame to handle the inputs and animate the model
function update_frame(dt, engine, brake, steering) {
    steering *= 0.6;
    this.steer(-2, steering);

    if (this.automaticTransmission) {
        this.updateAutomaticTransmission(engine);
    }

    var wheelsRpm = this.max_rpm();
    if (sign(this.speed()) != sign(this.gear.index) && this.gear.index != 0) {
        wheelsRpm = 0;
    }

    if (engine < 0) {
        engine = -engine;
    }

    var appliedTorque = 0;
    if (this.starting) {
        if (this.engineRPM > chassis.minimumRPM) {
            this.starting = false;
            this.started = true;
        } else {
            appliedTorque = 200 * (1 - this.engineRPM / chassis.minimumRPM);
        }
    }

    var oldRPM = this.engineRPM;
    var clutch = this.clutchValue(wheelsRpm);
    this.engineRPM = this.engineRPM * (1 - clutch) + wheelsRpm * clutch * this.gear.ratio * chassis.differentialRatio;

    var running = this.started || this.starting;
    if (running) {
        if (this.engineRPM < chassis.minimumRPM && engine < chassis.minimum) {
            engine = chassis.minimum;
        }
        if (chassis.speedLimiter < 0 || Math.abs(this.speed()) < chassis.speedLimiter) {
            appliedTorque += engine * this.torque(this.engineRPM);
        }
    }

    this.engineRPM += appliedTorque * dt / chassis.engineInertia;

    var s = sign(this.engineRPM);
    var friction = 0.2;
    var eb = -sign(this.engineRPM) * chassis.maxPowerTP.torque * chassis.engineBrakeCoefficient *
                  (friction + (1.0 - friction) * this.engineRPM / chassis.maxRPM);

    this.engineRPM += eb * dt / chassis.engineInertia;
    if (s != sign(this.engineRPM)) {
        this.engineRPM = 0;
    } else {
        appliedTorque += eb;
    }

    var tq = appliedTorque + chassis.engineInertia * (oldRPM - this.engineRPM) / dt;
    var force = tq * clutch * this.gear.ratio * chassis.differentialRatio * chassis.efficiency / chassis.wheelRadius;
    if (this.gear.index < 0) {
        force = -force;
    }

    this.wheel_force(2, force / 2);
    this.wheel_force(3, force / 2);

    brake *= chassis.breakingForce;
    this.wheel_brake(-1, brake);

    this.animate();

    var rpm = (this.engineRPM + oldRPM) / 2.0; // stabilize the fluctuations when the wheelsRpm and engineRPM
                                               // are very different
    var pitch = chassis.pitchMultiplier * (rpm - chassis.minimumRPM) / chassis.maxRPM;
    var vol = rpm / chassis.minimumRPM * 2.0;
    vol = (vol > 1 ? 1 : (vol < 0 ? 0 : vol));

    this.snd.set_pitch(this.onSource, pitch + chassis.basePitch[2]);
    this.snd.set_gain(this.onSource, chassis.relVolumes[2] * engine * vol);

    var speed = Math.abs(this.speed());
    this.snd.set_pitch(this.offSource, pitch + chassis.basePitch[1]);
    var v = speed / 5;
    this.snd.set_gain(this.offSource, chassis.relVolumes[1] * (1 - engine * vol) * Math.min(v, 1.0));

    var v = speed / 20;
    if (!clutch) {
        v = rpm / chassis.maxRPM;
    }
    if (!running && this.idleSound > 0) {
        this.idleSound -= dt;
        this.idleSound = (this.idleSound < 0 ? 0 : this.idleSound);
    }
    this.snd.set_pitch(this.idleSource, pitch + chassis.basePitch[0]);
    this.snd.set_gain(this.idleSource, this.idleSound * chassis.relVolumes[0] * (1 - engine) * Math.max(vol - v, 0));

    if (rpm < 100) { //dead
        if (!this.clutchOverride) {
            this.clutch = 1.0;
            this.clutchState = ClutchState.Disengaged;
        }
        this.started = false;
    }
}

Title: Re: Vehicle script [version 1.3.1]
Post by: ZeosPantera on May 27, 2013, 03:55:38 pm
Setting the Lateral slip to 1.0 is what happens when you don't add that line.. It means the lateral slip is equal to the slip. A higher value means more lateral grip than longitudinal and less means less.
Title: Re: Vehicle script [version 1.3.1]
Post by: giucam on May 27, 2013, 04:05:23 pm
Mmh... http://xtrac.outerraworld.com/trac.fcgi/wiki/wheel (http://xtrac.outerraworld.com/trac.fcgi/wiki/wheel) says the lateral slip is by default 0.5 the slip.
Title: Re: Vehicle script [version 1.3.1]
Post by: Atrax on May 29, 2013, 03:59:11 am
Hi guys, I have a tiny problem with the M3, when I turn (let's say left) my car leans to the left like a bike, but it should lean to the right when turning left (centrifugal force). I hope you get what I mean. Is there a way to fix this?
I also noticed like many others that car sticks to the ground like a tiger no matter what speed you're doing and rear doesn't break out at all at any speed (at higher speeds it just flips it over). I guess that b/c of current tyre physics, but I'm no expert.
Title: Re: Vehicle script [version 1.3.1]
Post by: mLichy on May 29, 2013, 09:14:06 am
Not sure about the leaning. My belair will break free. This is one thing I worked on a bit, since I agree, other cars stuck to the ground too much.
Title: Re: Vehicle script [version 1.3.1]
Post by: ZeosPantera on May 29, 2013, 01:40:40 pm
Make the slip < 0.7 and it should be better.
Title: Re: Vehicle script [version 1.3.1]
Post by: Atrax on May 29, 2013, 03:27:16 pm
I'll try that now. Thanks!

Oh yea, I love how Belair drives, lot better than others imo, also Thumbler is pretty good to drive as it can slide a bit, other cars are getting better every time someone fiddles with those values. :)
Title: Re: Vehicle script [version 1.3.2]
Post by: giucam on June 03, 2013, 12:42:39 pm
1.3.2:

Code: [Select]
/*
    Vehicle script file
    Copyright (C) 2012 - 2013  Giulio Camuffo <giuliocamuffo@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 3.0 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//see http://xtrac.outerraworld.com/trac.fcgi/wiki/vehicle for example and documentation

// VERSION 1.3.2

var chassis = {};

// --- Vehicle specific stuff ---
function chassis_setup() {
    var wheelparam = {
        radius: 0.30,
        width: 0.20,
        suspension_max: 0.1,
        suspension_min: -0.05,
        suspension_stiffness: 50.0,
        damping_compression: 0.06,
        damping_relaxation: 0.05,
        slip: 2.0,
        roll_influence: 0.1,
        rotation: -1
    };

    this.add_wheel('wheel_FL', wheelparam);
    this.add_wheel('wheel_FR', wheelparam);
    this.add_wheel('wheel_RL', wheelparam);
    this.add_wheel('wheel_RR', wheelparam);

    this.load_sound("e90_onidle.ogg"); //idle
    this.load_sound("e90_offlow.ogg"); //off
    this.load_sound("e90_onlow.ogg"); //on
    chassis.basePitch = [ 1, 0.4, 0.4 ];             //base pitch for idle, off, on
    chassis.relVolumes = [ 0.8, 1, 1 ];
    this.add_sound_emitter("hood");

    //engine properties -- modify these to change the engine behaviour
    chassis.wheelRadius = 0.24;

    chassis.torquePoints = [ { rpm: 500,  torque: 0},
                             { rpm: 1000, torque: 250},
                             { rpm: 2000, torque: 335},
                             { rpm: 3000, torque: 380},
                             { rpm: 4000, torque: 400},
                             { rpm: 5000, torque: 395},
                             { rpm: 6000, torque: 400},
                             { rpm: 7000, torque: 390},
                             { rpm: 8000, torque: 365},
                             { rpm: 9000, torque: 0  } ];

    chassis.forwardGears = [ { ratio: 4.06, shiftUp: 8000, shiftDown: -1   },
                             { ratio: 2.40, shiftUp: 8000, shiftDown: 4500 },
                             { ratio: 1.58, shiftUp: 8000, shiftDown: 5000 },
                             { ratio: 1.19, shiftUp: 8000, shiftDown: 5500 },
                             { ratio: 1.00, shiftUp: 8000, shiftDown: 6000 },
                             { ratio: 0.87, shiftUp: -1,   shiftDown: 6000 } ];
    chassis.reverseGears = [ { ratio: 4.68, shiftUp: -1, shiftDown: -1 } ];

    chassis.differentialRatio = 3.85;
    chassis.efficiency = 0.9;
    chassis.speedLimiter = -1; // maximum speed in m/s. a value < 0 means there's no limiter
    chassis.engineBrakeCoefficient = 0.4;
    chassis.breakingForce = 3000.0;
    chassis.pitchMultiplier = 1.5;
    chassis.engineInertia = 0.015;
    chassis.minimumRPM = 1500;
    chassis.minimum = 0.2;
    //end of engine properties

    return { mass:1570, steering:1.2, steering_ecf:700, centering: 5.0, centering_ecf: 40, com: { z:0.1 } };
}

function vehicle_setup() {
    this.set_fps_camera_pos( {x: -0.33,y: -0.20, z: 1.15});
    this.soundRefDistance = 5;
    this.automaticTransmission = false;
    this.clutchMode = ClutchMode.Automatic;
}

function animate()
{
    this.animate_wheels();
}

// --- End of vehicle specific stuff ---



//invoked only the first time the model is loaded, or upon reload
function init_chassis() {
    this.setup = chassis_setup;
    var ret = this.setup();

    chassis.maxPowerTP = chassis.torquePoints[0];
    chassis.maxRPM = 0;
    var maxPw = 0;
    for (var i = 1; i < chassis.torquePoints.length; ++i) {
        var tp = chassis.torquePoints[i];
        if (tp.rpm * tp.torque > maxPw) {
            chassis.maxPowerTP = tp;
        }
        if (tp.rpm > chassis.maxRPM) {
            chassis.maxRPM = tp.rpm;
        }
    }

    chassis.neutroGear = { ratio: 0.0, shiftUp: -1, shiftDown: -1, index: 0 };
    var prev = null;
    for (var i = 0; i < chassis.forwardGears.length; ++i) {
        var gear = chassis.forwardGears[i];
        if (prev)
            prev.next = gear;
        gear.prev = prev;
        gear.index = i + 1;
        prev = gear;
    }

    prev = null;
    for (var i = 0; i < chassis.reverseGears.length; ++i) {
        var gear = chassis.reverseGears[i];
        if (prev)
            prev.prev = gear;
        gear.next = prev;
        gear.index = -i - 1;
        prev = gear;
    }

    return ret;
}

var ClutchMode = { Manual: 0, Automatic: 1, Mixed: 2 };
var ClutchState = { Engaged: 0, Disengaged: 1, Engaging: 2, Disengaging: 3 };

//invoked for each new instance of the vehicle
function init_vehicle() {
    this.snd = this.sound();

    this.torque = engineTorque;
    this.shiftUp = shift_up;
    this.shiftDown = shift_down;
    this.selectGear = select_gear;
    this.updateAutomaticTransmission = automatic_transmission;
    this.automaticClutch = automatic_clutch;
    this.clutchValue = clutch_value;
    this.animate = animate;
    this.setup = vehicle_setup;

    this.setup();

    this.idleSource = this.snd.create_source(0);
    this.onSource = this.snd.create_source(0);
    this.offSource = this.snd.create_source(0);
    this.idleSound = 1;
    this.snd.set_ref_distance(this.idleSource, this.soundRefDistance);
    this.snd.set_ref_distance(this.onSource, this.soundRefDistance);
    this.snd.set_ref_distance(this.offSource, this.soundRefDistance);
    this.snd.play(this.idleSource, 0, true, false);
    this.snd.play(this.offSource, 1, true, false);
    this.snd.play(this.onSource, 2, true, false);

    this.clutch = 1.0;
    this.clutchState = ClutchState.Disengaged;
    this.started = false;
    this.gear = chassis.neutroGear;
    this.direction = 0;
    this.engineRPM = 0;
    this.starting = false;
    this.clutchOverride = false;
}

//invoked when engine starts or stops
function engine(start) {
    this.started = false;
    this.starting = start;
    this.idleSound = 1.0;
}

function engineTorque(rpm) {
    var min = 0;
    var max = chassis.torquePoints.length - 1;

    if (rpm > chassis.torquePoints[max].rpm || rpm < chassis.torquePoints[min].rpm)
        return 0;

    while (max - min > 1) {
        var mid = Math.floor(min + (max - min) / 2);
        var tp = chassis.torquePoints[mid];
        if (tp.rpm == rpm) {
            return tp.torque;
        } else if (tp.rpm > rpm) {
            max = mid;
        } else {
            min = mid;
        }
    }

    var minTp = chassis.torquePoints[min];
    var maxTp = chassis.torquePoints[max];
    var TM = maxTp.torque;
    var Tm = minTp.torque;
    var RM = maxTp.rpm;
    var Rm = minTp.rpm;

    var a = (TM - Tm) / (RM - Rm);

    return rpm * a + Tm - Rm * a;
}

function sign(x) {
    return (x > 0.0 ? 1 : (x < 0.0 ? -1 : 0));
}

function action(key, value, dt)
{
    if (key == AAuxb1 && value == 0) {
        this.automaticTransmission = !this.automaticTransmission;
        this.log_inf("Automatic transmission " + (this.automaticTransmission ? "enabled" : "disabled"));
    }

    if (key == AAuxb2 || key == AAuxa2) {
        if (key == AAuxa2) {
            value = 1 - (value + 1) / 2;
        }

        this.clutchState = (value == 0 ? ClutchState.Disengaged : ClutchState.Engaged);
        this.clutch = 1 - value;
        this.clutchOverride = value != 0;
    }

    if (value == 1 && !this.automaticTransmission) {
        switch(key) {
            case AShiftUp:
                this.shiftUp();
                break;
            case AShiftDown:
                this.shiftDown();
                break;
            default:
                break;
        }
    }
}

function select_gear(gear)
{
    if (this.gear == gear) {
        return;
    }

    var i = this.gear.index;
    this.gear = gear;
    if (i == 0 && this.clutchMode == ClutchMode.Automatic && (this.started || this.starting)) {
        this.clutchState = ClutchState.Engaging;
    }
    this.log_inf("gear " + i + " => " + this.gear.index);
}

function shift_up()
{
    var i = this.gear.index;
    if (this.gear.next) {
        this.selectGear(this.gear.next);
    } else if (i == 0) {
        this.selectGear(chassis.forwardGears[0]);
    } else if (i == -1) {
        this.selectGear(chassis.neutroGear);
    }
}

function shift_down()
{
    var i = this.gear.index;
    if (this.gear.prev) {
        this.selectGear(this.gear.prev);
    } else if (i == 0) {
        this.selectGear(chassis.reverseGears[0]);
    } else if (i == 1) {
        this.selectGear(chassis.neutroGear);
    }
}

function automatic_transmission(engine)
{
    if (this.engineRPM < 10 && this.gear.index != 0) {
        this.direction = 0;
        this.selectGear(chassis.neutroGear);
    }
    if (engine != 0.0) {
        if (sign(engine) != this.direction) {
            this.direction = sign(engine);
            this.selectGear(chassis.neutroGear);
            if (this.direction == 1.0) {
                this.selectGear(chassis.forwardGears[0]);
            } else if (this.direction == -1.0) {
                this.selectGear(chassis.reverseGears[0]);
            }
        }
    }

    if (this.clutch != 0) {
        if (this.engineRPM > this.gear.shiftUp && this.gear.next) {
            this.shiftUp();
        } else if (this.engineRPM < this.gear.shiftDown && this.gear.prev) {
            this.shiftDown();
        }
    }
}

function automatic_clutch(wheelsRpm)
{
    var clutched = (this.clutchState == ClutchState.Disengaged  || this.clutchState == ClutchState.Disengaging) && this.gear.index != 0;
    var clutchRPM = chassis.minimumRPM * 7 / 4;
    var w = wheelsRpm * this.gear.ratio * chassis.differentialRatio;

    if (this.clutchMode == ClutchMode.Automatic && !this.clutchOverride && (this.started || this.starting)) {
        if (clutched && this.engineRPM < chassis.minimumRPM && w < chassis.minimumRPM) {
            this.clutchState = ClutchState.Engaging;
        } else if (!clutched && (this.engineRPM > clutchRPM || w > chassis.minimumRPM)) {
            this.clutchState = ClutchState.Disengaging;
        }
    }

    if (this.clutchState == ClutchState.Disengaging) {
        if (this.gear.index != 0) {
            this.clutch = (w > chassis.minimumRPM ? chassis.minimumRPM : w) / chassis.minimumRPM;

            // This makes an horizontal S-like shape. (mirrored, like Z)
            var f = this.clutch * 2;
            if (f < 0.5) {
                this.clutch = -(f * f * f * f - 1) / 5;
            } else {
                f = f - 1;
                this.clutch = (f * f * f * f) + 0.2;
            }
            this.clutch += 0.4;
            var r = (this.engineRPM - chassis.minimumRPM) / clutchRPM;
            r = (r > 1 ? 1 : (r < 0 ? 0 : r));
            r = Math.sqrt(r);
            this.clutch *= r
        } else {
            this.clutch = 1;
        }

        if (this.clutch >= 1) {
            this.clutch = 1;
            this.clutchState = ClutchState.Disengaged;
        }
    } else if (this.clutchState == ClutchState.Engaging) {
        this.clutch = 0;
        this.clutchState = ClutchState.Engaged;
    }
    return this.clutch;
}

function clutch_value(wheelsRpm)
{
    var clutch = this.clutch;
    if (this.clutchMode != ClutchMode.Manual && !this.clutchOverride) {
        clutch = this.automaticClutch(wheelsRpm);
    }
    if (this.gear.index == 0) {
        clutch = 0;
    }
    return clutch;
}

//invoked each frame to handle the inputs and animate the model
function update_frame(dt, engine, brake, steering) {
    steering *= 0.6;
    this.steer(-2, steering);

    if (this.automaticTransmission) {
        this.updateAutomaticTransmission(engine);
    }

    var wheelsRpm = Math.abs(this.max_rpm());
    if (sign(this.speed()) != sign(this.gear.index) && this.gear.index != 0) {
        wheelsRpm = 0;
    }

    if (engine < 0) {
        engine = -engine;
    }

    var appliedTorque = 0;
    if (this.starting) {
        if (this.engineRPM > chassis.minimumRPM) {
            this.starting = false;
            this.started = true;
        } else {
            appliedTorque = 200 * (1 - this.engineRPM / chassis.minimumRPM);
        }
    }

    var oldRPM = this.engineRPM;
    var clutch = this.clutchValue(wheelsRpm);
    this.engineRPM = this.engineRPM * (1 - clutch) + wheelsRpm * clutch * this.gear.ratio * chassis.differentialRatio;

    var running = this.started || this.starting;
    if (running) {
        if (this.engineRPM < chassis.minimumRPM && engine < chassis.minimum) {
            engine = chassis.minimum;
        }
        if (chassis.speedLimiter < 0 || Math.abs(this.speed()) < chassis.speedLimiter) {
            appliedTorque += engine * this.torque(this.engineRPM);
        }
    }

    this.engineRPM += appliedTorque * dt / chassis.engineInertia;

    var s = sign(this.engineRPM);
    var friction = 0.2;
    var eb = -sign(this.engineRPM) * chassis.maxPowerTP.torque * chassis.engineBrakeCoefficient *
                  (friction + (1.0 - friction) * this.engineRPM / chassis.maxRPM);

    this.engineRPM += eb * dt / chassis.engineInertia;
    if (s != sign(this.engineRPM)) {
        this.engineRPM = 0;
    } else {
        appliedTorque += eb;
    }

    var tq = appliedTorque + chassis.engineInertia * (oldRPM - this.engineRPM) / dt;
    var force = tq * clutch * this.gear.ratio * chassis.differentialRatio * chassis.efficiency / chassis.wheelRadius;
    if (this.gear.index < 0) {
        force = -force;
    }

    this.wheel_force(2, force / 2);
    this.wheel_force(3, force / 2);

    brake *= chassis.breakingForce;
    this.wheel_brake(-1, brake);

    this.animate();

    var rpm = (this.engineRPM + oldRPM) / 2.0; // stabilize the fluctuations when the wheelsRpm and engineRPM
                                               // are very different
    var pitch = chassis.pitchMultiplier * (rpm - chassis.minimumRPM) / chassis.maxRPM;
    var vol = rpm / chassis.minimumRPM * 2.0;
    vol = (vol > 1 ? 1 : (vol < 0 ? 0 : vol));

    this.snd.set_pitch(this.onSource, pitch + chassis.basePitch[2]);
    this.snd.set_gain(this.onSource, chassis.relVolumes[2] * engine * vol);

    var speed = Math.abs(this.speed());
    this.snd.set_pitch(this.offSource, pitch + chassis.basePitch[1]);
    var v = speed / 5;
    this.snd.set_gain(this.offSource, chassis.relVolumes[1] * (1 - engine * vol) * Math.min(v, 1.0));

    var v = speed / 20;
    if (!clutch) {
        v = Math.min(v, rpm / chassis.maxRPM);
    }
    if (!running && this.idleSound > 0) {
        this.idleSound -= dt;
        this.idleSound = (this.idleSound < 0 ? 0 : this.idleSound);
    }
    this.snd.set_pitch(this.idleSource, pitch + chassis.basePitch[0]);
    this.snd.set_gain(this.idleSource, this.idleSound * chassis.relVolumes[0] * (1 - engine) * Math.max(vol - v, 0));

    if (rpm < 100) { //dead
        if (!this.clutchOverride) {
            this.clutch = 1.0;
            this.clutchState = ClutchState.Disengaged;
        }
        this.started = false;
    }
}


Using the clutch manually now shows how bad the automatic clutch works when starting. I'll have to work on that  :-\
Title: Re: Vehicle script [version 1.3.2]
Post by: bugsblake on June 03, 2013, 01:09:21 pm
sounds good! will try it out later! just one thing, when i copy and paste this into notepad or notepad++, the lines get all messed up! is there another way im ment to be doing it? or maybe you can upload the .js file! thanks
Title: Re: Vehicle script [version 1.3.2]
Post by: giucam on June 03, 2013, 01:17:33 pm
Odd... pasting it in notepad++ here works all right. Anyway, i've attached the js.
Title: Re: Vehicle script [version 1.3.2]
Post by: bugsblake on June 03, 2013, 01:57:17 pm
thanks! yh i know, its pastes alright sometimes and not others? :/ anyway thanks for the file! and thanks for your advice in my other post, all engine sounds working now! will update the mustang and charger later tonight! thanks a lot mate! :D
Title: Re: Vehicle script [version 1.3.2]
Post by: M7 on June 03, 2013, 11:50:42 pm
in 132 for the high rev sound you use 2 simultanious sound ? I like the idea that it can be script,i have some idea how to use it on tanks but on the BMW, i'm not so sure it sound better than the high rev alone. The e90_onlow.ogg is probably the best high rev sound for land vehicle ingame. Looks like it will be a pretty tough job to improve it.

On the other end, i'm so glad you're taking on to add these new features, really appreciate!!
Title: Re: Vehicle script [version 1.3.2]
Post by: giucam on June 04, 2013, 05:50:54 am
Actually 3 simultanious sounds, but you may hear only one because two have 0 volume. I needed to change the sound handling because when using an axis to accellerate the 90_onlow.ogg was playing only when fully accellerating. So i changed it to always play it and to set the volume of it to the engine value passed to the update function (which goes from 0 to 1), and the off sound uses 1 - engine. That way the off and on sounds blend nicely when pushing more or less on the accellerator.
Then there's the idle sound, which can be heard only when going not too fast (more or less), fading away as you gain speed.

So yeah, if you use the keyboard you should not notice much difference with before.
Title: Re: Vehicle script [version 1.3.2]
Post by: M7 on June 04, 2013, 09:15:06 am
I use keyboard and it sounds much different than previous version. The acceleration sound quite mushy (not clear like before)

But now i will have to try it with a pedal. This is really cool stuff if you can blend
different sounds during acceleration/decelaration. It might make it much easier to create sound for high rev if you dont have to use the pitch multiplier as much. Wow great stuff!!!!

But people using  keyboard only , they might be better off these new versions.
Title: Re: Vehicle script [version 1.3.2]
Post by: M7 on June 08, 2013, 10:03:28 pm
 I tried this time with a pedal and it was pretty much the same thing. Then i thought to checkt the .impcfg (which unfortunatly we dont get anymore when using the .otx to export) to look for another bone than the wheel to set the soundEmitter. I didn't find a bone  but pick up a random part, i actually choose 'box61' and BINGO!

This thing sounds incredible now  compare to one sound at a time. To my ears, it sound almost like a formula one and you get perfect transitions. Fken good job Giucam!!!!!!

The only thing is the idle sound, i barely ear it and the sound cut as well. It's really weird cause if i put the soundEmitter back to Wheel_RF , e90_onidle.ogg sounds perfect.

Title: Re: Vehicle script [version 1.3.2]
Post by: giucam on June 09, 2013, 10:35:36 am
Wait, was it using the wheel as sound emitter? I changed it to the bone "hood" in the latest versions. Are you using the bmw version 2 or still the 1?
Title: Re: Vehicle script [version 1.3.2]
Post by: M7 on June 09, 2013, 12:59:12 pm
Hehe! Wrong car and wrong script ::) didnt know there was a new car and i used the engine1.3.2.js that you have last as attachment which still have the wheel as sound emitter.

But now that i got all the right parts, everything sound perfect!!!!!!!!  And also you can reload sounds within the game now, which makes it so much easier to experiment. Looking forward to try new sound combo with this script . Thanks so much Giucam!

Title: Re: Vehicle script [version 1.3.2]
Post by: M7 on June 09, 2013, 11:20:26 pm
I tried this script with the Dodge bigfoot. Everything works as expected except for sound. First, the idle sound is barely perceptible and breaks, then acceleration/decelaration sounds just woble. I also tried to use the BMW sounds but it didn't play at all.

Also looks like you dont need to set a bone as sound emitter anymore. Sounds will play with or without (unlike before)

I made an .otx if you  want to take a look.

https://docs.google.com/file/d/0B96RrTcNJsI2N0Q5ajJNcEowWVE/edit?usp=sharing
Title: Re: Vehicle script [version 1.3.3]
Post by: giucam on June 10, 2013, 10:43:35 am
I made a mistake with the sound emitter indeed.

Version 1.3.3

Code: [Select]
/*
    Vehicle script file
    Copyright (C) 2012 - 2013  Giulio Camuffo <giuliocamuffo@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 3.0 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//see http://xtrac.outerraworld.com/trac.fcgi/wiki/vehicle for example and documentation

// VERSION 1.3.3

var chassis = {};

// --- Vehicle specific stuff ---
function chassis_setup() {
    var wheelparam = {
        radius: 0.30,
        width: 0.20,
        suspension_max: 0.1,
        suspension_min: -0.05,
        suspension_stiffness: 50.0,
        damping_compression: 0.06,
        damping_relaxation: 0.05,
        slip: 2.0,
        roll_influence: 0.1,
        rotation: -1
    };

    this.add_wheel('wheel_FL', wheelparam);
    this.add_wheel('wheel_FR', wheelparam);
    this.add_wheel('wheel_RL', wheelparam);
    this.add_wheel('wheel_RR', wheelparam);

    this.load_sound("e90_onidle.ogg"); //idle
    this.load_sound("e90_offlow.ogg"); //off
    this.load_sound("e90_onlow.ogg"); //on
    chassis.basePitch = [ 1, 0.4, 0.4 ];             //base pitch for idle, off, on
    chassis.relVolumes = [ 0.8, 1, 1 ];
    chassis.soundEmitter = "hood";

    //engine properties -- modify these to change the engine behaviour
    chassis.wheelRadius = 0.24;

    chassis.torquePoints = [ { rpm: 500,  torque: 0},
                             { rpm: 1000, torque: 250},
                             { rpm: 2000, torque: 335},
                             { rpm: 3000, torque: 380},
                             { rpm: 4000, torque: 400},
                             { rpm: 5000, torque: 395},
                             { rpm: 6000, torque: 400},
                             { rpm: 7000, torque: 390},
                             { rpm: 8000, torque: 365},
                             { rpm: 9000, torque: 0  } ];

    chassis.forwardGears = [ { ratio: 4.06, shiftUp: 8000, shiftDown: -1   },
                             { ratio: 2.40, shiftUp: 8000, shiftDown: 4500 },
                             { ratio: 1.58, shiftUp: 8000, shiftDown: 5000 },
                             { ratio: 1.19, shiftUp: 8000, shiftDown: 5500 },
                             { ratio: 1.00, shiftUp: 8000, shiftDown: 6000 },
                             { ratio: 0.87, shiftUp: -1,   shiftDown: 6000 } ];
    chassis.reverseGears = [ { ratio: 4.68, shiftUp: -1, shiftDown: -1 } ];

    chassis.differentialRatio = 3.85;
    chassis.efficiency = 0.9;
    chassis.speedLimiter = -1; // maximum speed in m/s. a value < 0 means there's no limiter
    chassis.engineBrakeCoefficient = 0.4;
    chassis.breakingForce = 3000.0;
    chassis.pitchMultiplier = 1.5;
    chassis.engineInertia = 0.015;
    chassis.minimumRPM = 1500;
    chassis.minimum = 0.2;
    //end of engine properties

    return { mass:1570, steering:1.2, steering_ecf:700, centering: 5.0, centering_ecf: 40, com: { z:0.1 } };
}

function vehicle_setup() {
    this.set_fps_camera_pos( {x: -0.33,y: -0.20, z: 1.15});
    this.soundRefDistance = 5;
    this.automaticTransmission = false;
    this.clutchMode = ClutchMode.Automatic;
}

function animate()
{
    this.animate_wheels();
}

// --- End of vehicle specific stuff ---



//invoked only the first time the model is loaded, or upon reload
function init_chassis() {
    this.setup = chassis_setup;
    var ret = this.setup();

    chassis.maxPowerTP = chassis.torquePoints[0];
    chassis.maxRPM = 0;
    var maxPw = 0;
    for (var i = 1; i < chassis.torquePoints.length; ++i) {
        var tp = chassis.torquePoints[i];
        if (tp.rpm * tp.torque > maxPw) {
            chassis.maxPowerTP = tp;
        }
        if (tp.rpm > chassis.maxRPM) {
            chassis.maxRPM = tp.rpm;
        }
    }

    chassis.neutroGear = { ratio: 0.0, shiftUp: -1, shiftDown: -1, index: 0 };
    var prev = null;
    for (var i = 0; i < chassis.forwardGears.length; ++i) {
        var gear = chassis.forwardGears[i];
        if (prev)
            prev.next = gear;
        gear.prev = prev;
        gear.index = i + 1;
        prev = gear;
    }

    prev = null;
    for (var i = 0; i < chassis.reverseGears.length; ++i) {
        var gear = chassis.reverseGears[i];
        if (prev)
            prev.prev = gear;
        gear.next = prev;
        gear.index = -i - 1;
        prev = gear;
    }

    return ret;
}

var ClutchMode = { Manual: 0, Automatic: 1, Mixed: 2 };
var ClutchState = { Engaged: 0, Disengaged: 1, Engaging: 2, Disengaging: 3 };

//invoked for each new instance of the vehicle
function init_vehicle() {
    this.snd = this.sound();

    this.torque = engineTorque;
    this.shiftUp = shift_up;
    this.shiftDown = shift_down;
    this.selectGear = select_gear;
    this.updateAutomaticTransmission = automatic_transmission;
    this.automaticClutch = automatic_clutch;
    this.clutchValue = clutch_value;
    this.animate = animate;
    this.setup = vehicle_setup;

    this.setup();

    var id = this.get_geomob(0).get_joint(chassis.soundEmitter);
    this.idleSource = this.snd.create_source(id);
    this.onSource = this.snd.create_source(id);
    this.offSource = this.snd.create_source(id);
    this.idleSound = 1;
    this.snd.set_ref_distance(this.idleSource, this.soundRefDistance);
    this.snd.set_ref_distance(this.onSource, this.soundRefDistance);
    this.snd.set_ref_distance(this.offSource, this.soundRefDistance);
    this.snd.play(this.idleSource, 0, true, false);
    this.snd.play(this.offSource, 1, true, false);
    this.snd.play(this.onSource, 2, true, false);

    this.clutch = 1.0;
    this.clutchState = ClutchState.Disengaged;
    this.started = false;
    this.gear = chassis.neutroGear;
    this.direction = 0;
    this.engineRPM = 0;
    this.starting = false;
    this.clutchOverride = false;
}

//invoked when engine starts or stops
function engine(start) {
    this.started = false;
    this.starting = start;
    this.idleSound = 1.0;
}

function engineTorque(rpm) {
    var min = 0;
    var max = chassis.torquePoints.length - 1;

    if (rpm > chassis.torquePoints[max].rpm || rpm < chassis.torquePoints[min].rpm)
        return 0;

    while (max - min > 1) {
        var mid = Math.floor(min + (max - min) / 2);
        var tp = chassis.torquePoints[mid];
        if (tp.rpm == rpm) {
            return tp.torque;
        } else if (tp.rpm > rpm) {
            max = mid;
        } else {
            min = mid;
        }
    }

    var minTp = chassis.torquePoints[min];
    var maxTp = chassis.torquePoints[max];
    var TM = maxTp.torque;
    var Tm = minTp.torque;
    var RM = maxTp.rpm;
    var Rm = minTp.rpm;

    var a = (TM - Tm) / (RM - Rm);

    return rpm * a + Tm - Rm * a;
}

function sign(x) {
    return (x > 0.0 ? 1 : (x < 0.0 ? -1 : 0));
}

function action(key, value, dt)
{
    if (key == AAuxb1 && value == 0) {
        this.automaticTransmission = !this.automaticTransmission;
        this.log_inf("Automatic transmission " + (this.automaticTransmission ? "enabled" : "disabled"));
    }

    if (key == AAuxb2 || key == AAuxa2) {
        if (key == AAuxa2) {
            value = 1 - (value + 1) / 2;
        }

        this.clutchState = (value == 0 ? ClutchState.Disengaged : ClutchState.Engaged);
        this.clutch = 1 - value;
        this.clutchOverride = value != 0;
    }

    if (value == 1 && !this.automaticTransmission) {
        switch(key) {
            case AShiftUp:
                this.shiftUp();
                break;
            case AShiftDown:
                this.shiftDown();
                break;
            default:
                break;
        }
    }
}

function select_gear(gear)
{
    if (this.gear == gear) {
        return;
    }

    var i = this.gear.index;
    this.gear = gear;
    if (i == 0 && this.clutchMode == ClutchMode.Automatic && (this.started || this.starting)) {
        this.clutchState = ClutchState.Engaging;
    }
    this.log_inf("gear " + i + " => " + this.gear.index);
}

function shift_up()
{
    var i = this.gear.index;
    if (this.gear.next) {
        this.selectGear(this.gear.next);
    } else if (i == 0) {
        this.selectGear(chassis.forwardGears[0]);
    } else if (i == -1) {
        this.selectGear(chassis.neutroGear);
    }
}

function shift_down()
{
    var i = this.gear.index;
    if (this.gear.prev) {
        this.selectGear(this.gear.prev);
    } else if (i == 0) {
        this.selectGear(chassis.reverseGears[0]);
    } else if (i == 1) {
        this.selectGear(chassis.neutroGear);
    }
}

function automatic_transmission(engine)
{
    if (this.engineRPM < 10 && this.gear.index != 0) {
        this.direction = 0;
        this.selectGear(chassis.neutroGear);
    }
    if (engine != 0.0) {
        if (sign(engine) != this.direction) {
            this.direction = sign(engine);
            this.selectGear(chassis.neutroGear);
            if (this.direction == 1.0) {
                this.selectGear(chassis.forwardGears[0]);
            } else if (this.direction == -1.0) {
                this.selectGear(chassis.reverseGears[0]);
            }
        }
    }

    if (this.clutch != 0) {
        if (this.engineRPM > this.gear.shiftUp && this.gear.next) {
            this.shiftUp();
        } else if (this.engineRPM < this.gear.shiftDown && this.gear.prev) {
            this.shiftDown();
        }
    }
}

function automatic_clutch(wheelsRpm)
{
    var clutched = (this.clutchState == ClutchState.Disengaged  || this.clutchState == ClutchState.Disengaging) && this.gear.index != 0;
    var clutchRPM = chassis.minimumRPM * 7 / 4;
    var w = wheelsRpm * this.gear.ratio * chassis.differentialRatio;

    if (this.clutchMode == ClutchMode.Automatic && !this.clutchOverride && (this.started || this.starting)) {
        if (clutched && this.engineRPM < chassis.minimumRPM && w < chassis.minimumRPM) {
            this.clutchState = ClutchState.Engaging;
        } else if (!clutched && (this.engineRPM > clutchRPM || w > chassis.minimumRPM)) {
            this.clutchState = ClutchState.Disengaging;
        }
    }

    if (this.clutchState == ClutchState.Disengaging) {
        if (this.gear.index != 0) {
            this.clutch = (w > chassis.minimumRPM ? chassis.minimumRPM : w) / chassis.minimumRPM;

            // This makes an horizontal S-like shape. (mirrored, like Z)
            var f = this.clutch * 2;
            if (f < 0.5) {
                this.clutch = -(f * f * f * f - 1) / 5;
            } else {
                f = f - 1;
                this.clutch = (f * f * f * f) + 0.2;
            }
            this.clutch += 0.4;
            var r = (this.engineRPM - chassis.minimumRPM) / clutchRPM;
            r = (r > 1 ? 1 : (r < 0 ? 0 : r));
            r = Math.sqrt(r);
            this.clutch *= r
        } else {
            this.clutch = 1;
        }

        if (this.clutch >= 1) {
            this.clutch = 1;
            this.clutchState = ClutchState.Disengaged;
        }
    } else if (this.clutchState == ClutchState.Engaging) {
        this.clutch = 0;
        this.clutchState = ClutchState.Engaged;
    }
    return this.clutch;
}

function clutch_value(wheelsRpm)
{
    var clutch = this.clutch;
    if (this.clutchMode != ClutchMode.Manual && !this.clutchOverride) {
        clutch = this.automaticClutch(wheelsRpm);
    }
    if (this.gear.index == 0) {
        clutch = 0;
    }
    return clutch;
}

//invoked each frame to handle the inputs and animate the model
function update_frame(dt, engine, brake, steering) {
    steering *= 0.6;
    this.steer(-2, steering);

    if (this.automaticTransmission) {
        this.updateAutomaticTransmission(engine);
    }

    var wheelsRpm = Math.abs(this.max_rpm());
    if (sign(this.speed()) != sign(this.gear.index) && this.gear.index != 0) {
        wheelsRpm = 0;
    }

    if (engine < 0) {
        engine = -engine;
    }

    var appliedTorque = 0;
    if (this.starting) {
        if (this.engineRPM > chassis.minimumRPM) {
            this.starting = false;
            this.started = true;
        } else {
            appliedTorque = 200 * (1 - this.engineRPM / chassis.minimumRPM);
        }
    }

    var oldRPM = this.engineRPM;
    var clutch = this.clutchValue(wheelsRpm);
    this.engineRPM = this.engineRPM * (1 - clutch) + wheelsRpm * clutch * this.gear.ratio * chassis.differentialRatio;

    var running = this.started || this.starting;
    if (running) {
        if (this.engineRPM < chassis.minimumRPM && engine < chassis.minimum) {
            engine = chassis.minimum;
        }
        if (chassis.speedLimiter < 0 || Math.abs(this.speed()) < chassis.speedLimiter) {
            appliedTorque += engine * this.torque(this.engineRPM);
        }
    }

    this.engineRPM += appliedTorque * dt / chassis.engineInertia;

    var s = sign(this.engineRPM);
    var friction = 0.2;
    var eb = -sign(this.engineRPM) * chassis.maxPowerTP.torque * chassis.engineBrakeCoefficient *
                  (friction + (1.0 - friction) * this.engineRPM / chassis.maxRPM);

    this.engineRPM += eb * dt / chassis.engineInertia;
    if (s != sign(this.engineRPM)) {
        this.engineRPM = 0;
    } else {
        appliedTorque += eb;
    }

    var tq = appliedTorque + chassis.engineInertia * (oldRPM - this.engineRPM) / dt;
    var force = tq * clutch * this.gear.ratio * chassis.differentialRatio * chassis.efficiency / chassis.wheelRadius;
    if (this.gear.index < 0) {
        force = -force;
    }

    this.wheel_force(2, force / 2);
    this.wheel_force(3, force / 2);

    brake *= chassis.breakingForce;
    this.wheel_brake(-1, brake);

    this.animate();

    var rpm = (this.engineRPM + oldRPM) / 2.0; // stabilize the fluctuations when the wheelsRpm and engineRPM
                                               // are very different
    var pitch = chassis.pitchMultiplier * (rpm - chassis.minimumRPM) / chassis.maxRPM;
    var vol = rpm / chassis.minimumRPM * 2.0;
    vol = (vol > 1 ? 1 : (vol < 0 ? 0 : vol));

    this.snd.set_pitch(this.onSource, pitch + chassis.basePitch[2]);
    this.snd.set_gain(this.onSource, chassis.relVolumes[2] * engine * vol);

    var speed = Math.abs(this.speed());
    this.snd.set_pitch(this.offSource, pitch + chassis.basePitch[1]);
    var v = speed / 5;
    this.snd.set_gain(this.offSource, chassis.relVolumes[1] * (1 - engine * vol) * Math.min(v, 1.0));

    var v = speed / 20;
    if (!clutch) {
        v = Math.min(v, rpm / chassis.maxRPM);
    }
    if (!running && this.idleSound > 0) {
        this.idleSound -= dt;
        this.idleSound = (this.idleSound < 0 ? 0 : this.idleSound);
    }
    this.snd.set_pitch(this.idleSource, pitch + chassis.basePitch[0]);
    this.snd.set_gain(this.idleSource, this.idleSound * chassis.relVolumes[0] * (1 - engine) * Math.max(vol - v, 0));

    if (rpm < 100) { //dead
        if (!this.clutchOverride) {
            this.clutch = 1.0;
            this.clutchState = ClutchState.Disengaged;
        }
        this.started = false;
    }
}



You can adjust the volume of the different sounds by changing the values of chassis.relVolumes, the first one for the idle sound.
Title: Re: Vehicle script [version 1.3.3]
Post by: M7 on June 10, 2013, 02:48:00 pm
I still had sound wobling with 1.3.2 but with this new line you added...

    var id = this.get_geomob(0).get_joint(chassis.soundEmitter);
    this.idleSource = this.snd.create_source(id);
    this.onSource = this.snd.create_source(id);
    this.offSource = this.snd.create_source(id);

...the wobling is gone. Thanks!
Title: Re: Vehicle script [version 1.3.3]
Post by: M7 on June 10, 2013, 10:27:23 pm
I don't know if we can make special request but one thing that would be cool for the immersion factor, would be to include is a startup and turn off sound. I'd say the best way i'seen it done (at least for startup) is with the Lamborghini. When you hit forward, the startup sound plays but the car dont move forward like in many script, instead the startup sound play till the end of its time and only then can you rev the engine and and put it in gear.

 Have you ever thought to include this in future versions?
Title: Re: Vehicle script [version 1.3.3]
Post by: giucam on June 11, 2013, 06:38:09 am
Yeah, but i don't have a good enough startup sound. What I'd want is the sound of the starter motor only, so i could make it blend with the existing sound.
Title: Re: Vehicle script [version 1.3.3]
Post by: M7 on June 11, 2013, 08:53:55 am
Yeah might not be obvious to find a good startup sound to fit with the BMW set. But i beleive it's your intention to have this script as a possible basis for all ground vehicle since it has manual and automatic and the amazing new possibilities for sound.

Then people could adapt the script and sound to the specific vehicle they want to emulate. So even if you dont have the right startup sound for your specific car, i think it could be a good idea to release a version with these scripted in so that others could work on their sound set.

I know i want to try to get this script working with a tank. It might be tough to merge Cameni's tank steering script with this one. But to have the startup and turnoff already in will make it possibly easier in long term since i might not have to merge it twice.

As for updates, i found myself some tricks so to not start over at editing value of specific vehicle every times there's a new version. I copy/paste the script in a drawing program (in my case i use illustrator) so i can have a view of whole script at once. When there's an update, i copy paste the new version and then justapose the 2 versions. Much easier to find the new lines. Then i will copy only the new lines into my vehicle script.
Title: Re: Vehicle script [version 1.4.0]
Post by: giucam on July 05, 2013, 12:34:41 pm
Version 1.4.0:

Code: [Select]
[/*
    Vehicle script file
    Copyright (C) 2012 - 2013  Giulio Camuffo <giuliocamuffo@gmail.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    version 3.0 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//see http://xtrac.outerraworld.com/trac.fcgi/wiki/vehicle for example and documentation

// VERSION 1.4.0

//thanks to hhrhhr for the code in calc_sound: http://www.outerra.com/forum/index.php?topic=1884.msg21810#msg21810

var chassis = {};

// --- Vehicle specific stuff ---
function chassis_setup() {
    var wheelparam = {
        radius: 0.30,
        width: 0.20,
        suspension_max: 0.1,
        suspension_min: -0.05,
        suspension_stiffness: 50.0,
        damping_compression: 0.06,
        damping_relaxation: 0.05,
        slip: 2.0,
        roll_influence: 0.1,
        rotation: -1
    };

    this.add_wheel('wheel_FL', wheelparam);
    this.add_wheel('wheel_FR', wheelparam);
    this.add_wheel('wheel_RL', wheelparam);
    this.add_wheel('wheel_RR', wheelparam);
   
    chassis.soundEmitter = "hood";
    chassis.soundRefDistance = 10;

    chassis.onSamples = [ { rpm: 859, sound: this.load_sound("v8/859_P.ogg") },
                          { rpm: 984, sound: this.load_sound("v8/984_P.ogg") },
                          { rpm: 1126, sound: this.load_sound("v8/1126_P.ogg") },
                          { rpm: 1476, sound: this.load_sound("v8/1476_P.ogg") },
                          { rpm: 2215, sound: this.load_sound("v8/2215_P.ogg") },
                          { rpm: 3324, sound: this.load_sound("v8/3324_P.ogg") },
                          { rpm: 4358, sound: this.load_sound("v8/4358_P.ogg") },
                          { rpm: 5712, sound: this.load_sound("v8/5712_P.ogg") },
                          { rpm: 6540, sound: this.load_sound("v8/6540_P.ogg") } ];

    chassis.offSamples = [ { rpm: 859, sound: this.load_sound("v8/859.ogg") },
                           { rpm: 984, sound: this.load_sound("v8/984.ogg") },
                           { rpm: 1126, sound: this.load_sound("v8/1126.ogg") },
                           { rpm: 1476, sound: this.load_sound("v8/1476.ogg") },
                           { rpm: 2215, sound: this.load_sound("v8/2215.ogg") },
                           { rpm: 3324, sound: this.load_sound("v8/3324.ogg") },
                           { rpm: 4358, sound: this.load_sound("v8/4358.ogg") },
                           { rpm: 5712, sound: this.load_sound("v8/5712.ogg") },
                           { rpm: 6540, sound: this.load_sound("v8/6540.ogg") } ];

    //engine properties -- modify these to change the engine behaviour
    chassis.wheelRadius = 0.24;

    chassis.torquePoints = [ { rpm: 500,  torque: 0},
                             { rpm: 1000, torque: 250},
                             { rpm: 2000, torque: 335},
                             { rpm: 3000, torque: 380},
                             { rpm: 4000, torque: 400},
                             { rpm: 5000, torque: 395},
                             { rpm: 6000, torque: 400},
                             { rpm: 7000, torque: 390},
                             { rpm: 8000, torque: 365},
                             { rpm: 9000, torque: 0  } ];

    chassis.forwardGears = [ { ratio: 4.06, shiftUp: 8000, shiftDown: -1   },
                             { ratio: 2.40, shiftUp: 8000, shiftDown: 4500 },
                             { ratio: 1.58, shiftUp: 8000, shiftDown: 5000 },
                             { ratio: 1.19, shiftUp: 8000, shiftDown: 5500 },
                             { ratio: 1.00, shiftUp: 8000, shiftDown: 6000 },
                             { ratio: 0.87, shiftUp: -1,   shiftDown: 6000 } ];
    chassis.reverseGears = [ { ratio: 4.68, shiftUp: -1, shiftDown: -1 } ];

    chassis.differentialRatio = 3.85;
    chassis.efficiency = 0.9;
    chassis.speedLimiter = -1; // maximum speed in m/s. a value < 0 means there's no limiter
    chassis.engineBrakeCoefficient = 0.4;
    chassis.breakingForce = 3000.0;
    chassis.pitchMultiplier = 1.5;
    chassis.engineInertia = 0.025;
    chassis.minimumRPM = 1100;
    chassis.minimum = 0.5;
    //end of engine properties

    return { mass:1570, steering:1.2, steering_ecf:700, centering: 5.0, centering_ecf: 40, com: { z:0.1 } };
}

function vehicle_setup() {
    this.set_fps_camera_pos( {x: -0.33,y: -0.40, z: 1.15});
    this.automaticTransmission = false;
    this.clutchMode = ClutchMode.Automatic;
}

function animate()
{
    this.animate_wheels();
}

// --- End of vehicle specific stuff ---



//invoked only the first time the model is loaded, or upon reload
function init_chassis() {
    this.setup = chassis_setup;
    var ret = this.setup();

    chassis.maxPowerTP = chassis.torquePoints[0];
    chassis.maxRPM = 0;
    var maxPw = 0;
    for (var i = 1; i < chassis.torquePoints.length; ++i) {
        var tp = chassis.torquePoints[i];
        if (tp.rpm * tp.torque > maxPw) {
            chassis.maxPowerTP = tp;
        }
        if (tp.rpm > chassis.maxRPM) {
            chassis.maxRPM = tp.rpm;
        }
    }

    for (var i = 0; i < chassis.offSamples.length; ++i) {
        chassis.offSamples[i].source = this.add_sound_emitter(chassis.soundEmitter);
    }
    for (var i = 0; i < chassis.onSamples.length; ++i) {
        chassis.onSamples[i].source = this.add_sound_emitter(chassis.soundEmitter);
    }

    chassis.neutroGear = { ratio: 0.0, shiftUp: -1, shiftDown: -1, index: 0 };
    var prev = null;
    for (var i = 0; i < chassis.forwardGears.length; ++i) {
        var gear = chassis.forwardGears[i];
        if (prev)
            prev.next = gear;
        gear.prev = prev;
        gear.index = i + 1;
        prev = gear;
    }

    prev = null;
    for (var i = 0; i < chassis.reverseGears.length; ++i) {
        var gear = chassis.reverseGears[i];
        if (prev)
            prev.prev = gear;
        gear.next = prev;
        gear.index = -i - 1;
        prev = gear;
    }

    return ret;
}

var ClutchMode = { Manual: 0, Automatic: 1, Mixed: 2 };
var ClutchState = { Engaged: 0, Disengaged: 1, Engaging: 2, Disengaging: 3 };

//invoked for each new instance of the vehicle
function init_vehicle() {
    this.snd = this.sound();

    this.torque = engineTorque;
    this.shiftUp = shift_up;
    this.shiftDown = shift_down;
    this.selectGear = select_gear;
    this.updateAutomaticTransmission = automatic_transmission;
    this.automaticClutch = automatic_clutch;
    this.clutchValue = clutch_value;
    this.animate = animate;
    this.setup = vehicle_setup;
    this.calcSound = calc_sound;

    for (var i = 0; i < chassis.offSamples.length; ++i) {
        this.snd.set_ref_distance(chassis.offSamples[i].source, chassis.soundRefDistance);
        this.snd.play(chassis.offSamples[i].source, chassis.offSamples[i].sound, true, false);
        this.snd.set_gain(chassis.offSamples[i].source, 0);
    }
    for (var i = 0; i < chassis.onSamples.length; ++i) {
        this.snd.set_ref_distance(chassis.onSamples[i].source, chassis.soundRefDistance);
        this.snd.play(chassis.onSamples[i].source, chassis.onSamples[i].sound, true, false);
        this.snd.set_gain(chassis.onSamples[i].source, 0);
    }

    this.setup();

    this.clutch = 1.0;
    this.clutchState = ClutchState.Disengaged;
    this.started = false;
    this.gear = chassis.neutroGear;
    this.direction = 0;
    this.engineRPM = 0;
    this.starting = false;
    this.clutchOverride = false;
}

//invoked when engine starts or stops
function engine(start) {
    this.started = false;
    this.starting = start;
    this.idleSound = 1.0;
}

function engineTorque(rpm) {
    var min = 0;
    var max = chassis.torquePoints.length - 1;

    if (rpm > chassis.torquePoints[max].rpm || rpm < chassis.torquePoints[min].rpm)
        return 0;

    while (max - min > 1) {
        var mid = Math.floor(min + (max - min) / 2);
        var tp = chassis.torquePoints[mid];
        if (tp.rpm == rpm) {
            return tp.torque;
        } else if (tp.rpm > rpm) {
            max = mid;
        } else {
            min = mid;
        }
    }

    var minTp = chassis.torquePoints[min];
    var maxTp = chassis.torquePoints[max];
    var TM = maxTp.torque;
    var Tm = minTp.torque;
    var RM = maxTp.rpm;
    var Rm = minTp.rpm;

    var a = (TM - Tm) / (RM - Rm);

    return rpm * a + Tm - Rm * a;
}

function sign(x) {
    return (x > 0.0 ? 1 : (x < 0.0 ? -1 : 0));
}

function action(key, value, dt)
{
    if (key == AAuxb1 && value == 0) {
        this.automaticTransmission = !this.automaticTransmission;
        this.log_inf("Automatic transmission " + (this.automaticTransmission ? "enabled" : "disabled"));
    }

    if (key == AAuxb2 || key == AAuxa2) {
        if (key == AAuxa2) {
            value = 1 - (value + 1) / 2;
        }
        value = 1 - value;
        value *= value;

        this.clutchState = (value == 1 ? ClutchState.Disengaged : ClutchState.Engaged);
        this.clutch = value;
        this.clutchOverride = value != 1;
    }

    if (value == 1 && !this.automaticTransmission) {
        switch(key) {
            case AShiftUp:
                this.shiftUp();
                break;
            case AShiftDown:
                this.shiftDown();
                break;
            default:
                break;
        }
    }
}

function select_gear(gear)
{
    if (this.gear == gear) {
        return;
    }

    var i = this.gear.index;
    this.gear = gear;
    if (i == 0 && this.clutchMode == ClutchMode.Automatic && (this.started || this.starting)) {
        this.clutchState = ClutchState.Engaging;
    }
    this.log_inf("gear " + i + " => " + this.gear.index);
}

function shift_up()
{
    var i = this.gear.index;
    if (this.gear.next) {
        this.selectGear(this.gear.next);
    } else if (i == 0) {
        this.selectGear(chassis.forwardGears[0]);
    } else if (i == -1) {
        this.selectGear(chassis.neutroGear);
    }
}

function shift_down()
{
    var i = this.gear.index;
    if (this.gear.prev) {
        this.selectGear(this.gear.prev);
    } else if (i == 0) {
        this.selectGear(chassis.reverseGears[0]);
    } else if (i == 1) {
        this.selectGear(chassis.neutroGear);
    }
}

function automatic_transmission(engine)
{
    if (this.engineRPM < 10 && this.gear.index != 0) {
        this.direction = 0;
        this.selectGear(chassis.neutroGear);
    }
    if (engine != 0.0) {
        if (sign(engine) != this.direction) {
            this.direction = sign(engine);
            this.selectGear(chassis.neutroGear);
            if (this.direction == 1.0) {
                this.selectGear(chassis.forwardGears[0]);
            } else if (this.direction == -1.0) {
                this.selectGear(chassis.reverseGears[0]);
            }
        }
    }

    if (this.clutch != 0) {
        if (this.engineRPM > this.gear.shiftUp && this.gear.next) {
            this.shiftUp();
        } else if (this.engineRPM < this.gear.shiftDown && this.gear.prev) {
            this.shiftDown();
        }
    }
}

function automatic_clutch(wheelsRpm)
{
    var clutched = (this.clutchState == ClutchState.Disengaged  || this.clutchState == ClutchState.Disengaging) && this.gear.index != 0;
    var clutchRPM = chassis.minimumRPM * 7 / 4;
    var w = wheelsRpm * this.gear.ratio * chassis.differentialRatio;

    if (this.clutchMode == ClutchMode.Automatic && !this.clutchOverride && (this.started || this.starting)) {
        if (clutched && this.engineRPM < chassis.minimumRPM && w < chassis.minimumRPM) {
            this.clutchState = ClutchState.Engaging;
        } else if (!clutched && (this.engineRPM > clutchRPM || w > chassis.minimumRPM)) {
            this.clutchState = ClutchState.Disengaging;
        }
    }

    if (this.clutchState == ClutchState.Disengaging) {
        if (this.gear.index != 0) {
            this.clutch = (w > chassis.minimumRPM ? chassis.minimumRPM : w) / chassis.minimumRPM;

            // This makes an horizontal S-like shape. (mirrored, like Z)
            var f = this.clutch * 2;
            if (f < 0.5) {
                this.clutch = -(f * f * f * f - 1) / 5;
            } else {
                f = f - 1;
                this.clutch = (f * f * f * f) + 0.2;
            }
            this.clutch += 0.4;
            var r = (this.engineRPM - chassis.minimumRPM) / clutchRPM;
            r = (r > 1 ? 1 : (r < 0 ? 0 : r));
            r = Math.sqrt(r);
            this.clutch *= r
        } else {
            this.clutch = 1;
        }

        if (this.clutch >= 1) {
            this.clutch = 1;
            this.clutchState = ClutchState.Disengaged;
        }
    } else if (this.clutchState == ClutchState.Engaging) {
        this.clutch = 0;
        this.clutchState = ClutchState.Engaged;
    }
    return this.clutch;
}

function clutch_value(wheelsRpm)
{
    var clutch = this.clutch;
    if (this.clutchMode != ClutchMode.Manual && !this.clutchOverride) {
        clutch = this.automaticClutch(wheelsRpm);
    }
    if (this.gear.index == 0) {
        clutch = 0;
    }
    return clutch;
}

const PI_2 = Math.PI / 2.0;

function calc_sound(rpm, volmul, list)
{
    var l = list.length;

    if (rpm <= list[1].rpm) {
        // only first sample
        this.snd.set_pitch(list[1].source, rpm / list[1].rpm);
        this.snd.set_gain(list[1].source, volmul * 1.0);
        for (var i = 2; i < l; i++) {
            this.snd.set_gain(list[i].source, 0.0);
        }
    } else if (rpm > list[l - 2].rpm) {
        this.snd.set_pitch(list[l - 2].source, rpm / list[l - 2].rpm);
        this.snd.set_gain(list[l - 2].source, volmul * 1.0);
        for (var i = 0; i < l - 2; i++) {
            this.snd.set_gain(list[i].source, 0);
        }
    } else {
        for (var i = 0; i < l - 1; ++i) {
            var sample = list[i];
            var next = list[i + 1]

            if (rpm > next.rpm || rpm <= list[i - 1].rpm) {
                this.snd.set_gain(sample.source, 0.0);
            } else if (rpm > sample.rpm) {
                var drpm = next.rpm - sample.rpm;
                var vol_l = (next.rpm - rpm) / drpm;
                var vol_h = (rpm - sample.rpm) / drpm;

                this.snd.set_gain(sample.source, volmul * Math.sin(vol_l * PI_2));
                this.snd.set_gain(next.source, volmul * Math.sin(vol_h * PI_2));

                this.snd.set_pitch(sample.source, rpm / sample.rpm);
                this.snd.set_pitch(next.source, rpm / next.rpm);
            }
        }
    }
}

//invoked each frame to handle the inputs and animate the model
function update_frame(dt, engine, brake, steering) {
    steering *= 0.6;
    this.steer(-2, steering);

    if (this.automaticTransmission) {
        this.updateAutomaticTransmission(engine);
    }

    var wheelsRpm = Math.abs(this.max_rpm());
    if (sign(this.speed()) != sign(this.gear.index) && this.gear.index != 0) {
        wheelsRpm = 0;
    }

    if (engine < 0) {
        engine = -engine;
    }

    var appliedTorque = 0;
    if (this.starting) {
        if (this.engineRPM > chassis.minimumRPM) {
            this.starting = false;
            this.started = true;
        } else {
            appliedTorque = 200 * (1 - this.engineRPM / chassis.minimumRPM);
        }
    }

    var oldRPM = this.engineRPM;
    var clutch = this.clutchValue(wheelsRpm);
    this.engineRPM = this.engineRPM * (1 - clutch) + wheelsRpm * clutch * this.gear.ratio * chassis.differentialRatio;

    var s = sign(this.engineRPM);
    var friction = 0.2;
    var eb = -s * chassis.maxPowerTP.torque * chassis.engineBrakeCoefficient *
              (friction + (1.0 - friction) * this.engineRPM / chassis.maxRPM);
    this.engineRPM += eb * dt / chassis.engineInertia;

    if (s != sign(this.engineRPM)) {
        this.engineRPM = 0;
    } else {
        appliedTorque += eb;
    }

    var running = this.started || this.starting;
    if (running) {
        if (this.engineRPM < chassis.minimumRPM) {
            var e = (chassis.minimumRPM - this.engineRPM) * chassis.minimum / this.torque(this.engineRPM);
            if (engine < e) {
                engine = (e > 1 ? 1 : e);
            }
        }
        if (chassis.speedLimiter < 0 || Math.abs(this.speed()) < chassis.speedLimiter) {
            appliedTorque += engine * this.torque(this.engineRPM);
        }
    } else {
        engine = 0;
    }

    this.engineRPM += (1 - clutch) * appliedTorque * dt / chassis.engineInertia;

    var tq = appliedTorque + chassis.engineInertia * (oldRPM - this.engineRPM) / dt;
    var force = tq * clutch * this.gear.ratio * chassis.differentialRatio * chassis.efficiency / chassis.wheelRadius;
    if (this.gear.index < 0) {
        force = -force;
    }

    this.wheel_force(2, force / 2);
    this.wheel_force(3, force / 2);

    brake *= chassis.breakingForce;
    this.wheel_brake(-1, brake);

    this.animate();

    var rpm = (this.engineRPM + oldRPM) / 2.0; // stabilize the fluctuations when the wheelsRpm and engineRPM
                                               // are very different

    this.calcSound(rpm, engine, chassis.onSamples);
    this.calcSound(rpm, 1 - engine, chassis.offSamples);

    if (rpm < 100) { //dead
        if (!this.clutchOverride) {
            this.clutch = 1.0;
            this.clutchState = ClutchState.Disengaged;
        }
        this.started = false;
    }
}

Title: Re: Vehicle script [version 1.4.0]
Post by: John514 on August 01, 2013, 03:49:45 pm
I cant test the script now since I`m on a Integrated Gfx Laptop but how is the vehicle behaviour on this one?
What I cant stand on Outerra is that on higher speeds the vehicles behave like there is no weight or downforce, often spinning out and flipping. I`d like to see some nice and heavy handling for a change.
Title: Re: Vehicle script [version 1.4.0]
Post by: mLichy on August 01, 2013, 07:50:25 pm
Yeah. Part of that is messing with suspension limits and weight.
But even still, if u hit small rocks u go tumbling.

Part of the flipping though could be damped if the body could collide instead of only tires.
Title: Re: Vehicle script [version 1.4.0]
Post by: John514 on August 02, 2013, 01:08:45 am
Right, lets wait for that!
Title: Re: Vehicle script [version 1.4.0]
Post by: PytonPago on August 02, 2013, 07:01:11 pm
Yeah. Part of that is messing with suspension limits and weight.
But even still, if u hit small rocks u go tumbling.

Part of the flipping though could be damped if the body could collide instead of only tires.

There seems to be a little problem at such speeds, that those forces do not get partially consumed when hitting the ground. Maybe some script, that by some exceeding speed (or on a speed function dependance base) the force, witch the dumpers compensate, will be significantly reduced. So it doesn't try to jump away. ... but still, if there would be possible to make a "broken" script, where, if the force exceeds a certain value, the "axes break" - thus making the stiffens go zero, disable the force feed to it (maybe even invert it or make it resist the movement notion of the rest of the car in some way). There could be even a lighter version for just get a flat tire.
Title: Re: Vehicle script [version 1.4.0]
Post by: John514 on August 04, 2013, 03:00:05 pm
To be honest, I`d like THAT kind of driving in Outerra.
It would be the best Rally Game EVER!
VW Polo R WRC - Jari-Matti Latvala 2013 [HD] Pure Sound (http://www.youtube.com/watch?v=2v9AevmYu6c#ws)
Title: Re: Vehicle script [version 1.4.0]
Post by: PytonPago on August 04, 2013, 04:59:59 pm
Had an idea ... many trucks have a second gear option - maybe a little script, saying key XY turns on/off and if on, then the RPM is aether + 120 or multiplied by 1,05 (just for example)  ?  Then, the gears will be complete and truck sims can go on.  :)
Title: Re: Vehicle script [version 1.4.0]
Post by: PytonPago on August 10, 2013, 06:13:21 am
Hi Giucam ... i got a lot of thinking about your script and what i would like to get of a car simulation out of it ... but runned on some knowledge and function problems ...

Basically, i would like to try to enhance some gear functions. Something, like adding a Max and Min for each gear instance to simulate bad gearbox entry (like trying to move from the place at the highest gear - giving an specific sound and engine turns off, or getting to a too low gear - gear-braking into the right speeds for the gear applied and whyte its own sound, etc. other functions like cabin gear rod animations for each and gearbox destruction).  ... i just didnt figure out yet - your engine works like inside the gearbox (just one general RPM)?      Meaning, my approach would need a separate engine RPM and on it based interactions whyte the gearbox (that needs its own separate RPM value based and interacting whyte the engines one) on each gear at the restrains mentioned.

Maybe, if there would be a way to get the sim. working, whyte an IF function depending on actual chassis.forwardGears ratio ? Or would it not be a much good idea to get the functions be dependent on such value ?

Im just starting to get the scripting into my head and i would rather modify your script than get to make my own complex one written, cause im not sure, if i could find the right logics and errors in it ...
Title: Re: Vehicle script [version 1.4.0]
Post by: PytonPago on May 25, 2014, 01:04:06 am
Just got finally to merge my Stuff opening script whyte this vehicle script - found out, that there is a little problem in the way the action function is done so i made a little change to use the switch for multiple stuff :

Code: [Select]
function action(k,v,dt)
{

    switch(k){

      // Engine-needed switch part //
     
     case AAuxb1: {
         if (k == AAuxb1 && v == 0) {
            this.automaticTransmission = !this.automaticTransmission;
            this.log_inf("Automatic transmission " + (this.automaticTransmission ? "enabled" : "disabled"));
         }
     }break;
     
     case AAuxb2: {
         if (k == AAuxb2 || k == AAuxa2) {
              if (k == AAuxa2) {
                 v = 1 - (v + 1) / 2;
              }
             v = 1 - v;
             v *= v;

             this.clutchState = (v == 1 ? ClutchState.Disengaged : ClutchState.Engaged);
             this.clutch = v;
             this.clutchOverride = v != 1;
         }
     }break;
     
     case AShiftUp: {
        if (v == 1 && aShiftUpBool == true) {
           this.shiftUp();
        }

        if (v == 1 && aShiftUpBool == false) {
         aShiftUpTime = aShiftUpTime + (2 * 1);
           this.shiftUp();
        }

        if (v == 1) {
           if (aShiftUpBool == false) {aShiftUpBool = true;}
           else{aShiftUpBool = false;}
        }
      } break;
     
           case AShiftDown: {
        if (v == 1 && aShiftDownBool == true) {
           this.shiftDown();
        }

        if (v == 1 && aShiftDownBool == false) {
         aShiftDownTime = aShiftDownTime + (2 * 1);
           this.shiftDown();
        }

        if (v == 1) {
           if (aShiftDownBool == false) {aShiftDownBool = true;}
           else{aShiftDownBool = false;}
        }
      } break;
     
     
            default:
                break;
     
      // Engine-needed switch part End //
     
        //Lights

       case ALight: {

if (v == 1 && aLightBool == true) {
           this.log_inf("Light OFF");
         this.geom.set_mesh_visible_id(this.light_id, true);
         this.geom.move_joint_orig(this.pb14, {x:0,y:0,z:0});
}

if (v == 1 && aLightBool == false) {
         aOpenTime = aOpenTime + (2 * 1);
           this.log_inf("Light ON");
         this.geom.set_mesh_visible_id(this.light_id, false);
         this.geom.move_joint_orig(this.pb14, {x:0,y:-0.01,z:0});
}

if (v == 1) {
    if (aLightBool == false) {aLightBool = true;}
    else{aLightBool = false;}
}

     }break;


  //HornSound

  case AHorn: {

      if (v == 1 && aHornBool == true) {
           this.log_inf("hOONKhOONK");
      this.snd.play(1, 4, false, true);
}

if (v == 1 && aHornBool == false) {
         aHornTime = aHornTime + (2 * 1);
           this.log_inf("hOONKhOONK");
         this.snd.play(1, 4, false, false);
}

if (v == 1) {
    if (aHornBool == false) {aHornBool = true;}
    else{aHornBool = false;}
}

       }break;


  //Fire


  case AFire: {


       }break;


  case AOpen: {

      var d = this.d1.value>0 ? -1 : 1;
      this.d1.set(d);
      this.d2.set(d);
      this.d3.set(d);
      this.d4.set(d);
      this.d5.set(d);
      this.d6.set(d);
            this.d7.set(d);
      this.d8.set(d);
      this.d9.set(d);
            this.d10.set(d);
      this.d11.set(d);
      this.d12.set(d);
            this.d13.set(d);
      this.d14.set(d);
      this.d15.set(d);
            this.d16.set(d);
      this.d17.set(d);
      this.d18.set(d);
            this.d19.set(d);
      this.d20.set(d);
      this.d21.set(d);
      this.d22.set(d);
      this.d23.set(d);
 //     this.snd.play_sound(2,4);

    }break;

    // We need a selector button - so we create a counter dependance on the aux button

        case AAuxb3: {  // For Cabin stuff
         
          if (v == 1 && aAAuxb3Bool == true) {
            cabcount = ( cabcount + 1 );
     // this.snd.play(1, 4, false, true);
}

if (v == 1 && aAAuxb3Bool == false) {
         aAAuxb3Time = aAAuxb3Time + (2 * 1);
           cabcount = ( cabcount + 1 );
      //   this.snd.play(1, 4, false, false);
}

if (v == 1) {
    if (aAAuxb3Bool == false) {aAAuxb3Bool = true;}
    else{aAAuxb3Bool = false;}
}

    if (cabcount > 10) {cabcount = 1;}

     if (cabcount == 1) {this.log_inf("SetOpen Hood");}
     if (cabcount == 2) {this.log_inf("SetOpen Door-Left");}
     if (cabcount == 3) {this.log_inf("SetOpen Door-Right");}
     if (cabcount == 4) {this.log_inf("SetOpen DoorWindow-Left");}
     if (cabcount == 5) {this.log_inf("SetOpen DoorWindow-Right");}
     if (cabcount == 6) {this.log_inf("SetOpen CaseBig-Left");}
     if (cabcount == 7) {this.log_inf("SetOpen CaseSmall-Right");}
     if (cabcount == 8) {this.log_inf("SetOpen SunCower-Left");}
     if (cabcount == 9) {this.log_inf("SetOpen SunCower-Right");}
     if (cabcount == 10) {this.log_inf("SetOpen HydrWheelHolder");}

    }break;

    case AAuxb4: { // For Back-section stuff

     
  if (v == 1 && aAAuxb4Bool == true) {
            bscount = ( bscount + 1 );
     // this.snd.play(1, 4, false, true);
  }

  if (v == 1 && aAAuxb4Bool == false) {
         aAAuxb4Time = aAAuxb4Time + (2 * 1);
           bscount = ( bscount + 1 );
      //   this.snd.play(1, 4, false, false);
  }

  if (v == 1) {
    if (aAAuxb4Bool == false) {aAAuxb4Bool = true;}
    else{aAAuxb4Bool = false;}
  }

    if (bscount > 5) {bscount = 1;}

     if (bscount == 1) {this.log_inf("SetOpen BSDoor-Back");}
     if (bscount == 2) {this.log_inf("SetOpen BSBackDoor-Left");}
     if (bscount == 3) {this.log_inf("SetOpen BSBackDoor-Right");}
     if (bscount == 4) {this.log_inf("SetOpen BSFrontDoor-Left");}
     if (bscount == 5) {this.log_inf("SetOpen BSFrontDoor-Right");}

    }break;


  }


}

... i had to use the " aShiftUpTime" n "aShiftUpBool" stuff, cause whiteout it, gears were shifted trough two of them (0 to 2 to 4 ... - it switched at pressing and releasing the button).

Other than that - works like charm. Now tweaking the RPM needle and engine characteristics again.  ;)

P.S. ... only thing i cant find is some koeficient, that makes the car slow down faster when no gas is given. Any idea ? My actual state is :

Code: [Select]
    chassis.wheelRadius = 0.24;

    chassis.torquePoints = [ { rpm: 0,    torque: 560},
                             { rpm: 100,  torque: 950},
                             { rpm: 200,  torque: 1000},
                             { rpm: 400,  torque: 1130},
                             { rpm: 600,  torque: 1150},
                             { rpm: 800,  torque: 1156},
                             { rpm: 1000, torque: 1176},
                             { rpm: 1200, torque: 1200},
                             { rpm: 1400, torque: 1060},
                             { rpm: 1600, torque: 960},
                             { rpm: 1800, torque: 820},
                             { rpm: 2000, torque: 960},
                             { rpm: 2200, torque: 750},
                             { rpm: 2400, torque: 620},
                             { rpm: 2600, torque: 410},
                             { rpm: 2800, torque: 15},
                             { rpm: 3000, torque: 0} ];

    chassis.forwardGears = [ { ratio: 8.10, shiftUp: 2300, shiftDown: -1   },
                             { ratio: 4.75, shiftUp: 2300, shiftDown: 800 },
                             { ratio: 2.90, shiftUp: 2400, shiftDown: 700 },
                             { ratio: 1.96, shiftUp: 2400, shiftDown: 600 },
                             { ratio: 1.50, shiftUp: -1,   shiftDown: 500 } ];

    chassis.reverseGears = [ { ratio: 7.80, shiftUp: -1, shiftDown: -1 } ];

    chassis.differentialRatio = 3.85;
    chassis.efficiency = 0.10;
    chassis.speedLimiter = -1; // maximum speed in m/s. a value < 0 means there's no limiter
    chassis.engineBrakeCoefficient = 1.4;
    chassis.breakingForce = 60000.0;
    chassis.pitchMultiplier = 1.5;
    chassis.engineInertia = 0.005;
    chassis.minimumRPM = 400;
    chassis.minimum = 0.5;
    //end of engine properties

    return { com:{y:-0.47, z:0.31}, mass:13710, steering:2.0, steering_ecf:60, centering: 2.6, centering_ecf: 20, com: { z:0.1 } };
}
Title: Re: Vehicle script [version 1.4.0]
Post by: Atrax on September 19, 2014, 06:05:17 am
Oh man it would be nice if someone that knows something about Outerra's physics would do a proper physics for at elast one car. Cause now it is drivable, but it's not satisfying at all to drive the cars, nor is it realistic. So anything you put into the game is just there as a number and not something you can actualy use and have fun with.
Wish I knew more about scripting and all the other stuff to do this myself, but unfortunately I don't. :\
Title: Re: Vehicle script [version 1.4.0]
Post by: ZeosPantera on September 19, 2014, 10:32:03 am
Find the Sound Test Car. Someone (not sure who) made a box with wheels and a screen that demo'd how a car could use 16 samples to generate the audio. It handled shifting and I tweaked the vehicle properties and it is probably the best handling vehicle "fast" until a tire model is released.
Title: Re: Vehicle script [version 1.4.0]
Post by: thx_nb on September 19, 2014, 04:41:27 pm
The sound test car probably has the most realistic sound, although right now the sound seems to be tied to the speed of the car, instead of the RPM.
However, when I accelerate in a straight line to over 150 km/h on for example the nurburgring it just slides away, which is not very realistic. In the script I only see a 'slip'-property which seems to me to be far too simple to model the grip of a car with different speeds, even if we don't take the surface type into account. I guess that's what Atrax means with proper physics. Problem is I don't know what I'm doing when I increase or decrease this slip factor for example.

Adding a tire model seems a whole leap further to me, together with things like tire screeching when you lose grip.
Title: Re: Vehicle script [version 1.4.0]
Post by: HiFlyer on September 19, 2014, 06:43:54 pm
Is this of interest?

http://www.gritengine.com/ (http://www.gritengine.com/)
Title: Re: Vehicle script [version 1.4.0]
Post by: PytonPago on October 29, 2014, 07:36:10 am
 ... i dont know if it was my fiddling with the script earlier (the multiple stuff usage - actually, the switch enhancement) and i thought, the clutch should be used if you want to change the gear, so i rearranged the part and added the need of pressing the clutch button (AAuxb2) :

Code: [Select]
     case AAuxb2: {
         if (k == AAuxb2 || k == AAuxa2) {
              if (k == AAuxa2) {
                 v = 1 - (v + 1) / 2;
              }
             v = 1 - v;
             v *= v;

             this.clutchState = (v == 1 ? ClutchState.Disengaged : ClutchState.Engaged);
             this.clutch = v;
             this.clutchOverride = v != 1;
         }
     }break;

    case AShiftUp: {
      if ( this.clutchState == ClutchState.Engaged ) {
        if (v == 1 && aShiftUpBool == true) {
           this.shiftUp();
        }

        if (v == 1 && aShiftUpBool == false) {
         aShiftUpTime = aShiftUpTime + (2 * 1);
           this.shiftUp();
        }

        if (v == 1) {
           if (aShiftUpBool == false) {aShiftUpBool = true;}
           else{aShiftUpBool = false;}
        }
      }
    } break;

    case AShiftDown: {
      if ( this.clutchState == ClutchState.Engaged ) {
        if (v == 1 && aShiftDownBool == true) {
           this.shiftDown();
        }

        if (v == 1 && aShiftDownBool == false) {
         aShiftDownTime = aShiftDownTime + (2 * 1);
           this.shiftDown();
        }

        if (v == 1) {
           if (aShiftDownBool == false) {aShiftDownBool = true;}
           else{aShiftDownBool = false;}
        }
      }
    } break;

     case AAuxb1: {
       
         if (k == AAuxb1 && v == 0) {
            this.automaticTransmission = !this.automaticTransmission;
            this.log_inf("Automatic transmission " + (this.automaticTransmission ? "enabled" : "disabled"));
         }
       
     }break;

 ... so at the next update of the ural, you have to use the clutch when changing gears ... ( not sure when that will be, im changing a lot of stuff there )


still, there is a problem ... the clutch should be pressed to brake too ... otherwise, ya know. Also, the base speed shouldnt be zero if the clutch isnt disengaged ... this tuff still needs some twindling.
Title: Re: Vehicle script [version 1.4.0]
Post by: PytonPago on January 03, 2015, 01:01:44 pm
still, there is a problem ... the clutch should be pressed to brake too ... otherwise, ya know. Also, the base speed shouldnt be zero if the clutch isnt disengaged ... this tuff still needs some twindling.


 ... Now i found whats wrong whyte the damned clutch ! ... first the idle rpm state when the clutch is disengaged has some value, doe when engaging the clutch, there is a little moment when rpm comes down lower than the minimum, that shuts the engine down (at the very end of the script) - this is no problem, doe should not happen at the first gear (or at least not every-time). You need to apply gas when wanting to move when getting to 1 gear, but there is no (or seems not to work properly) real effect of the gas whyle engaging the clutch - making the RPM go right under the minimum and shutting down the engine right away.  ---- this needs some modding.

The second thing is the idle RPMs torque/force - it does not seem to be taken from the  chassis.torquePoints ... how is it working ?

Title: Re: Vehicle script [version 1.4.0]
Post by: PytonPago on January 30, 2015, 04:50:10 am
I found a way to do a simple differential - this may be interesting in doing a better script for trucks and off-road vehicles. Basic is, i dont have to include it in the computation of the "force" that is applied to the wheel, but make an addition to that. As OT has a way to get individual wheel RPMs, ya make just a set of difference variables between the two at an axle and multiply the "force" by it at each proper wheel. adding an constant to the difference variables can be used as an tuning of the differentials ratio.

  ... in addition, an binding-key controlled variable is included witch says if the differential lock is on or off, making the addition be equal to the computed number, or 1 (as the differential being locked, thus giving all wheels the same force)

    Doe, when a wheel gets off ground, the force at an differential-driven axle goes entirely to the wheel white less resistance (the one in the air). So there is also an script witch looks into the height of the wheel and if one of them goes off, it renders the other force-less - giving the negative effect of the axle differential to effect.
 (off course, you must use the "log" script to first find the minimum position of the wheel (when it just hangs in the air) manually, cause its a different number to the minimum in the wheel-parameters as it takes a few more stuff into accout)

   Off course, using it on an basic setup of wheels (where the wheel in script is also having the mesh) is not good, as all wheels getting off ground stop rotating in the basic OT script way. So for showing it off at a graphical level, the mesh of the wheel should be a separate one with rotation projected thanks to the ".rotation " value of the actual script wheel. Witch, when going off ground, will switch to an rotation based on intensity of the gas pedal (and maybe even the gear selected, so its different at different gears and stops or reverses at neutral and reverse gear positions). 
  .. and also rotate the both wheels on the axle the same angle-speed if the differential lock is engaged (cause the basic OT way it turns due to the ground).

 ... will do some testing with some details and try to paste-bin it for ya. If ya have some idea of pimping it up a little more, let me know !
Title: Re: Vehicle script [version 1.4.0]
Post by: PRiME on February 10, 2015, 08:37:16 am
Just been messing around with M7's Tiger 2 tank and was wondering would it be possible to add gears to the tank? and also would it be possible to make it either manual or automatic gear changing or must they be mapped to the gear shift up/down only?

Atm the Tiger2 has a single gear and its quite unrealistic in the way it takes off.

PS. I'm doing this for learning purposes, once I'm happy I'll make my own tank(s). Real rusty in doing scripts, will get there.
Title: Re: Vehicle script [version 1.4.0]
Post by: M7 on February 10, 2015, 09:21:23 am
I really wanted to add gears to the tiger2 but because of the script to get the tank turning without using turning wheels, i couldn't just add script for gears and get it to move more than a snail pace. It would be really great if you could get this to work though.
Title: Re: Vehicle script [version 1.4.0]
Post by: PRiME on February 10, 2015, 09:29:28 am
I'm making decent progress, I have the gears added but unsure how they function in general. I managed to fix the reverse gear which wasn't working but it seems to go at the forward max speed. Will need to figure out howto cap it to 2 reverse gears at max of -14kph. (The tank moves more like a heavy tiger 2 tank now. Still couple little quirks such as not being able to turn when going above 24kph or so, this is likely a realistic quirk however when you think about it).

The other thing I want to play with is adding a projectile to the gun including sound (since you can shoot now), then after that I was going to look into hooking the view to locations so you can turn turret and commander view moves with it (over the cupola).

But first things first, figuring out howto cap the reverse speed.
Title: Re: Vehicle script [version 1.4.0]
Post by: M7 on February 10, 2015, 10:14:26 am
Are you using already made gear script (from giucam or hrrrrr) or you do plan to make a new one from scratch?
Title: Re: Vehicle script [version 1.4.0]
Post by: PRiME on February 10, 2015, 10:16:45 am
I'm going to try and get giucam's one working correctly first, then maybe I can simplify it if possible since Tiger 2 isn't exactly a sports performance vehicle.

Title: Re: Vehicle script [version 1.4.0]
Post by: PRiME on February 11, 2015, 10:20:44 am
Well I figured out the issue with this script for the Tiger, its mostly related to the Torque values being extremely rigid, if you go too far out of what they are (~400) such as 1850N.m which is what the Tiger 2 had then it basically stalls the script in a loop.

I'm in the process of finding where this hold up is happening, being the very amateur that I am, this could take some time. It will be some basic value, will figure it out.


Edit: Seems the max torque you can have before things go downhill (tank doesn't move) is around 700. Will continue looking into this tomorrow.

Edit2: So the problem turns out to be a double edged blade, yes the script does react odd when dealing with high torque engines, but the main contribution to the Tiger 2 movement problem is you (M7) have specified weights (large) inside the model itself, thus compounding the issue. You should only use a 0-1 value to help with balance then when you put the mass in with the scripts it distributes this correctly.

Atm the Tiger 2 tank appears to have ~10k-20k weight added to it via the model alone (you specified 20k in the script, this is added to the model weighting/or multiplied, whichever). Outtera appears to factor this into its own mass calculation (as to determine weight distribution and balance, or the tank would flip over etc) thus you have a compounding effect. I will now just move onto making my own tank (Sherman firefly or something), my skills in this area have been dormant for 10years, this will be interesting to say the least, LOL


PS. I'm 10years rusty to all this tank building and script stuff, please be gentle with me if I turn out to be completely wrong  =|
Title: Re: Vehicle script [version 1.4.0]
Post by: PRiME on February 25, 2015, 10:21:15 am
Just wanted to update this and say there is nothing wrong with M7's Tiger model, the issue turns out to be related to wheel assignment, seems the more wheels you add the more it factors into vehicle weight or ability to move. Bit odd, but now I know whats directly causing it I can find a solution, really only the middle wheels need animation effect only.

PS. Got my M4A4VC Firefly working at very basic level, now to iron out the scripting and hope they add in something to allow you to rig the ballistic firing system to the cannon barrel (with adjusted properties to suit the caliber).
Title: Re: Vehicle script [version 1.4.0]
Post by: ZeosPantera on February 25, 2015, 07:05:17 pm
Keep working. If we don't get ground vehicle scripts working as best we can the Fly-Boys are going to flight sim us into oblivion.

It is a war now.
Title: Re: Vehicle script [version 1.4.0]
Post by: PRiME on February 26, 2015, 01:19:38 am
Well atm I'm going to make my own suited for larger vehicles such as tanks, (tracked vehicles infact). I will use some ideas from what other people have done, will probably take me a while to suss it all out, only got the basics down atm.

Also while I'm at it, does anyone know a tutorial on howto rig a Turret/Mantlet/Barrel correctly? I have each set as a bone because each part can move separate from the other but in different axis, and atm they kinda don't move correctly. I have a idea of fixing it by changing the center of mass to match the turret but was wondering if there was a simpler way.

1) Turret pivots around the Z axis in a circle.
2) Mantlet moves up/down the Z axis in a but also moves a bit in the X axis as it pivots up/down.
3) The gun follows the Mantlet but also has additional action when fired causing it to recoil back.

All of them follow the Turrets pivot around the Z axis obviously. Anyway was wondering if I was doing it wrong by selecting each as a individual bone or if there is something I can do to make the mantlet/barrel follow the turret but allow addition separate movement themselves.

Think I explained all that in a complicated way, sorry. Anyway any tips are welcome.

PS. Can't wait for ballistics function to open up for scripting and also functions to allow for track movement (rotation around the wheel cogs would be cool, perhaps follow a BezierCircle bone which is how I make my tracks in blender)
Title: Re: Vehicle script [version 1.4.0]
Post by: PytonPago on February 26, 2015, 02:33:47 am
Well atm I'm going to make my own suited for larger vehicles such as tanks, (tracked vehicles infact). I will use some ideas from what other people have done, will probably take me a while to suss it all out, only got the basics down atm.


That would be nice !

As for the turret - its off topic, so check your PM for my message.
Title: Re: Vehicle script [version 1.4.0]
Post by: PRiME on February 26, 2015, 10:17:06 am
I have tried to message you a few times PytonPago but for some reason non of the messages are recorded in my Send Out box, so I have no clue as to if you ever get replies from me. :(
Title: Re: Vehicle script [version 1.4.0]
Post by: HiFlyer on February 26, 2015, 10:23:45 am
I have tried to message you a few times PytonPago but for some reason non of the messages are recorded in my Send Out box, so I have no clue as to if you ever get replies from me. :(

I had that problem here before. I found that sending a BCC to myself as well solved the problem.
Title: Re: Vehicle script [version 1.4.0]
Post by: PRiME on February 26, 2015, 12:06:34 pm
Ok, will check that out.

Meanwhile I'm puzzled on howto use the following

method / wheel / int wheel_id - 0..n wheel id returns wheel_data structure with wheel info, get run-time wheel data

I have been trying testwheeldata = this.wheel(0, rpm); with no luck, I tried just having (0) and it reports back object, Object. I need it to report back the RPM for each wheel so I can detect better when the vehicle spins out or does weird things (tanks never spin out btw).
Title: Re: Vehicle script [version 1.4.0]
Post by: cameni on February 26, 2015, 12:27:43 pm
Code: [Select]
//get wheel data for wheel 0
var testwheeldata = this.wheel(0);
var rpm = testwheeldata.rpm;
Title: Re: Vehicle script [version 1.4.0]
Post by: PRiME on February 27, 2015, 12:09:49 am
Cheers for that nugget,

Making slow progress here, now I wish to apply a gradual increase of wheel force the longer the user holds down forward or reverse (to my max defined value). I have come up with the idea to use something like the following in the update frame section. Similar to how the turrets work.


Clearly the below is wrong, basically when the user has forward down (1) I want a value to be added, and if finger is off forward then a value to decrease. Anyone have any ideas about this one? :)

function action(k,v,dt)
{
  switch(k){
  case AForward: EFMaxRate = EFMaxRate + AccelValue(v); break;
  }
}
Title: Re: Vehicle script [version 1.4.0]
Post by: cameni on February 27, 2015, 01:40:55 am
There's no AForward action, as the engine power is passed into update function instead (old way). In the upcoming version with extended key handling there will be a way to configure the ramp parameters for all actions, including forward/reverse, allowing you to define how fast the input value can change.
Title: Re: Vehicle script [version 1.4.0]
Post by: PRiME on February 27, 2015, 01:44:42 am
Ok, thank you for the reply. I will go back to messing with my older method of solving this problem, bit messy but its getting there.