views:

1846

answers:

4

I know how to rotate image on any angle with drawTexturePath:

int displayWidth = Display.getWidth();
int displayHeight = Display.getHeight();
int[] x = new int[] { 0, displayWidth, displayWidth, 0 };
int[] x = new int[] { 0, 0, displayHeight, displayHeight };
int angle = Fixed32.toFP( 45 );
int dux = Fixed32.cosd(angle );
int dvx = -Fixed32.sind( angle );
int duy = Fixed32.sind( angle );         
int dvy = Fixed32.cosd( angle );       
graphics.drawTexturedPath( x, y, null, null, 0, 0, dvx, dux, dvy, duy, image);

but what I need is a 3d projection of simple image with 3d transformation (something like this)

Can you please advice me how to do this with drawTexturedPath (I'm almost sure it's possible)?
Are there any alternatives?

+2  A: 

Hello,

With the following code you can skew your image and get a perspective like effect:

        int displayWidth = Display.getWidth();
        int displayHeight = Display.getHeight();
        int[] x = new int[] { 0, displayWidth, displayWidth, 0 };
        int[] y = new int[] { 0, 0, displayHeight, displayHeight };                

        int dux = Fixed32.toFP(-1);
        int dvx = Fixed32.toFP(1);
        int duy = Fixed32.toFP(1);
        int dvy = Fixed32.toFP(0);
        graphics.drawTexturedPath( x, y, null, null, 0, 0, dvx, dux, dvy, duy, image);

This will skew your image in a 45º angle, if you want a certain angle you just need to use some trigonometry to determine the lengths of your vectors.

Lucas S.
Hi Lucas, thanks for you answer. What you provide is a simple skew. Frankly I need to be able do any combined 3d transformation (rotate, skew, resize). Take a closer look at http://stackoverflow.com/questions/660304/4-point-transform-images , its resized partly, not just skewed. As I said, there should be some way to do this, because: 1) - they provide dvx, dux, dvy, duy args which are behaves like some trans matrix; 2) there are undocumented setMatrix() and setIdentity() methods in net.rim.device.api.ui.Graphics class (unfortunatley can't make them work)
Max Gontar
+3  A: 

Hi,

The method used by this function(2 walk vectors) is the same as the oldskool coding tricks used for the famous 'rotozoomer' effect. rotozoomer example video

This method is a very fast way to rotate, zoom, and skew an image. The rotation is done simply by rotating the walk vectors. The zooming is done simply by scaling the walk vectors. The skewing is done by rotating the walkvectors in respect to one another (e.g. they don't make a 90 degree angle anymore).

Nintendo had made hardware in their SNES to use the same effect on any of the sprites and or backgrounds. This made way for some very cool effects.

One big shortcoming of this technique is that one can not perspectively warp a texture. To do this, every new horizontal line, the walk vectors should be changed slightly. (hard to explain without a drawing).

On the snes they overcame this by altering every scanline the walkvectors (In those days one could set an interrupt when the monitor was drawing any scanline). This mode was later referred to as MODE 7 (since it behaved like a new virtual kind of graphics mode). The most famous games using this mode were Mario kart and F-zero

So to get this working on the blackberry, you'll have to draw your image "displayHeight" times (e.g. Every time one scanline of the image). This is the only way to achieve the desired effect. (This will undoubtedly cost you a performance hit since you are now calling the drawTexturedPath function a lot of times with new values, instead of just one time).

I guess with a bit of googling you can find some formulas (or even an implementation) how to calc the varying walkvectors. With a bit of paper (given your not too bad at math) you might deduce it yourself too. I've done it myself too when I was making games for the Gameboy Advance so I know it can be done.

Be sure to precalc everything! Speed is everything (especially on slow machines like phones)

EDIT: did some googling for you. Here's a detailed explanation how to create the mode7 effect. This will help you achieve the same with the Blackberry function. Mode 7 implementation

Toad
Reinier, thanks for answer! To be earnest, I'm bad at math, that's why I've took this question up to bounty... You answer is really interesting, and although it may resolve in performance issues, I'll accept it if you provide a Blackberry code for a Mode 7.
Max Gontar
unfortunately I don't own a blackberry. I do have access to it when I'm at a client doing some porting work (which also involves the blackberry). Don't think I'll be there next week though. =^|
Toad
I thought my added link explained a lot of the math involved behind it... Did you try to read it?
Toad
Well, there are free simulators. Can you write at least an algorithm for getting those xv yv xu yu values?
Max Gontar
+1  A: 

You want to do texture mapping, and that function won't cut it. Maybe you can kludge your way around it but the better option is to use a texture mapping algorithm.

This involves, for each row of pixels, determining the edges of the shape and where on the shape those screen pixels map to (the texture pixels). It's not so hard actually but may take a bit of work. And you'll be drawing the pic only once.

GameDev has a bunch of articles with sourcecode here:

http://www.gamedev.net/reference/list.asp?categoryid=40#212

Wikipedia also has a nice article:

http://en.wikipedia.org/wiki/Texture_mapping

Another site with 3d tutorials:

http://tfpsly.free.fr/Docs/TomHammersley/index.html

In your place I'd seek out a simple demo program that did something close to what you want and use their sources as base to develop my own - or even find a portable source library, I´m sure there must be a few.

Kristoffon
Hi Kristoffon, thanks for answer! As I understood, I can paint color per pixel, save it once in bitmap, and draw that bitmap on each refresh. That is slow, more than draw line per y, but can be a solution as well. For proof of concept, can you provide blackberry code for perspective transformation, like in http://www.wholetomato.com/images/tour/mondoPerspectiveTrans.gif?
Max Gontar
Texture mapping by hand(so 1 pixel at a time) is way to slow for j2me. J2me is only 'fast' when it can use API functions to do the heavy lifting.
Toad
A: 

Thanks for answers and guidance, +1 to you all.
MODE 7 was the way I choose to implement 3D transformation, but unfortunately I couldn't make drawTexturedPath to resize my scanlines... so I came down to simple drawImage.

Assuming you have a Bitmap inBmp (input texture), create new Bitmap outBmp (output texture).

Bitmap mInBmp = Bitmap.getBitmapResource("map.png");
int inHeight = mInBmp.getHeight();
int inWidth = mInBmp.getWidth();

int outHeight = 0;
int outWidth = 0;
int outDrawX = 0;
int outDrawY = 0;
Bitmap mOutBmp = null;

public Scr() {
 super();
 mOutBmp = getMode7YTransform();
 outWidth = mOutBmp.getWidth();
 outHeight = mOutBmp.getHeight();
 outDrawX = (Display.getWidth() - outWidth) / 2;
 outDrawY = Display.getHeight() - outHeight;
}

Somewhere in code create a Graphics outBmpGraphics for outBmp.
Then do following in iteration from start y to (texture height)* y transform factor:
1.create a Bitmap lineBmp = new Bitmap(width, 1) for one line
2.create a Graphics lineBmpGraphics from lineBmp
3.paint i line from texture to lineBmpGraphics
4.encode lineBmp to EncodedImage img
5.scale img according to MODE 7
6.paint img to outBmpGraphics
Note: Richard Puckett's PNGEncoder BB port used in my code

private Bitmap getMode7YTransform() {
 Bitmap outBmp = new Bitmap(inWidth, inHeight / 2);
 Graphics outBmpGraphics = new Graphics(outBmp);
 for (int i = 0; i < inHeight / 2; i++) {
  Bitmap lineBmp = new Bitmap(inWidth, 1);
  Graphics lineBmpGraphics = new Graphics(lineBmp);
  lineBmpGraphics.drawBitmap(0, 0, inWidth, 1, mInBmp, 0, 2 * i);
  PNGEncoder encoder = new PNGEncoder(lineBmp, true);
  byte[] data = null;
  try {
   data = encoder.encode(true);
  } catch (IOException e) {
   e.printStackTrace();
  }
  EncodedImage img = PNGEncodedImage.createEncodedImage(data, 
    0, -1);
  float xScaleFactor = ((float) (inHeight / 2 + i))
    / (float) inHeight;
  img = scaleImage(img, xScaleFactor, 1);
  int startX = (inWidth - img.getScaledWidth()) / 2;
  int imgHeight = img.getScaledHeight();
  int imgWidth = img.getScaledWidth();
  outBmpGraphics.drawImage(startX, i, imgWidth, imgHeight, img, 
    0, 0, 0);
 }
 return outBmp;
}

Then just draw it in paint()

protected void paint(Graphics graphics) {
 graphics.drawBitmap(outDrawX, outDrawY, outWidth, outHeight, mOutBmp,
   0, 0);
}

To scale, I've do something similar to method described in Resizing a Bitmap using .scaleImage32 instead of .setScale

private EncodedImage scaleImage(EncodedImage image, float ratioX,
  float ratioY) {

 int currentWidthFixed32 = Fixed32.toFP(image.getWidth());
 int currentHeightFixed32 = Fixed32.toFP(image.getHeight());

 double w = (double) image.getWidth() * ratioX;
 double h = (double) image.getHeight() * ratioY;
 int width = (int) w;
 int height = (int) h;

 int requiredWidthFixed32 = Fixed32.toFP(width);
 int requiredHeightFixed32 = Fixed32.toFP(height);

 int scaleXFixed32 = Fixed32.div(currentWidthFixed32,
   requiredWidthFixed32);
 int scaleYFixed32 = Fixed32.div(currentHeightFixed32,
   requiredHeightFixed32);

 EncodedImage result = image.scaleImage32(scaleXFixed32, scaleYFixed32);
 return result;
}

See also
J2ME Mode 7 Floor Renderer - something much more detailed & exciting if you writing a 3D game!

Max Gontar