Board index FlightGear Development Scenery

Simple animation help...

Questions and discussion about enhancing and populating the FlightGear world.

Simple animation help...

Postby MD-Terp » Fri Dec 12, 2008 4:45 pm

Hey, everyone...

At least, I *think* this will be simple... :)

I have a model which I would like to rotate on the pitch axis, in a 3-second cycle, from about 5 degrees up to about 5 degrees down, in a sine-wave pattern. At the same time, I would also like to rotate it on the roll-axis, on maybe more of a 10-degree oscillation, also on a 3-second sine-wave pattern, but I want THAT cycle to be offset by 1 second from the other one.

Maybe if I tell you what I am after, it will be easier to picture...

I have a model of a small boat. I want to simulate it gently rocking while tied to a pier. I figure the sine-wave is a nice mathematical approximation of this pattern. I would like to see it both pitching and rolling slightly, but of course it won't peak on the pitch axis at the same time it peaks on the roll axis or that would look silly -- hence the offset.

Let's say the model is called police-boat.ac... so, what would police-boat.xml have to look like?

Thanks for the help...
Cheers,
-Rob.

"Retired" from FlightGear involvement as of July 2010.
viewtopic.php?f=3&t=8809
User avatar
MD-Terp
 
Posts: 2410
Joined: Wed Jan 23, 2008 7:37 am
Location: Baltimore, Maryland, USA
Callsign: N531MD, AVA0025

Re: Simple animation help...

Postby MD-Terp » Fri Dec 12, 2008 6:26 pm

Update:

I took the animation code from VicMar's Shoreham terminal building clock, and I modified it so that it referred to the whole model of my boat, instead of just two small pieces of his, as in the hands of the clock. I also figured out how to change the two animations so that one affected the roll axis and one affected the pitch axis. On the very first try, even, the changes I made worked exactly as I expected!...
Code: Select all
<?xml version="1.0"?>
<PropertyList>
<path>balt-co-police-boat.ac</path>

<animation>
  <type>rotate</type>
  <property>/sim/time/utc/day-seconds</property>
  <factor>0.1</factor>
  <axis>
   <x>0</x>
   <y>1</y>
   <z>0</z>
  </axis>
</animation>
 
<animation>
  <type>rotate</type>
  <property>/sim/time/utc/day-seconds</property>
  <factor>0.008333</factor>
  <axis>
   <x>1</x>
   <y>0</y>
   <z>0</z>
  </axis>
</animation>

</PropertyList>

Of course, the result of this is that the boat flips over on its side once per hour, and on its nose once per day... LOL!

(I know I only did it as a test, but I must admit, it was quite comical to see it in warp speed...)

Now, the problem is figuring out how to change the straight linear scaling accomplished by the <factor> tags, and replace it with the sine wave formula. I know the math I need to do, and I could probably script it (clumsily) in NASAL; I just don't know how to express it in XML. Even if it has to be done with Nasal, I don't know how to embed that code in this .xml file and how to "pass the variable" from the animation tag to the Nasal script and back to the resulting rotation.

Anyway, the formula will do the following:
(1) take that day-seconds value and divide by three, and then lop off the integer portion so we're left with the digits beyond the decimal point. (The result will cycle from 0 to 1 every three seconds.)
(2) scale that up by a factor of 360, so that it cycles from 0 to 360 every three seconds.
(3) take the sin of that value, the result of which will oscillate from 0 to 1, back to 0 and then to -1, and then back to 0, every three seconds.
(4) scale the result of that by 10 and put the result on the y-axis (roll) rotation.

Then the same formula will take the day-seconds PLUS ONE (for that offset) and then do the exact same thing, except in step four it will scale the result by 5 and use the x-axis (pitch) rotation.

Of course I just realized that for an even more realistic effect, instead of using 10 and 5 as the pitch variations, I could throw in a scaling factor based on the current wind speed as well. So maybe if whoever posts a reply can include that step...??

So can anyone help me with how to do that in XML?
Cheers,
-Rob.

"Retired" from FlightGear involvement as of July 2010.
viewtopic.php?f=3&t=8809
User avatar
MD-Terp
 
Posts: 2410
Joined: Wed Jan 23, 2008 7:37 am
Location: Baltimore, Maryland, USA
Callsign: N531MD, AVA0025

Re: Simple animation help...

Postby glazmax » Fri Dec 12, 2008 7:21 pm

Hi MD-Terp!

Just a quick thought:

You could use some nasal code to manipulate a property from 0 to 1 and back using sim/wave/status as property:

var wave_3sec = func {
var status = getprop("sim/wave/status");
if (status == nil){
status = 0;
setprop("sim/wave/status",0);
}
if (status == 0){
interpolate("sim/wave/status",1,3);#3 is the time in sec
} elsif (status == 1){
interpolate("sim/wave/status",0,3);#3 is the time in sec
}
settimer(wave_3sec,3.1)
}

you could embed this nasal in your xml file and start it with a listener.

Fly on,
glazmax
XXX pilot of the caribbean - bones and porters XXX
User avatar
glazmax
 
Posts: 453
Joined: Sat Jan 27, 2007 11:38 am
Location: Austria
Callsign: jettoo/OE-JTO
Version: cvs

Re: Simple animation help...

Postby MD-Terp » Fri Dec 12, 2008 8:34 pm

Glazmax, thanks for the response. I'm not exactly sure how the syntax of XML and Nasal work together so I'm not sure how to combine what I have with what you have. Also, if I follow what you are doing, the value of the interpolation is going to move back and forth on a linear scale, not following the pattern of the sine wave (i.e. slowing down as it approaches 1 or -1, and accelerating towards 0). That will work, of course, but it won't look quite as "natural."

One minor modification I've found is that I can't use /sim/time/utc/day-seconds, as that is only an integer value and therefore won't create a smooth flow from 0 to 1 -- it will jump once per second in increments of .33. I've found /sim/time/elapsed-sec to use instead.

And, I definitely decided I want to factor in wind speed as I suggested. So the formula is now:
(1) take /sim/time/elapsed-sec for the y-axis (roll) computation, (/sim/time/elapsed)+1 for the x-axis (pitch) one
(2) divide this value by three and discard the integer portion
(3) multiply the result by 360
(4) take the sine of this value
(5) take the wind speed (in knots) plus one, and multiply that by the sine result
(6) apply this value to the y-axis (roll), or divide in half and apply to the x-axis (pitch)

So, for example, if the wind speed is 0, the roll value will oscillate from -1 to 1 while pitching from -0.5 to 0.5. If the wind speed is 30, the boat will roll from -31 to 31 while pitching from -15.5 to 15.5.

Also, I am aware that many steps of the formula can be worked out in a single equation-style calculation. I'm simply thinking through the entire process as not to get lost in all of the embedded parenthesis for now.
Cheers,
-Rob.

"Retired" from FlightGear involvement as of July 2010.
viewtopic.php?f=3&t=8809
User avatar
MD-Terp
 
Posts: 2410
Joined: Wed Jan 23, 2008 7:37 am
Location: Baltimore, Maryland, USA
Callsign: N531MD, AVA0025

Re: Simple animation help...

Postby Jester » Sat Dec 13, 2008 11:02 pm

The logic is similar, though. You create a nasal function that updates a private property which in turn drives the animation.
For embedding nasal into models, you can have a look at the mpcarrier for example. Basically you use <nasal><load>...</load><unload>...</unload></nasal> and make sure you stop any loop you start.
Jester
 
Posts: 1191
Joined: Wed Feb 28, 2007 4:53 pm
Location: Hungary
Callsign: BA996,Rescue1
IRC name: Jester01
Version: GIT
OS: Debian Linux

Re: Simple animation help...

Postby glazmax » Sun Dec 14, 2008 5:00 pm

From the nasal documentation:

interpolate()

The first argument specifies a property. It can be either a string representing a global property name or a props.Node object. The remaining arguments specify pairs of value/delta-time numbers. The property is interpolated smoothly from its current value to the new value over the specified time delta, in seconds. Multiple value pairs can be used to indicate successive values or to acheive a piecewise linear approximation to a non-linear function.


Anyone knows how to address the multiple value pairs?
XXX pilot of the caribbean - bones and porters XXX
User avatar
glazmax
 
Posts: 453
Joined: Sat Jan 27, 2007 11:38 am
Location: Austria
Callsign: jettoo/OE-JTO
Version: cvs

Re: Simple animation help...

Postby MD-Terp » Mon Dec 15, 2008 1:24 pm

Well, actually I think I understand what it's saying, Glazmax.

What I am gathering from this discussion is that there is not a simple sine-wave calculation function in Nasal. So what I need to do, in order to have the appearance of a non-linear movement, is to use either Trig (somehow computing the triangle lengths and dividing height by hypotenuse) or Calc (what the paragraph sounded like it was describing, i.e. making the acceleration rate a constant or linearly-derived value and then applying that value to the location/rotation variable using the delta-t) and create a multi-step function to do the computation.

Well, the math part isn't really the part I need the help with... it's the syntax.

I am a little disappointed to find that I can't just compute the sine, but that's not the bigger issue. Jester mentioned <load> and <unload> tags, and Glazmax mentioned setting a listener, and I need pointers to references I can use to help figure out what those are for and how to structure them. All of the resources I've been able to find so far have been a bit over my head. But I'll keep trying.
Cheers,
-Rob.

"Retired" from FlightGear involvement as of July 2010.
viewtopic.php?f=3&t=8809
User avatar
MD-Terp
 
Posts: 2410
Joined: Wed Jan 23, 2008 7:37 am
Location: Baltimore, Maryland, USA
Callsign: N531MD, AVA0025

Re: Simple animation help...

Postby Jester » Mon Dec 15, 2008 2:56 pm

Of course you can calculate sine :) There is a math.sin function in nasal for that. As I said, create a nasal loop from the <load> element that calculates the sine and puts it in a property. Then use that property in the animation.

Code: Select all
<nasal>
    <load>
        var loopid = 1;
        var loop = func(id) {
            if (id != loopid) return;
            setprop("/path/to/anim/prop", math.sin(getprop("/sim/time/elapsed-sec")));
            settimer(func { loop(id); }, 0);
        }
        settimer(func { loop(loopid); }, 0);
    </load>
    <unload>
        # stop loop
        loopid += 1;
    </unload>
</nasal>
Jester
 
Posts: 1191
Joined: Wed Feb 28, 2007 4:53 pm
Location: Hungary
Callsign: BA996,Rescue1
IRC name: Jester01
Version: GIT
OS: Debian Linux

Re: Simple animation help...

Postby MD-Terp » Mon Dec 15, 2008 9:23 pm

Ah! That was exactly what I needed. There's a little more mathematical manipulation that needs to be done (as described earlier) but I can probably work that out myself. Thanks for the more specific example, Jester.
Cheers,
-Rob.

"Retired" from FlightGear involvement as of July 2010.
viewtopic.php?f=3&t=8809
User avatar
MD-Terp
 
Posts: 2410
Joined: Wed Jan 23, 2008 7:37 am
Location: Baltimore, Maryland, USA
Callsign: N531MD, AVA0025

Re: Simple animation help...

Postby MD-Terp » Mon Dec 15, 2008 11:53 pm

I GOT IT I GOT IT I GOT IT I GOT IT I GOT IT!!!!!

Thank you so much, Jester!

Please look this over and see if there is anything I need to do in order to ensure this is compliant with all coding standards. In particular I was not at all sure what properties to use for the boats' roll and pitch so I took a stab. It works fine, but since I am not a coder involved in the core project, I don't know if the properties I selected fit into the overall scheme of things the way they should.

Also, you'll see that I've split up the calculation into three different functions instead of one giant one. This was done at first just for ease of debugging; however, also, when I tried combining the calculation into a single line within the "setprop", I kept running into parse errors. I imagine this was just me throwing a parenthesis somewhere in the process but I tried it twice to no avail so I gave up and left it as-is. I have a feeling that the result is not as processor-load-efficient, so if anyone here wants to take a stab at optimizing it, please do.

Of course, you guys don't have the boat model yet. Well that's because it's not finished. I need to adjust the centerpoint (particularly, it's sitting too low in the water), and put the police logo on it yet, before I can send it along. But for now if you want to look at it, just attach it to another boat model or even a building or something. Try it with different values for wind speed. It's a pretty cute effect. :)

Also, in case you are curious, you'll notice that I don't use "3" as the cycle time anymore; I use a slightly lower value for the roll cycle, and a slightly larger one for the pitch cycle. These numbers are arbitrary and serve only to make the cycles appear a little more "random" and not synchronized. For larger vessels, you'll want to increase these values, as the oscillation time will be longer (slower).

Finally, a question -- I don't at all know why I have to use a factor of 6.25 to make the sine computation work correctly. I was under the impression that a full cycle of sine, from 0 to 1 to 0 to -1 and back to 0, was supposed to happen for input values of 0 to 360. However, this sine seems to do that in 6.25 and I only figured out the correct value after much trial and error and several hours of seeing my boats rock at warp-speed. Any ideas? (EDIT: Holy crap, I'm an idiot. I bet if I replace that 6.25 with 2 * math.pi, I'll get an even smoother animation... am I on track now? Geeze. Sorry.)

Anyway, here's the code...

Code: Select all
<?xml version="1.0"?>
<PropertyList>
<path>balt-co-police-boat.ac</path>

<animation>
  <type>rotate</type>
  <property>/ai/models/kmtn-boats/roll</property>
  <factor>0.25</factor>
  <axis>
   <x>0</x>
   <y>1</y>
   <z>0</z>
  </axis>
</animation>

<animation>
  <type>rotate</type>
  <property>/ai/models/kmtn-boats/pitch</property>
  <factor>0.125</factor>
  <axis>
   <x>1</x>
   <y>0</y>
   <z>0</z>
  </axis>
</animation>

<nasal>
    <load>
        var loopid = 1;
        var loop = func(id) {
            if (id != loopid) return;

            var rollcyclesec = 2.55;
            var pitchcyclesec = 3.08;

            var rolltimer = getprop("sim/time/elapsed-sec")/rollcyclesec - int(getprop("sim/time/elapsed-sec")/rollcyclesec);
            var rollwave = math.sin(6.25*rolltimer);
            var rolldepth = rollwave*(getprop("/environment/wind-speed-kt")+1);
            setprop("/ai/models/kmtn-boats/roll",rolldepth);

            var pitchtimer = (getprop("sim/time/elapsed-sec")+1)/pitchcyclesec - int((getprop("sim/time/elapsed-sec")+1)/pitchcyclesec);
            var pitchwave = math.sin(6.25*pitchtimer);
            var pitchdepth = pitchwave*(getprop("/environment/wind-speed-kt")+1);
            setprop("/ai/models/kmtn-boats/pitch",pitchdepth);
           
            settimer(func { loop(id); }, 0);
        }
        settimer(func { loop(loopid); }, 0);
    </load>
    <unload>
        # stop loop
        loopid += 1;
    </unload>
</nasal>

</PropertyList>
Cheers,
-Rob.

"Retired" from FlightGear involvement as of July 2010.
viewtopic.php?f=3&t=8809
User avatar
MD-Terp
 
Posts: 2410
Joined: Wed Jan 23, 2008 7:37 am
Location: Baltimore, Maryland, USA
Callsign: N531MD, AVA0025

Re: Simple animation help...

Postby Jester » Tue Dec 16, 2008 4:52 pm

HE GOT IT! :mrgreen:

You might want to consider what will happen for multiple instances of the boat, if applicable.
For more efficiency, you should avoid getprop/setprop. Instead, get a hold of the nodes before the loop, and use setValue/getValue in the loop.
And of course you are right about the PI thing, the sin function uses radians.
Jester
 
Posts: 1191
Joined: Wed Feb 28, 2007 4:53 pm
Location: Hungary
Callsign: BA996,Rescue1
IRC name: Jester01
Version: GIT
OS: Debian Linux

Re: Simple animation help...

Postby MD-Terp » Thu Dec 18, 2008 12:29 am

Jester wrote:You might want to consider what will happen for multiple instances of the boat, if applicable.

I have two present in my enhanced (WIP) KMTN scenery -- they both rock in sync with one another.
Jester wrote:For more efficiency, you should avoid getprop/setprop. Instead, get a hold of the nodes before the loop, and use setValue/getValue in the loop.

Ugh. Advice taken, but example me please, if you have a moment to do so.
Jester wrote:And of course you are right about the PI thing, the sin function uses radians.

Yeah. Hit myself on the head when I deduced the (estimated) correct constant of 6.25 and it still took me an hour of doing non-FG related stuff to ponder the significance. Is it obvious that it's been a long time since I took trig? LOL.
Cheers,
-Rob.

"Retired" from FlightGear involvement as of July 2010.
viewtopic.php?f=3&t=8809
User avatar
MD-Terp
 
Posts: 2410
Joined: Wed Jan 23, 2008 7:37 am
Location: Baltimore, Maryland, USA
Callsign: N531MD, AVA0025

Re: Simple animation help...

Postby Jester » Thu Dec 18, 2008 2:29 am

MD-Terp wrote:I have two present in my enhanced (WIP) KMTN scenery -- they both rock in sync with one another.

Exactly. I don't see an easy way around that. On the other hand, if they are in sync anyway, then no need for 2 loops. No easy way around that either. Guess we'll have to live that for the moment. (Unless such models get their own property branch as AI/MP models do?)

MD-Terp wrote:
Jester wrote:For more efficiency, you should avoid getprop/setprop. Instead, get a hold of the nodes before the loop, and use setValue/getValue in the loop.

Ugh. Advice taken, but example me please, if you have a moment to do so.

For example, instead of writing
Code: Select all
var rolldepth = rollwave*(getprop("/environment/wind-speed-kt")+1);

You could write:
Code: Select all
var wind_node = props.globals.getNode("/environment/wind-speed-kt");
...
var loop = func(id) {
...
var rolldepth = rollwave*(wind_node.getValue()+1);
...
}


So the time consuming node lookup is only done once during load, and in the loop the node is directly queried. Similarly for setValue().
Jester
 
Posts: 1191
Joined: Wed Feb 28, 2007 4:53 pm
Location: Hungary
Callsign: BA996,Rescue1
IRC name: Jester01
Version: GIT
OS: Debian Linux

Re: Simple animation help...

Postby MD-Terp » Sun Dec 21, 2008 8:18 am

Boy, you have a way of making the simple more complex. LOL.

I gave it a shot -- of course, I had to guess at some of the syntax, but miraculously on the first try I checked the boats and they were still rockin'. It may have been my imagination (because I know you would never steer me wrong, Jester, LOL!), but it even felt to me like there were a little more FPS there, too.

Here's what I wound up with and you and others can double-check. But since it works, didn't give any errors or warnings, and seems to follow your optimization suggestion, I'm starting the packaging and distribution process already. If there are some corrections or other suggestions, please send them along promptly!

Here are the contents of balt-co-police-boat.xml:
Code: Select all
<?xml version="1.0"?>
<PropertyList>
<path>balt-co-police-boat.ac</path>

<animation>
  <type>rotate</type>
  <property>/ai/models/kmtn-boats/roll</property>
  <factor>0.25</factor>
  <axis>
   <x>0</x>
   <y>1</y>
   <z>0</z>
  </axis>
</animation>

<animation>
  <type>rotate</type>
  <property>/ai/models/kmtn-boats/pitch</property>
  <factor>0.125</factor>
  <axis>
   <x>1</x>
   <y>0</y>
   <z>0</z>
  </axis>
</animation>

<nasal>
    <load>
        var wind_node = props.globals.getNode("/environment/wind-speed-kt");
        var time_node = props.globals.getNode("/sim/time/elapsed-sec");
        var roll_node = props.globals.getNode("/ai/models/kmtn-boats/roll");
        var pitch_node = props.globals.getNode("/ai/models/kmtn-boats/pitch");
        var loopid = 1;
        var loop = func(id) {
            if (id != loopid) return;

            var rollcyclesec = 2.55;
            var pitchcyclesec = 3.08;

            var rolltimer = time_node.getValue()/rollcyclesec - int(time_node.getValue()/rollcyclesec);
            var rollwave = math.sin(2*math.pi*rolltimer);
            roll_node.setValue(rollwave*(wind_node.getValue()+1));

            var pitchtimer = (time_node.getValue()+1)/pitchcyclesec - int((time_node.getValue()+1)/pitchcyclesec);
            var pitchwave = math.sin(2*math.pi*pitchtimer);
            pitch_node.setValue(pitchwave*(wind_node.getValue()+1));
           
            settimer(func { loop(id); }, 0);
        }
        settimer(func { loop(loopid); }, 0);
    </load>
    <unload>
       loopid += 1;
    </unload>
</nasal>

</PropertyList>
Cheers,
-Rob.

"Retired" from FlightGear involvement as of July 2010.
viewtopic.php?f=3&t=8809
User avatar
MD-Terp
 
Posts: 2410
Joined: Wed Jan 23, 2008 7:37 am
Location: Baltimore, Maryland, USA
Callsign: N531MD, AVA0025

Re: Simple animation help...

Postby MD-Terp » Mon Dec 22, 2008 7:37 am

MD-Terp wrote:I have two present in my enhanced (WIP) KMTN scenery -- they both rock in sync with one another.

Jester wrote:Exactly. I don't see an easy way around that.

Holy crap Jester, are you and I both dense? I was conversing via e-mail to VicMar about how to do a whole harbor of boats, and it just came to me. The solution is really not that hard.

If you have:
pier.ac
boat1.ac
boat2.ac
boat3.ac
...
boat9.ac

Why not create a single "harbor.xml" which, instead of providing a single pitch/roll animation for one boat, defines relative offset positions for the pier and all boats, and then calculates (with ONE LOOP!) the correct pitch/roll/elevation offsets for ALL of the boats? You'd start with the time/wind-based calculation, but with additional algorithmic steps based on the relative lateral positions of the boats. But you'd get them acting in concert instead of in unison.

It's not as elegant as a single XML which takes the wind/time calculation and interpolates it universally by lat/lon (which is what I think you and I were initially dreaming of), but it's elegant, simple, efficient, and completely effective for the visual effect we were both envisioning, don't you think?
Cheers,
-Rob.

"Retired" from FlightGear involvement as of July 2010.
viewtopic.php?f=3&t=8809
User avatar
MD-Terp
 
Posts: 2410
Joined: Wed Jan 23, 2008 7:37 am
Location: Baltimore, Maryland, USA
Callsign: N531MD, AVA0025

Next

Return to Scenery

Who is online

Users browsing this forum: No registered users and 12 guests