rotoranimation.nas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. ####################################################
  2. ## Maik Justus < fg # mjustus : de >, ##
  3. ## based on bo105.nas by Melchior FRANZ, ##
  4. ## < mfranz # aon : at > ##
  5. ####################################################
  6. if (!contains(globals, "cprint")) {
  7. globals.cprint = func {};
  8. }
  9. var optarg = aircraft.optarg;
  10. var makeNode = aircraft.makeNode;
  11. var sin = func(a) { math.sin(a * math.pi / 180.0) }
  12. var cos = func(a) { math.cos(a * math.pi / 180.0) }
  13. var pow = func(v, w) { math.exp(math.ln(v) * w) }
  14. var npow = func(v, w) { math.exp(math.ln(abs(v)) * w) * (v < 0 ? -1 : 1) }
  15. var clamp = func(v, min = 0, max = 1) { v < min ? min : v > max ? max : v }
  16. var normatan = func(x) { math.atan2(x, 1) * 2 / math.pi }
  17. # timers ============================================================
  18. var turbine_timer = aircraft.timer.new("/sim/time/hobbs/turbines", 10);
  19. aircraft.timer.new("/sim/time/hobbs/helicopter", nil).start();
  20. # engines/rotor =====================================================
  21. var state = props.globals.getNode("sim/model/r44/state");
  22. var engine = props.globals.getNode("sim/model/r44/engine");
  23. var rotor = props.globals.getNode("controls/engines/engine/magnetos");
  24. var rotor_rpm = props.globals.getNode("rotors/main/rpm");
  25. var torque = props.globals.getNode("rotors/gear/total-torque", 1);
  26. var collective = props.globals.getNode("controls/engines/engine[0]/throttle");
  27. var turbine = props.globals.getNode("sim/model/r44/turbine-rpm-pct", 1);
  28. var torque_pct = props.globals.getNode("sim/model/r44/torque-pct", 1);
  29. var stall = props.globals.getNode("rotors/main/stall", 1);
  30. var stall_filtered = props.globals.getNode("rotors/main/stall-filtered", 1);
  31. var torque_sound_filtered = props.globals.getNode("rotors/gear/torque-sound-filtered", 1);
  32. var target_rel_rpm = props.globals.getNode("controls/rotor/reltarget", 1);
  33. var max_rel_torque = props.globals.getNode("controls/rotor/maxreltorque", 1);
  34. var cone = props.globals.getNode("rotors/main/cone-deg", 1);
  35. var cone1 = props.globals.getNode("rotors/main/cone1-deg", 1);
  36. var cone2 = props.globals.getNode("rotors/main/cone2-deg", 1);
  37. var cone3 = props.globals.getNode("rotors/main/cone3-deg", 1);
  38. var cone4 = props.globals.getNode("rotors/main/cone4-deg", 1);
  39. var battery = props.globals.getNode("controls/electric/battery-switch");
  40. # state:
  41. # 0 off
  42. # 1 engine startup
  43. # 2 engine startup with small torque on rotor
  44. # 3 engine idle
  45. # 4 engine accel
  46. # 5 engine sound loop
  47. var update_state = func {
  48. var s = state.getValue();
  49. var new_state = arg[0];
  50. if (new_state == (s+1)) {
  51. state.setValue(new_state);
  52. if (new_state == (1)) {
  53. settimer(func { update_state(2) }, 2);
  54. interpolate(engine, 0.03, 0.1, 0.002, 0.3, 0.02, 0.1, 0.003, 0.7, 0.03, 0.1, 0.01, 0.7);
  55. } else {
  56. if (new_state == (2)) {
  57. settimer(func { update_state(3) }, 3);
  58. rotor.setValue(1);
  59. max_rel_torque.setValue(0.01);
  60. target_rel_rpm.setValue(0.002);
  61. interpolate(engine, 0.05, 0.2, 0.03, 1, 0.07, 0.1, 0.04, 0.9, 0.02, 0.5);
  62. } else {
  63. if (new_state == (3)) {
  64. if (rotor_rpm.getValue() > 100) {
  65. #rotor is running at high rpm, so accel. engine faster
  66. max_rel_torque.setValue(1);
  67. target_rel_rpm.setValue(1.03);
  68. state.setValue(5);
  69. interpolate(engine, 1.03, 10);
  70. } else {
  71. settimer(func { update_state(4) }, 7);
  72. max_rel_torque.setValue(0.05);
  73. target_rel_rpm.setValue(0.02);
  74. interpolate(engine, 0.07, 0.1, 0.03, 0.25, 0.075, 0.2, 0.08, 1, 0.06,2);
  75. }
  76. } else {
  77. if (new_state == (4)) {
  78. settimer(func { update_state(5) }, 30);
  79. max_rel_torque.setValue(0.25);
  80. target_rel_rpm.setValue(0.8);
  81. } else {
  82. if (new_state == (5)) {
  83. max_rel_torque.setValue(1);
  84. target_rel_rpm.setValue(1.03);
  85. }
  86. }
  87. }
  88. }
  89. }
  90. }
  91. }
  92. var engines = func {
  93. if (props.globals.getNode("sim/crashed",1).getBoolValue()) {return; }
  94. var s = state.getValue();
  95. if (arg[0] == 1) {
  96. if (s == 0) {
  97. update_state(1);
  98. }
  99. } else {
  100. rotor.setValue(0); # engines stopped
  101. state.setValue(0);
  102. interpolate(engine, 0, 4);
  103. }
  104. }
  105. var update_engine = func {
  106. if (state.getValue() > 3 ) {
  107. interpolate (engine, clamp( rotor_rpm.getValue() / 235 ,
  108. 0.05, target_rel_rpm.getValue() ), 0.25 );
  109. }
  110. }
  111. var update_rotor_cone_angle = func {
  112. r = rotor_rpm.getValue();
  113. #print("r = ", r);
  114. var f = r / 186;
  115. #print("f1 = ", f);
  116. f = clamp (f, 0 , 1);
  117. #print("f2 = ", f);
  118. c = cone.getValue();
  119. #print("c = ", c);
  120. cone1.setDoubleValue( (c * 1.00) + (f * c));
  121. cone2.setDoubleValue( (c * 0.10) + (f * c));
  122. cone3.setDoubleValue( (c * 0.15) + (f * c));
  123. cone4.setDoubleValue( (c * 0.20) + (f * c));
  124. }
  125. # torquemeter
  126. var torque_val = 0;
  127. torque.setDoubleValue(0);
  128. var update_torque = func(dt) {
  129. var f = dt / (0.2 + dt);
  130. torque_val = torque.getValue() * f + torque_val * (1 - f);
  131. torque_pct.setDoubleValue(torque_val / 5300);
  132. }
  133. # sound =============================================================
  134. # stall sound
  135. var stall_val = 0;
  136. stall.setDoubleValue(0);
  137. var update_stall = func(dt) {
  138. var s = stall.getValue();
  139. if (s < stall_val) {
  140. var f = dt / (0.3 + dt);
  141. stall_val = s * f + stall_val * (1 - f);
  142. } else {
  143. stall_val = s;
  144. }
  145. var c = collective.getValue();
  146. stall_filtered.setDoubleValue(stall_val + 0.006 * (1 - c));
  147. }
  148. # modify sound by torque
  149. var torque_val = 0;
  150. var update_torque_sound_filtered = func(dt) {
  151. var t = torque.getValue();
  152. t = clamp(t * 0.000001);
  153. t = t*0.25 + 0.75;
  154. var r = clamp(rotor_rpm.getValue()*0.02-1);
  155. torque_sound_filtered.setDoubleValue(t*r);
  156. }
  157. # skid slide sound
  158. var Skid = {
  159. new : func(n) {
  160. var m = { parents : [Skid] };
  161. var soundN = props.globals.getNode("sim/sound", 1).getChild("slide", n, 1);
  162. var gearN = props.globals.getNode("gear", 1).getChild("gear", n, 1);
  163. m.compressionN = gearN.getNode("compression-norm", 1);
  164. m.rollspeedN = gearN.getNode("rollspeed-ms", 1);
  165. m.frictionN = gearN.getNode("ground-friction-factor", 1);
  166. m.wowN = gearN.getNode("wow", 1);
  167. m.volumeN = soundN.getNode("volume", 1);
  168. m.pitchN = soundN.getNode("pitch", 1);
  169. m.compressionN.setDoubleValue(0);
  170. m.rollspeedN.setDoubleValue(0);
  171. m.frictionN.setDoubleValue(0);
  172. m.volumeN.setDoubleValue(0);
  173. m.pitchN.setDoubleValue(0);
  174. m.wowN.setBoolValue(1);
  175. m.self = n;
  176. return m;
  177. },
  178. update : func {
  179. me.wowN.getBoolValue() or return;
  180. var rollspeed = abs(me.rollspeedN.getValue());
  181. me.pitchN.setDoubleValue(rollspeed * 0.6);
  182. var s = normatan(20 * rollspeed);
  183. var f = clamp((me.frictionN.getValue() - 0.5) * 2);
  184. var c = clamp(me.compressionN.getValue() * 2);
  185. me.volumeN.setDoubleValue(s * f * c * 2);
  186. #if (!me.self) {
  187. # cprint("33;1", sprintf("S=%0.3f F=%0.3f C=%0.3f >> %0.3f", s, f, c, s * f * c));
  188. #}
  189. },
  190. };
  191. var skid = [];
  192. for (var i = 0; i < 3; i += 1) {
  193. append(skid, Skid.new(i));
  194. }
  195. var update_slide = func {
  196. forindex (var i; skid) {
  197. skid[i].update();
  198. }
  199. }
  200. # crash handler =====================================================
  201. #var load = nil;
  202. var crash = func {
  203. if (arg[0]) {
  204. # crash
  205. setprop("rotors/main/rpm", 0);
  206. setprop("rotors/main/blade[0]/flap-deg", -60);
  207. setprop("rotors/main/blade[1]/flap-deg", -50);
  208. setprop("rotors/main/blade[2]/flap-deg", -40);
  209. setprop("rotors/main/blade[3]/flap-deg", -30);
  210. setprop("rotors/main/blade[0]/incidence-deg", -30);
  211. setprop("rotors/main/blade[1]/incidence-deg", -20);
  212. setprop("rotors/main/blade[2]/incidence-deg", -50);
  213. setprop("rotors/main/blade[3]/incidence-deg", -55);
  214. setprop("rotors/tail/rpm", 0);
  215. strobe_switch.setValue(0);
  216. beacon_switch.setValue(0);
  217. nav_light_switch.setValue(0);
  218. rotor.setValue(0);
  219. torque_pct.setValue(torque_val = 0);
  220. stall_filtered.setValue(stall_val = 0);
  221. state.setValue(0);
  222. } else {
  223. # uncrash (for replay)
  224. setprop("rotors/tail/rpm", 1500);
  225. setprop("rotors/main/rpm", 235);
  226. for (i = 0; i < 4; i += 1) {
  227. setprop("rotors/main/blade[" ~ i ~ "]/flap-deg", 0);
  228. setprop("rotors/main/blade[" ~ i ~ "]/incidence-deg", 0);
  229. }
  230. strobe_switch.setValue(1);
  231. beacon_switch.setValue(1);
  232. rotor.setValue(1);
  233. state.setValue(5);
  234. }
  235. }
  236. # "manual" rotor animation for flight data recorder replay ============
  237. var rotor_step = props.globals.getNode("sim/model/r44/rotor-step-deg");
  238. var blade1_pos = props.globals.getNode("rotors/main/blade[0]/position-deg", 1);
  239. var blade2_pos = props.globals.getNode("rotors/main/blade[1]/position-deg", 1);
  240. var blade3_pos = props.globals.getNode("rotors/main/blade[2]/position-deg", 1);
  241. var blade4_pos = props.globals.getNode("rotors/main/blade[3]/position-deg", 1);
  242. var rotorangle = 0;
  243. var rotoranim_loop = func {
  244. i = rotor_step.getValue();
  245. if (i >= 0.0) {
  246. blade1_pos.setValue(rotorangle);
  247. blade2_pos.setValue(rotorangle + 60);
  248. blade3_pos.setValue(rotorangle + 120);
  249. blade4_pos.setValue(rotorangle + 180);
  250. rotorangle += i;
  251. settimer(rotoranim_loop, 0.1);
  252. }
  253. }
  254. var init_rotoranim = func {
  255. if (rotor_step.getValue() >= 0.0) {
  256. settimer(rotoranim_loop, 0.1);
  257. }
  258. }
  259. # view management ===================================================
  260. var elapsedN = props.globals.getNode("/sim/time/elapsed-sec", 1);
  261. var flap_mode = 0;
  262. var down_time = 0;
  263. controls.flapsDown = func(v) {
  264. if (!flap_mode) {
  265. if (v < 0) {
  266. down_time = elapsedN.getValue();
  267. flap_mode = 1;
  268. dynamic_view.lookat(
  269. 5, # heading left
  270. -20, # pitch up
  271. 0, # roll right
  272. 0.2, # right
  273. 0.6, # up
  274. 0.85, # back
  275. 0.2, # time
  276. 55, # field of view
  277. );
  278. } elsif (v > 0) {
  279. flap_mode = 2;
  280. var p = "/sim/view/dynamic/enabled";
  281. setprop(p, !getprop(p));
  282. }
  283. } else {
  284. if (flap_mode == 1) {
  285. if (elapsedN.getValue() < down_time + 0.2) {
  286. return;
  287. }
  288. dynamic_view.resume();
  289. }
  290. flap_mode = 0;
  291. }
  292. }
  293. # register function that may set me.heading_offset, me.pitch_offset, me.roll_offset,
  294. # me.x_offset, me.y_offset, me.z_offset, and me.fov_offset
  295. #
  296. dynamic_view.register(func {
  297. var lowspeed = 1 - normatan(me.speedN.getValue() / 50);
  298. var r = sin(me.roll) * cos(me.pitch);
  299. me.heading_offset = # heading change due to
  300. (me.roll < 0 ? -50 : -30) * r * abs(r); # roll left/right
  301. me.pitch_offset = # pitch change due to
  302. (me.pitch < 0 ? -50 : -50) * sin(me.pitch) * lowspeed # pitch down/up
  303. + 15 * sin(me.roll) * sin(me.roll); # roll
  304. me.roll_offset = # roll change due to
  305. -15 * r * lowspeed; # roll
  306. });
  307. # main() ============================================================
  308. var delta_time = props.globals.getNode("/sim/time/delta-realtime-sec", 1);
  309. var adf_rotation = props.globals.getNode("/instrumentation/adf/rotation-deg", 1);
  310. var hi_heading = props.globals.getNode("/instrumentation/heading-indicator/indicated-heading-deg", 1);
  311. var main_loop = func {
  312. # adf_rotation.setDoubleValue(hi_heading.getValue());
  313. var dt = delta_time.getValue();
  314. update_torque(dt);
  315. update_stall(dt);
  316. update_torque_sound_filtered(dt);
  317. update_slide();
  318. update_engine();
  319. update_rotor_cone_angle();
  320. settimer(main_loop, 0);
  321. }
  322. var crashed = 0;
  323. var variant = nil;
  324. var doors = nil;
  325. var config_dialog = nil;
  326. # initialization
  327. setlistener("/sim/signals/fdm-initialized", func {
  328. init_rotoranim();
  329. collective.setDoubleValue(1);
  330. setlistener("/sim/signals/reinit", func {
  331. cmdarg().getBoolValue() and return;
  332. cprint("32;1", "reinit");
  333. turbine_timer.stop();
  334. collective.setDoubleValue(1);
  335. variant.scan();
  336. crashed = 0;
  337. });
  338. setlistener("sim/crashed", func {
  339. cprint("31;1", "crashed ", cmdarg().getValue());
  340. turbine_timer.stop();
  341. if (cmdarg().getBoolValue()) {
  342. crash(crashed = 1);
  343. }
  344. });
  345. setlistener("/sim/freeze/replay-state", func {
  346. cprint("33;1", cmdarg().getValue() ? "replay" : "pause");
  347. if (crashed) {
  348. crash(!cmdarg().getBoolValue())
  349. }
  350. });
  351. # the attitude indicator needs pressure
  352. # settimer(func { setprop("engines/engine/rpm", 3000) }, 8);
  353. main_loop();
  354. });