Jump to content

[SOLVED] WPF ViewCube


photo

Recommended Posts

Hi Kzharov,

ViewCube is a custom widget for Editor and not available via API.

It's pretty much the mesh of cube drawn inside WidgetCanvas + some additiona logic that handles the intersection and input and rotates camera accordingly. Right now I can say if it would ever be available through the API, sorry.

Thanks!

How to submit a good bug report
---
FTP server for test scenes and user uploads:

Link to comment

OK, thank you. I tryed to do other way.

I created ViewCube  usind WPF graphics. It works fine, but I can not display my cube overlaying Unigine Viewport. 

If I understand correctly, this is because of the use of WindowsFormsHost in WPF window. Here is thread about it - https://stackoverflow.com/questions/9920480/windowsformshost-is-always-the-most-top-from-wpf-element.

Can you give me a solution of this problem?

vc.png

Edited by kzaharov
Link to comment

Hi,

Sorry, but we don't know any solution to the WPF z-order problem.

We can, however, provide you with a sample of viewcube implementation in terms of Unigine widgets.
Basically, it's the same viewcube that you see in the editor.
Please, check the attachment.

It's somewhat non-trivial, but if you'll manage to integrate it into your project, we won't mind.

  • Like 1
Link to comment

Half succeed.

I translated your code to C# and integated it into wpf test project (project attached to this message).

ViewCube rotates corresponding players view direction, BUT mouse click on view cube doesn't work. 

For some reason getMouseButton method always return 0 and getMouseButtonState doesn't handle pressing the mouse button (look at line 277 in ViewCube.cs).

Why it doesn't work? Is this related to using WPF?

ViewCubeTestProj.zip

vc.png

Link to comment

Hi,

I've investigated your problem and found a reason.
It's a bug in the C# Unigine.App wrapper class: some of its methods, including getMouseButtonState() don't work correctly.
We, of course, will fix it in the nearest SDK release, but meanwhile, you'll have to avoid using it.

Try replacing all App::get() occurrences in your application with reference to UnigineApp instance.
This way you will access overridden App methods directly, bypassing problematic wrapper code.
To be more concrete, replace

App.get().getMouseButtonState(App.BUTTON_LEFT | App.BUTTON_DCLICK)

with something like

unigineControl.UnigineApp.getMouseButtonState(App.BUTTON_LEFT | App.BUTTON_DCLICK)

 

  • Like 1
Link to comment

I would like to draw the attention of Unigine developers to two problems that I faced modificating ViewCube for my needs. Maybe it's small flaws in the .NET API.

1. Method getWorldRotation for PlayerActor works not like for other player types. It looks like it only takes into account the rotation in the horizontal plane.

2. It seems that lookAt method  not takes into account up parameter.

I was able to work around these issues

Link to comment

I attached test project to this message.

1. If you edit file data\editor2\resources\template.usc replacing PlayerSpectrator by PlayerActor, you will see that view cube allways rotated the top face to the camera (look at first attached image). That's because method getWorldRotation for PlayerActor works not like for other player types.

2. Look ViewCube.cs line 243 - 246. Variable vec3 up does not affect the operation of the program at all (you can even delete lines 243-244). lookAt method  ignores up parameter.

 

I also added compass to ViewCube (I used the textures you gave me).

After many attempts I was able to specify correct texture coordinates for compass vertexes (I'm new at this).  But it turned out that textures are mirrored (look at second attached image). So I had to make these polygons two-sided and rotate them 180 degrees (ViewCube.cs line 188, 212 - 213.). I think this solution is not very good. I'd like to keep them one-sided.

Can you give me any other solution?

ViewCubeTestProj.zip

playerActor.png

textures_mirrored.png

Edited by kzaharov
Link to comment

Hi,

First, lookAt definitely uses vector "up", maybe you just don't see how.
There is an infinite number of matrices that make your game object "look at" some direction.
Vector "up" helps you choose one of them.
Invert it, and you'll see what happens:

up = -up; // Rotates viewcube upside-down.
quat rot = new quat(lookAt(vec3.ZERO, direction, up));

The code on lines 243 and 244

if (direction.Equals(new vec3(0, 0, 1)))
    up = new vec3(0, 1, 0);

handles a special case when vectors "up" and  "direction" are collinear.
When that's true, you have to choose another vector.
Actually, this code is incorrect, but we'll fix this in a minute.


As for PlayerActor, it's just not correct to observe or modify its view direction through the node transformation-related methods.
It works for PlayerDummy and PlayerSpectator, but it actually hasn't to.
PlayerActor consists of a physical capsule, that provides collision to the scene objects, camera attached to its "head" and a logic that responds to the controls and makes a player walk, run, jump, etc.
And when we work with WorldRotation, WorldPosition or WorldTransform, we manipulate a node transformation matrix, not the camera modelview.
What we should do instead is to work with getViewDirection and setViewDirection methods.
This way we can obtain the correct behavior of the viewcube for all player types.
It's our fault that we overlooked that in our viewcube implementation.

Based on the sample you gave us I suggest you the following modifications:

public void update()
{
    if (!IsVisible)
        return;

    Player player = Game.get().getPlayer();
    rotation_ = get_direction_quat(player.getViewDirection());

    ...
}

get_direction_quat looks like this:

private static quat get_direction_quat(vec3 direction)
{
    vec3 up = vec3.UP;
    if (MathLib.abs(MathLib.dot(direction, up) - 1.0f) < MathLib.EPSILON)
        up = vec3.BACK;

    return new quat(MathLib.lookAt(vec3.ZERO, direction, up));
}

You may implement get_polygon_rot in terms of get_direction_quat:

private static quat get_polygon_rot(vec3 pos)
{
    return get_direction_quat(MathLib.normalize(-pos));
}

And apply_rotation now looks like this:

private void apply_rotation(quat rotation)
{
    Player player = Game.get().getPlayer();
    player.setViewDirection(vec3.DOWN * rotation);
}

You may notice that I removed unnecessary inversions and conjugations of quaternions, a good thing too.
I'll make similar changes in the editor viewcube implementation.

Link to comment

As for your compass geometry, I suggest you to keep polygons transformation as is, but to flip the UV coordinates instead.
That is something you often have to do when you work with the DirectX.

Consider the following modifications:

private const float COMPASS_UV_ROTATE = 45.0f; // Degrees
private const float COMPASS_UV_SCALE = 0.32f;

...

private void AddCompassPolygonByMesh(string meshPath, vec3 poligonPos, float uvXTranslation, float uvYTranslation)
{
    Mesh mesh = new Mesh(meshPath);

    int surface = 0;
    cube_polygon_positions_.Add(poligonPos);
    cube_polygon_rotations_.Add(get_polygon_rot(poligonPos));

    mesh.remapCVertex(surface);
    int polygon = cube_canvas_.addPolygon();

    for (int i = 0; i < mesh.getNumVertex(surface); ++i)
    {
        vec3 point = mesh.getVertex(i, surface);

        cube_canvas_.addPolygonPoint(polygon, point);

        mat3 flipY = MathLib.rotate3(new vec3(1, 0, 0), 180.0f);
        mat3 uvRotate = MathLib.rotate3(new vec3(0, 0, 1), COMPASS_UV_ROTATE);
        mat3 uvScale = MathLib.scale3(new vec3(COMPASS_UV_SCALE));
        vec2 uvTranslate = new vec2(uvXTranslation, uvYTranslation);
        vec2 uv = uvTranslate + (uvScale * uvRotate * flipY * new vec2(point.x, point.y));

        cube_canvas_.setPolygonTexCoord(polygon, uv);
    }

    for (int i = 0; i < mesh.getNumCIndices(surface); ++i)
    {
        int index = mesh.getCIndex(i, surface);
        cube_canvas_.addPolygonIndex(polygon, index);
    }
}

...

AddCompassPolygonByMesh(MESH_COMPASS_EAST, new vec3(3, 0, 0), 0.6451f, 0.3957f);
AddCompassPolygonByMesh(MESH_COMPASS_NORTH, new vec3(0, 3, 0), 0.6447f, 0.3493f);
AddCompassPolygonByMesh(MESH_COMPASS_WEST, new vec3(-3, 0, 0), 0.3496f, 0.7301f);
AddCompassPolygonByMesh(MESH_COMPASS_SOUTH, new vec3(0, -3, 0), 0.3517f, 0.0354f);

 

Link to comment

Thank you for your guidance!

18 hours ago, ded said:

It's our fault that we overlooked that in our viewcube implementation.

Don't blame yourself!))) I already was able to work around these issues using other methods of API. Although your solution seems to me more concise.

18 hours ago, ded said:

flip the UV coordinates

19 hours ago, ded said:

mat3 flipY = MathLib.rotate3(new vec3(1, 0, 0), 180.0f);

Thank you very much for this recommendation! It broadened my understanding.

Link to comment
  • silent changed the title to [SOLVED] WPF ViewCube
×
×
  • Create New...