|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Junior Member
Iscritto dal: Sep 2007
Messaggi: 1
|
[C++ | DirectX] Aiuto sull'uso di SetTransform
Salve a tutti!
Sto iniziando a utilizzare le DirectX ed ho già incontrato un ostacolo che non riesco a superare ![]() Finora, nonostante l'uso di SetTransform(...) per impostare world matrix, view matrix e e portview, le DrawPrimitive continuano a disegnare interpretando i vertici come coordinate schermo. C'è qualche istruzione che "attiva" le trasformazioni di coordinate? Sono proprio incasinato . . . ![]() Codice:
#include <windows.h> #include <d3d9.h> #include <d3dx9.h> LPDIRECT3D9 pD3D; //oggetto LPDIRECT3DDEVICE9 pd3dDevice; //device IDirect3DVertexBuffer9* vBuffer; IDirect3DSurface9* pD3DSurface; const int DSP_LARG = 1024; // const int DSP_ALT = 768; LARGE_INTEGER timerStart, timerEnd, timerFreq; float anim_rate; int nFrame = 0; const char g_szClassName[] = "LaMiaClasseWindow"; void Render(); void InitD3D(HWND); void CleanUp(); struct CUSTOMVERTEX { FLOAT x,y,z, rhw; DWORD color; }; CUSTOMVERTEX g_vertici[3] = {{0.0f, 0.0f, 1.0f, 1.0f, 0x00FF0000}, {10.0f, 0.0f, 1.0f, 1.0f, 0x00FFFF00}, {0.0f, 10.0f, 1.0f, 1.0f, 0x00FF00FF}}; LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hDC; // dichiaro l'handle al drive context static int contatore = 1; switch(msg) { case WM_CLOSE: DestroyWindow(hwnd); CleanUp(); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_KEYDOWN: if(wParam == VK_ESCAPE) { PostQuitMessage(0); return 0; } break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hwnd; MSG Msg; // //Step 1: Registering the Window Class wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); //IDC_CROSS); wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = g_szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&wc)) { MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } // Step 2: Creating the Window hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, g_szClassName, "Prova Rotazioni", WS_POPUP |WS_EX_TOPMOST| WS_VISIBLE, //| WS_DLGFRAME, 0,0,1024,768, NULL, NULL, hInstance, NULL); InitD3D(hwnd); //inizializzo DirectX //***************************************************************************************** //***************************************************************************************** if(hwnd == NULL) { MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); ShowCursor(FALSE); // Step 3: The Message Loop QueryPerformanceFrequency(&timerFreq);//ricavo la frequenza while( TRUE ) { if( PeekMessage( &Msg, NULL, 0, 0, PM_REMOVE ) ) { // Check for a quit message if( Msg.message == WM_QUIT ) break; TranslateMessage( &Msg ); DispatchMessage( &Msg ); } else { QueryPerformanceCounter(&timerStart); Render(); QueryPerformanceCounter(&timerEnd); anim_rate = (float)(timerEnd.QuadPart - timerStart.QuadPart)/timerFreq.QuadPart; } } } void InitD3D(HWND wnd) { pD3D = Direct3DCreate9 (D3D_SDK_VERSION); //creo l'object if(NULL == pD3D) { MessageBox(NULL, "Errore nella creazione dell\'object",NULL,MB_OK); return; } D3DPRESENT_PARAMETERS d3dpp; ZeroMemory( &d3dpp, sizeof(d3dpp)); d3dpp.Windowed = false;// Full Screen, quindi cambia anche la risoluzione del monitor d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; d3dpp.BackBufferCount =1; d3dpp.BackBufferHeight = DSP_ALT; d3dpp.BackBufferWidth = DSP_LARG; d3dpp.hDeviceWindow = wnd; //creo il device: HRESULT hret = 0; hret = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &pd3dDevice); if(FAILED(hret)) { char lpMess[255]; switch (hret) { case D3DERR_DEVICELOST: strcpy(lpMess,"Errore device_lost"); break; case D3DERR_INVALIDCALL: strcpy(lpMess,"Errore D3DERR_INVALIDCALL"); break; case D3DERR_NOTAVAILABLE: strcpy(lpMess,"Errore D3DERR_NOTAVAILABLE"); break; case D3DERR_OUTOFVIDEOMEMORY: strcpy(lpMess,"Errore D3DERR_OUTOFVIDEOMEMORY"); break; } MessageBox(NULL, lpMess ,"Errore",MB_OK); return; } hret = pd3dDevice->CreateVertexBuffer(sizeof(g_vertici), 0, D3DFVF_XYZRHW | D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &vBuffer, NULL); if(FAILED(hret)) { return ; } } void CleanUp() { if(pD3D != NULL) pD3D->Release(); if(pd3dDevice != NULL) pd3dDevice->Release(); } void Render() { IDirect3DSurface9* backBuffer; DWORD hret; if (NULL == pd3dDevice) { return; } static float tempo = 0.0f; pd3dDevice->Clear(0,NULL,D3DCLEAR_TARGET, 0x00000000, 1.0f, 0); pd3dDevice->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO, &backBuffer); // pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); ///////////////////////////////////////////////////////////////////////////////////////////// void * pVertici; //buffer di lavoro che conterrà i vertici copiati hret = vBuffer->Lock(0, sizeof(g_vertici), (void **) &pVertici, 0); memcpy(pVertici, g_vertici, sizeof(g_vertici)); vBuffer->Unlock(); //-----------------------------------------------#################### /// rotazioni D3DXMATRIX finalMat, dummyMat, matRotazione, matProj, matTraslazione; D3DXVECTOR3 position(0.0f, 0.0f, -5.0f); D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIX v; D3DXMatrixIdentity(&matRotazione); //inizializzo la matrice identità D3DXMatrixIdentity(&matTraslazione); //inizializzo la matrice identità D3DXMatrixIdentity(&finalMat); //alimento la matrice di rotazione: D3DXMatrixRotationZ(&matRotazione, D3DX_PI * 0.25f); D3DXMatrixTranslation(&matTraslazione, 400.0f, 100.0f, 0.0f); D3DXMatrixMultiply(&finalMat, &finalMat, &matTraslazione); //apply translation //applico la rotazione moltiplicando: D3DXMatrixMultiply(&finalMat, &finalMat, &matRotazione); pd3dDevice->SetTransform(D3DTS_WORLD, &finalMat); D3DXMatrixLookAtLH(&v, &position, &target, &up); pd3dDevice->SetTransform(D3DTS_VIEW, &v); D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI * 1.5f, (float) 1024 / (float) 768, 1.0f, 100.0f); pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj); hret = pd3dDevice->BeginScene(); if(FAILED(hret)) { MessageBox(NULL, "ERRORE in BeginScene()","Errore",MB_OK); return; } hret = pd3dDevice->SetStreamSource(0, vBuffer, 0, sizeof(CUSTOMVERTEX));//dichiaro quale buffer usare per i vertici if(FAILED(hret)) { MessageBox(NULL, "ERRORE in SetStreamSource","Errore",MB_OK); return; } hret = pd3dDevice->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE); if(FAILED(hret)) { MessageBox(NULL, "ERRORE in SetFVF","Errore",MB_OK); return; } D3DVIEWPORT9 vp = { 0, 0, 1024, 768, 0.0f, 1.0f }; pd3dDevice->SetViewport(&vp); hret = pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST,0,1); if(FAILED(hret)) { MessageBox(NULL, "ERRORE in DrawIndexedPrimitive","Errore",MB_OK); return; } pd3dDevice->EndScene(); nFrame++; pd3dDevice->Present(NULL, NULL, NULL, NULL); tempo += anim_rate; } ; |
![]() |
![]() |
![]() |
#2 |
Senior Member
Iscritto dal: Feb 2002
Messaggi: 906
|
ti manca la matrice mondo ovvero coordinate worldspace
te l'ho corretto così: guarda le correzzioni poi magari prova a fare le traslazioni e rotazioni: Codice HTML:
#include <windows.h> #include <d3d9.h> #include <d3dx9.h> LPDIRECT3D9 pD3D; //oggetto LPDIRECT3DDEVICE9 pd3dDevice; //device IDirect3DVertexBuffer9* vBuffer; IDirect3DSurface9* pD3DSurface; const int DSP_LARG = 1024; // const int DSP_ALT = 768; LARGE_INTEGER timerStart, timerEnd, timerFreq; float anim_rate; int nFrame = 0; const char g_szClassName[] = "LaMiaClasseWindow"; void Render(); void InitD3D(HWND); void CleanUp(); struct CUSTOMVERTEX { FLOAT x, y, z; DWORD color; }; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE) CUSTOMVERTEX g_vertici[] = { { -1.0f,-1.0f, 0.0f, 0xffff0000, }, { 1.0f,-1.0f, 0.0f, 0xff0000ff, }, { 0.0f, 1.0f, 0.0f, 0xffffffff, }, }; LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hDC; // dichiaro l'handle al drive context static int contatore = 1; switch(msg) { case WM_CLOSE: DestroyWindow(hwnd); CleanUp(); break; case WM_DESTROY: PostQuitMessage(0); break; case WM_KEYDOWN: if(wParam == VK_ESCAPE) { PostQuitMessage(0); return 0; } break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hwnd; MSG Msg; // //Step 1: Registering the Window Class wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); //IDC_CROSS); wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = g_szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if(!RegisterClassEx(&wc)) { MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } // Step 2: Creating the Window hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, g_szClassName, "Prova Rotazioni", WS_OVERLAPPEDWINDOW | WS_VISIBLE,//WS_POPUP |WS_EX_TOPMOST| WS_VISIBLE, //| WS_DLGFRAME, 0,0,1024,768, NULL, NULL, hInstance, NULL); InitD3D(hwnd); //inizializzo DirectX //***************************************************************************************** //***************************************************************************************** if(hwnd == NULL) { MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK); return 0; } ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); ShowCursor(true); // Step 3: The Message Loop QueryPerformanceFrequency(&timerFreq);//ricavo la frequenza while( TRUE ) { if( PeekMessage( &Msg, NULL, 0, 0, PM_REMOVE ) ) { // Check for a quit message if( Msg.message == WM_QUIT ) break; TranslateMessage( &Msg ); DispatchMessage( &Msg ); } else { QueryPerformanceCounter(&timerStart); Render(); QueryPerformanceCounter(&timerEnd); anim_rate = (float)(timerEnd.QuadPart - timerStart.QuadPart)/timerFreq.QuadPart; } } } void InitD3D(HWND wnd) { pD3D = Direct3DCreate9 (D3D_SDK_VERSION); //creo l'object if(NULL == pD3D) { MessageBox(NULL, "Errore nella creazione dell\'object",NULL,MB_OK); return; } D3DPRESENT_PARAMETERS d3dpp; ZeroMemory( &d3dpp, sizeof(d3dpp)); d3dpp.Windowed = true;// Full Screen, quindi cambia anche la risoluzione del monitor d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8; d3dpp.BackBufferCount =1; d3dpp.BackBufferHeight = DSP_ALT; d3dpp.BackBufferWidth = DSP_LARG; d3dpp.hDeviceWindow = wnd; //creo il device: HRESULT hret = 0; hret = pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, wnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &pd3dDevice); if(FAILED(hret)) { char lpMess[255]; switch (hret) { case D3DERR_DEVICELOST: strcpy(lpMess,"Errore device_lost"); break; case D3DERR_INVALIDCALL: strcpy(lpMess,"Errore D3DERR_INVALIDCALL"); break; case D3DERR_NOTAVAILABLE: strcpy(lpMess,"Errore D3DERR_NOTAVAILABLE"); break; case D3DERR_OUTOFVIDEOMEMORY: strcpy(lpMess,"Errore D3DERR_OUTOFVIDEOMEMORY"); break; } MessageBox(NULL, lpMess ,"Errore",MB_OK); return; } // hret = pd3dDevice->CreateVertexBuffer(sizeof(g_vertici), 0, D3DFVF_XYZRHW | D3DFVF_DIFFUSE, // D3DPOOL_DEFAULT, &vBuffer, NULL); hret = pd3dDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &vBuffer, NULL ); if(FAILED(hret)) { return ; } } void CleanUp() { if(pD3D != NULL) pD3D->Release(); if(pd3dDevice != NULL) pd3dDevice->Release(); } void Render() { IDirect3DSurface9* backBuffer; DWORD hret; if (NULL == pd3dDevice) { return; } static float tempo = 0.0f; pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 ); ///////////////////////////////////////////////////////////////////////////////////////////// void * pVertici; //buffer di lavoro che conterrà i vertici copiati hret = vBuffer->Lock(0, sizeof(g_vertici), (void **) &pVertici, 0); memcpy(pVertici, g_vertici, sizeof(g_vertici)); vBuffer->Unlock(); /* D3DXMATRIXA16 matProj; D3DXMATRIX finalMat, dummyMat, matRotazione, matTraslazione; D3DXVECTOR3 position(0.0f, 0.0f, -5.0f); D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); D3DXMATRIXA16 v; D3DXMatrixLookAtLH(&v, &position, &target, &up); pd3dDevice->SetTransform(D3DTS_VIEW, &v); D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI * 1.5f, (float) 1024 / (float) 768, 1.0f, 100.0f); pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj); */ //-----------------------------------------------#################### /// rotazioni D3DXMATRIXA16 matWorld; FLOAT fAngle = 0.5f; D3DXMatrixRotationY( &matWorld, fAngle ); pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); D3DXVECTOR3 vEyePt( 0.0f, 3.0f, -5.0f ); D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f ); D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f ); D3DXMATRIXA16 matView; D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec ); pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); D3DXMATRIXA16 matProj; D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 1000.0f ); pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); // Turn off culling, so we see the front and back of the triangle pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); // Turn off D3D lighting, since we are providing our own vertex colors pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); /* D3DXMatrixIdentity(&matRotazione); //inizializzo la matrice identità D3DXMatrixIdentity(&matTraslazione); //inizializzo la matrice identità D3DXMatrixIdentity(&finalMat); //alimento la matrice di rotazione: D3DXMatrixRotationZ(&matRotazione, D3DX_PI * 0.25f); D3DXMatrixTranslation(&matTraslazione, 400.0f, 100.0f, 0.0f); D3DXMatrixMultiply(&finalMat, &finalMat, &matTraslazione); //apply translation //applico la rotazione moltiplicando: D3DXMatrixMultiply(&finalMat, &finalMat, &matRotazione); pd3dDevice->SetTransform(D3DTS_WORLD, &finalMat); D3DXMatrixLookAtLH(&v, &position, &target, &up); pd3dDevice->SetTransform(D3DTS_VIEW, &v); D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI * 1.5f, (float) 1024 / (float) 768, 1.0f, 100.0f); pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj); */ hret = pd3dDevice->BeginScene(); if(FAILED(hret)) { MessageBox(NULL, "ERRORE in BeginScene()","Errore",MB_OK); return; } hret = pd3dDevice->SetStreamSource(0, vBuffer, 0, sizeof(CUSTOMVERTEX));//dichiaro quale buffer usare per i vertici if(FAILED(hret)) { MessageBox(NULL, "ERRORE in SetStreamSource","Errore",MB_OK); return; } //hret = pd3dDevice->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE); hret = pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX ); if(FAILED(hret)) { MessageBox(NULL, "ERRORE in SetFVF","Errore",MB_OK); return; } // D3DVIEWPORT9 vp = { 0, 0, 1024, 768, 0.0f, 1.0f }; // pd3dDevice->SetViewport(&vp); hret = pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST,0,1); if(FAILED(hret)) { MessageBox(NULL, "ERRORE in DrawIndexedPrimitive","Errore",MB_OK); return; } pd3dDevice->EndScene(); nFrame++; pd3dDevice->Present(NULL, NULL, NULL, NULL); tempo += anim_rate; }; |
![]() |
![]() |
![]() |
#3 |
Senior Member
Iscritto dal: Feb 2002
Messaggi: 906
|
nelle proprietà-->Linker-->input--Addition dipendencies:
aggiungi winmm.lib se aggiungi questo nel render fai le rotazioni Codice HTML:
D3DXMATRIXA16 matWorld; UINT iTime = timeGetTime() % 1000; FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f; // FLOAT fAngle = 0.5f; invece per il tuo codice và bene questo: UINT iTime = timeGetTime() % 1000; FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f; D3DXMATRIX finalMat, dummyMat, matRotazione, matTraslazione; D3DXMatrixIdentity(&matRotazione); //inizializzo la matrice identità D3DXMatrixRotationZ(&finalMat, D3DX_PI * fAngle); pd3dDevice->SetTransform(D3DTS_WORLD, &finalMat); un' altra cosa: Inizia da subito a mettere la routine per girarti con il mouse e/o tasti freccie nel mondo infatti facendo lo spostamento di oggetti potresi avere che tutto funziona ma non vedi il triangolo perchè magari sta a coordinate fuori vista. Mentre se ti muovi con il mouse intorno puoi vedere il triangolo renderizzato bene proprio dietro di te o sopra di te ecc ecc. In unltimo metti: d3dpp.Windowed = true;// Full Screen, quindi cambia anche la risoluzione del monitor non debbuggare mai in fullscrenn potresti bloccarti da qualche parte senza riuscire ad uscire dall'applicazione perdendo il codice aggiornato fino a quel momento senza avere il modo di salvarlo in seguito al blocco del programma. QUindi resettare il pc. ciao Ultima modifica di okay : 14-09-2007 alle 10:16. |
![]() |
![]() |
![]() |
#4 |
Senior Member
Iscritto dal: Feb 2002
Messaggi: 906
|
Ancora una cosa:
utilizza il mio codice: metti così: D3DXVECTOR3 vEyePt( 0.0f, 0.0f, -50.0f ); con z uguale a -50.0f così di avere il triangolo di vista nel mondo + in profondità. Codice HTML:
D3DXMatrixIdentity(&finalMat); D3DXMatrixTranslation(&matTraslazione, -5.953f, 0.091f, -35.915f); D3DXMatrixMultiply(&finalMat, &finalMat, &matTraslazione); pd3dDevice->SetTransform(D3DTS_WORLD, &finalMat); Ora questo è ciò che volevi fare: Codice HTML:
D3DXMatrixRotationZ(&matRotazione, D3DX_PI * fAngle); D3DXMatrixIdentity(&finalMat); D3DXMatrixTranslation(&matTraslazione, -5.953f, 0.091f, -35.915f); D3DXMatrixMultiply(&finalMat, &matRotazione, &matTraslazione); // D3DXMatrixMultiply(&finalMat, &finalMat, &matRotazione); pd3dDevice->SetTransform(D3DTS_WORLD, &finalMat); se invece decommenti avrai rotazione sia del triangolo su se stesso e della posizione dello stesso nel mondo: provare per credere. ciao Ultima modifica di okay : 14-09-2007 alle 10:00. |
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 14:38.