Board index FlightGear Development New features

Alternative camera control

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

Alternative camera control

Postby Marius_A » Sun Dec 29, 2013 11:53 am

Flightgear virtual camera. Written in NASAL. Adds features similar to Ezdok Camera Addon for FSX.

Preview:


The script and usage instructions will be posted here later.

EDIT:
Download link (tested on FGFS v2.12 for windows and configured for Piper Cub):
https://drive.google.com/folderview?id=0B7ZpQgmbsSZGcTZ4SWQxa0FIUEU&usp=drive_web

Installation:
1. Put "fgcamera" folder into "../FlightGear/data/Nasal/";
2. Set "fgcamera_cfg.xml" as flightgear's config (use advanced settings of fgrun);
3. Copy contents of "/Cub" folder into "../FlightGear/data/Aircraft/Cub/". (Make copies of original files before overwriting).

Number keys 1, 2, 3, 4, 5 selects cameras. Key 8 toggles FGcamera on/off. Arrow keys and PageUp/PageDown keys moves camera.
Last edited by Marius_A on Thu Jan 09, 2014 6:24 pm, edited 2 times in total.
Marius_A
 
Posts: 92
Joined: Wed Dec 04, 2013 3:20 pm

Re: Alternative camera control

Postby Gijs » Sun Dec 29, 2013 12:14 pm

Nice work! Looking forward to give it a try. If it's complementary to the current cameras, we might be able to integrate it before the upcoming release (if you like that and if the code is GPL). Looking at your previous post, this may not be the case... But maybe you've added an option to en-/disable it this time?
Airports: EHAM, EHLE, KSFO
Aircraft: 747-400
User avatar
Gijs
Moderator
 
Posts: 9544
Joined: Tue Jul 03, 2007 3:55 pm
Location: Delft, the Netherlands
Callsign: PH-GYS
Version: Git
OS: Windows 10

Re: Alternative camera control

Postby Hooray » Sun Dec 29, 2013 1:06 pm

please feel free to add your video (and project description) to the upcoming newsletter (see my signature).
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: Alternative camera control

Postby Marius_A » Sun Dec 29, 2013 6:32 pm

... If it's complementary to the current cameras ...


There will be two additional views named "FGcamera internal view" and "FGcamera external view" . When you switch to one of them, FGcamera will be activated. If any different view is selected, FGcamera will be disabled automatically.
Marius_A
 
Posts: 92
Joined: Wed Dec 04, 2013 3:20 pm

Re: Alternative camera control

Postby Michat » Sun Dec 29, 2013 7:50 pm

Nice Work.
User avatar
Michat
 
Posts: 1226
Joined: Mon Jan 25, 2010 7:24 pm
Location: Spain
Version: 191b
OS: MX 21 Fluxbox oniMac

Re: Alternative camera control

Postby Johan G » Mon Dec 30, 2013 5:33 am

Indeed! :D
Low-level flying — It's all fun and games till someone looses an engine. (Paraphrased from a YouTube video)
Improving the Dassault Mirage F1 (Wiki, Forum, GitLab. Work in slow progress)
Some YouTube videos
Johan G
Moderator
 
Posts: 6625
Joined: Fri Aug 06, 2010 6:33 pm
Location: Sweden
Callsign: SE-JG
IRC name: Johan_G
Version: 2020.3.4
OS: Windows 10, 64 bit

Re: Alternative camera control

Postby Marius_A » Mon Dec 30, 2013 7:15 pm

Decided not to use automatic enabling/disabling dependent on view index. FGcamera will be turned on or off by function "fgcamera.camera.toggle()":
Code: Select all
   toggle: func {
      me.enabled = !me.enabled;
      if (me.enabled) {
         if (!cameraManager_loop_running) {
            cameraManager_loop_running = 1;
            cameraManager.loop();
         }
         setprop("/input/mice/mouse/mode[2]/x-axis/binding/property", "/sim/current-view/mouse-heading-offset-deg");
         setprop("/input/mice/mouse/mode[2]/y-axis/binding/property", "/sim/current-view/mouse-pitch-offset-deg");
         gui.popupTip("FGcamera enabled");
      } else {
         cameraManager_loop_running = 0;
         setprop("/input/mice/mouse/mode[2]/x-axis/binding/property", "/sim/current-view/heading-offset-deg");
         setprop("/input/mice/mouse/mode[2]/y-axis/binding/property", "/sim/current-view/pitch-offset-deg");
         gui.popupTip("FGcamera disabled");
      }
   },

In-flight preview (camera enabled/disabled several times using keyboard):
Marius_A
 
Posts: 92
Joined: Wed Dec 04, 2013 3:20 pm

Re: Alternative camera control

Postby Marius_A » Mon Jan 06, 2014 8:14 pm

Added basic dynamic head movement:

Almost ready for release (needs minor adjustments and testing on another pc with "fresh" FG installation).

EDIT:
Flight in turbulence:
Marius_A
 
Posts: 92
Joined: Wed Dec 04, 2013 3:20 pm

Re: Alternative camera control

Postby Johan G » Tue Jan 07, 2014 6:17 pm

Impressive. 8) Seems very smooth and authentic.*


As for the head tilt I just the other day read about something I had not heard of before, the opto-kinetic cervical reflex (OKCR). I obviously knew about pilots (if possible) tilt their head to match the horizon, but not as a reflex action and not with a name to it.

For a very short summary see, The Opto-kinetic Cervical Reflex (OKCR). Do note that the graphs are from non-moving flight simulators.

I do not know if this will be helping you, but for a more detailed paper on the subject, see The optokinetic cervical reflex in pilots of high-performance aircraft (pdf), and for the problems it can induce, see this paper: Conflict Between the Human Sensory System and Cockpit Design Standards (pdf).

* Authentic as in "I don not know what it looks like for real, but it looks highly plausible". ;)
Low-level flying — It's all fun and games till someone looses an engine. (Paraphrased from a YouTube video)
Improving the Dassault Mirage F1 (Wiki, Forum, GitLab. Work in slow progress)
Some YouTube videos
Johan G
Moderator
 
Posts: 6625
Joined: Fri Aug 06, 2010 6:33 pm
Location: Sweden
Callsign: SE-JG
IRC name: Johan_G
Version: 2020.3.4
OS: Windows 10, 64 bit

Re: Alternative camera control

Postby HelldiverSquadron » Wed Jan 08, 2014 1:57 am

Wow, this is fantastic. When can we expect a beta release?
User avatar
HelldiverSquadron
 
Posts: 392
Joined: Sat Feb 16, 2013 7:35 pm
Callsign: Friend
Version: 3.0
OS: Windows 7 Ultimate

Re: Alternative camera control

Postby Marius_A » Thu Jan 09, 2014 6:16 pm

... When can we expect a beta release? ...

First release. Pre-configured for Piper Cub.


Download link at the first post.

EDIT:
POV hat binding example:
Code: Select all
   <!-- Bottom stick hat -->
   <axis>
      <number>
         <mac>7</mac>
         <unix>7</unix>
         <windows>6</windows>
      </number>
      <desc>View Direction</desc>
      <binding>
         <command>property-scale</command>
         <property>/data/hat-H</property>
         <factor type="double">-1.0</factor>
      </binding>
      <binding>
         <command>nasal</command>
         <script>
            <![CDATA[
               var hat_H = getprop("/data/hat-H");
               fgcamera.point.adjust("h", hat_H);
            ]]>
         </script>
      </binding>
   </axis>

   <axis>
      <number>
         <mac>8</mac>
         <unix>8</unix>
         <windows>7</windows>
      </number>
      <desc>View Elevation</desc>
      <binding>
         <command>property-scale</command>
         <property>/data/hat-V</property>
         <factor type="double">1.0</factor>
      </binding>
      <binding>
         <command>nasal</command>
         <script>
            <![CDATA[
               var hat_V = getprop("/data/hat-V");
               fgcamera.point.adjust("p", hat_V);
   
            ]]>
         </script>
      </binding>
   </axis>
Marius_A
 
Posts: 92
Joined: Wed Dec 04, 2013 3:20 pm

Re: Alternative camera control

Postby Marius_A » Fri Jan 10, 2014 3:02 pm

Preview of headtracker inputs handling.


Used TrackIR and PPJoy (virtual joystick) combination:
Code: Select all
<?xml version="1.0"?>

<PropertyList>
<axis n="0">
  <desc>6DOFYaw</desc>
  <tolerance>0.00001</tolerance>
  <binding>
   <command>property-scale</command>
   <property>/sim/TrackIR/heading-deg</property>
   <factor type="double">-180.0</factor>
  </binding>
 </axis>
 
 <axis n="1">
  <desc>6DOFPitch</desc>
  <tolerance>0.00001</tolerance>
  <binding>
   <command>property-scale</command>
   <property>/sim/TrackIR/pitch-deg</property>
   <factor type="double">-100.0</factor>
  </binding>
 </axis> 
 
 <axis n="2">
  <desc>6DOFRoll</desc>
  <tolerance>0.00001</tolerance>
  <binding>
   <command>property-scale</command>
   <property>/sim/TrackIR/roll-deg</property>
   <factor type="double">-100.0</factor>
  </binding>
</axis>
 
 <axis n="3">
  <desc>6DOFX</desc>
  <tolerance>0.00001</tolerance>
  <binding>
   <command>property-scale</command>
   <property>/sim/TrackIR/x-m</property>
   <factor type="double">-0.5</factor>
  </binding>
 </axis>
 
  <axis n="4">
  <desc>6DOFY</desc>
  <tolerance>0.00001</tolerance>
  <binding>
   <command>property-scale</command>
   <property>/sim/TrackIR/y-m</property>
   <factor type="double">0.5</factor>
  </binding>
 </axis>
 
  <axis n="5">
  <desc>6DOFZ</desc>
  <tolerance>0.00001</tolerance>
  <binding>
   <command>property-scale</command>
   <property>/sim/TrackIR/z-m</property>
   <factor type="double">0.5</factor>
  </binding>
  </axis>
 
</PropertyList>

<!-- end of PPJoy.xml -->
Marius_A
 
Posts: 92
Joined: Wed Dec 04, 2013 3:20 pm

Re: Alternative camera control

Postby StuartC » Fri Jan 10, 2014 3:09 pm

I have track IR. Interested in this.

where does the code go to enable Track IR ?
StuartC
 
Posts: 3168
Joined: Fri Jun 18, 2010 9:18 pm
Location: Arse end of the Universe
Callsign: WF01
Version: 2019.1
OS: W10 64 bit

Re: Alternative camera control

Postby DFaber » Fri Jan 10, 2014 3:13 pm

Hi Marius_A,

That is great work, thanks a lot. I had a flight with it yesterday and was really impressed about the smoothness of motion. Some effect seemed a little exaggerated to me, but sure that's a case of tuning.

Greetings
Detlef Faber
FlightGear Development:
http://flightgear-de.net

my 3D-Art:
https://www.sol2500.net
DFaber
 
Posts: 709
Joined: Fri Dec 01, 2006 8:51 pm
Location: Aachen, Germany
Version: GIT
OS: Linux

Re: Alternative camera control

Postby Marius_A » Fri Jan 10, 2014 5:07 pm

I have track IR. Interested in this.

where does the code go to enable Track IR ?


To be able to use TrackIR ir FGFS, i used information from here:
http://forum.flightgear.org/viewtopic.php?f=36&t=16052&p=160055&hilit=ppjoy#p160055

Then downloaded this:
http://www.dikant.de/tir2joy/

Also used PPJoy (64 bit version). Cannot find download links at this moment.

The updated fgcamera Nasal code (goes to "../data/Nasal/fgcamera/"):
Code: Select all
var globalData = {"dt": 0, "speedUp": 1};
var cameraManager_loop_running = 1;
var changePoint_loop_running = 0;
var adjustPoint_loop_running = 0;
var effectRND_loop_running = 1;
var camera = {
   started: 0,
   enabled: 1,
   init: func {
      var storeN = props.Node.new();
      props.copy(props.globals.getNode("/sim/fgcamera"), storeN);
      me.globalData = storeN.getValues();
      me.current = {"x": 0, "y": 0, "z": 0, "h": 0, "p": 0, "r": 0};
      me.from = {"x": 0, "y": 0, "z": 0, "h": 0, "p": 0, "r": 0};
      me.to = {"x": 0, "y": 0, "z": 0, "h": 0, "p": 0, "r": 0};
      me.offset = {"x": 0, "y": 0, "z": 0, "h": 0, "p": 0, "r": 0};
      me.target_offset = {"x": 0, "y": 0, "z": 0, "h": 0, "p": 0, "r": 0};
      me.blend = {"x": -1, "y": -1, "z": -1, "h": -1, "p": -1, "r": -1};
      me.velocity = {};
      me.velocity.current = {"x": 0, "y": 0, "z": 0, "h": 0, "p": 0, "r": 0};
      me.time = 0.5;
      me.id = "camera1";
      me.PTP = 1;
      me.set(me.id);
      me.started = 1;
      setprop("/input/mice/mouse/mode[2]/x-axis/binding/property", "/sim/current-view/mouse-heading-offset-deg");
      setprop("/input/mice/mouse/mode[2]/y-axis/binding/property", "/sim/current-view/mouse-pitch-offset-deg");
   },
   set: func(cam) {
      if (me.globalData[cam].config.internal != me.globalData[me.id].config.internal) {
         if (!me.globalData[cam].config.internal) {
            var vn = 8;
         } else var vn = 0;
         me.current.x = me.globalData[cam].config["x-offset-m"];
         me.current.y = me.globalData[cam].config["y-offset-m"];
         me.current.z = me.globalData[cam].config["z-offset-m"];
         me.current.h = me.globalData[cam].config["heading-offset-deg"];
         me.current.p = me.globalData[cam].config["pitch-offset-deg"];
         me.current.r = me.globalData[cam].config["roll-offset-deg"];
         setprop("/sim/view[" ~ vn ~ "]/config/x-offset-m", me.globalData[cam].config["x-offset-m"]);
         setprop("/sim/view[" ~ vn ~ "]/config/y-offset-m", me.globalData[cam].config["y-offset-m"]);
         setprop("/sim/view[" ~ vn ~ "]/config/z-offset-m", me.globalData[cam].config["z-offset-m"]);
         setprop("/sim/view[" ~ vn ~ "]/config/heading-offset-deg", me.globalData[cam].config["heading-offset-deg"]);
         setprop("/sim/view[" ~ vn ~ "]/config/pitch-offset-deg", me.globalData[cam].config["pitch-offset-deg"]);
         setprop("/sim/view[" ~ vn ~ "]/config/roll-offset-deg", me.globalData[cam].config["roll-offset-deg"]);
         setprop("/sim/current-view/view-number", vn);
      } else {
         me.current.x = getprop("/sim/current-view/x-offset-m");
         me.current.y = getprop("/sim/current-view/y-offset-m");
         me.current.z = getprop("/sim/current-view/z-offset-m");
         me.current.h = normdeg(getprop("/sim/current-view/heading-offset-deg"));
         me.current.p = normdeg(getprop("/sim/current-view/pitch-offset-deg"));
         me.current.r = normdeg(getprop("/sim/current-view/roll-offset-deg"));
         changePoint_loop_running = 1;
      }
      adjustPoint_loop_running = 0;
      me.velocity.x = me.velocity.y = me.velocity.z = me.globalData[cam].config["linear-velocity-m_s"];
      me.velocity.h = me.velocity.p = me.velocity.r = me.globalData[cam].config["angular-velocity-deg_s"];
      me.to.x = me.globalData[cam].config["x-offset-m"];
      me.to.y = me.globalData[cam].config["y-offset-m"];
      me.to.z = me.globalData[cam].config["z-offset-m"];
      me.to.h = normdeg(me.globalData[cam].config["heading-offset-deg"]);
      me.to.p = normdeg(me.globalData[cam].config["pitch-offset-deg"]);
      me.to.r = 0.0;
      if (!me.started)
         point.init();
      foreach(var a; keys(me.current)) {
         me.from[a] = me.current[a] - RND.out[a] - DHM.out[a] - headtracker.out[a];
         me.offset[a] = 0;
         me.target_offset[a] = 0;
         me.blend[a] = -1;
         point.lp[a].set(0);
         point.lp[a].setFilter(me.globalData[cam].config["keyboard-filter"] or 0);
      }
      me.PTP = me.globalData[me.id].config["PTP"] * me.globalData[cam].config["PTP"];
      me.id = cam;
      mouse.reset();
      if (!cameraManager_loop_running) cameraManager.loop();
   },
   toggle: func {
      me.enabled = !me.enabled;
      if (me.enabled) {
         if (!cameraManager_loop_running) {
            cameraManager_loop_running = 1;
            cameraManager.loop();
         }
         setprop("/input/mice/mouse/mode[2]/x-axis/binding/property", "/sim/current-view/mouse-heading-offset-deg");
         setprop("/input/mice/mouse/mode[2]/y-axis/binding/property", "/sim/current-view/mouse-pitch-offset-deg");
         gui.popupTip("FGcamera enabled");
      } else {
         cameraManager_loop_running = 0;
         setprop("/input/mice/mouse/mode[2]/x-axis/binding/property", "/sim/current-view/heading-offset-deg");
         setprop("/input/mice/mouse/mode[2]/y-axis/binding/property", "/sim/current-view/pitch-offset-deg");
         gui.popupTip("FGcamera disabled");
      }
   },
};
var point = {
   init: func {
      me.lp = {};
      foreach(var a; ["x", "y", "z", "h", "p", "r"]) {
         me.lp[a] = lowpass.new(camera.globalData[camera.id].config["keyboard-filter"] or 0);
      }
   },
   move_loop: func(dt) {
      if (changePoint_loop_running) {
         changePoint_loop_running = 0;
         foreach(var a; keys(camera.current)) {
            if (camera.current[a] != camera.to[a]) {
               camera.blend[a] += dt/camera.time;
               if ( (camera.blend[a] > 1) or (camera.PTP == 0) )
                  camera.blend[a] = 1;
               var b = (math.sin(camera.blend[a] * math.pi / 2) + 1) / 2; # range 0 .. 1
               camera.current[a] = camera.from[a] + b * (camera.to[a] - camera.from[a]);
               if (camera.blend[a] == 1) {
                  camera.blend[a] = -1;
                  camera.current[a] = camera.to[a];
               } else changePoint_loop_running = 1;
            }
         }
      }
   },
   adjust: func(dof, v) {
      if (camera.started) {
         camera.velocity.current[dof] = v * camera.velocity[dof];
         if (v != 0)
            adjustPoint_loop_running = 1;
      }
   },
   adjust_loop: func(dt) {
      if (adjustPoint_loop_running) {
         adjustPoint_loop_running = 0;
         var heading_offset = (getprop("/sim/current-view/heading-offset-deg")) * math.pi / 180;
         var dx = camera.velocity.current.x * dt;
         var dz = camera.velocity.current.z * dt;
         camera.target_offset.x += dx * math.cos(heading_offset) + dz * math.sin(heading_offset);
         camera.target_offset.z += dz * math.cos(heading_offset) - dx * math.sin(heading_offset);
         camera.offset.x = me.lp.x.filter(camera.target_offset.x);
         camera.offset.z = me.lp.z.filter(camera.target_offset.z);
         foreach(var a; ["y", "h", "p", "r"]) {
            camera.target_offset[a] += camera.velocity.current[a] * dt;
            camera.offset[a] = me.lp[a].filter(camera.target_offset[a]);
         }
         foreach(var a; keys(camera.offset)) {
            if ( (camera.target_offset[a] != camera.offset[a]) or (camera.velocity.current[a] != 0) )
               adjustPoint_loop_running = 1;
         }
      }
   },
};
var mouse = {
   init: func {
      me.lp1 = angular_lowpass.new(0.1);
      me.lp2 = angular_lowpass.new(0.1);
      me.offset = {"h": 0, "p": 0};
   },
   output: func {
      if (getprop("/devices/status/mice/mouse/mode") == 2) {
         me.h = getprop("/sim/current-view/mouse-heading-offset-deg") or 0;
         me.p = getprop("/sim/current-view/mouse-pitch-offset-deg") or 0;
         me.offset.h = me.lp1.filter(me.h);
         me.offset.p = me.lp2.filter(me.p);
      }
   },
   reset: func {
      me.offset = {"h": 0, "p": 0};
      me.lp1.set(0);
      me.lp2.set(0);
      me.lp1.setFilter(camera.globalData[camera.id].config["mouse-filter"]);
      me.lp2.setFilter(camera.globalData[camera.id].config["mouse-filter"]);
      setprop("/sim/current-view/mouse-pitch-offset-deg", 0);
      setprop("/sim/current-view/mouse-heading-offset-deg", 0);   
   },
};
var headtracker = {
   lp: {},
   out: {"x":0, "y":0, "z":0, "h":0, "p":0, "r":0},
   init: func {
      foreach (var a; ["x", "y", "z", "h", "p", "r"]) {
         me.lp[a] = lowpass.new(0.15);
      }
   },
   output: func {
      var output = {};
      output.x = getprop("/sim/TrackIR/x-m") or 0;
      output.y = getprop("/sim/TrackIR/y-m") or 0;
      output.z = getprop("/sim/TrackIR/z-m") or 0;
      output.h = getprop("/sim/TrackIR/heading-deg") or 0;
      output.p = getprop("/sim/TrackIR/pitch-deg") or 0;
      output.r = getprop("/sim/TrackIR/roll-deg") or 0;
      foreach (var a; keys(output)) {
         me.out[a] = me.lp[a].filter(output[a]);
      }
   },
};
##
# Normalize angle to  -180 <= angle < 180
#
var normdeg = func(a) {
   while (a >= 180)
      a -= 360;
   while (a < -180)
      a += 360;
   return a;
}
var cameraManager = {
   init: func {
      #point.init();
      mouse.init();
      headtracker.init();
      DHM.init();
      camera.init();
      RND.init(camera.globalData);
      me.loop();
   },
   loop: func {
      globalData.dt = getprop("/sim/time/delta-sec");
      globalData.speedUp = getprop("/sim/speed-up");
      point.move_loop(globalData.dt);
      point.adjust_loop(globalData.dt);
      mouse.output();
      headtracker.output();
      DHM.output();
      RND.output(globalData.dt, camera.id);
      setprop("/sim/current-view/x-offset-m", camera.current.x + camera.offset.x + RND.out.x + DHM.out.x + headtracker.out.x);
      setprop("/sim/current-view/y-offset-m", camera.current.y + camera.offset.y + RND.out.y + DHM.out.y + headtracker.out.y);
      setprop("/sim/current-view/z-offset-m", camera.current.z + camera.offset.z + RND.out.z + DHM.out.z + headtracker.out.z);
      setprop("/sim/current-view/heading-offset-deg", camera.current.h + camera.offset.h + mouse.offset.h + RND.out.h + headtracker.out.h);
      setprop("/sim/current-view/pitch-offset-deg", camera.current.p + camera.offset.p + mouse.offset.p + RND.out.p + headtracker.out.p);
      setprop("/sim/current-view/roll-offset-deg", camera.current.r + camera.offset.r + RND.out.r + DHM.out.r + headtracker.out.r);
      if (cameraManager_loop_running)
         settimer(cameraManager.loop, 0);
   },
};
var DHM = {
   init: func {
      me.out = {"x":0, "y":0, "z":0, "h":0, "p":0, "r":0};
      me.r_lp = lowpass.new(1.0);
      me.lp1 = lowpass.new(0.40);
      me.lp2 = lowpass.new(0.40);
      me.lp3 = lowpass.new(0.40);
      me.lp4 = lowpass.new(0.40);
   },
   output: func {
      if (camera.globalData[camera.id].config.DHM) {
         var ax = getprop("/accelerations/pilot/x-accel-fps_sec") or 0;
         var ay = getprop("/accelerations/pilot/y-accel-fps_sec") or 0;
         var az = getprop("/accelerations/pilot/z-accel-fps_sec") or 0;
         var kx = 2000;
         var ky = 2000;
         var kz = 5000;
         var roll = 150;
         var mass = 10;
         var output1 = {};
         var a = getprop("/orientation/model/roll-deg") or 0;
         if ((a < -20) and (a > -160))
            a = -20;
         if ((a > 20) and (a < 160))
            a = 20;
         if (a < -160)
            a += 180;
         if (a > 160)
            a -= 180;
         output1.x = mass * ax / kx ;
         output1.y = -1 * mass * ay / ky ;
         output1.z = mass * (az + 32.18516) / kz;
         if (output1.z < camera.globalData[camera.id].DHM["y-negative-limit-m"])
            output1.z = camera.globalData[camera.id].DHM["y-negative-limit-m"];
         if (output1.z > camera.globalData[camera.id].DHM["y-positive-limit-m"])
            output1.z = camera.globalData[camera.id].DHM["y-positive-limit-m"];
         output1.r = output1.y * roll + me.r_lp.filter(-a);
         me.out.x = me.lp1.filter(output1.y);
         me.out.y = me.lp2.filter(output1.z);
         me.out.z = me.lp3.filter(output1.x);
         me.out.r = me.lp4.filter(output1.r);
      } else {
         foreach(var a; keys(me.out)) {
            camera.current[a] += me.out[a];
            camera.from[a] = camera.current[a];
            me.out[a] = 0;
         }
      }

   },
};
########################################
#         RND effects
########################################
var lowpass = {
   new: func(coeff) {
      var m = { parents: [lowpass] };
      m.coeff = coeff >= 0 ? coeff : die("lowpass(): coefficient must be >= 0");
      m.value = nil;
      return m;
   },
   # filter(raw_value)    -> push new value, returns filtered value
   filter: func(v) {
      me.filter = me._filter_;
      me.value = v;
   },
   # get()                -> returns filtered value
   get: func {
      me.value;
   },
   # set()                -> sets new average and returns it
   set: func(v) {
      me.value = v;
   },
   # setFilter()                -> sets new coefficient
   setFilter: func(v) {
      me.coeff = v;
   },
   _filter_: func(v) {
      var dt = globalData.dt * globalData.speedUp;
      var c = dt / (me.coeff + dt);
      me.value = v * c + me.value * (1 - c);
   },
};
var angular_lowpass = {
   new: func(coeff) {
      var m = { parents: [angular_lowpass] };
      m.sin = lowpass.new(coeff);
      m.cos = lowpass.new(coeff);
      m.value = nil;
      return m;
   },
   filter: func(v) {
      v *= D2R;
      me.value = math.atan2(me.sin.filter(math.sin(v)), me.cos.filter(math.cos(v))) * R2D;
   },
   set: func(v) {
      v *= D2R;
      me.sin.set(math.sin(v));
      me.cos.set(math.cos(v));
   },
   # setFilter()                -> sets new coefficient
   setFilter: func(v) {
      me.sin.setFilter(v);
      me.cos.setFilter(v)
   },
   get: func {
      me.value;
   },
};
var hi_pass = {
   new: func(coeff) {
      var m = { parents: [hi_pass] };
      m.coeff = coeff >= 0 ? coeff : die("lowpass(): coefficient must be >= 0");
      m.value = 0;
      m.v1 = 0;
      return m;
   },
   # filter(raw_value)    -> push new value, returns filtered value
   filter: func(v) {
      var dt = globalData.dt * globalData.speedUp;
      var c = me.coeff / (me.coeff + dt);
      me.value = me.value * c + (v - me.v1) * c;
      me.v1 = v;
      return me.value;
      },
   # get()                -> returns filtered value
   get: func {
      me.value;
   },
   # set()                -> sets new average and returns it
   set: func(v) {
      me.value = v;
   },
   # setFilter()                -> sets new coefficient
   setFilter: func(v) {
      me.coeff = v;
   },
   _filter_: func(v) {
      var dt = globalData.dt * globalData.speedUp;
      var c = me.coeff / (me.coeff + dt);
      me.value = me.value * c + (v - me.v1) * c;
   },
};
var sine = {
   new: func(data) {
      var m = { parents: [sine] };
      m.frequency = data.frequency;
      m.level = data.level;
      m.time = 0;
      m.enabled = data.enabled;
      m.out = 0;
      return m;
   },
   setValues: func(data) {
      me.frequency = data.frequency;
      me.level = data.level;
   },
   output: func(dt) {
      if (me.enabled) {
         me.time += dt;
         me.out = me.level * math.sin(2 * math.pi * me.frequency * me.time);
      } else me.out = 0;
      return me.out;
   },
};
var noise = {
   new: func(data) {
      var m = { parents: [noise] };
      m.frequency = data.frequency;
      m.level = data.level;
      m.filter = data.frequency;
      m.lp = lowpass.new(data.frequency);
      m.enabled = data.enabled;
      m.out = 0;
      return m;
   },
   setFilter: func(coeff) {
      me.lp.setFilter(coeff);
   },
   setValues: func(data) {
      me.frequency = data.frequency;
      me.level = data.level;
   },
   output: func {
      if (me.enabled) {
         var a = rand() * 2 - 1;
         me.out = me.level * me.lp.filter(a);
      } else me.out = 0;
      return me.out;
   },
};
var sineLFO = {
   new: func(data) {
      var m = { parents: [sineLFO] };
      m.filter = data.filter;
      m.intensity = m.Time = data.intensity;
      m.level = data.level;
      m.filter = data.filter;
      m.lp = lowpass.new(data.filter);
      m.enabled = data.enabled;
      m.out = 0;
      m.time = 0;
      return m;
   },
   setValues: func(data) {
      me.filter = data.filter;
      me.intensity = me.Time = data.intensity;
      me.level = data.level;
   },
   setFilter: func(coeff) {
      me.lp.setFilter(coeff);
   },
   output: func(dt) {
      if (me.enabled) {
         me.time += dt;
         if (me.time >= me.Time) {
            me.time = 0;
            me.Time = rand() * me.intensity;
         }
         var a = me.level * math.sin(2 * math.pi * me.time / me.Time);
         me.out = me.lp.filter(a);
      } else me.out = 0;
      return me.out;
   },
};
var rectLFO = {
   new: func(data) {
      var m = { parents: [rectLFO] };
      m.filter = data.filter;
      m.intensity = m.Time = data.intensity;
      m.level = data.level;
      m.filter = data.filter;
      m.lp = lowpass.new(data.filter);
      m.out = 0;
      m.enabled = data.enabled;
      m.time = 0;
      return m;
   },
   setFilter: func(coeff) {
      me.lp.setFilter(coeff);
   },
   setValues: func(data) {
      me.filter = data.filter;
      me.intensity = me.Time = data.intensity;
      me.level = data.level;
   },
   output: func(dt) {
      if (me.enabled) {
         me.time += dt;
         if (me.time >= me.Time) {
            me.time = 0;
            me.Time = rand() * me.intensity;
         }
         var a = me.level * math.sgn( math.sin(2 * math.pi * me.time / me.Time) );
         me.out = me.lp.filter(a);
      } else me.out = 0;
      return me.out;
   },
};
var resonance = {
   new: func(data) {
      var m = { parents: [resonance] };
      m.frequency = data.frequency;
      m.attack = data.attack;
      m.intensity = data.intensity;
      m.release = data.release;
      m.level = data.level;
      m.time1 = 0;
      m.time2 = 0;
      m.time3 = 0;
      m.Time1 = data.intensity;
      m.Time2 = data.attack + data.release;
      m.start = 0;
      m.enabled = data.enabled;
      return m;
   },
   setValues: func(data) {
      me.frequency = data.frequency;
      me.attack = data.attack;
      me.intensity = data.intensity;
      me.release = data.release;
      me.level = data.level;
   },
   output: func(dt) {
      var a = 0;
      if (me.enabled) {
         me.time1 += dt;
         me.time2 += dt;
         if (me.time2 >= me.Time1) {
            me.time2 = 0;
            me.start = 1;
            me.Time1 = rand() * me.intensity;
         }
         if (me.start) {
            me.time3 += dt;
            if (me.time3 < me.Time2) {
               if (me.time3 <= me.attack) {
                  a = me.time3 * me.level / me.attack;
               } else {
                  a = me.level * (1 - (me.time3 - me.attack) / me.release);
               }
            } else {
               me.time3 = 0;
               me.start = 0;
            }
         }
         a *= math.sin(2 * math.pi * me.frequency * me.time1);
      }
      return a;
   },
};
var findValue = func(value, x, y) {
   var x1 = 0;
   var x2 = 0;
   var y1 = 0;
   var y2 = 0;
   var found1 = 0;
   for(var i = 1; i <= 8; i += 1) {
      var k = "v" ~ i;
      if (value >= x[k]) {
         x1 = x[k];
         y1 = y[k];
      } elsif (!found1) {
         found1 = 1;
         x2 = x[k];
         y2 = y[k];
      }
   }
   return( y1 + (value - x1) * (y2 - y1) / (x2 - x1) );
}
var RND = {
   out: {"x":0, "y":0, "z":0, "h":0, "p":0, "r":0,},
   init: func(data) {
      me.data = data;
      me.camera_internal = 1;
      me.lp = {"x":, "y":, "z":, "h":, "p":, "r":,};
      foreach(var a; keys(me.lp)) {
         me.lp[a] = lowpass.new(0);
      }
      foreach(var camera; keys(me.data)) {
         foreach(var a; keys(me.data[camera])) {
            if (a == "ground" or a == "air") {
               foreach(var b; keys(me.data[camera][a].generators)) {
                  me.data[camera][a].generators[b].sine = sine.new(data[camera][a].generators[b].sine);
                  me.data[camera][a].generators[b].noise = noise.new(data[camera][a].generators[b].noise);
                  me.data[camera][a].generators[b].rectLFO = rectLFO.new(data[camera][a].generators[b].rectLFO);
                  me.data[camera][a].generators[b].sineLFO = sineLFO.new(data[camera][a].generators[b].sineLFO);
                  me.data[camera][a].generators[b].resonance = resonance.new(data[camera][a].generators[b].resonance);
               } #generator
            }
         } #profile
      } #camera
   }, #init
   _output_: func(generator) {
      var _out_ = 0;
      foreach(var a; keys(me.data[me.profile].generators[generator])) {
         _out_ += me.data[me.profile].generators[generator][a].output(me.dt);
      }
      return _out_;
   },
   output: func(dt, camera) {
      var wow0 = getprop("/gear/gear[0]/wow");
      var wow1 = getprop("/gear/gear[1]/wow");
      var wow2 = getprop("/gear/gear[2]/wow");
      if (!wow0 and !wow1 and !wow2) {
         me.profile = "air";
      } else me.profile = "ground";
      if (me.profile == "ground") {
         me.velocity = getprop("/velocities/groundspeed-kt");
      } else me.velocity = getprop("/velocities/airspeed-kt");
      me.globalGain = findValue(me.velocity, me.data[camera][me.profile].curves.gain.x, me.data[camera][me.profile].curves.gain.y);
      if (me.camera_internal == me.data[camera].config.internal) {
         me.globalFilter = findValue(me.velocity, me.data[camera][me.profile].curves.filter.x, me.data[camera][me.profile].curves.filter.y);
      } else me.globalFilter = 0;
      var out = {x:0, y:0, z:0, h:0, p:0, r:0};
      var _out_ = {"A": 0, "B": 0, "C": 0};
      foreach(var generator; keys(me.data[camera][me.profile].generators)) {
         foreach(var a; keys(me.data[camera][me.profile].generators[generator])) {
            _out_[generator] += me.data[camera][me.profile].generators[generator][a].output(dt);
         }
      }
      foreach(var dof; keys(me.data[camera][me.profile].mixer)) {
         foreach(var generator; keys(me.data[camera][me.profile].mixer[dof])) {
            out[dof] += _out_[generator] * me.data[camera][me.profile].mixer[dof][generator];
         }
         out[dof] *= me.globalGain;
         me.lp[dof].setFilter(me.globalFilter);
         out[dof] = me.lp[dof].filter(out[dof]);
      }
      me.camera_internal = me.data[camera].config.internal;
      me.out = out;
   },
};
var fdm_init_listener = _setlistener("/sim/signals/fdm-initialized", func {
   removelistener(fdm_init_listener);
   cameraManager.init();
});
Marius_A
 
Posts: 92
Joined: Wed Dec 04, 2013 3:20 pm

Next

Return to New features

Who is online

Users browsing this forum: No registered users and 2 guests