Physics Helper in WPF

Jan 23, 2009 at 6:11 PM
Hi!
Will Physics Helper work in a WPF project ?

thanks
Coordinator
Jan 23, 2009 at 6:52 PM
Unfortunately, Silverlight and WPF are not binary compatible yet, so you will need to recompile everything under a WPF solution.

I haven't tried this yet, but I would probably tackle it like so:

1. Convert the Farseer Physics Engine into a new WPF Class Library project. You can get the code for that here - http://www.codeplex.com/FarseerPhysics  (alternatively, you could probably download the XNA build for Farseer, and just reference the Farseer project code from that distribution).

2. Create a new WPF User Control Library and add in all of the code and User Controls from the PhysicsHelper library

3. Create a new WPF application and add in the code from "Demo1" (or another simple demo), and give it a try.



Jan 23, 2009 at 8:08 PM
Hi!
I already tried the FarSeer Engine with WPF. It starts ok but then I have some strange problems.
I'll give it a try with the Physics Helper.

bye,
Tiago
Jan 27, 2009 at 3:51 PM
Hi!
 PhysicsHelper will not compile with PhysicsEngine 2.01 because of a breaking change in the new version:

Error    19    'FarseerGames.FarseerPhysics.Collisions.Geom' does not contain a definition for 'Collision' and no extension method 'Collision' accepting a first argument of type 'FarseerGames.FarseerPhysics.Collisions.Geom' could be found (are you missing a using directive or an assembly reference?)    C:\Development\PhysicsHelperWPF\PhysicsHelperWPF\PhysicsSprite.xaml.cs    165    28    PhysicsHelperWPF

It will compile with PhysicsEngine 1.0.6 but then there is an error trying to load the System Assembly when it runs:
Simulator = new PhysicsSimulator(new Vector2(GravityHorizontal, GravityVertical));
+        exc    {"Could not load file or assembly 'System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' or one of its dependencies. The system cannot find the file specified.":"System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e"}    System.Exception {System.IO.FileNotFoundException}

this is because PhysicsEngine1.0.6 is dependent on Silverlight assemblies (but it does not appear in reflector) !?
So, we would need to put  PhysicsHelper working with PhysicsEngine2.01 to use the Helper in WPF ... !?

Tiago Andrade e Silva

Coordinator
Jan 27, 2009 at 4:54 PM
Are you sure you have the latest download (1.0.0.4) of the Physics Helper?

1.0.0.4 was updated to use Farseer 2.0 - but the previous version (1.0.0.3) used Farseer 1.x.
Jan 28, 2009 at 1:46 PM
Hi!
I was using 1.0.0.3. Already upgraded :)
Now I have a problem in the migration to WPF because FindElementsInHostCoordinates does not exist in WPF.
DO you know if InputHitTest does the same thing ?

//WPF Version 
List<UIElement> hits = element.InputHitTest(ptTest) as List<UIElement>
//SL Version 
//List<UIElement> hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(ptTest, element) as List<UIElement>
 


Coordinator
Jan 28, 2009 at 2:00 PM
My understanding is that WPF's HitTest behaves somewhat differently than FindElementsInHostCoordinates, but only under certain circumstances.

Here is one related post - http://silverlight.net/forums/t/44026.aspx
Jan 28, 2009 at 5:35 PM
must be different ...

I'm always getting the exception
throw new ArgumentException("The UIElement " + element.GetValue(Canvas.NameProperty) + " is too small to determine outline (must be at least 3x3 pixels)");

because in
List<UIElement> hits = element.InputHitTest(ptTest) as List<UIElement>;

hits is always null :(


Jan 29, 2009 at 10:22 PM
Hi!
I would really like to put Physics Helper working in WPF so I could use it with FarSeer in Microsoft Surface. Here is a simple demo of FarSeer working in Surface :)
http://www.youtube.com/watch?v=xlZI4akPGdQ

bye,
Tiago
Coordinator
Jan 29, 2009 at 10:27 PM
Hi Tiago,

If you look at this post - http://silverlight.net/forums/t/44026.aspx

... and scroll down to the post from Yi-Lun Luo, he offers a workaround for the difference. Basically, the SL FindElementsInHostCoordinates is using the global coordinates (from the Browser Host).

I think his Matrix workaround will work, but I don't have much time right now to play around with it. Maybe you can take a swing at it, and if you run into problems I'll try to give it a go too.
Feb 2, 2009 at 4:34 PM
Hi!
I was already able to put Physics Helper working in WPF.

In BoundaryHelper.cs I had to change
List<UIElement> hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(ptTest, element) as List<UIElement>;
to
HitTestResult hits = System.Windows.Media.VisualTreeHelper.HitTest(element,ptTest);

on both places, and had to change the ptTest so it uses local coordinates and not global coordinates.
for (int x = Convert.ToInt32(left + width - 1); x > left - 1; --x)
was changed to
for (int x = Convert.ToInt32(width); x > 0; --x)
and when the point is found changed to ptFirstHit = new Point(x+left, y+top);

It works. but the problem is that in WPF the GetPointsForPath method takes 14 seconds to process the PATH used in your first demo, while in Silverlight it takes 1 second.

I was looking at the manual of Farseer 2.0 on the Text to Vertices section and it seems to have a new function that might take care of what GetPointsForPath is doing !?

//Calculate the vertices from the array

Vertices verts = Vertices.CreatePolygon(data, polygonTexture.Width, polygonTexture.Height);

any ideas ?




Coordinator
Feb 2, 2009 at 5:08 PM
Wow that is interesting that there is such a performance difference!

The Vertices.CreatePolygon method is new to Farseer 2 and I haven't tried it yet, but unfortunately I think it may be slower than the Physics Helper algorithm. Because it would require that you HitTest _every_ pixel through the entire Width and Height of your UIElement (if I understand it correctly). That is different than the PhysicsHelper algorithm which traces a path around the object instead by kind of walking around the outside of it.

I am adding functionality to the PhysicsHelper to cache the points that are determined by the algorithm, so that the overhead is only accrued once at application design time. But this might not help in every circumstance, like if the user is drawing out new elements or the application is creating new elements with different outlines.

Would this help you out? Or do you need to dynamically create new outlines at runtime?



Feb 2, 2009 at 5:13 PM
Hi!
For now I only need to do it at design time.

You said "but unfortunately I think it may be slower than the Physics Helper algorithm. Because it would require that you HitTest _every_ pixel", but isn't that what more or less what GetPointsForPath is dowing ?
I think there is a bug ? the break only exits the first for cycle. Don't you want to leave the cycle after you find the first point ?

            // get the first point
            for (int x = Convert.ToInt32(left + width - 1); x > left - 1; --x)
            {
                for (int y = Convert.ToInt32(top); y < top + height; ++y)
                {
                    Point ptTest = new Point(x, y);
                    List<UIElement> hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(ptTest, element) as List<UIElement>;
                    if (hits.Contains(element))
                    {
                        ptFirstHit = new Point(x, y);
                        break;
                    }
                }
            }
Coordinator
Feb 2, 2009 at 6:13 PM
Yeah, that's a good catch!

But I don't think that would make much of a performance improvement :( because it is only finding the first point to start the algorithm at.

The real work is done just below that code, where it calls GetNextVertex  within a "while" loop. That while loop is what is "tracing" out the outline of the path.
Coordinator
Feb 2, 2009 at 6:13 PM
How are your shapes created? Do you create them in code, or are they from XAML in your project?
Feb 2, 2009 at 9:55 PM
Hi!
My shapes are all based on XAML for the moment. I'm interested in that cache functionality. How are you implementing it ? it would be something done at design time, compile time or at the first run ?

Regarding the code for GetPointsForPath the strange thing is that in WPF it is this code portion that is taking a lot and not the GetNextVertex. At least for the path (ground) you have in your demo (that has a big width and height).
I changed the code to break when it founds the first vertice but then it stoped working and did not yet find the reason why.

I changed to something like this:
            // get the first point
            bool found=false;
            for (int x = Convert.ToInt32(left + width - 1); x > left - 1 && !found; --x)
            {
                for (int y = Convert.ToInt32(top); y < top + height; ++y)
                {
                    Point ptTest = new Point(x, y);
                    List<UIElement> hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(ptTest, element) as List<UIElement>;
                    if (hits.Contains(element))
                    {
                        ptFirstHit = new Point(x, y);
                        found=true;
                        break;
                    }
                }
            }

if you want I can share the WPF solution with you :)
Coordinator
Feb 3, 2009 at 12:55 AM

Right now, the point caching I am working on for the next release allows you to add a PointCollection list to the PhysicsController, and define the outline instead of doing the "trace algorithm." It makes a great performance improvement at startup.

I'm not sure what's wrong with your loop, I did it a bit differently below and it seems ok? It would be cool to have a WPF build of the Physics Helper library. If you would like to collaborate to put that together, let me know!

Point ptFirstHit = new Point();

bool bFoundPoint = false;

 

// get the first point

for (int x = Convert.ToInt32(left + width - 1); x > left - 1; --x)

{

    for (int y = Convert.ToInt32(top); y < top + height; ++y)

    {

        Point ptTest = new Point(x, y);

        List<UIElement> hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(ptTest, element) as List<UIElement>;

        if (hits.Contains(element))

        {

            ptFirstHit = new Point(x, y);

                     bFoundPoint = true;

            break;

        }

    }

       if (bFoundPoint) break;

}

Coordinator
Feb 4, 2009 at 1:26 AM

Tiago,

I found a similar project to yours I think - looks like they took the Physics Helper and are using it from Farseer (don't those demos look familiar?). I think they ported to WPF as well:

http://nuigroup.com/forums/viewthread/3717/

Feb 4, 2009 at 3:53 PM
Hi!
I was already able to put it working in WPF in a efficient manner (after breaking that double for loop with the bFound variable).
I would like to collaborate this WPF. How do we proceed ?

I was also able to put a sample working in Microsoft Surface, after adding multiple contact support :)
Take a look at the video: http://www.youtube.com/watch?v=J8nc6HcTuQI

The Path was Silverlight -> WPF ->Surface.

bye,
Tiago
Coordinator
Feb 4, 2009 at 6:05 PM
Tiago,

Awesome! Glad you got it working. I hope you don't mind, I blogged about it here - http://www.andybeaulieu.com/Home/tabid/67/EntryID/135/Default.aspx

I will contact you separately to see how we can integrate your changes for WPF.
Feb 5, 2009 at 8:57 AM
Edited Feb 5, 2009 at 9:53 AM
Hi Andy! glad you blogged about it :).

I'm finding some issues while trying to use the other demos in WPF/Surface.
The FindName method returns null in some occasions and in Silverlight it doesn't.
Still need to find out why this is happening.
Just asked this question on the WPF Forum http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/2abe7e3f-fadb-4ce9-a571-b616242fba7b

Feb 5, 2009 at 1:47 PM
Hi!
I solved the FindName issue by creating my function that would loop the Children collection.
After replacing the FindName for MyFindName in the problematic places the issue was solved.

in PhysicsController:  
public static FrameworkElement MyFindName (UIElementCollection parent, string name)
{
    foreach (FrameworkElement elem in parent)
    {
        string foo = elem.GetValue(Canvas.NameProperty).ToString();
        if (foo == name)  return elem;
    }
    return null;
}
Coordinator
Feb 5, 2009 at 2:19 PM
That's great!

Did you get my message (through CodePlex) about trying to integrate the two projects (Silverlight and WPF) into a single solution? I don't know if it is possible, but it would make it much easier to test and add functionality in the future.
Feb 5, 2009 at 2:21 PM
Hi!
Another issue on the WPF version was the sequence of events on the loading and initialization of components.
Physics controller got loaded and initialized before the Camera, so when the camera was initialized it registered for the initialized event of the controller that already happened, so, the camera would never be initialized.
Here is what I changed:

in PhysicsController.xaml.cs
                    static public bool userControlLoaded;
 
In CameraController.xaml.cs
                    if (PhysicsController.userControlLoaded)
                    {
                        CameraController_Initialized(null);
                    }
                    (element as PhysicsController).Initialized += new PhysicsController.InitializedHandler(CameraController_Initialized);

bye,
Tiago Andrade e Silva
Jun 4, 2009 at 9:06 AM

Hi again!

Here is a video showing the projects we developed for Microsoft Surface with FarSeer and Physics Helper :)

http://www.youtube.com/watch?v=uzpIlDIVShA

 

Bye,

Tiago Andrade e Silva