Add ubershader and dirt map.
[fg:toms-fgdata.git] / Aircraft / sopwithCamel / Models / camel-utils.nas
1 #####################################################################################
2 #                                                                                   #
3 #  this script contains a number of utilities for use with the Camel (YASim fdm)    #
4 #                                                                                   #
5 #####################################################################################
6
7 ##############
8 # Define
9 #does what it says on the tin
10 var clamp = func(v, min, max) { v < min ? min : v > max ? max : v }
11
12 # ================================ Initalize ======================================
13 # Make sure all needed properties are present and accounted
14 # for, and that they have sane default values.
15
16 aircraft.livery.init("Aircraft/sopwithCamel/Models/Liveries",
17                      "sim/model/livery/name",
18                      "sim/model/livery/index"
19                      );
20
21 view_number_Node = props.globals.getNode("/sim/current-view/view-number",1);
22 view_number_Node.setDoubleValue( 0 );
23
24 enabledNode = props.globals.getNode("/sim/headshake/enabled", 1);
25 enabledNode.setBoolValue(1);
26
27 instrumentation_yawstring_Node = props.globals.getNode("instrumentation/yawstring[0]", 1);
28 instrumentation_yawstring_Node.setValue(0);
29
30 instrumentation_airstring_Node = props.globals.getNode("instrumentation/airstring[0]", 1);
31 instrumentation_airstring_Node.setValue(0);
32
33 instrumentation_yawstring_flutter_Node = props.globals.getNode("instrumentation/yawstring-flutter[0]", 1);
34 instrumentation_yawstring_flutter_Node.setValue(0);
35
36 generic_float0_Node = props.globals.getNode("sim/multiplay/generic/float[0]", 1);
37 generic_float0_Node.setValue(0);
38
39 generic_float1_Node = props.globals.getNode("sim/multiplay/generic/float[1]", 1);
40 generic_float1_Node.setValue(0);
41
42 generic_float2_Node = props.globals.getNode("sim/multiplay/generic/float[2]", 1);
43 generic_float2_Node.setValue(0);
44
45 sim_model_camel_show_pilot_Node = props.globals.getNode("sim/model/camel/show-pilot[0]", 1);
46 sim_model_camel_show_pilot_Node.setBoolValue(1);
47
48 sim_model_camel_show_face_mask_Node = props.globals.getNode("sim/model/camel/show-face-mask[0]", 1);
49 sim_model_camel_show_face_mask_Node.setBoolValue(0);
50
51 generic_bool0_Node = props.globals.getNode("sim/multiplay/generic/int[0]", 1);
52 generic_bool0_Node.setBoolValue(0);
53
54 controls_gear_brake_parking_Node = props.globals.getNode("controls/gear/brake-parking[0]", 1);
55 sim_model_camel_show_pilot_Node.setBoolValue(0);
56
57 generic_bool1_Node = props.globals.getNode("sim/multiplay/generic/int[1]", 1);
58 generic_bool1_Node.setBoolValue(0);
59
60 props.globals.initNode("sim/model/livery/dirt-factor",0);
61
62 var engine_running_Node = props.globals.initNode("engines/engine[0]/running", 1, "BOOL");
63 var hobbs_engine = aircraft.timer.new("sim/time/hobbs/engine[0]", 60, 0);
64
65 hobbs_engine.reset();
66
67 controls.fullBrakeTime = 0;
68
69 pilot_g = nil;
70 headshake = nil;
71 magneto = nil;
72 smoke = nil;
73
74 var time = 0;
75 var dt = 0;
76 var last_time = 0.0;
77
78 var xDivergence_damp = 0;
79 var yDivergence_damp = 0;
80 var zDivergence_damp = 0;
81
82 var last_xDivergence = 0;
83 var last_yDivergence = 0;
84 var last_zDivergence = 0;
85
86 initialize = func {
87
88     print( "Initializing Camel utilities ..." );
89
90 # initialize objects
91     pilot_g = PilotG.new();
92     headshake = HeadShake.new();
93     magneto = Magneto.new();
94     smoke = Smoke.new();
95
96     updateHobbs();
97
98     setprop("sim/model/position/latitude-deg", getprop("position/latitude-deg"));
99     setprop("sim/model/position/longitude-deg", getprop("position/longitude-deg"));
100     setprop("sim/model/position/altitude-ft", getprop("position/altitude-ft"));
101
102 #set listeners
103     setlistener( "controls/gear/brake-left", func { magneto.blipMagswitch();
104     } );
105     setlistener( "controls/gear/brake-right", func { magneto.blipMagswitch();
106     } );
107
108     setlistener( "engines/engine/cranking", func { smoke.updateSmoking();
109     } );
110     setlistener( "engines/engine/running", func { smoke.updateSmoking();
111     } );
112
113     setlistener( "sim/model/camel/show-face-mask[0]", func {
114                 generic_bool0_Node.setValue(sim_model_camel_show_face_mask_Node.getValue());
115             } );
116
117     setlistener( "controls/gear/brake-parking", func {
118                 if(getprop("gear/gear[0]/wow") and getprop("gear/gear[1]/wow")){
119                     generic_bool1_Node.setValue(controls_gear_brake_parking_Node.getValue());
120                 }
121             } );
122
123 # set it running on the next update cycle
124     settimer( update, 0 );
125
126     print( "... running Camel utilities" );
127
128 } # end func
129
130 ###
131 # ====================== end Initialization ========================================
132 ###
133
134 ###
135 # ==== this is the Main Loop which keeps everything updated ========================
136 ##
137 update = func {
138
139     generic_float0_Node.setValue(instrumentation_yawstring_Node.getValue());
140     generic_float1_Node.setValue(instrumentation_airstring_Node.getValue());
141     generic_float2_Node.setValue(instrumentation_yawstring_flutter_Node.getValue());
142
143     pilot_g.update();
144     pilot_g.gmeter_update();
145
146     var hobbs = getprop("sim/time/hobbs/engine[0]");
147     var dirt_factor = clamp(hobbs*2/3600, 0, 0.5);
148     setprop("sim/model/livery/dirt-factor", dirt_factor);
149
150 #    print ("hobbs ", hobbs);
151
152     if ( enabledNode.getValue() and view_number_Node.getValue() == 0 ) {
153         headshake.update();
154     }
155
156     settimer( update, 0 );
157
158 }# end main loop func
159
160 # ============================== end Main Loop ===============================
161
162 # ============================== specify classes ===========================
163
164
165
166 # =================================== fuel tank stuff ===================================
167 # Class that specifies fuel cock functions
168 #
169 FuelCock = {
170     new : func ( name,
171     control,
172     initial_pos
173     ){
174         var obj = { parents : [FuelCock] };
175         obj.name = name;
176         obj.control = props.globals.getNode( control, 1 );
177         obj.control.setIntValue( initial_pos );
178
179         print ( obj.name );
180         return obj;
181     },
182
183 set: func ( pos ) {# operate fuel cock
184      me.control.setValue( pos );
185     },
186 }; #
187
188
189
190 # ========================== end fuel tank stuff ======================================
191
192
193 # =========================== magneto stuff ====================================
194 # Class that specifies magneto functions
195 #
196 Magneto = {
197     new : func ( name = "magneto",
198     right = "controls/engines/engine/mag-switch-right",
199     left = "controls/engines/engine/mag-switch-left",
200     magnetos = "controls/engines/engine/magnetos",
201     left_brake = "controls/gear/brake-left",
202     right_brake = "controls/gear/brake-right"
203     ){
204         var obj = { parents : [Magneto] };
205         obj.name = name;
206         obj.right = props.globals.getNode( right, 1 );
207         obj.left = props.globals.getNode( left, 1 );
208         obj.magnetos = props.globals.getNode( magnetos, 1 );
209         obj.left_brake = props.globals.getNode( left_brake, 1 );
210         obj.right_brake = props.globals.getNode( right_brake, 1 );
211         obj.left.setBoolValue( 0 );
212         obj.right.setBoolValue( 0 );
213         print ( obj.name );
214         return obj;
215     },
216
217 updateMagnetos: func{     # set the magneto value according to the switch positions
218 # print("updating Magnetos");
219                 if (me.left.getValue() and me.right.getValue()){                  # both
220                     me.magnetos.setValue( 3 );
221                 }
222                 elsif (me.left.getValue() and !me.right.getValue()) {             # left
223                     me.magnetos.setValue( 1 );
224                 }
225                 elsif (!me.left.getValue() and me.right.getValue()) {             # right
226                     me.magnetos.setValue( 2 );
227                 }
228                 else{
229                     me.magnetos.setValue( 0 );            # none
230                 }
231
232     }, # end function
233
234 setleftMagswitch:   func ( left ) {
235
236     me.left.setValue( left );
237     me.updateMagnetos();
238
239     }, # end function
240
241 setrightMagswitch:  func ( right) {
242
243     me.right.setValue( right );
244     me.updateMagnetos();
245
246     }, # end function
247
248 toggleleftMagswitch:    func{
249 # print ("left in ", me.left.getValue());
250     var left = me.left.getValue();
251     left = !left;
252     me.left.setBoolValue( left );
253     me.updateMagnetos();
254
255     }, # end function
256
257 togglerightMagswitch:   func{
258 # print ("right in ", me.right.getValue());
259     var right = me.right.getValue();
260     right = !right;
261     me.right.setBoolValue( right );
262     me.updateMagnetos();
263
264     }, # end function
265
266 blipMagswitch:   func{
267 # print ("blip in right ", me.right.getValue()," left ", me.left.getValue());
268 # print ("blip in brake right ", me.right_brake.getValue()," left ", me.left_brake.getValue());
269     if ( me.right_brake.getValue() != 0 or me.left_brake.getValue() != 0 ) {;
270     me.magnetos.setValue( 0 );
271     setprop("sim/model/camel/blip_switch",1);
272     } else {
273         me.updateMagnetos();
274         setprop("sim/model/camel/blip_switch",0);
275     }
276
277 # print ("blip out right ", me.right.getValue()," left ", me.left.getValue());
278     }, # end function
279     }; #
280
281
282 # =============================== end magneto stuff =========================================
283
284 # =============================== Pilot G stuff ================================
285 # Class that specifies pilot g functions
286 #
287     PilotG = {
288         new : func ( name = "pilot-g",
289         acceleration = "accelerations",
290         pilot_g = "pilot-g",
291         g_timeratio = "timeratio",
292         pilot_g_damped = "pilot-g-damped",
293         g_min = "pilot-gmin",
294         g_max = "pilot-gmax"
295         ){
296             var obj = { parents : [PilotG] };
297             obj.name = name;
298             obj.accelerations = props.globals.getNode("accelerations", 1);
299             obj.pilot_g = obj.accelerations.getChild( pilot_g, 0, 1 );
300             obj.pilot_g_damped = obj.accelerations.getChild( pilot_g_damped, 0, 1 );
301             obj.g_timeratio = obj.accelerations.getChild( g_timeratio, 0, 1 );
302             obj.g_min = obj.accelerations.getChild( g_min, 0, 1 );
303             obj.g_max = obj.accelerations.getChild( g_max, 0, 1 );
304             obj.pilot_g.setDoubleValue(0);
305             obj.pilot_g_damped.setDoubleValue(0);
306             obj.g_timeratio.setDoubleValue(0.0075);
307             obj.g_min.setDoubleValue(0);
308             obj.g_max.setDoubleValue(0);
309
310             print ( obj.name );
311             return obj;
312         },
313 update : func () {
314         var n = me.g_timeratio.getValue();
315         var g = me.pilot_g.getValue();
316         var g_damp = me.pilot_g_damped.getValue();
317
318         g_damp = ( g * n) + (g_damp * (1 - n));
319
320         me.pilot_g_damped.setDoubleValue(g_damp);
321
322 # print(sprintf("pilot_g_damped in=%0.5f, out=%0.5f", g, g_damp));
323         },
324 gmeter_update : func () {
325         if( me.pilot_g_damped.getValue() < me.g_min.getValue() ){
326             me.g_min.setDoubleValue( me.pilot_g_damped.getValue() );
327         } elsif( me.pilot_g_damped.getValue() > me.g_max.getValue() ){
328             me.g_max.setDoubleValue( me.pilot_g_damped.getValue() );
329         }
330         },
331 get_g_timeratio : func () {
332         return me.g_timeratio.getValue();
333         },
334     };  
335
336
337
338 # Class that specifies head movement functions under the force of gravity
339
340 #  - this is a modification of the original work by Josh Babcock
341
342     HeadShake = {
343         new : func ( name = "headshake",
344         x_accel_fps_sec = "x-accel-fps_sec",
345         y_accel_fps_sec = "y-accel-fps_sec",
346         z_accel_fps_sec = "z-accel-fps_sec",
347         x_max_m = "x-max-m",
348         x_min_m = "x-min-m",
349         y_max_m = "y-max-m",
350         y_min_m = "y-min-m",
351         z_max_m = "z-max-m",
352         z_min_m = "z-min-m",
353         x_threshold_g = "x-threshold-g",
354         y_threshold_g = "y-threshold-g",
355         z_threshold_g = "z-threshold-g",
356         x_config = "z-offset-m", 
357         y_config = "x-offset-m",
358         z_config = "y-offset-m",
359         time_ratio = "time-ratio",
360         ){
361             var obj = { parents : [HeadShake] };
362             obj.name = name;
363             obj.accelerations = props.globals.getNode( "accelerations/pilot", 1 );
364             obj.xAccelNode = obj.accelerations.getChild(  x_accel_fps_sec, 0, 1 );
365             obj.yAccelNode = obj.accelerations.getChild(  y_accel_fps_sec, 0, 1 );
366             obj.zAccelNode = obj.accelerations.getChild(  z_accel_fps_sec, 0, 1 );
367             obj.sim = props.globals.getNode( "sim/headshake", 1 );
368             obj.xMaxNode = obj.sim.getChild( x_max_m, 0, 1 );
369             obj.xMaxNode.setDoubleValue( 0.025 );
370             obj.xMinNode = obj.sim.getChild( x_min_m, 0, 1 );
371             obj.xMinNode.setDoubleValue( -0.01 );
372             obj.yMaxNode = obj.sim.getChild( y_max_m, 0, 1 );
373             obj.yMaxNode.setDoubleValue( 0.01 );
374             obj.yMinNode = obj.sim.getChild( y_min_m, 0, 1 );
375             obj.yMinNode.setDoubleValue( -0.01 );
376             obj.zMaxNode = obj.sim.getChild( z_max_m, 0, 1 );
377             obj.zMaxNode.setDoubleValue( 0.01 );
378             obj.zMinNode = obj.sim.getChild( z_min_m, 0, 1 );
379             obj.zMinNode.setDoubleValue( -0.03 );
380             obj.xThresholdNode = obj.sim.getChild(x_threshold_g, 0, 1 );
381             obj.xThresholdNode.setDoubleValue( 0.5 );
382             obj.yThresholdNode = obj.sim.getChild(y_threshold_g, 0, 1 );
383             obj.yThresholdNode.setDoubleValue( 0.5 );
384             obj.zThresholdNode = obj.sim.getChild(z_threshold_g, 0, 1 );
385             obj.zThresholdNode.setDoubleValue( 0.5 );
386             obj.time_ratio_Node = obj.sim.getChild( time_ratio , 0, 1 );
387             obj.time_ratio_Node.setDoubleValue( 0.5 );
388             obj.config = props.globals.getNode("/sim/view/config", 1);
389             obj.xConfigNode = obj.config.getChild( x_config , 0, 1 );
390             obj.yConfigNode = obj.config.getChild( y_config , 0, 1 );
391             obj.zConfigNode = obj.config.getChild( z_config , 0, 1 );
392
393             obj.seat_vertical_adjust_Node = props.globals.getNode( "/controls/seat/vertical-adjust", 1 );
394             obj.seat_vertical_adjust_Node.setDoubleValue( 0 );
395
396             print ( obj.name );
397             return obj;
398         },
399 update : func () {
400
401 # There are two coordinate systems here, one used for accelerations, 
402 # and one used for the viewpoint.
403 # We will be using the one for accelerations.
404
405         var n = pilot_g.get_g_timeratio(); 
406         var seat_vertical_adjust = me.seat_vertical_adjust_Node.getValue();
407
408         var xMax = me.xMaxNode.getValue();
409         var xMin = me.xMinNode.getValue();
410         var yMax = me.yMaxNode.getValue();
411         var yMin = me.yMinNode.getValue();
412         var zMax = me.zMaxNode.getValue();
413         var zMin = me.zMinNode.getValue();
414
415 #work in G, not fps/s
416         var xAccel = me.xAccelNode.getValue()/32;
417         var yAccel = me.yAccelNode.getValue()/32;
418         var zAccel = (me.zAccelNode.getValue() + 32)/32; # We aren't counting gravity
419
420             var xThreshold =  me.xThresholdNode.getValue();
421         var yThreshold =  me.yThresholdNode.getValue();
422         var zThreshold =  me.zThresholdNode.getValue();
423
424         var xConfig = me.xConfigNode.getValue();
425         var yConfig = me.yConfigNode.getValue();
426         var zConfig = me.zConfigNode.getValue();
427
428 # Set viewpoint divergence and clamp
429 # Note that each dimension has its own special ratio and +X is clamped at 1cm
430 # to simulate a headrest.
431
432         if (xAccel < -1) {
433             xDivergence = ((( -0.0506 * xAccel ) - ( 0.538 )) * xAccel - ( 0.9915 ))
434                 * xAccel - 0.52;
435         } elsif (xAccel > 1) {
436             xDivergence = ((( -0.0387 * xAccel ) + ( 0.4157 )) * xAccel - ( 0.8448 )) 
437                 * xAccel + 0.475;
438         } else {
439             xDivergence = 0;
440         }
441
442         if (yAccel < -0.5) {
443             yDivergence = ((( -0.013 * yAccel ) - ( 0.125 )) * yAccel - (  0.1202 )) * yAccel - 0.0272;
444         } elsif (yAccel > 0.5) {
445             yDivergence = ((( -0.013 * yAccel ) + ( 0.125 )) * yAccel - (  0.1202 )) * yAccel + 0.0272;
446         } else {
447             yDivergence = 0;
448         }
449
450         if (zAccel < -1) {
451             zDivergence = ((( -0.0506 * zAccel ) - ( 0.538 )) 
452                 * zAccel - ( 0.9915 )) * zAccel - 0.52;
453         } elsif (zAccel > 1) {
454             zDivergence = ((( -0.0387 * zAccel ) + ( 0.4157 )) 
455                 * zAccel - ( 0.8448 )) * zAccel + 0.475;
456         } else {
457             zDivergence = 0;
458         }
459
460         xDivergence_total = ( xDivergence * 0.25 ) + ( zDivergence * 0.25 );
461
462         if (xDivergence_total > xMax){ xDivergence_total = xMax; }
463         if (xDivergence_total < xMin){ xDivergence_total = xMin; }
464         if (abs(last_xDivergence - xDivergence_total) <= xThreshold){
465             xDivergence_damp = ( xDivergence_total * n) + ( xDivergence_damp * (1 - n));
466 #       print ("x low pass");
467         } else {
468             xDivergence_damp = xDivergence_total;
469 #       print ("x high pass");
470         }
471
472         last_xDivergence = xDivergence_damp;
473
474 #print (sprintf("x total=%0.5f, x min=%0.5f, x div damped=%0.5f", xDivergence_total,
475 # xMin , xDivergence_damp));    
476
477         yDivergence_total = yDivergence;
478         if ( yDivergence_total >= yMax ){ yDivergence_total = yMax; }
479         if ( yDivergence_total <= yMin ){ yDivergence_total = yMin; }
480
481         if (abs(last_yDivergence - yDivergence_total) <= yThreshold){
482             yDivergence_damp = ( yDivergence_total * n) + ( yDivergence_damp * (1 - n));
483 #               print ("y low pass");
484         } else {
485             yDivergence_damp = yDivergence_total;
486 #               print ("y high pass");
487         }
488
489         last_yDivergence = yDivergence_damp;
490
491 #       print (sprintf("y=%0.5f, y total=%0.5f, y min=%0.5f, y div damped=%0.5f",
492 #                                               yDivergence, yDivergence_total, yMin , yDivergence_damp));
493
494         zDivergence_total =  xDivergence + zDivergence;
495         if ( zDivergence_total >= zMax ){ zDivergence_total = zMax; }
496         if ( zDivergence_total <= zMin ){zDivergence_total = zMin; }
497
498         if (abs(last_zDivergence - zDivergence_total) <= zThreshold){ 
499             zDivergence_damp = ( zDivergence_total * n) + ( zDivergence_damp * (1 - n));
500 #       print ("z low pass");
501         } else {
502             zDivergence_damp = zDivergence_total;
503 #       print ("z high pass");
504         }
505
506         last_zDivergence = zDivergence_damp;
507
508 #       print (sprintf("z total=%0.5f, z min=%0.5f, z div damped=%0.5f", 
509 #                                                                               zDivergence_total, zMin , zDivergence_damp));
510
511         setprop( "/sim/current-view/z-offset-m", xConfig + xDivergence_damp );
512         setprop( "/sim/current-view/x-offset-m", yConfig + yDivergence_damp );
513         setprop( "/sim/current-view/y-offset-m", zConfig + zDivergence_damp 
514             + seat_vertical_adjust );
515
516         },
517     };
518
519
520 # ============================ end Pilot G stuff ============================
521
522 # =========================== smoke stuff ====================================
523 # Class that specifies smoke functions 
524 #
525     Smoke = {
526         new : func ( name = "smoke",
527         cranking = "engines/engine/cranking",
528         running = "engines/engine/running",
529         smoking = "sim/ai/engines/engine/smoking"
530         ){
531             var obj = { parents : [Smoke] };
532             obj.name = name;
533             obj.cranking = props.globals.getNode( cranking, 1 );
534             obj.running = props.globals.getNode( running, 1 );
535             obj.smoking = props.globals.getNode( smoking, 1 );
536             obj.smoking.setBoolValue( 0 );
537             print ( obj.name );
538             return obj;
539         },
540
541 updateSmoking: func{     # set the smoke value according to the engine conditions
542 #               print("updating Smoke");
543                if (me.cranking.getValue() and !me.running.getValue()){  
544                    me.smoking.setValue( 1 );
545                } else{
546                    me.smoking.setValue( 0 );            # none
547                }
548
549         }, # end function
550
551     }; #
552
553
554 # =============================== end smoke stuff ================================
555
556 # ==================================  Engine Hobbs Meter ================================
557
558 updateHobbs = func{
559         var running = engine_running_Node.getValue();
560
561         if(running){
562                 hobbs_engine.start();
563         } else {
564                 hobbs_engine.stop();
565         }
566
567         settimer(updateHobbs,0)
568 }
569
570 # ================================== End Engine Hobbs Meter ================================
571
572 # Fire it all up
573
574 setlistener("sim/signals/fdm-initialized", initialize);
575
576 # end