FIXED - Floating in water

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
Eclectus
Posts: 20
Joined: Sat Feb 04, 2012 11:22 pm

FIXED - Floating in water

Post by Eclectus »

Greetings :)

I've had a look but not seen anything posted related to floating in water/swimming. I would like to cause a character to smoothly float in water, so that a portion of them generally visible (just like real swimming, I don't expect it to be visible 100% of the time, but just the majority). So far I've tried applying an upward impulse (Body.Mass * timestep * -gravity), this kind of makes them bob in the water. I may be able to make this smoother by scaling the impulse by how deep they are in the water. Another way would be to scale/invert their mass by their depth in the water.

Thought I'd post about this as it seems like an interesting topic, that others may encounter too.

On a tangent, why differentiate between windows and Xbox like this? Vector3 is a value type so it shouldn't matter, but I suspect you know something about the 360 JIT compiler I don't... :?

Code: Select all

#if WINDOWS
                Vector3 offset;
#else
                Vector3 offset = new Vector3();
#endif
Once again thanks for BEPU, it is a joy to use.
Last edited by Eclectus on Thu Oct 18, 2012 12:56 pm, edited 1 time in total.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Floating in water

Post by Norbo »

The FluidVolume could be used. You can specify per-object behavior with the DensityMultipliers dictionary so that the character floats differently than, say, a crate of similar mass and volume.

The FluidVolume is pretty heavy though. If you just need something really simple and just for the character alone, then making use of the character's consistent geometry could help. You can compute the exact amount of volume beneath the water plane using the plane equation, cylinder height, radius, and position (or you could just simplify the math ever so slightly by considering it to be a box). From there, you can apply a proper buoyancy force according to the density of the 'fluid' and the displaced volume. You'd probably want to damp the character motion a bit too to stop endless bobbing.
On a tangent, why differentiate between windows and Xbox like this? Vector3 is a value type so it shouldn't matter, but I suspect you know something about the 360 JIT compiler I don't...
The former results in a compiler error on the Xbox360. If I remember correctly, it's because the IDE-referenced xbox assemblies are actually stand-ins. Vector3 and a few other struct types have an extra private field in these stand-ins which cannot be initialized without explicitly calling a constructor.

So, this works on the PC, but not the Xbox:

Code: Select all

        void Test(out Vector3 v)
        {
            v.X = 1;
            v.Y = 1;
            v.Z = 1;
        }
The use of an ugly preprocessor directive rather than just using the Xbox360 way for both is a result of compulsive, sometimes misguided, micro-optimizing. Unfortunately, I was just never motivated enough to actually check the disassembly to see if there was any difference at all when default constructor is called explicitly on the PC. But now's a good a time as any to find out!

It's time for an Adventure in (Dis)assembly!

Everything below will be compiled in release mode with optimizations enabled.

Here's the IL of the above Test method:

Code: Select all

.method private hidebysig instance void Test([out] valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector3& v) cil managed
{
    .maxstack 8
    L_0000: ldarg.1 
    L_0001: ldc.r4 1
    L_0006: stfld float32 [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector3::X
    L_000b: ldarg.1 
    L_000c: ldc.r4 1
    L_0011: stfld float32 [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector3::Y
    L_0016: ldarg.1 
    L_0017: ldc.r4 1
    L_001c: stfld float32 [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector3::Z
    L_0021: ret 
}
Now, we'll modify the Test method so that it can compile on the Xbox360:

Code: Select all

        void Test(out Vector3 v)
        {
            v = new Vector3();
            v.X = 1;
            v.Y = 1;
            v.Z = 1;
        }
And here's the disassembly for comparison:

Code: Select all

.method private hidebysig instance void Test([out] valuetype [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector3& v) cil managed
{
    .maxstack 8
    L_0000: ldarg.1 
    L_0001: initobj [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector3
    L_0007: ldarg.1 
    L_0008: ldc.r4 1
    L_000d: stfld float32 [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector3::X
    L_0012: ldarg.1 
    L_0013: ldc.r4 1
    L_0018: stfld float32 [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector3::Y
    L_001d: ldarg.1 
    L_001e: ldc.r4 1
    L_0023: stfld float32 [Microsoft.Xna.Framework]Microsoft.Xna.Framework.Vector3::Z
    L_0028: ret 
}
So, at the IL level, using an explicit empty constructor is not optimized out. The initobj instruction goes through and sets the default values for fields in the object before they are all set once again. This lack of optimization isn't very surprising; this stage should handle straightforward constant folding and similar activities, but the JIT is left to perform many optimizations.

In comparison, the first version without an explicit empty constructor just loads the three fields directly without first initializing them. This is potentially (mostly meaninglessly) faster.

Of course, this says nothing about what the JIT will do. The two methods could resolve to the exact same native instructions. This can be tested by actually examining the final native instructions.

For the version without an empty constructor, the method was inlined into a few instructions:

Code: Select all

00000273  fld1 
00000275  fstp        dword ptr [ebp-48h] 
00000278  fld1 
0000027a  fstp        dword ptr [ebp-4Ch] 
0000027d  fld1 
0000027f  fstp        dword ptr [ebp-50h] 
For the version with an empty constructor, something interesting happens:

Code: Select all

00000273  fld1 
00000275  fstp        dword ptr [ebp-48h] 
00000278  fld1 
0000027a  fstp        dword ptr [ebp-4Ch] 
0000027d  fld1 
0000027f  fstp        dword ptr [ebp-50h] 
It's identical! Good job, JIT! :)

Let's try one more thing. I'll throw in a completely worthless loop to try to confuse the JIT. Here's what the method looks like now:

Code: Select all

        void Test(out Vector3 v)
        {
            //v = new Vector3();
            v.X = 1;
            v.Y = 1;
            v.Z = 1;
            for (int i = 0; i < 100; i++)
            {
                v.X = 1;
                v.Y = 1;
                v.Z = 1;
            }
        }
A smartypants compiler would probably fold the loop into nonexistence. Let's see what the JIT does:

Code: Select all

0000000b  fld1 
0000000d  fstp        dword ptr [esi] 
0000000f  fld1 
00000011  fstp        dword ptr [esi+4] 
00000014  fld1 
00000016  fstp        dword ptr [esi+8] 
00000019  xor         eax,eax 
0000001b  fld1 
0000001d  fstp        dword ptr [esi] 
0000001f  fld1 
00000021  fstp        dword ptr [esi+4] 
00000024  fld1 
00000026  fstp        dword ptr [esi+8] 
00000029  inc         eax 
0000002a  cmp         eax,64h 
0000002d  jl          0000001B 
0000002f  pop         esi 
00000030  pop         ebp 
00000031  ret 
Oops, the loop's still there and the method is no longer inlined.

How about if the empty constructor is uncommented?

Code: Select all

0000000c  mov         edi,esi 
0000000e  xor         eax,eax 
00000010  xorps       xmm0,xmm0 
00000013  movq        mmword ptr [edi],xmm0 
00000017  add         edi,8 
0000001a  stos        dword ptr es:[edi]
0000001b  fld1 
0000001d  fstp        dword ptr [esi] 
0000001f  fld1 
00000021  fstp        dword ptr [esi+4] 
00000024  fld1 
00000026  fstp        dword ptr [esi+8] 
00000029  fld1 
0000002b  fstp        dword ptr [esi] 
0000002d  fld1 
0000002f  fstp        dword ptr [esi+4] 
00000032  fld1 
00000034  fstp        dword ptr [esi+8] 
00000037  inc         eax 
00000038  cmp         eax,64h 
0000003b  jl          00000029 
0000003d  pop         esi 
0000003e  pop         edi 
0000003f  pop         ebp 
00000040  ret 
Those first 6 instructions weren't there before. They correspond to the v = new Vector3() line. You tried your best, JIT :( (Of course, these results from the JIT on my machine do not necessarily match the results of a JIT on another platform. Additionally, future JIT implementations could completely change the story.)

So, from these results, it appears that avoiding explicit empty constructors on value types can, under certain circumstances, sometimes speed things up a tiny amount. Whether or not that is worth the ugliness of preprocessor directives is another question :)

[An interesting side note: see that 'xorps' and 'movq' at the top of the disassembly? The JIT gets some flak for not making use of some processor extensions that could speed up math routines, among other things. While it's true that the current .NET JIT doesn't try to do as many optimizations as its C/C++ counterparts, 'movq' is, in fact, an MMX SIMD instruction and 'xorps' is an SSE SIMD instruction! There's no theoretical limitation on the JIT's ability to make use of more such instructions and compiler tricks; it's an exciting thought that a new JIT could offer some significant numerical speedups without many code changes. In fact, WP7 did something quite similar with SIMD/ARM-NEON instructions, just on a more restricted level. On the other hand, certain rumors suggest there may be other paths to extra managed language speediness :)]
Eclectus
Posts: 20
Joined: Sat Feb 04, 2012 11:22 pm

Re: Floating in water

Post by Eclectus »

A truly Epic reply - :)

Floating - I'm only going to need it for a character, so I'm leaning towards your 2nd suggestion of computing the exact amount of volume beneath the water using box height. I would have gone with a cylinder, but since the cylinder I use is deliberately larger then the character and even then, the character himself is not a cylinder, using a box to approximate his volume seems equally valid - cheers! With the character motion, I'll adjust the linear dampening yes, I think this is what your referring to?

Vector3 - Interesting results, I can only describe your process as thorough, and a good example of the Scientific Method :) I spend most of optimization time on graphics related issues - it makes me wish that I opened up the disasembly a bit more. Very thorough, including the loop like this.

Cheers
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Floating in water

Post by Norbo »

With the character motion, I'll adjust the linear dampening yes, I think this is what your referring to?
That's one option. Modifying the character body's LinearDamping property would also slow the character's movement on land, though, so it would have to be reset upon exiting the water. Damping could also be scaled dynamically depending on the submerged volume. The damping impulses can be applied externally alongside the buoyancy force if you find that you'd rather control it directly.
Eclectus
Posts: 20
Joined: Sat Feb 04, 2012 11:22 pm

Re: Floating in water

Post by Eclectus »

ps. Those rumors are rather interesting :O Thanks for sharing this. Do you use Twitter?
Eclectus
Posts: 20
Joined: Sat Feb 04, 2012 11:22 pm

Re: Floating in water

Post by Eclectus »

Norbo wrote:Modifying the character body's LinearDamping property would also slow the character's movement on land, though, so it would have to be reset upon exiting the water. Damping could also be scaled dynamically depending on the submerged volume. The damping impulses can be applied externally alongside the buoyancy force if you find that you'd rather control it directly.
I'm currently looking at attenuating the dampening only when the character is in water, to provide fluid resistance. Will also be attenuating the buoyancy force, yes. Together with some tweaking I should be able to get it to work in conjunction with the waves. Will post a video when its done :)
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Floating in water

Post by Norbo »

ps. Those rumors are rather interesting :O Thanks for sharing this. Do you use Twitter?
I do not have a twitter account... yet. I'm probably going to need to make one once we go public with some other stuff. At the moment, I just don't have much to yell into the cavernous void of social media besides maybe a "hey download this new version!" every once in a while :)
Eclectus
Posts: 20
Joined: Sat Feb 04, 2012 11:22 pm

Re: Floating in water

Post by Eclectus »

I suspect you'll find a lot of followers on Twitter once you do. New version announcements are exciting :)
Eclectus
Posts: 20
Joined: Sat Feb 04, 2012 11:22 pm

Re: FIXED - Floating in water

Post by Eclectus »

The swimming is working well and feels good. I'll hold off posting a video until our animation matches it.
Eclectus
Posts: 20
Joined: Sat Feb 04, 2012 11:22 pm

Re: FIXED - Floating in water

Post by Eclectus »

Here is an Astralis video featuring the swimming, and showing BEPU in action :)

viewtopic.php?f=9&t=1035&start=30#p11446
Post Reply