Rotation + Translation

Started by
28 comments, last by Tiege 12 years, 7 months ago
That isn’t really an identity matrix, but if you want the world to move 30 units up that will suffice.
And assuming you want to rotate the character but not move him.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Advertisement

That isn’t really an identity matrix, but if you want the world to move 30 units up that will suffice.
And assuming you want to rotate the character but not move him.


L. Spiro


I'm guessing I'm just having a hard time wrapping my head around the method to do this.

What I want is:

My model loads and flips to the correct orientation.
I can the move it left and right via translation along the x axis.

I don't know how to change the world to flip him just ONCE and then reset it to normal because it's in a loop. I can't say:
orient -> draw model -> reset world
because then the model gets redrawn in the reset world matrix because of the render loop the next run will do
reset world -> draw model ->reset world -> draw model

Here's my program:


#include "model.h"

char* filename = "BarbA2.x";

//globals
LPDIRECT3D9 d3dObject=NULL;
LPDIRECT3DDEVICE9 d3dDevice;
LPDIRECTINPUT8 din; // the pointer to our DirectInput interface
LPDIRECTINPUTDEVICE8 dinkeyboard; // the pointer to the keyboard device
BYTE keystate[256]; // the storage for the key-information

D3DXMATRIXA16 matWorld;
D3DXMATRIX matRotX;
D3DXMATRIX matRotY;
D3DXMATRIX resRotX;
D3DXMATRIX resRotY;
D3DXMATRIX matID;

Model* pModel = NULL;
Model* pModel2 = NULL;

static float vector = 0;
static bool jump = false;

static float indexx = 0.0f;
static float indexy = 0.0f;
bool right = true;

void initDInput(HINSTANCE hInstance, HWND hWnd); // sets up and initializes DirectInput
void detect_input(void); // gets the current input state
void cleanDInput(void); // closes DirectInput and releases memory
void Render();


HRESULT InitD3D( HWND hWnd )
{
//create D3D Object
if( NULL == ( d3dObject=Direct3DCreate9(D3D_SDK_VERSION) ) )
return E_FAIL;

//setup structure for parameters for D3D Device
D3DPRESENT_PARAMETERS presParams;
ZeroMemory(&presParams,sizeof(presParams));
presParams.Windowed=TRUE;
presParams.SwapEffect=D3DSWAPEFFECT_DISCARD;
presParams.BackBufferFormat=D3DFMT_UNKNOWN;
presParams.PresentationInterval=D3DPRESENT_INTERVAL_ONE;


//create D3D Device
if( FAILED (d3dObject->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING, &presParams, &d3dDevice) ) )
{
return E_FAIL;
}
// Turn on ambient lighting
d3dDevice->SetRenderState( D3DRS_AMBIENT, 0xffffffff );


return S_OK;
}

VOID SetupMatrices()
{
// Set up world matrix

D3DXMatrixIdentity(&matID);

//Set Model Orientation
D3DXMatrixRotationX( &matRotX, D3DXToRadian(90.0f) );
D3DXMatrixRotationY( &matRotY, D3DXToRadian(90.0f) );
d3dDevice->SetTransform( D3DTS_WORLD, &(matRotX * matRotY) );

// Set up our view matrix. A view matrix can be defined given an eye point,
// a point to lookat, and a direction for which way is up. Here, we set the
// eye five units back along the z-axis and up three units, look at the
// origin, and define "up" to be in the y-direction.
D3DXVECTOR3 vEyePt( 0.0f, 0.0f, 50.0f );
D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
d3dDevice->SetTransform( D3DTS_VIEW, &matView );


// For the projection matrix, we set up a perspective transform (which
// transforms geometry from 3D view space to 2D viewport space, with
// a perspective divide making objects smaller in the distance). To build
// a perpsective transform, we need the field of view (1/4 pi is common),
// the aspect ratio, and the near and far clipping planes (which define at
// what distances geometry should be no longer be rendered).
D3DXMATRIXA16 matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI / 4, 1.0f, 1.0f, 500.0f );
d3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );




}

void checkInput()
{

if(keystate[DIK_LEFT] & 0x80)
{
indexx -= 0.5f;
right = false;
}

if(keystate[DIK_RIGHT] & 0x80)
{
indexx += 0.5f;
right = true;
}

if(keystate[DIK_UP] & 0x80)
{
if ( jump == false)
{
jump = true;
vector = 2;
}
}

return;
}

void Advance()
{
static float orientation = 0;
static DWORD lastTime=timeGetTime();
float timeElapsed=0.001f*(timeGetTime()-lastTime);
lastTime=timeGetTime();

//gravity
indexy-=vector;

if (indexy < 0)
vector-=0.25;
if (indexy > 0)
{
jump = false;
vector=0;
}

if(keystate[DIK_R] & 0x80)
orientation = 3.14159;

if(keystate[DIK_L] & 0x80)
orientation = 3;

//pModel->SetOrientation(&matRot, right, indexx, indexy);
//pModel->SetPosition(&matWorld,0,indexx,indexy);


D3DXMatrixTranslation(&matWorld, indexx, indexy, 0);

// d3dDevice->SetTransform( D3DTS_WORLD, &(matRotX * matRotY * matWorld) );


pModel->FrameMove(timeElapsed,&matWorld);




return;
}

void Render()
{
//clear buffer
d3dDevice->Clear(0,NULL,D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,50,100),1.0f,0);

//begin scene
d3dDevice->BeginScene();

Advance();

checkInput();

//Render Model
pModel->DrawFrame(pModel->GetFrameRoot());

//Reset World Identity
d3dDevice->SetTransform( D3DTS_WORLD, &(matID) );


//end scene
d3dDevice->EndScene();

//present screen
d3dDevice->Present( NULL, NULL, NULL, NULL );
}



void initDInput(HINSTANCE hInstance, HWND hWnd)
{
// create the DirectInput interface
DirectInput8Create(hInstance, // the handle to the application
DIRECTINPUT_VERSION, // the compatible version
IID_IDirectInput8, // the DirectInput interface version
(void**)&din, // the pointer to the interface
NULL); // COM stuff, so we'll set it to NULL

// create the keyboard device
din->CreateDevice(GUID_SysKeyboard, // the default keyboard ID being used
&dinkeyboard, // the pointer to the device interface
NULL); // COM stuff, so we'll set it to NULL

// set the data format to keyboard format
dinkeyboard->SetDataFormat(&c_dfDIKeyboard);

// set the control we will have over the keyboard
dinkeyboard->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
}


// this is the function that gets the latest input data
void detect_input(void)
{
// get access if we don't have it already
dinkeyboard->Acquire();

// get the input data
dinkeyboard->GetDeviceState(256, (LPVOID)keystate);
}


// this is the function that closes DirectInput
void cleanDInput(void)
{
dinkeyboard->Unacquire(); // make sure the keyboard is unacquired
din->Release(); // close DirectInput before exiting
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
//case WM_COMMAND:
// handle menu selections etc.
//break;
//case WM_PAINT:
// draw our window - note: you must paint something here or not trap it!
//break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
// We do not want to handle this message so pass back to Windows
// to handle it in a default way
return DefWindowProc(hWnd, message, wParam, lParam);
}

return 0;
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wcex;

wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style= CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc= (WNDPROC)WndProc;
wcex.cbClsExtra= 0;
wcex.cbWndExtra= 0;
wcex.hInstance= hInstance;
wcex.hIcon= 0;
wcex.hCursor= LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground= (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName= 0;
wcex.lpszClassName= "MyWindowClass";
wcex.hIconSm= 0;

// Now we can go ahead and register our new window class
RegisterClassEx(&wcex);

HWND hWnd = CreateWindow("MyWindowClass", "Poop", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

//call initD3D
InitD3D(hWnd);
initDInput(hInstance, hWnd); // initialize DirectInput



//Make model
pModel = new Model(d3dDevice);

//Load the new
pModel->Load(filename);

SetupMatrices();


ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

//enter main loop
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);

detect_input();
Render();
}
cleanDInput();

return (int)msg.wParam;
}
The world matrix is meant to be used for every single object in your world.
You are trying to use it for only the character.

Set the world matrix for every object before rendering that object. Don’t try to use the world matrix as if it were intended only for the main character in your world.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


The world matrix is meant to be used for every single object in your world.
You are trying to use it for only the character.

Set the world matrix for every object before rendering that object. Don’t try to use the world matrix as if it were intended only for the main character in your world.


L. Spiro


It's only the main character now for testing. Obviously I plan to add more later but I can't even get it to work for one object?

I just don't get how to rotate objects without messing up the world itself. Because it doesn't rotate the object itself, it rotates the world around it to make it look like the object is rotated.
I can’t see what the struggle is.
If you set the world matrix to identity before rendering the world then you have messed up nothing. You are trying to set the world matrix once per frame when it should be done once per object or more.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid


I can’t see what the struggle is.
If you set the world matrix to identity before rendering the world then you have messed up nothing. You are trying to set the world matrix once per frame when it should be done once per object or more.


L. Spiro


In the code in the Setupmatrices function (which is ran once before everything is drawn) I have the lines:

[color="#880000"]//Set Model Orientation[color="#000000"]
D3DXMatrixRotationX[color="#666600"]( [color="#666600"]&[color="#000000"]matRotX[color="#666600"],[color="#000000"] D3DXToRadian[color="#666600"]([color="#006666"]90.0f[color="#666600"]) [color="#666600"]);[color="#000000"]
D3DXMatrixRotationY[color="#666600"]( [color="#666600"]&[color="#000000"]matRotY[color="#666600"],[color="#000000"] D3DXToRadian[color="#666600"]([color="#006666"]90.0f[color="#666600"]) [color="#666600"]);[color="#000000"]
d3dDevice[color="#666600"]->[color="#660066"]SetTransform[color="#666600"]([color="#000000"] D3DTS_WORLD[color="#666600"], [color="#666600"]&([color="#000000"]matRotX [color="#666600"]*[color="#000000"] matRotY[color="#666600"]) [color="#666600"]);
[color="#666600"]This makes the mesh I load standing upright and facing right.
[color="#666600"]Then in the advance function I have:



D3DXMatrixTranslation[color="#666600"][color=#1C2837](&[color="#000000"][color=#1C2837]matWorld[color="#666600"][color=#1C2837],[color="#000000"][color=#1C2837] indexx[color="#666600"][color=#1C2837],[color="#000000"][color=#1C2837] indexy[color="#666600"][color=#1C2837],[color="#000000"][color=#1C2837] [color="#006666"][color=#1C2837]0[color="#666600"][color=#1C2837]);[color="#666600"][color=#1C2837]
[color="#666600"][color=#1C2837]

[color="#666600"][color=#1C2837]This moves the character left or right when I hit the left or right arrow keys.[color="#666600"][color=#1C2837]
[color="#666600"][color=#1C2837]

[color="#666600"][color=#1C2837]HOWEVER, the character moves in and out of the screen instead of left or right. But this only happens when I rotate him to make him face right. If I remove those rotations the left and right work but then my character is upside down facing into outer space. I'm just trying to fix this. It looks like when I rotate the mesh the world axises get rotated aswell which would result in my problem.
Your game is side-scrolling. The character should rotate only around the Z axis, which points into the distance (actually the camera points towards -Z, so +Z is actually pointing behind the camera).
So you will first have to replace the code that translates around both the X and the Y and replace it with one rotation around the Z.
Then make a translation matrix representing the position of the character and apply thusly:


D3DXMatrixTranslation( &matTrans, fX, fY, -1.0f );
D3DXMatrixRotationZ( &matRotZ, D3DXToRadian(fPlayerRot) );
d3dDevice->SetTransform( D3DTS_WORLD, &(matRotZ * matTrans) );

pCharacter.Render();


D3DXMatrixIdentity( &matTrans );
d3dDevice->SetTransform( D3DTS_WORLD, &matTrans );
pWorld.Render();







The flaw in your logic is exactly the fact that you set the matrices only once in a single call to SetupMatrices().
This is what I have been trying to tell you. The camera and projection matrices won’t change during the render, but the world matrix will be different for every object.
You can’t just set it once and render everything. You have to set the world matrix before every object you want to render.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

I think that because its a side scroller you want to rotate the player around the Y axis (ie the axis running vertical to the screen), rotating around the Z axis would tilt the character forward and back. Tiege seems very confused about how to use World matrices properly. Rather than having your translation/rotation/world matrices as global variables, having them as local variables for the render function seems more foolproof, so each time render() is called you declare them, and initialize to an identity matrix. From there you would do

psuedocode:
Render()
{
declare all matrices (world/translation/rotation)
initialize to identity

set rotation/translation matrices for the terrain
set the world matrix (world= scale*rotate*translate)
render the terrain

set all matrices back to identity

set rotation/translation matricies for the player
set the world matrix (world= scale*rotate*translate)
render the player
}

basically the world matrix should not persist between frames being rendered (for simplicity) , it should be calculated every frame (of course persistence is fine if the matrix never changes)
It seems me that a misunderstanding of what a space is and how a transformation works is on the way here.

The "world" is a space that is used as common reference frame for all objects and the camera. If you apply a transformation w/o actually changing vertices, you simply do a re-interpretation of the vertices in another space. Every single transformation is applied w.r.t. the origin and axes in the space that is "current" when the transformation is applied. Naming spaces is a convention. E.g. the model space is the space where the co-ordnates of the vertices are given in. The view space is the space local to the camera. Only the fact that the screen shows content w.r.t. the view space dictates that the end of the (full 3D) transformation pipeline is the view space.

When speaking of the world matrix (or model matrix in OpenGL) one usually means all transformation that are used to step from the model local space to the world space. When speaking of the view matrix then one means the sequence of transformations to step from the worlds space into the view (a.k.a camera local) space. The world matrix is a transformation from a (model) local space to the world space, and the view matrix from the world space into a (camera) local space. Hence each object may (and usually does) have its own world matrix.

An example: A translation is applied to a model. The translation is "the first" transformation and hence is applied with the model local space as reference. That means that the model is shifted along what we call the model local axes. In the space behind the translation the axes are the same but the origin is somewhere else (because that is the nature of translations). What does this mean? Well, the point [ 0 0 0 ] w.r.t. the model space and the point [ 0 0 0 ] w.r.t. the new space are not coincident. However, the point [ 0 0 0 ] w.r.t. the model space and the point [ a b c ] (when T(a,b,c) defines the translation) are coincident. But, and that is important, the next transformation sees [ 0 0 0 ] w.r.t. the new space as the origin! The same is true for other transformation, of course: E.g. a rotation lets the origin of the space before and behind it at the same location, but it changes the axes.

All this is the reasoning how to order and compose transformations to yield in he desired effect. E.g. scaling, rotating behind scaling, and translating behind rotating is a nice order because:
(1) The origin and axes orientations before and after the scaling are all coincident.
(2) The origin before and after the rotation are coincident.
Also of interest (see below):
(3) The axes before and behind a translation are coincident.

An example: If you want to rotate around a specific location, determine the co-ordinates [ a b c ] of that location w.r.t. the space in which you want to apply the rotation. Then apply a translation by the negated difference T(-a,-b,-c) of that point to the current origin. Due to rule (3) the orientation hasn't changed, so we can apply the rotation as-is w/o any rotational correction. Due to rule (2) the origins before and after the rotation are the same. However, we want to be in a space where the origin is at the original location, so we need to translate back by applying T(a,b,c). Due to rule (3) this don't effects the orientation, so we are happy.

I think that because its a side scroller you want to rotate the player around the Y axis (ie the axis running vertical to the screen), rotating around the Z axis would tilt the character forward and back. Tiege seems very confused about how to use World matrices properly. Rather than having your translation/rotation/world matrices as global variables, having them as local variables for the render function seems more foolproof, so each time render() is called you declare them, and initialize to an identity matrix. From there you would do

psuedocode:
Render()
{
declare all matrices (world/translation/rotation)
initialize to identity

set rotation/translation matrices for the terrain
set the world matrix (world= scale*rotate*translate)
render the terrain

set all matrices back to identity

set rotation/translation matricies for the player
set the world matrix (world= scale*rotate*translate)
render the player
}

basically the world matrix should not persist between frames being rendered (for simplicity) , it should be calculated every frame (of course persistence is fine if the matrix never changes)

You are probably correct about rotating around the Y axis, but after thinking of it this way he should probably use an X scale to flip the character rather than rotate it.
Also no need to set matrices back to identity between renders.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

This topic is closed to new replies.

Advertisement