Archived

This topic is now archived and is closed to further replies.

webmunkey

Copying TextureBuffer to UCHAR *

Recommended Posts

IDirect3DTexture8::LockRect

you get back a structure with a void pointer to the data and the pitch of the surface (distance in bytes between consecutive horizontal lines). cast the pointer to UCHAR and copy line by line.

Share this post


Link to post
Share on other sites
So just off the top of my head, I'd do something along the lines of:
UCHAR *surface;
D3DLOCKED_RECT gStruct;
TextureBuffer->LockRect(0, &gStruct, NULL, NULL);
memcpy(surface, (UCHAR *)gStruct.pBits, sizeof(gStruct));
TextureBuffer->UnlockRect(NULL);

Hmm, would that work? Something looks fishy...
Thanks,
-Jesse

Edited by - webmunkey on November 9, 2001 11:24:44 PM

Share this post


Link to post
Share on other sites
Not exactly. You have to use one memcpy per row, because you are not guaranteed that rows are contiguous -- thats why you get the pitch (pitch is sometimes NOT equal to width). I assume you already know the surface width, hieght, and depth.

    
UCHAR *b = new UCHAR [surfaceWidth*surfaceHieght*bytesPerPixel];
D3DLOCKED_RECT r;
surface->LockRect(r, NULL,D3DLOCK_READONLY); //should check error code

for (int y=0; y < surfaceHieght; y++)
memcpy ((void*)b,(r.pBits+r.Pitch*y),r.Pitch); //if you use memcpy it wants void *

surface->UnlockLockRect();



Edited by - invective on November 9, 2001 12:15:16 AM

Share this post


Link to post
Share on other sites
Ahh, wait, isn''t Pitch an int? So, wouldn''t I get an error if I tried to add the two? I could convert pBits to an int and the then cast the entire statement to const void *, but I might loose information. What do you think?
Thanks,
-Jesse

Share this post


Link to post
Share on other sites
You can always add an integer to any pointer type except pointer-to-void. Adding 1 to a pointer advances the pointer to the ''next item'' (for a byte-pointer this would mean an advancement of 1 byte; for a 32-bit-int-pointer it would mean the pointer is advanced 4 bytes; etc.).

Share this post


Link to post
Share on other sites
no, when you add to a void pointer it always adds one byte per number -- if you cast to int* then you have to divide by sizeof (int) because of the way pointer arithmatic works -- it always advances by sizeof(pointertype) bytes, or 4 for integers on win32. Pitch is specified in bytes. Its correct as written, but cast everything to char, then add, then cast it back to void if that makes it easier for you to understand.

char * pbits = (char *) r.pBits;

memcpy ((void*)b,(void*)(pbits+r.Pitch*y),r.Pitch)

pointer math example:

int *p=something;
*(p+4)=1; //same as p[4]=1

that means that the pointer is advanced by 16 bytes and then dereferenced, because each int is 4 bytes wide.

void *p=something;
p+=4; //advances p by only 4 bytes because void is always incremented one byte at a time

Share this post


Link to post
Share on other sites
Hmm, it''s not working in the game... The debuger fails when I try to exit and it is obvious the heightmap isn''t correct.
  
uchar *height_map_ptr = NULL;
// load in height map

hRet = D3DXCreateTextureFromFile(pID3DDevice, "heightd2.bmp", &HeightMap);
if (hRet != D3D_OK)
InitFail(hWnd, hRet, "Error loading bitmap!");

height_map_ptr = new UCHAR[HFIELD_WIDTH * HFIELD_HEIGHT * 24];

D3DLOCKED_RECT gSurface;
HeightMap->LockRect(0, &gSurface, NULL, D3DLOCK_READONLY);

char *pBits = (char *)gSurface.pBits;

for (int i = 0; i < HFIELD_HEIGHT; i++)
memcpy((void *)height_map_ptr, (void *)(pBits + gSurface.Pitch * i), gSurface.Pitch);

HeightMap->UnlockRect(NULL);

It compliles without error; however, inside the game the heightmap is flawed. I think that this because the heightmap is being filled with very high arbitrary number which means that something is going wrong inside of the load heightmap function. Sorry to be such a newbie, but thanks for your help so far,
-Jesse

Share this post


Link to post
Share on other sites
oops, forgot to mention you need to adjust your destination pointer too, otherwise you are copying everything over line one!
note you adjust by width of the image, not pitch of the surface.

memcpy((void *)(height_map_ptr+width*i), (void *)(pBits + gSurface.Pitch * i), gSurface.Pitch);

also, this is very large:

height_map_ptr = new UCHAR[HFIELD_WIDTH * HFIELD_HEIGHT * 24];

if you want a 24 bit image, it is 24 bits * 1/8 bytes per bit, or 3 bytes per pixel, not 24.

height_map_ptr = new UCHAR[HFIELD_WIDTH * HFIELD_HEIGHT * 3];

finally, if you have photoshop, consider converting your image to grayscale and saving as .raw. this gives you 1 byte per pixel (which is usually enough), and its just a byte dump, so you can read it like:

BYTE * pbHieghtField = new BYTE [ width * hieght ];

std::ifstream file;
file.open (filename, std::ios::binary );
file.read ( (char *)pbHieghtField, width * hieght );
file.close();

Share this post


Link to post
Share on other sites
Another thing, D3DXCreateTextureFromFile generates mip maps and might fudge your file size and pixel depth to fit it to something the card likes. You might want to call IDirect3DTexture8::GetSurfaceLevel to make sure image width and hieght and depth are really what you expect them to be.

Edited by - invective on November 10, 2001 1:46:34 PM

Share this post


Link to post
Share on other sites
Okay thanks, we''re close now... Almost there, however, we still have a problem. The heightfield looks like noise, but not random noise, it semi-takes on the shape of valleys and hills, so there must just be something wrong with the image information or the memcpy, this is what the function looks like now:
  
IDirect3DSurface8 * HeightMapSurface = NULL;

// load in height map

hRet = D3DXCreateTextureFromFile(pID3DDevice, "heightd2.bmp", &HeightMap);
if (hRet != D3D_OK)
InitFail(hWnd, hRet, "Error loading bitmap!");

height_map_ptr = new UCHAR[HFIELD_WIDTH * HFIELD_HEIGHT * 3];

HeightMap->GetSurfaceLevel(0, &HeightMapSurface);

D3DLOCKED_RECT gSurface;
HeightMapSurface->LockRect(&gSurface, NULL, D3DLOCK_READONLY);

unsigned char *pBits = (unsigned char *)gSurface.pBits;

for (int i = 0; i < HFIELD_HEIGHT; i++)
memcpy((void *)(height_map_ptr + HFIELD_WIDTH * i), (void *)(pBits + gSurface.Pitch * i), gSurface.Pitch);

HeightMapSurface->UnlockRect();

At first I tried it without the extra surface, and I got the same noise problem. So I changed according to your last post about mipmapping problems, but the problem didn''t change. It''s close, but not quite...
Thanks in advance,
-Jesse

Share this post


Link to post
Share on other sites
heh, you would think i could get one memcpy right after 2 tries, but it should be

memcpy((void *)(height_map_ptr + HFIELD_WIDTH * i), (void *)(pBits + gSurface.Pitch * i), HFIELD_WIDTH);

Also it looks like you are reading in and RGB image, so what channel are you using for the hieght value, or are you summing all 3? If you are adding all 3 colors it might not look the same as the bitmap, thats why its usually easier to use grayscale, or one channel.

Share this post


Link to post
Share on other sites
Doh! I am not a smart person. Of course, now instead of indexed colors, the colors come to the height_map_ptr in 4 parts, r, g, b and a. So for uses in a heightmap, all I have to do is make the bitmap greyscale and then use the blue value (since they''ll all be the same anyway). The blue value can be found by multiplying the height_map_ptr[x] by 4. Thanks for your help,
Cheers,
-Jesse

Share this post


Link to post
Share on other sites