Board index FlightGear Development New features

"Real Cockpit" Shaking effect script

Discussion and requests for new features. Please note that FlightGear developers are volunteers and may or may not be able to consider these requests.

"Real Cockpit" Shaking effect script

Postby mmagnin » Tue Dec 30, 2014 9:41 pm

Hello everyone!

This is my first post ever, so first I want to apologize in advance because English is not my native language, so I may do some grammar errors; in second place I want to thank all of you, for these wonderful peace of software.
I made some code additions to the dynamic_view.nas script, to add some “cockpit shaking” effects just like those addons for FSX that you can see youtubeing (like 3d real cockpit).
I think it turned out pretty nice, I use it all the time, maybe it worth sharing it, and some of you can take a look at it and help me do that.
Explanation:
In the function that do the “default calculations for a plane”, I added the following logic
1) Shake effect when taxing proportional to the aircraft speed.
2) Reverse thrust induced shake, proportional to the amount applied.
3) Speedbrake actuation induced shake (flying), proportional to the aircraft speed.
4) Flaps induced shake (flying), proportional to the aircraft speed and flap value.

My ToDo list:
1) Share it, to get feedback about the coding (first nasal script for me), and to see if some one like it.
2) Eliminate the hardcoded factor numbers that affects the intensity of the effects, adding sliders in the gui.

My first question would be, how can I upload the script? Shoud I copy / paste all of it here?

Thank you in advance.

Greetings
Martín
mmagnin
 
Posts: 16
Joined: Mon Dec 29, 2014 7:59 pm

Re: "Real Cockpit" Shaking effect script

Postby Michat » Tue Dec 30, 2014 11:41 pm

I think 707 from Lake of Constance has shake effects.
User avatar
Michat
 
Posts: 1226
Joined: Mon Jan 25, 2010 7:24 pm
Location: Spain
Version: 191b
OS: MX 21 Fluxbox oniMac

Re: "Real Cockpit" Shaking effect script

Postby wlbragg » Wed Dec 31, 2014 12:02 am

Should I copy / paste all of it here?

If it's not huge you could do that. Put it in "code" tags.
Kansas and Ohio/Midwest scenery development.
KEQA, 3AU, KRCP Airport Layout
Intel i7/GeForce RTX 2070/Max-Q
User avatar
wlbragg
 
Posts: 7586
Joined: Sun Aug 26, 2012 12:31 am
Location: Kansas (Tornado Alley), USA
Callsign: WC2020
Version: next
OS: Win10/Linux/RTX 2070

Re: "Real Cockpit" Shaking effect script

Postby hamzaalloush » Wed Dec 31, 2014 1:56 am

i think Marius_A is doing something similar with fg_camera, but for turbulence!

[/quote]
hamzaalloush
 
Posts: 631
Joined: Sat Oct 26, 2013 10:31 am
OS: Windows 10

Re: "Real Cockpit" Shaking effect script

Postby mmagnin » Wed Dec 31, 2014 2:10 am

Oh I see… ok, I will try the code tag; if you want to give it a try, backup your dynamic_view.nas and put the new code; I removed all the comments to try to make it smaller (except where I ended putting mine). Let me know what you think, may be if someone else is working on some thing similar I can search other thing to contribute.

Thank you very much!

Martín

Code: Select all
var FREEZE_DURATION = 2;
var BLEND_TIME = 0.2;

var sin = func(a) math.sin(a * D2R);
var cos = func(a) math.cos(a * D2R);
var sigmoid = func(x) { 1 / (1 + math.exp(-x)) }
var nsigmoid = func(x) { 2 / (1 + math.exp(-x)) - 1 }
var pow = func(v, w) { v < 0 ? nil : v == 0 ? 0 : math.exp(math.ln(v) * w) }
var npow = func(v, w) { v == 0 ? 0 : math.exp(math.ln(abs(v)) * w) * (v < 0 ? -1 : 1) }
var clamp = func(v, min, max) { v < min ? min : v > max ? max : v }
var normatan = func(x) { math.atan2(x, 1) * 2 / math.pi }

var normdeg = func(a) {
   while (a >= 180)
      a -= 360;
   while (a < -180)
      a += 360;
   return a;
}

var Input = {
   new : func(prop = "/null", factor = 1, offset = 0, filter = 0, min = nil, max = nil) {
      var m = { parents : [Input] };
      m.prop = isa(props.Node, prop) ? prop : props.globals.getNode(prop, 1);
      m.factor = factor;
      m.offset = offset;
      m.min = min;
      m.max = max;
      m.lowpass = filter ? aircraft.lowpass.new(filter) : nil;
      return m;
   },
   get : func {
      var v = me.prop.getValue() * me.factor + me.offset;
      if (me.min != nil and v < me.min)
         v = me.min;
      if (me.max != nil and v > me.max)
         v = me.max;

      return me.lowpass == nil ? v : me.lowpass.filter(v);
   },
   set : func(v) {
      me.prop.setDoubleValue(v);
   },
};

var ViewAxis = {
   new : func(prop) {
      var m = { parents : [ViewAxis] };
      m.prop = props.globals.getNode(prop, 1);
      if (m.prop.getType() == "NONE")
         m.prop.setDoubleValue(0);
      m.reset();
      return m;
   },
   reset : func {
      me.applied_offset = 0;
   },
   add_offset : func {
      me.prop.setValue(me.prop.getValue() + me.applied_offset);
   },
   sub_offset : func {
      var raw = me.prop.getValue() - me.applied_offset;
      me.prop.setValue(raw);
      return raw;
   },
   apply : func(v) {
      var raw = me.prop.getValue() - me.applied_offset;
      me.applied_offset = v;
      me.prop.setDoubleValue(raw + me.applied_offset);
   },
   static : func(v) {
      normdeg(v - me.prop.getValue() + me.applied_offset);
   },
};

var view_manager = {
   init : func {

      me.elapsedN = props.globals.getNode("/sim/time/elapsed-sec", 1);
      me.deltaN = props.globals.getNode("/sim/time/delta-realtime-sec", 1);

      me.headingN = props.globals.getNode("/orientation/heading-deg", 1);
      me.pitchN = props.globals.getNode("/orientation/pitch-deg", 1);
      me.rollN = props.globals.getNode("/orientation/roll-deg", 1);
      me.slipN = props.globals.getNode("/orientation/side-slip-deg", 1);
      me.speedN = props.globals.getNode("velocities/airspeed-kt", 1);

      me.wind_dirN = props.globals.getNode("/environment/wind-from-heading-deg", 1);
      me.wind_speedN = props.globals.getNode("/environment/wind-speed-kt", 1);

      me.axes = [
         me.heading_axis = ViewAxis.new("/sim/current-view/goal-heading-offset-deg"),
         me.pitch_axis = ViewAxis.new("/sim/current-view/goal-pitch-offset-deg"),
         me.roll_axis = ViewAxis.new("/sim/current-view/goal-roll-offset-deg"),
         me.x_axis = ViewAxis.new("/sim/current-view/x-offset-m"),
         me.y_axis = ViewAxis.new("/sim/current-view/y-offset-m"),
         me.z_axis = ViewAxis.new("/sim/current-view/z-offset-m"),
         me.fov_axis = ViewAxis.new("/sim/current-view/field-of-view"),
      ];

      # accelerations are converted to G (Earth gravitation is omitted)
      me.ax = Input.new("/accelerations/pilot/x-accel-fps_sec", 0.03108095, 0, 0.58, 0);
      me.ay = Input.new("/accelerations/pilot/y-accel-fps_sec", 0.03108095, 0, 0.95);
      me.az = Input.new("/accelerations/pilot/z-accel-fps_sec", -0.03108095, -1, 0.46);

      # velocities are converted to knots
      me.vx = Input.new("/velocities/uBody-fps", 0.5924838, 0, 0.45);
      me.vy = Input.new("/velocities/vBody-fps", 0.5924838, 0);
      me.vz = Input.new("/velocities/wBody-fps", 0.5924838, 0);

      # turn WoW bool into smooth values ranging from 0 to 1
      me.wow = Input.new("/gear/gear/wow", 1, 0, 0.74);
      me.hdg_change = aircraft.lowpass.new(0.95);
      me.ubody = aircraft.lowpass.new(0.95);
      me.last_heading = me.headingN.getValue();
      me.size_factor = getprop("/sim/chase-distance-m") / -25;

      # "lookat" blending
      me.blendN = props.globals.getNode("/sim/view/dynamic/blend", 1);
      me.blendN.setDoubleValue(0);
      me.blendtime = BLEND_TIME;
      me.frozen = 0;

      if (props.globals.getNode("rotors", 0) != nil)
         me.calculate = me.default_helicopter;
      else
         me.calculate = me.default_plane;
         
      me.reset();
   
   },
   reset : func {
      me.heading_offset = me.heading = me.target_heading = 0;
      me.pitch_offset = me.pitch = me.target_pitch = 0;
      me.roll_offset = me.roll = me.target_roll = 0;
      me.x_offset = me.x = me.target_x = 0;
      me.y_offset = me.y = me.target_y = 0;
      me.z_offset = me.z = me.target_z = 0;
      me.fov_offset = me.fov = me.target_fov = 0;

      interpolate(me.blendN);
      me.blendN.setDoubleValue(0);
      foreach (var a; me.axes)
         a.reset();

      me.add_offset();
   },
   add_offset : func {
      me.heading_axis.add_offset();
      me.pitch_axis.add_offset();
      me.roll_axis.add_offset();
      me.fov_axis.add_offset();
   },
   apply : func {
      if (me.elapsedN.getValue() < me.frozen)
         return;
      elsif (me.frozen)
         me.unfreeze();

      me.pitch = me.pitchN.getValue();
      me.roll = me.rollN.getValue();

      me.calculate();

      var b = me.blendN.getValue();
      var B = 1 - b;
      me.heading = me.target_heading * b + me.heading_offset * B;
      me.pitch = me.target_pitch * b + me.pitch_offset * B;
      me.roll = me.target_roll * b + me.roll_offset * B;
      me.x = me.target_x * b + me.x_offset * B;
      me.y = me.target_y * b + me.y_offset * B;
      me.z = me.target_z * b + me.z_offset * B;
      me.fov = me.target_fov * b + me.fov_offset * B;

      me.heading_axis.apply(me.heading);
      me.pitch_axis.apply(me.pitch);
      me.roll_axis.apply(me.roll);
      me.x_axis.apply(me.x);
      me.y_axis.apply(me.y);
      me.z_axis.apply(me.z);
      me.fov_axis.apply(me.fov);
   },
   lookat : func(heading, pitch, roll, x, y, z, time, fov) {
      me.target_heading = me.heading_axis.static(heading);
      me.target_pitch = me.pitch_axis.static(pitch);
      me.target_roll = me.roll_axis.static(roll);
      me.target_x = me.x_axis.static(x);
      me.target_y = me.y_axis.static(y);
      me.target_z = me.z_axis.static(z);
      me.target_fov = me.fov_axis.static(fov);

      me.blendtime = time;
      me.blendN.setValue(0);
      interpolate(me.blendN, 1, me.blendtime);
   },
   resume : func {
      interpolate(me.blendN, 0, me.blendtime);
      me.blendtime = BLEND_TIME;
   },
   freeze : func {
      if (!me.frozen) {
         me.target_heading = me.heading;
         me.target_pitch = me.pitch;
         me.target_roll = me.roll;
         me.target_x = me.x;
         me.target_y = me.y;
         me.target_z = me.z;
         me.target_fov = me.fov;
         me.blendN.setDoubleValue(1);
      }
      me.frozen = me.elapsedN.getValue() + FREEZE_DURATION;
   },
   unfreeze : func {
      if (me.frozen) {
         me.frozen = 0;
         me.resume();
      }
   },
};

view_manager.default_plane = func {
   var wow = me.wow.get();

   var hdg = me.headingN.getValue();
   var hdiff = normdeg(me.last_heading - hdg);
   me.last_heading = hdg;
   var steering = normatan(me.hdg_change.filter(hdiff)) * me.size_factor; # mhm

   var az = me.az.get();
   var vx = me.vx.get();

   var wspd = me.wind_speedN.getValue();
   var wdir = me.headingN.getValue() - me.wind_dirN.getValue();
   var u = vx - wspd * cos(wdir);
   var slip = sin(me.slipN.getValue()) * me.ubody.filter(normatan(u / 10));

   me.heading_offset =                     
      -15 * sin(me.roll) * cos(me.pitch)      
      + 40 * steering * wow               
      + 10 * slip * (1 - wow);            

   me.pitch_offset =                     
      10 * sin(me.roll) * sin(me.roll)      
      + 30 * (1 / (1 + math.exp(2 - az))      
         - 0.119202922);                  

   var speed = getprop("/velocities/mach");
   me.roll_offset = wow * rand() * speed;       # taxing
   
   #mhm reversethrust induced shake
   if(reverser_act)
      me.roll_offset = me.roll_offset + rand() * getprop("/engines/engine/itt-norm") * reversethrust_factor;
   
   if(wow < 1)
   {
      #mhm speedbrake induced shake
      if(speedbrake_act)
         me.roll_offset = me.roll_offset + rand() * speed * speedbrake_factor;
      
      #mhm flap induced shake
      if( flaps_value > 0.01)
         me.roll_offset = me.roll_offset + rand() * flaps_value * speed * flaps_factor;
   }
   
}

view_manager.default_helicopter = func {
   var lowspeed = 1 - normatan(me.speedN.getValue() / 20);
   me.heading_offset =                                    
      -50 * npow(sin(me.roll) * cos(me.pitch), 2);            
   me.pitch_offset =                                    
      (me.pitch < 0 ? -35 : -40) * sin(me.pitch) * lowspeed      
      + 15 * sin(me.roll) * sin(me.roll);                     
   me.roll_offset =                                    
      -15 * sin(me.roll) * cos(me.pitch) * lowspeed;            
}

var main_loop = func(id) {
   id == loop_id or return;
   if (cockpit_view and !panel_visible) {
      if (mouse_button)
         freeze();
      else
         view_manager.apply();
   }
   settimer(func { main_loop(id) }, 0);
}

var freeze = func {
   if (mouse_mode == 0)
      view_manager.freeze();
}

var register = func(f) {
   view_manager.calculate = f;
}

var reset = func {
   view_manager.reset();
}

var lookat = func {
   call(view_manager.lookat, arg, view_manager);
}

var resume = func {
   view_manager.resume();
}

var original_resetView = nil;
var panel_visibilityN = nil;
var dynamic_view = nil;

var cockpit_view = nil;
var panel_visible = nil;
var elapsedN = nil;
var mouse_mode = nil;
var mouse_button = nil;
var enabled = nil;

var flaps_value = 0;    # mhm
var speedbrake_act = 0; # mhm
var reverser_act = 0;    # mhm

#todo mhm: get these values from gui
var flaps_factor = 0.50;         # mhm: flaps_factor range [0 0.5 1]
var speedbrake_factor = 0.55;    # mhm: speedbrake_factor range [0 0.55 1.1]
var reversethrust_factor = 0.25; # mhm: reversethrust_factor [0 0.25 0.5]

var loop_id = 0;

_setlistener("/sim/signals/nasal-dir-initialized", func {
   var fdms = {
      acms:0, ada:0, balloon:0, external:0,
      jsb:1, larcsim:1, magic:0, network:0,
      null:0, pipe:0, ufo:0, yasim:1,
   };
   var fdm = getprop("/sim/flight-model");
   if (!contains(fdms, fdm) or !fdms[fdm])
      return;

   enabled = props.globals.getNode("/sim").getChildren("view");
   forindex (var i; enabled)
      enabled[i] = ((var n = enabled[i].getNode("config/dynamic-view")) != nil) and n.getBoolValue();

   props.globals.initNode("/accelerations/pilot/x-accel-fps_sec", 0);
   props.globals.initNode("/accelerations/pilot/y-accel-fps_sec", 0);
   props.globals.initNode("/accelerations/pilot/z-accel-fps_sec", -32);
   props.globals.initNode("/orientation/side-slip-deg", 0);
   props.globals.initNode("/gear/gear/wow", 1, "BOOL");
   elapsedN = props.globals.getNode("/sim/time/elapsed-sec", 1);

   setlistener("/sim/panel/visibility", func(n) { panel_visible = n.getValue() }, 1);
   setlistener("/sim/current-view/view-number", func(n) { cockpit_view = enabled[n.getValue()] }, 1);
   setlistener("/devices/status/mice/mouse/button", func(n) { mouse_button = n.getValue() }, 1);
   setlistener("/devices/status/mice/mouse/x", freeze);
   setlistener("/devices/status/mice/mouse/y", freeze);
   setlistener("/devices/status/mice/mouse/mode", func(n) {
      if (mouse_mode = n.getValue())
         view_manager.unfreeze();
   }, 1);
   setlistener("/sim/signals/reinit", func(n) {
      n.getValue() and return;
      cockpit_view = enabled[getprop("/sim/current-view/view-number")];
      view_manager.reset();
   }, 0);

   setlistener("/controls/flight/flaps",func(n) { flaps_value = n.getValue() });                 #mhm
   setlistener("/controls/flight/speedbrake",func(n) { speedbrake_act = n.getValue() });           #mhm
   setlistener("/controls/engines/engine/reverser",func(n) { reverser_act = n.getValue() });       #mhm
   
   view_manager.init();

   original_resetView = view.resetView;
   view.resetView = func {
      original_resetView();
      if (cockpit_view and dynamic_view)
         view_manager.add_offset();
   }
   
   settimer(func {
      setlistener("/sim/current-view/dynamic-view", func(n) {
         dynamic_view = n.getBoolValue();
         loop_id += 1;
         view.resetView();
         if (dynamic_view)
            main_loop(loop_id);
      }, 1);
   }, 0);

});
mmagnin
 
Posts: 16
Joined: Mon Dec 29, 2014 7:59 pm

Re: "Real Cockpit" Shaking effect script

Postby Hooray » Wed Dec 31, 2014 3:30 am

I agree, you should probably team up with Marius_A and get your changes integrated with the fgcamera work he's doing
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: "Real Cockpit" Shaking effect script

Postby hamzaalloush » Wed Dec 31, 2014 3:35 am

tested, i really quite like it! good work, yes Hooray, really good idea!

hamzaalloush
 
Posts: 631
Joined: Sat Oct 26, 2013 10:31 am
OS: Windows 10

Re: "Real Cockpit" Shaking effect script

Postby Hooray » Wed Dec 31, 2014 3:42 am

For details, please refer to: http://wiki.flightgear.org/FGCamera
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: "Real Cockpit" Shaking effect script

Postby mmagnin » Wed Dec 31, 2014 4:06 am

I’m glad you liked it! The flaps and speedbrake shake, gives the approach a touch of vibration, just like the video you posted.
I was thinking of adding four sliders to the gui dialog, to make possible adjustments to the shake factors; I thought the default ones (as harcoded in the script I posted) would be the middle of the range.
But, I will love to help if someone is kind enough to guide me; I discovered flightgear a few weeks ago and I am a total junior…
Cheers
Martín
mmagnin
 
Posts: 16
Joined: Mon Dec 29, 2014 7:59 pm

Re: "Real Cockpit" Shaking effect script

Postby mmagnin » Wed Dec 31, 2014 4:07 am

Ok, thank you very much, I will read the WIKI.
mmagnin
 
Posts: 16
Joined: Mon Dec 29, 2014 7:59 pm

Re: "Real Cockpit" Shaking effect script

Postby Hooray » Wed Dec 31, 2014 10:29 am

The main things useful for fgcamera related additions are Nasal, the property tree and PropertyList XML files:

http://wiki.flightgear.org/Nasal
http://wiki.flightgear.org/Property_tree
http://wiki.flightgear.org/PropertyList_XML_File
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 12707
Joined: Tue Mar 25, 2008 9:40 am
Pronouns: THOU

Re: "Real Cockpit" Shaking effect script

Postby abassign » Thu Jan 01, 2015 12:40 pm

I tried it and it is very nice, is the same function that runs for years in X-Plane, it seemed strange that was not present in FGFs. Certainly with fgcamera you can have an even better, but at present the extension fgcamera is not very usable, I hope in version 1.2 that I have not been able to try, but the 1.1, at least in Linux, does not work very well.
Developer of the program https://wiki.flightgear.org/Julia_photoscenery_generator
FDM developer of the G91R1B aircraft https://wiki.flightgear.org/FIAT_G91R1B
JSBSim collaborator
abassign
 
Posts: 947
Joined: Mon Feb 27, 2012 6:09 pm
Location: Italy (living 5 Km from airport LIME)
Callsign: I-BASSY
Version: 2020.4
OS: Ubuntu 20.10

Re: "Real Cockpit" Shaking effect script

Postby mmagnin » Fri Jan 02, 2015 3:47 pm

I`m glad you think it`s nice. I`m having a nice read of the wiki, it`s very interesting, I`m very impressed by this awesome simulator.
Happy new year.
Martìn
mmagnin
 
Posts: 16
Joined: Mon Dec 29, 2014 7:59 pm

Re: "Real Cockpit" Shaking effect script

Postby legoboyvdlp » Fri Jan 02, 2015 11:38 pm

Congratulations Martin! Unlike others, whose first post is a complaint of a bug, your first post is to add a feature that makes FlightGear even more realistic! Thanks!
User avatar
legoboyvdlp
 
Posts: 7981
Joined: Sat Jul 26, 2014 2:28 am
Location: Northern Ireland
Callsign: G-LEGO
Version: next
OS: Windows 10 HP

Re: "Real Cockpit" Shaking effect script

Postby mmagnin » Sat Jan 03, 2015 8:27 pm

Thank you very much, legoboyvdlp.

To continue with my learning, I changed part of the code, and added several sliders to the gui dialog to control the parameters in the script, so you can now adjust to taste the shaking strength; I also added sliders to control the dynamic view parameters, like for example if you want it to turn the heading less degrees while steering, or the other effects in the air, like while changing the heading.

I hope you like it, and enjoy it like I am doing :).

I leave a link to download the two files, remember to backup the originals (I hope the sharing of the zip file Works It´s the first time I use it).

https://onedrive.live.com/redir?resid=5069D249BD9C0D83!13444&authkey=!AM1O9B2FITPRpD0&ithint=file%2czip

Let me know what you think.

Cheers

Martin
mmagnin
 
Posts: 16
Joined: Mon Dec 29, 2014 7:59 pm

Next

Return to New features

Who is online

Users browsing this forum: No registered users and 6 guests