Missing Viewport Project/Unproject Methods

Apr 30, 2013 at 9:39 PM
Coordinator
May 1, 2013 at 1:57 AM
Hi TypeOverride, those are good suggestions, and I can include some functionality like that in the next commit. In the mean time, you can check out the Camera::GetWorldSpacePickRay(...) method. This does something similar, but constructs a ray instead of returning a point. Of course, then you can evaluate the ray at the near or far plane to get the point.

I think the methods you linked to above would fit better into the Camera class, since it is the owner of the projection and view matrices, as well as the viewport. Do you agree with this?
May 1, 2013 at 1:47 PM
Edited May 2, 2013 at 5:11 PM
Yes the camera class is good for this.

For helping doing that i can refer you to https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/Camera.java
There are project and unproject methods too.

One other good method is something like:
http://docs.unity3d.com/Documentation/ScriptReference/Camera.WorldToScreenPoint.html

ive made one for me myself. But it would be nice to see something like this nativly inside the camera class.
Vector2f Camera::WorldToScreenPoint(Vector3f pos) {
    Matrix4f view = this->GetCameraView()->GetViewMatrix();
    Matrix4f proj = this->GetCameraView()->GetProjMatrix();

    Matrix4f viewProj = view * proj;

    float screenWidth = this->GetCameraView()->GetViewPort(0).GetWidth();
    float screenHeight = this->GetCameraView()->GetViewPort(0).GetHeight();

    Vector4f modelViewProjPos = viewProj * Vector4f(pos.x, pos.y, pos.z, 1);

    float screenX = ((modelViewProjPos.x/modelViewProjPos.z) * (screenWidth * 0.5f)) + (screenWidth * 0.5f);
    float screenY = -((modelViewProjPos.y/modelViewProjPos.z) * (screenHeight * 0.5f)) + (screenHeight * 0.5f);
 
    return Vector2f(screenX, screenY);
}
and off course http://docs.unity3d.com/Documentation/ScriptReference/Camera.ScreenToWorldPoint.html
May 2, 2013 at 4:02 PM
Edited May 2, 2013 at 5:11 PM
Here is the "project" method (its a port from the link i gave to you above from libgdx library) it does the same as "WorldSpaceToScreenSpace" method.
Dont know which method should be used. Both gives the same result on converting world to screen position. Your decision what the common way is.

point in “world” -> modelview -> projection -> viewport -> point on “screen” (project)
const int Matrix4f::fM11 = 0;
const int Matrix4f::fM12 = 1;
const int Matrix4f::fM13 = 2;
const int Matrix4f::fM14 = 3;

const int Matrix4f::fM21 = 4;
const int Matrix4f::fM22 = 5;
const int Matrix4f::fM23 = 6;
const int Matrix4f::fM24 = 7;

const int Matrix4f::fM31 = 8;
const int Matrix4f::fM32 = 9;
const int Matrix4f::fM33 = 10;
const int Matrix4f::fM34 = 11;

const int Matrix4f::fM41 = 12;
const int Matrix4f::fM42 = 13;
const int Matrix4f::fM43 = 14;
const int Matrix4f::fM44 = 15;

Vector3f Vector3f::prj(Matrix4f matrix) {
    Matrix4f mat(matrix);

    float w = 1.0f / (this.x * mat[Matrix4f::fM14] + this.y * mat[Matrix4f::fM24] + this.z * mat[Matrix4f::fM34] + mat[Matrix4f::fM44]);
    
    return Vector3f(
        (this.x * mat[Matrix4f::fM11] + this.y * mat[Matrix4f::fM21] + this.z * mat[Matrix4f::fM31] + mat[Matrix4f::fM41]) * w, 
        (this.x * mat[Matrix4f::fM12] + this.y * mat[Matrix4f::fM22] + this.z * mat[Matrix4f::fM32] + mat[Matrix4f::fM42]) * w, 
        (this.x * mat[Matrix4f::fM13] + this.y * mat[Matrix4f::fM23] + this.z * mat[Matrix4f::fM33] + mat[Matrix4f::fM43]) * w
    );
}
void Camera::project(Vector3f& vec) {
    project(vec, 0, 0, this->GetCameraView()->GetViewPort(0).GetWidth(),  this->GetCameraView()->GetViewPort(0).GetHeight());
}

void Camera::project(Vector3f& vec, float viewportX, float viewportY, float viewportWidth, float viewportHeight) {
    Matrix4f mView = this->GetCameraView()->GetViewMatrix();
    Matrix4f mProj = this->GetCameraView()->GetProjMatrix();
    Matrix4f mViewProj = mView * mProj;

    Vector3f result = prj(vec, mViewProj);
    vec.x = result.x;
    vec.y = -result.y;
    vec.z = result.z;

    vec.x = viewportWidth * (vec.x + 1) / 2 + viewportX;
    vec.y = viewportHeight * (vec.y + 1) / 2 + viewportY;
    vec.z = (vec.z + 1) / 2;
}
Use:
Vector3f modelPos = _actor->GetNode()->Position();

Vector3f screenPos = Vector3f(modelPos);
_camera->project(screenPos);
The unproject method i dont have at the moment (point on “screen” -> viewport–1 -> projection–1 -> modelview–1 -> point in “world” (unproject))
Coordinator
May 2, 2013 at 10:07 PM
Thanks or the examples and suggestions. I will try hard to get these into the engine as quickly as possible - maybe even later today... It has been a while since I made a commit anyways, and there is a pretty good size build up that needs to get pushed.

I'll be sure to post here once it is uploaded.
May 2, 2013 at 10:25 PM
Edited May 2, 2013 at 11:33 PM
Thats great.

here is the unproject method.
point on “screen” -> viewport–1 -> projection–1 -> modelview–1 -> point in “world” (unproject)
void Camera::unproject(Vector3f& vec) {
    const ViewPortDX11& viewport = this->GetCameraView()->GetViewPort(0);
    float screenWidth = viewport.GetWidth();
    float screenHeight = viewport.GetHeight();

    unproject(vec, 0, 0, screenWidth, screenHeight);
}
void Camera::unproject(Vector3f& vec, float viewportX, float viewportY, float viewportWidth, float viewportHeight) {
    Matrix4f view = _camera->GetCameraView()->GetViewMatrix();
    Matrix4f proj = _camera->GetCameraView()->GetProjMatrix();
    Matrix4f viewProj = view * proj;
    Matrix4f viewProjInv = viewProj.Inverse();
    
    float x = vec.x; 
    float y = vec.y;
    x = x - viewportX;
    y = viewportHeight - y - 1;
    y = y - viewportY;
    vec.x = (2 * x) / viewportWidth - 1;
    vec.y = (2 * y) / viewportHeight - 1;
    vec.z = 2 * vec.z - 1;

    Vector3f result = prj(vec, viewProjInv);
    vec.x = result.x;
    vec.y = result.y;
    vec.z = result.z;
}
I tested it so:
Vector3f worldPos0(_currentMousePosition.x, _currentMousePosition.y, 0);
unproject(worldPos0); //to world

Vector3f worldPosBackToScreenPos(worldPos0);
project(worldPosBackToScreenPos); //to screen
result -> worldPosBackToScreenPos == currentmousePosition, so it should work.


.................................................................................................
Other topic ... i wrote it somewhere, cant find it now:

dont know if you have already inside the new build but can you implement this inside EvtKeyboardMsg

EvtKeyboardMsg.h
unsigned int GetASCIICode();
EvtKeyboardMsg.cpp
unsigned int EvtKeyboardMsg::GetASCIICode()
{
    BYTE buf[256];
    WORD p;
    GetKeyboardState (&buf[0]);
    ToAscii (m_wparam, m_lparam, &buf[0], &p, 0);
    char c = (char)p;

    return c;
}
The reason is your GetCharacterCode method returns a virutal key:
//Virtual-Key Codes (Windows) "Hardware-Independent" -> http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx

And i need an ASCII-Code:
//ASCII_Key Code -> http://www.theasciicode.com.ar/extended-ascii-code/box-drawing-character-ascii-code-188.html

Perhaps the methods GetCharacterCode should be called GetVirtualKeyCode and the other method should take the name GetCharacterCode !?
Coordinator
May 5, 2013 at 2:51 PM
I have incorporated the two new methods for the Camera, and verified that they are working at least somewhat correctly. Here is the example code that I used to test them out:
Vector3f world = Vector3f( 1.0f, -2.0f, 3.0f );
Vector3f world_recalc = m_pCamera->ScreenToWorldSpace( m_pCamera->WorldToScreenSpace( world ) );

Vector2f cursor = Vector2f( 20.0f, 55.0f );
Vector2f cursor_recalc = m_pCamera->WorldToScreenSpace( m_pCamera->ScreenToWorldSpace( cursor ) );
One thing to keep in mind is that the world space point that is returned by ScreenToWorldSpace is actually on the near clipping plane. That means if the world space input point that we start with is not on the near clipping plane, that the re-calculated result will look different depending on where the camera is located (it will return the projection of the original world space point onto the near plane). That should be ok, but I wanted to bring it to your attention.

I can add your other methods for the events too. I'm in the middle of committing the current state of the codebase, so that will have to wait for the next commit... hopefully not too long.
May 5, 2013 at 11:34 PM
Nice. Tomorrow i will test the new hieroglyph version. And off course the new camera functions. Great work i love your render engine.
Coordinator
May 6, 2013 at 1:43 PM
Thanks for the positive feedback - it is always great to hear that people are getting some benefit from the project. I look forward to hearing how your tests go!