I'm trying to program the rendering of a 3D horizontal layer from a 2D image, like the "mode 7" effect from many SNES games.
I don't have any problem with the math, the problem I'm having is with Allegro's al_get_pixel and al_put_pixel methods. They seem to be extremely slow, way too slow for real time. It's slow regardless of whether the bitmap is locked or not.
My question is: is there a way to get and set pixels from a bitmap faster than al_get_pixel and al_put_pixel, like accessing the data directly?
Help is much appreciated!
Pixel by pixel this is going to be extremely slow. However, all you need to do is create a "cache" bitmap, render your tiles to it in regular 2D, then use a perspective transform (al_perspective_transform in 5.1) and then draw the cache bitmap. It's basically free mode 7 using the 3d hardware.
Here's an example where I do it in my game:
{"name":"Ss72.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/7\/0793e42e0f5013f966f31a301dc1e58e.png","w":720,"h":480,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/7\/0793e42e0f5013f966f31a301dc1e58e"}
EDIT: Forgot to mention, I do a rotation along the X axis as well as the perspective transform.
al_perspective_transform in 5.1
I'm using the windows binaries, which is only 5.0.8. According to the manual, 5.1 is unstable.
al_perspective_transform does indeed seem to what I want, however. Would you recommend switching to 5.1 despite this?
5.1 isn't really unstable. The API may change a bit and that's what "unstable" means. I'd recommend it, I've been using 5.1 in my games for years now.
EDIT: You could also just lift the code from 5.1 into your game.
I looked at the 5.1 code, and it seems to set the values of a 4x4 matrix to a projection matrix. When al_use_transform is called, it applies the 3D transformation to the image. It doesn't seem to do this in 5.0.8, instead only using the 2D components of the 4x4 matrix. al_use_transform has the same code in 5.0.8 and 5.1, so one of the functions it calls must be different.
Is there a way I can still use 5.0.8 but still have those nice 3D transformations
I think al_compose_transform is different in 5.1 compared to 5.0. It's exactly as you stated, 5.1 does a "3D" multiplication and I think 5.0 still only does 2D (since it didn't get any of the 3D transforms.) I didn't look at the code but I think you can just copy al_compose_transform from 5.1 and use that to combine your matrices.
al_compose_transform only modifies the matrix, and the matrix itself isn't the problem. The function that actually performs the transformations on the bitmap (which I believe is display->vt->update_transformation from al_use_transform) seems to ignore the 3D components of the matrix in 5.0.8. I tested this by manually modifying the matrix to a set that I know would produce a 3d perspective, but this only effects the bitmap in 2D.
I'm not very familiar with allegro and I can't find where display->vt->update_transformation is in the source code, although it seems to be a lost cause. I'm probably better off switching to 5.1, or finding a different way to produce the 3D effect.
Edit: I've figured out a trick to do it in 5.0.8 without getting or setting pixels
However, I would still like to know if there's a faster way to get/set pixels in general, especially get.
There are two ways basically. 1) lock the bitmap first, then unlock it when you're done. 2) lock it and don't use get/put pixel but use direct access to the lock buffer which can be slightly faster.
In general, try to make the format you create your bitmaps with the one you lock them with, otherwise a conversion has to be done (on both lock and unlock.)
use direct access to the lock buffer which can be slightly faster.
I've been trying to figure out how to do this with no success. Would you be willing to explain how this is done?
Something like that. I couldn't remember if ABGR_8888_LE is alpha first or red first... but just experiment with that.
Things to take note of: you don't have to use that bitmap format. It's a good format if you're using OpenGL and 32 bit bitmaps. Otherwise use something else. Second: the ALLEGRO_PIXEL_FORMAT_ANY means the lr->data block will be formatted in the same format as you created the bitmap. You CAN specify any format there, but if it's not the same as the bitmap format you get two conversions (thus slower.) Last: If you only need to read or write use ALLEGRO_LOCK_READONLY or ALLEGRO_LOCK_WRITEONLY. They're faster if you don't need to read and write.
Thanks for all the help! I got your code to work. It should be faster, plus I do prefer getting an integer from 0-255 rather than a float (checking float equality makes me paranoid). Not sure what the +10 was for, but I didn't seem to need it. The other tips you gave were quite helpful too.
The only problem is that in debug mode I get this error in the console and program crashes:
Assertion failed: bitmap != dest && bitmap != dest->parent, file allegro-git\src\bitmap.c, line 210
In release I have no such error and it runs fine.
Edit: I was drawing a bitmap region to itself, causing the error. Thank you for all the help Trent!
int r = *(ptr+0) + 10; int g = *(ptr+1) + 10; int b = *(ptr+2) + 10;
Can't you just simply write this as:
int r = ptr[0] + 10; int g = ptr[1] + 10; int b = ptr[2] + 10;
Or is there something I'm missing here?
You can do it either way.
It's compiled to exactly the same object code, so no difference whatsoever. But if you have void pointer, you have to cast it to right one first (before you add offset).
The 10 was not important and I forgot to mention why I put it there. I was just going to say this is a way to add brightness to the pixels (though IRL you would check that the value didn't go over 255 with MIN(255, p+10)).