Sign in to follow this  
Yours3!f

OpenGL glTexSubImage2D in directx?

Recommended Posts

Hi,

in OpenGL I used glTexSubImage2D to fill part of a texture with an image, what is the corresponding function in directx (9)? I googled a lot, but I couldn't find it, neither in msdn...

Best regards,
Yours3!f

Share this post


Link to post
Share on other sites
There is none.

OK, that's the short and unhelpful version. The slightly longer version is that you use much the same process as when creating a texture for the first time (using pure D3D, not D3DX). This involves issuing a LockRect call, copying in the data, then issuing an UnlockRect call. You need to ensure that you've got the correct memory pool and/or usage flags set at creation time (D3DPOOL_MANAGED is normally sufficient for all cases, but you can also use the default pool with D3DUSAGE_DYNAMIC). Another alternative is to have two textures - one in the default pool, the other in the system memory pool. Update the system memory version (LockRect/UnlockRect), then use UpdateTexture to copy to the default pool version. UpdateSurface is another option which lets you specify subrects. Finally, when using LockRect/UnlockRect you can have some control over when dirty regions of the texture get updated.

By now you're probably getting the idea that there are many different ways of doing this, and you'd be right. This is both good and bad; it's good because it gives you a very fine level of control over the texture update process but it's bad because it means that you need to experiment with all of these different ways to find what's optimal for your use case - you could spend days, weeks or months playing with all the combinations of options and get nothing else done!

Probably the simplest way all round is what I'd initially recommend; you can then experiment with other ways if you need more performance or more control. It goes something like this:

[list][*]Initially create the texture in D3DPOOL_MANAGED with D3DFMT_X8R8G8B8 (D3DFMT_A8R8G8B8) and usage 0.[*]Use LockRect to access the texture data, a rect of NULL will get the entire texture rectangle or you can specify a subrect.[*]Cast the pBits member of your D3DLOCKED_RECT to unsigned char * or unsigned int * as required.[*]Write in your data.[*]Use UnlockRect to finish up.[/list]

Share this post


Link to post
Share on other sites
Thanks for the clarifying comment :)

I think I did most of what you told me right, although I don't know how to write in the raw data into the texture...

here's the function I made for this:
[code]
HRESULT create_sub_image(HBITMAP bitmap, LPDIRECT3DTEXTURE9* tex, int width, int height)
{
HRESULT result;

//get raw image data
BITMAP bmp;
GetObject(bitmap, sizeof(bmp), &bmp);
int size = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
BYTE* bytes = new BYTE[size];
GetBitmapBits(bitmap, size, bytes);

//create the texture
result = D3DXCreateTexture(dx_dev, width, height, D3DX_DEFAULT, NULL, D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, tex);
if(FAILED(result))
{
return result;
}

//define sub-rectangle (so we place the texture in the middle of the other one
RECT rect;
rect.left = (width - bmp.bmWidth) / 2;
rect.right = rect.left + bmp.bmWidth;
rect.top = (height - bmp.bmHeight) / 2;
rect.bottom = rect.top + bmp.bmHeight;

D3DLOCKED_RECT locked_rect; //do I need to specify anything?

//lock memory
result = (*tex)->LockRect(D3DX_DEFAULT, &locked_rect, &rect, NULL);
if(FAILED(result))
{
return result;
}

//cast bits to char* raw RGB data (0...255)
char* bits = (char*)locked_rect.pBits;

//write in data???
//
//

result = (*tex)->UnlockRect(D3DX_DEFAULT);

return result;
}
[/code]

Share this post


Link to post
Share on other sites
I wrote a small example in which lockrect fails...
it needs two 24bit bitmaps, which can be created in resource view.
it supposed to copy the content of the second bitmap onto the first, and as a texture display it.

[code]
#define WIN32_LEAN_AND_MEAN

#include <Windows.h>
#include <d3d9.h>
#include <d3dx9.h>

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <tchar.h>
#include <sstream>
#include <stdexcept>
#include <assert.h>

#include "resource.h"

LPDIRECT3D9 dx;
LPDIRECT3DDEVICE9 dx_dev;
D3DPRESENT_PARAMETERS dx_pp = {0};

HWND the_hwnd;
HINSTANCE the_instance;
WNDCLASSEX wc;

int screen_width = 0;
int screen_height = 0;

#define CFVF (D3DFVF_XYZ | D3DFVF_TEX1)
struct CV
{
CV(FLOAT xx, FLOAT yy, FLOAT zz, FLOAT uu, FLOAT vv)
{
x = xx;
y = yy;
z = zz;
u = uu;
v = vv;
}
FLOAT x, y, z, u, v;
};

LPDIRECT3DVERTEXBUFFER9 vbo = NULL;
LPDIRECT3DTEXTURE9 texture;

DWORD global_time = GetTickCount();

float rotation = 0.0f;

float near_p = 1.0f;
float far_p = 1000.0f;
float fovy = D3DXToRadian(45.0f);
float wi = 0.0f;
float he = 0.0f;
float aspect = 0.0f;

LRESULT CALLBACK window_process(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
HRESULT init(int w, int h);
void shutdown();
void enter_main_loop();
HRESULT init_dx();
HRESULT init_vbo();
void render();
void clean_up();
BOOL build_present_parameters();
void handle_keypress(WPARAM wparam);
HRESULT load_bitmap(LPCTSTR bitmap, LPDIRECT3DTEXTURE9* tex);
HRESULT create_sub_image(HBITMAP bitmap, LPDIRECT3DTEXTURE9* tex);

//enables key press handling
#ifndef D3BUG
#define D3BUG
#endif

HRESULT load_bitmap(LPCTSTR bitmap, LPDIRECT3DTEXTURE9* tex)
{
HRESULT result = D3DXCreateTextureFromResource(dx_dev,
NULL,
bitmap,
tex);

if(FAILED(result))
{
return result;
}

return result;
}

HRESULT create_sub_image(HBITMAP bitmap, LPDIRECT3DTEXTURE9* tex)
{
HRESULT result;

//get raw image data
BITMAP bmp;
GetObject(bitmap, sizeof(bmp), &bmp);
int size = bmp.bmWidth * bmp.bmHeight * (bmp.bmBitsPixel / 8);
BYTE* bytes = new BYTE[size];
GetBitmapBits(bitmap, size, bytes);

D3DSURFACE_DESC tex_desc;
(*tex)->GetLevelDesc(0, &tex_desc);

//define sub-rectangle (so we place the texture in the middle of the other)
unsigned int width = bmp.bmWidth;
unsigned int height = bmp.bmHeight;
unsigned int pitch = bmp.bmWidth * (bmp.bmBitsPixel / 8);

assert(tex_desc.Width >= width);
assert(tex_desc.Height >= height);

RECT rect;
rect.left = (tex_desc.Width / 2) - (width / 2);
rect.right = width;
rect.top = (tex_desc.Height / 2) - (height / 2);
rect.bottom = height;


D3DLOCKED_RECT locked_rect; //do I need to specify anything?

std::basic_stringstream<TCHAR> ss;

//lock memory
result = (*tex)->LockRect(0, &locked_rect, &rect, NULL);
if(FAILED(result))
{
return result;
}

//cast bits to char* raw RGB data (0...255)
BYTE* bits = (BYTE*)locked_rect.pBits;

//write in data???
for(unsigned int y = 0; y < rect.bottom; y++)
{
for(unsigned int x = 0; x < rect.right; x++)
{
unsigned int dest_index = (x * 4 + (y * (locked_rect.Pitch)));

//flip the source image horizontally
unsigned int source_index = (((rect.right - x - 1) * 4) + (y * pitch));

bits[dest_index] = bytes[source_index]; //r
bits[dest_index + 1] = bytes[source_index + 1]; //g
bits[dest_index + 2] = bytes[source_index + 2]; //b
bits[dest_index + 3] = bytes[source_index + 3]; //a
}
}

result = (*tex)->UnlockRect(0);

return result;
}

int WINAPI _tWinMain(HINSTANCE instance, HINSTANCE, LPTSTR, int nCmdShow)
{
the_instance = instance;

HRESULT result = init(1024, 576);

enter_main_loop();

shutdown();

return result;
}

HRESULT init(int w, int h)
{
screen_width = w;
screen_height = h;

ZeroMemory(&wc, sizeof(WNDCLASSEX));

wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = window_process;
wc.hInstance = the_instance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = L"WindowClass";

RegisterClassEx(&wc);

the_hwnd = CreateWindowEx(NULL,
L"WindowClass",
L"DX",
WS_OVERLAPPEDWINDOW,
20,
20,
screen_width,
screen_height,
NULL,
NULL,
the_instance,
NULL);

ShowWindow(the_hwnd, SW_SHOW);

return init_dx();
}

HRESULT init_dx()
{
HRESULT result;

dx = Direct3DCreate9(D3D_SDK_VERSION);

//build pp
if(!build_present_parameters())
{
return -1;
}

result = dx->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
the_hwnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&dx_pp,
&dx_dev);
if(FAILED(result))
{
return result;
}

result = dx_dev->SetRenderState(D3DRS_LIGHTING, FALSE);
if(FAILED(result))
{
return result;
}
result = dx_dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
if(FAILED(result))
{
return result;
}
result = dx_dev->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
if(FAILED(result))
{
return result;
}
result = dx_dev->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
if(FAILED(result))
{
return result;
}
result = dx_dev->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
if(FAILED(result))
{
return result;
}

result = init_vbo();
if(FAILED(result))
{
return result;
}

result = load_bitmap(MAKEINTRESOURCE(IDB_BITMAP1), &texture);
if(FAILED(result))
{
return result;
}

HBITMAP bitmap = LoadBitmap(the_instance, MAKEINTRESOURCE(IDB_BITMAP2));
result = create_sub_image(bitmap, &texture);

return result;
}

BOOL build_present_parameters()
{
ZeroMemory(&dx_pp, sizeof(dx_pp));
dx_pp.Windowed = TRUE;
dx_pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
dx_pp.hDeviceWindow = the_hwnd;
dx_pp.BackBufferFormat = D3DFMT_X8R8G8B8;
dx_pp.BackBufferWidth = screen_width;
dx_pp.BackBufferHeight = screen_height;
dx_pp.EnableAutoDepthStencil = TRUE;
dx_pp.AutoDepthStencilFormat = D3DFMT_D16;

return TRUE;
}

HRESULT init_vbo()
{
HRESULT result;

aspect = (float)screen_width / (float)screen_height;
he = near_p * tan(fovy / 2.0f);
wi = abs(-he * aspect);
wi *= 2.0f;
he *= 2.0f;

CV vertices[] =
{
CV(wi / -2.0f, he / -2.0f, 0.0f, 0.0f, 1.0f),
CV(wi / 2.0f, he / -2.0f, 0.0f, 1.0f, 1.0f),
CV(wi / 2.0f, he / 2.0f, 0.0f, 1.0f, 0.0f),
CV(wi / -2.0f, he / -2.0f, 0.0f, 0.0f, 1.0f),
CV(wi / 2.0f, he / 2.0f, 0.0f, 1.0f, 0.0f),
CV(wi / -2.0f, he / 2.0f, 0.0f, 0.0f, 0.0f)
};

result = dx_dev->CreateVertexBuffer(6 * sizeof(CV), 0, CFVF, D3DPOOL_MANAGED, &vbo, NULL);
if(FAILED(result))
{
return result;
}

VOID* v;

vbo->Lock(0, 0, (void**)&v, 0);
memcpy(v, vertices, sizeof(vertices));
vbo->Unlock();
return result;
}

void render()
{
D3DXMATRIX mat_world, mat_view, mat_proj;

dx_dev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);

dx_dev->BeginScene();

dx_dev->SetFVF(CFVF);

D3DXMatrixPerspectiveFovLH(&mat_proj, fovy, aspect, near_p, far_p);

dx_dev->SetTransform(D3DTS_PROJECTION, &mat_proj);

D3DXMatrixLookAtLH( &mat_view, &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
&D3DXVECTOR3( 0.0f, 0.0f, -1.0f ),
&D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
dx_dev->SetTransform( D3DTS_VIEW, &mat_view );

D3DXMATRIX mat_sca;
D3DXMatrixTranslation(&mat_world, 0.0f, 0.0f, -near_p);
dx_dev->SetTransform(D3DTS_WORLD, &mat_world);

dx_dev->SetTexture(0, texture);

dx_dev->SetStreamSource(0, vbo, 0, sizeof(CV));
dx_dev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

dx_dev->EndScene();
dx_dev->Present(NULL, NULL, NULL, NULL);
}

void shutdown()
{
clean_up();
}

void clean_up()
{
vbo->Release();
dx_dev->Release();
dx->Release();
SendMessage(the_hwnd, WM_DESTROY, NULL, NULL);
}

void enter_main_loop()
{
MSG msg;
int return_value;

if(dx_dev)
{
render();
}

//the main message pump
while((return_value = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if(return_value == -1)
{
//error
break;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}

LRESULT CALLBACK window_process(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
break;
}
#ifdef D3BUG
case WM_KEYDOWN:
{
handle_keypress(wparam);
break;
}
#endif
default:
{
if(dx_dev)
{
int current_time = GetTickCount() - global_time;
if(current_time > 33)
{
render();
global_time = GetTickCount();
}
}
break;
}
}

return DefWindowProc(hwnd, msg, wparam, lparam);
}

void handle_keypress(WPARAM wparam)
{
switch(LOWORD(wparam))
{
case 0x20:
{
rotation += 1.0f;
}
default:
break;
};
}
}
[/code]

EDIT: ok I solved it. see the example above

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