Sign in to follow this  
deathknight2005

Texture and Rectangle Background won't show up!

Recommended Posts

I have tried to render a simple rectangle in the background of my pacman without texture just to get ready but it isn't working and I get an error that says: Unhandled exception at 0x00412627 in Pacman.exe: 0xC0000005: Access violation reading location 0xfeeefef6. at the line SAFE_RELEASE(PacTexture); Here is the code:
// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <dinput.h>
// define the screen resolution and keyboard macros
#define SCREEN_WIDTH  640
#define SCREEN_HEIGHT 480
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
#define SAFE_RELEASE(n) if(n != NULL) {n->Release(); n = NULL;}
#define CUSTOMFVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE) 
// include the Direct3D Library file
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")
#pragma comment (lib, "dinput.lib")
#pragma comment (lib, "dinput8.lib")
#pragma comment (lib, "dxguid.lib")

// global declarations
LPDIRECT3D9 d3d; // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev; // the pointer to the device class
LPD3DXSPRITE PacSprite;
LPDIRECT3DTEXTURE9 PacTexture = NULL;
LPDIRECT3DTEXTURE9 BackTexture = NULL;
LPDIRECT3DVERTEXBUFFER9 t_buffer = NULL;    // the pointer to the vertex buffer
LPDIRECT3DSURFACE9 z_buffer = NULL;
LPDIRECTINPUT8 din;    // the pointer to our DirectInput interface
LPDIRECTINPUTDEVICE8 dinkeyboard;
struct CUSTOMVERTEX {FLOAT X, Y, Z, RHW; DWORD COLOR;};

FLOAT posX, posY, posZ;
FLOAT speed;
FLOAT sChange = 0;
FLOAT sChange2 = 0;
bool movingLeft, movingRight, movingUp, movingDown;
bool fullscreen = true;
bool firstTime = true;
HWND hWnd;
// function prototypes
void initD3D(HWND hWnd); // sets up and initializes Direct3D
void render_frame(void); // renders a single frame
void cleanD3D(void); // closes Direct3D and releases memory
void initDInput(HINSTANCE hInstance, HWND hWnd);    // sets up and initializes DirectInput
bool detect_keys(BYTE DIKVal);    // gets the current keys being pressed
void cleanDInput(void);
void wrapRoom();
void switchMode();
void setLogic(void);
void movementLogic(void);
void TranslationLogic(void);
void switchPacPics(void);
void init_graphics(void); 
void draw_background(void);
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    WNDCLASSEX wc;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = (WNDPROC)WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    // wc.hbrBackground = (HBRUSH)COLOR_WINDOW;    // not needed any more
    wc.lpszClassName = L"WindowClass1";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL,
                          L"WindowClass1",
                          L"Our Direct3D Program",
                          WS_EX_TOPMOST | WS_POPUP,    // fullscreen values
                          0, 0,    // the starting x and y positions should be 0
                          SCREEN_WIDTH, SCREEN_HEIGHT,    // set the window to 640 x 480
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

    ShowWindow(hWnd, nCmdShow);

    // set up and initialize Direct3D
    initD3D(hWnd);
    initDInput(hInstance, hWnd);
  
    // enter the main loop:

    MSG msg;

    while(TRUE)
    {
        DWORD starting_point = GetTickCount();

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        render_frame();
        if(detect_keys(DIK_SPACE)) 
			switchMode();
        // check the 'escape' key
        if(KEY_DOWN(VK_ESCAPE))
            PostMessage(hWnd, WM_DESTROY, 0, 0);

        while ((GetTickCount() - starting_point) < 25);
    }

    // clean up DirectX and COM
    cleanD3D();
    cleanDInput();
    return msg.wParam;
}


// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
        case WM_DESTROY:
            {
                PostQuitMessage(0);
                return 0;
            } break;
    }

    return DefWindowProc (hWnd, message, wParam, lParam);
}


// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION); // create the Direct3D interface

    D3DPRESENT_PARAMETERS d3dpp; // create a struct to hold various device information

    ZeroMemory(&d3dpp, sizeof(d3dpp));    // clear out the struct for use
    d3dpp.Windowed = FALSE;    // program fullscreen, not windowed
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;    // discard old frames
    d3dpp.hDeviceWindow = hWnd;    // set the window to be used by Direct3D
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;    // set the back buffer format to 32-bit
    d3dpp.BackBufferWidth = SCREEN_WIDTH;    // set the width of the buffer
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;    // set the height of the buffer
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
     // create a device class using this information and the info from the d3dpp stuct
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);
	
	D3DXIMAGE_INFO d3dxImageInfo;

	D3DXCreateTextureFromFileEx(d3ddev, L"pacman.bmp", 
		                         67, 60,
								 1, 
		                         D3DPOOL_DEFAULT,
                                 D3DFMT_UNKNOWN,
                                 D3DPOOL_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f),
                                 &d3dxImageInfo,
                                 NULL,
                                 &PacTexture);
    D3DXIMAGE_INFO d3dxImageInfo2;

	D3DXCreateTextureFromFileEx(d3ddev, L"PacBack.bmp", 
		                         640, 480,
								 1, 
		                         D3DPOOL_DEFAULT,
                                 D3DFMT_UNKNOWN,
                                 D3DPOOL_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f),
                                 &d3dxImageInfo2,
                                 NULL,
                                 &BackTexture);
	d3ddev->CreateDepthStencilSurface(SCREEN_WIDTH,    // the z-buffer width
                                  SCREEN_HEIGHT,    // the z-buffer height
                                  D3DFMT_D16,    // 16-bit pixel format for the z-buffer
                                  D3DMULTISAMPLE_NONE,    // no multisampling
                                  0,    // no multisampling quality
                                  TRUE,    // discard old buffer data
                                  &z_buffer,    // the address of the buffer
                                  NULL);    // reserved...we don't use this.
    d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);
	 init_graphics();    
	D3DXCreateSprite(d3ddev, &PacSprite);
	
    return;
}


// this is the function used to render a single frame
void render_frame(void)
{
    // clear the window to a deep blue
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
    d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    d3ddev->BeginScene();    // begins the 3D scene
    draw_background();
    PacSprite->Begin(D3DXSPRITE_ALPHABLEND);
   
    setLogic();
    switchPacPics();
	movementLogic();
	TranslationLogic();
	D3DXVECTOR3 vPosition(posX, posY, posZ);
    PacSprite->Draw(PacTexture, NULL, NULL, &vPosition, D3DCOLOR_COLORVALUE(1.0f,1.0f,1.0f,1.0f));
    

	PacSprite->End();
    
   

    d3ddev->EndScene();    // ends the 3D scene

    d3ddev->Present(NULL, NULL, NULL, NULL);   // displays the created frame on the screen

    return;
}


// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
    SAFE_RELEASE(d3ddev); // close and release the 3D device
    SAFE_RELEASE(d3d); // close and release Direct3D
    SAFE_RELEASE(PacSprite);
	
	SAFE_RELEASE(PacTexture);
	SAFE_RELEASE(BackTexture);
	SAFE_RELEASE(z_buffer);
	
    return;
}
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

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

    // set the control you will have over the keyboard
    dinkeyboard->SetCooperativeLevel(hWnd,
                                     DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);

      return;    // return to WinMain()
}
bool detect_keys(BYTE DIKVal)
{
    static BYTE keystate[256];    // create a static storage for the key-states

    dinkeyboard->Acquire();    // get access if we don't have it already

    dinkeyboard->GetDeviceState(256, (LPVOID)keystate);    // fill keystate with values

    return (keystate[DIKVal] & 0x80);    // if the 'A' key was pressed...
}
void cleanDInput(void)
{
    dinkeyboard->Unacquire();    // make sure the keyboard is unacquired
    SAFE_RELEASE(din);    // close DirectInput before exiting

    return;
}
void switchMode(void)
{
	if(fullscreen == true)
	{
PacSprite->OnLostDevice();
PacTexture->Release();
BackTexture->Release();
D3DPRESENT_PARAMETERS d3dpp; // create a struct to hold various device information
    ZeroMemory(&d3dpp, sizeof(d3dpp));    // clear out the struct for use
    d3dpp.Windowed = TRUE;    // program fullscreen, not windowed
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;    // discard old frames
    d3dpp.hDeviceWindow = hWnd;    // set the window to be used by Direct3D
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;    // set the back buffer format to 32-bit
    d3dpp.BackBufferWidth = SCREEN_WIDTH;    // set the width of the buffer
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;    // set the height of the buffer
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3ddev->Reset(&d3dpp);
PacSprite->OnResetDevice();
D3DXIMAGE_INFO d3dxImageInfo;

	D3DXCreateTextureFromFileEx(d3ddev, L"pacman.bmp", 
		                         67, 60,
								 1, 
		                         D3DPOOL_DEFAULT,
                                 D3DFMT_UNKNOWN,
                                 D3DPOOL_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f),
                                 &d3dxImageInfo,
                                 NULL,
                                 &PacTexture);
 D3DXIMAGE_INFO d3dxImageInfo2;

	D3DXCreateTextureFromFileEx(d3ddev, L"PacBack.bmp", 
		                         640, 480,
								 1, 
		                         D3DPOOL_DEFAULT,
                                 D3DFMT_UNKNOWN,
                                 D3DPOOL_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f),
                                 &d3dxImageInfo2,
                                 NULL,
                                 &BackTexture);
fullscreen = false;
}
	else
	{
PacSprite->OnLostDevice();

PacTexture->Release();
BackTexture->Release();
 D3DPRESENT_PARAMETERS d3dpp; // create a struct to hold various device information

    ZeroMemory(&d3dpp, sizeof(d3dpp));    // clear out the struct for use
    d3dpp.Windowed = FALSE;    // program fullscreen, not windowed
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;    // discard old frames
    d3dpp.hDeviceWindow = hWnd;    // set the window to be used by Direct3D
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;    // set the back buffer format to 32-bit
    d3dpp.BackBufferWidth = SCREEN_WIDTH;    // set the width of the buffer
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;    // set the height of the buffer
d3ddev->Reset(&d3dpp);
PacSprite->OnResetDevice();

D3DXIMAGE_INFO d3dxImageInfo;

	D3DXCreateTextureFromFileEx(d3ddev, L"pacman.bmp", 
		                         67, 60,
								 1, 
		                         D3DPOOL_DEFAULT,
                                 D3DFMT_UNKNOWN,
                                 D3DPOOL_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f),
                                 &d3dxImageInfo,
                                 NULL,
                                 &PacTexture);
	 D3DXIMAGE_INFO d3dxImageInfo2;

	D3DXCreateTextureFromFileEx(d3ddev, L"PacBack.bmp", 
		                         640, 480,
								 1, 
		                         D3DPOOL_DEFAULT,
                                 D3DFMT_UNKNOWN,
                                 D3DPOOL_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f),
                                 &d3dxImageInfo2,
                                 NULL,
                                 &BackTexture);
	fullscreen = true;
	}

	DWORD dwstyle = fullscreen ? WS_POPUP : WS_OVERLAPPEDWINDOW;
SetWindowLong(hWnd, GWL_STYLE, dwstyle);
SetWindowPos(hWnd, NULL, 0, 0, 0, 0,SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);

}
void setLogic(void)
{
 if (firstTime == true)
	{
	posX = 200.0f;
	posY = 300.0f;
	posZ = 1.0f;
	 movingRight = false;
	movingLeft = false;
	movingUp = false;
	movingDown = false;
	firstTime = false;
	speed = 3.0f;
	}
   
if(detect_keys(DIK_LEFT))
	{
    movingRight = false;
	movingLeft = true;
	movingUp = false;
	movingDown = false;
    
	}
  else if(detect_keys(DIK_RIGHT))
	{
    movingRight = true;
	movingLeft = false;
	movingUp = false;
	movingDown = false;
	}
  else if(detect_keys(DIK_UP))
	{
     movingRight = false;
	movingLeft = false;
	movingUp = true;
	movingDown = false;
	}
  else if(detect_keys(DIK_DOWN))
	{
    movingRight = false;
	movingLeft = false;
	movingUp = false;
	movingDown = true;
	}
}
void movementLogic(void)
{
if(movingRight == true)
	{
posX += speed;
	}
	else if (movingLeft == true)
	{
  posX -= speed;
	}
	else if (movingUp == true)
	{
    posY -= speed;
	}
	else if (movingDown == true)
	{
    posY += speed;
	}
	
}
void TranslationLogic(void)
{

  if (posX < -70.0f)
  {
	posX = SCREEN_WIDTH;
  }
  else if (posX > SCREEN_WIDTH)
  {
    posX = -70.0f;
  }
  else if (posY < -70.0f)
  {
   posY = SCREEN_HEIGHT;
  }
  else if (posY > SCREEN_HEIGHT)
  {
  posY = -70.0f;
  }
}
void switchPacPics(void)
{
	SAFE_RELEASE(PacTexture);
	
	if (movingRight == true)
	{
D3DXIMAGE_INFO d3dxImageInfo;

	D3DXCreateTextureFromFileEx(d3ddev, L"pacman.bmp", 
		                         67, 60,
								 1, 
		                         D3DPOOL_DEFAULT,
                                 D3DFMT_UNKNOWN,
                                 D3DPOOL_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f),
                                 &d3dxImageInfo,
                                 NULL,
                                 &PacTexture);
	}
	else if (movingLeft == true)
	{
D3DXIMAGE_INFO d3dxImageInfo;

	D3DXCreateTextureFromFileEx(d3ddev, L"pacman2.bmp", 
		                         67, 60,
								 1, 
		                         D3DPOOL_DEFAULT,
                                 D3DFMT_UNKNOWN,
                                 D3DPOOL_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f),
                                 &d3dxImageInfo,
                                 NULL,
                                 &PacTexture);
	}
	else if (movingUp == true)
	{
D3DXIMAGE_INFO d3dxImageInfo;

	D3DXCreateTextureFromFileEx(d3ddev, L"pacman3.bmp", 
		                         67, 60,
								 1, 
		                         D3DPOOL_DEFAULT,
                                 D3DFMT_UNKNOWN,
                                 D3DPOOL_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f),
                                 &d3dxImageInfo,
                                 NULL,
                                 &PacTexture);
	}
	else if (movingDown == true)
	{
     D3DXIMAGE_INFO d3dxImageInfo;

	D3DXCreateTextureFromFileEx(d3ddev, L"pacman4.bmp", 
		                         67, 60,
								 1, 
		                         D3DPOOL_DEFAULT,
                                 D3DFMT_UNKNOWN,
                                 D3DPOOL_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f),
                                 &d3dxImageInfo,
                                 NULL,
                                 &PacTexture);
	}
	else {
    D3DXIMAGE_INFO d3dxImageInfo;

	D3DXCreateTextureFromFileEx(d3ddev, L"pacman5.bmp", 
		                         67, 60,
								 1, 
		                         D3DPOOL_DEFAULT,
                                 D3DFMT_UNKNOWN,
                                 D3DPOOL_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DX_DEFAULT,
                                 D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f),
                                 &d3dxImageInfo,
                                 NULL,
                                 &PacTexture);
	}
}
void init_graphics(void)
{
    // create the vertices using the CUSTOMVERTEX struct
    CUSTOMVERTEX t_vert[] = 
    {
        { 0.0f, 0.0f, 2.0f, 1.0f, D3DCOLOR_XRGB(0, 0, 255), },
        { 0.0f, 480.0f, 2.0f,1.0f, D3DCOLOR_XRGB(0, 255, 0), },
        { 640.f, 0.0f, 2.0f,1.0f, D3DCOLOR_XRGB(255, 0, 0), },
		{ 640.f, 480.0f, 2.0f,1.0f, D3DCOLOR_XRGB(255, 0, 0), },
    };

    // create a vertex buffer interface called t_buffer
    d3ddev->CreateVertexBuffer(3*sizeof(CUSTOMVERTEX),
                               0,
                               CUSTOMFVF,
                               D3DPOOL_MANAGED,
                               &t_buffer,
                               NULL);

    VOID* pVoid;    // a void pointer

    // lock t_buffer and load the vertices into it
    t_buffer->Lock(0, 0, (void**)&pVoid, 0);
    memcpy(pVoid, t_vert, sizeof(t_vert));
    t_buffer->Unlock();

    return;
}
void draw_background(void){
d3ddev->SetFVF(CUSTOMFVF);
d3ddev->SetStreamSource(0, t_buffer, 0, sizeof(CUSTOMVERTEX));
d3ddev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
}


But it isn't working!

Share this post


Link to post
Share on other sites
Endurion    5408
The location 0xfeeefeee is a value the debug runtime sets on pointers which are being deleted.

Therefor my assumption, there's a few places where you call PacTexture->Release(), in other places you use the SAFERELEASE macro. Replace the PacTexture->Release() with SAFERELEASE( PacTexture ).

On another note:

You're loading a new texture everytime pacman changes the direction. Don't do that, preload the textures and simple change a pointer for displaying.

Share this post


Link to post
Share on other sites
Thanks but for some reason I am still getting that error whenever I change to windowed mode. It is strange because when I changed from Fullscreen to windowed or vice versa before it worked. Maybe it's because I added the background?
Also you're saying that I should have 4 different texture objects for the pacman?

Share this post


Link to post
Share on other sites
Endurion    5408
When i change both occurences of PacTexture->Release to SAFE_RELEASE() it doesn't crash anymore for me.

Something's wrong when changing to windowed mode, i can't get the window correctly. But it doesn't crash anymore :)

And yes, i mean either having 5 different texture pointers in memory or put them all on one texture and adjust the texture coordinates accordingly. You can provide a RECT in Sprite->Draw for this.

Share this post


Link to post
Share on other sites
Well, it only crashes if I leave it for a while and then maximize the windowed screen but I don't understand why it is doing this and not working. It was working great just a while ago when I only had my pacman who changed pictures when he went in different directions and he could wrap the room, I think it might be because I added the background or the z buffer although I can't understand why it would do that.
Note: The background doesn't work either for some reason!
Edit: I have changed my pacman bmp so that it is a line of all the different sprites and so that the rectangle changes depending on what way you are facing and it still works fine and with only one texture. I still need to get the background to work though and the windowed mode to work again.

[Edited by - deathknight2005 on November 27, 2006 12:38:00 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this