Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Mapping a texture on a sphere.

This thread is locked; no one can reply to it. rss feed Print
 1   2 
Mapping a texture on a sphere.
Johan Peitz
Member #9
April 2000
avatar

I planning a game where the player rolls balls around. Very exciting. :)

Anyway, I need to map a texture on a 2d circle so that it looks like a ball in 3d. The texture is 64x64 in this case and the ball is a circle with a radius of 16 pixels.

What I would like is a lookup table for the circle so I know what pixel to draw from the texture.

Has anyone got some cool mathematic formulas or something for this? I'm pretty sure someone has posted a question like this before and that someone else wrote something with a picture of the earth mapped on a circle... Any ideas?

--
johan peitz :: things I made

Shade
Member #1,152
April 2001
avatar

Maybe the easiest way is to use a rotating sprite...

formulas with sin/cosine are nice but you can't really use then to fill an entire circle.

If you do something like this:

for (r=0;r<50;r++){
for (t=0;t<256;t++){
x=r*sin(2*PI*t/256);
y=r*cos(2*pi*t/256);
draw_pixel(x,y);
}}

you'll see that there are points missing.

Shade8-)

Johan Peitz
Member #9
April 2000
avatar

Hmm... Maybe I didn't express myself very clear. I don't want to simply rotate a sprite. I want to wrap a texture around a ball.

The thought was to create a lookup table for a circle and the map the texture to the circle. The effect of getting the ball to roll would be achieved by shifting the texture. But! How to I calculate the lookup?

--
johan peitz :: things I made

Paul Pridham
Member #250
April 2000
avatar

Johan, I think I have some old Allegro demo code of this very thing. I will find it on my HD later tonight and let you know.

Gabhonga
Member #1,247
February 2001
avatar

also there's this library once made for allegro, maybe it was plush or p3d...whatever, it had several routines for rendering "3d" spheres, i.e. the all-famous, vectorballs having their textures projected and even shaded in different ways. it doesn't include perspective warp, but you don't actually notice that for spheres unless they're very close, or your fov is very twisted.

--------------------------------------------------------
sigs suck

Oscar Giner
Member #2,207
April 2002
avatar

You could use ray-tracing. With this technique you can get perfect spheres, because it uses the sphere formula. I don't know how to do it, maybe someone can help you.

Bob
Free Market Evangelist
September 2000
avatar

I posted code to do this on [AGP] a little while ago. I'm not at my computer (for a few months), so I can't send it to you. Perhaps you can ask there if someone still has a copy of it.

--
- Bob
[ -- All my signature links are 404 -- ]

Fladimir da Gorf
Member #1,565
October 2001
avatar

How come I was just asking the same question? ??? :) I've already tried to make my own one, but it doesn't exactly look like a sphere... or actually I didn't even expect it to... I KNEW it'd look like a pyramid with a circle bottom...

If I remember corrent the concept I'm using is:

For every upleft pixel: Count the current radius. Divide it with the maxium radius to get the number 'm'. Now, use a special function (I've got a wrong one) which gets the number 'm' and produces the number 'n'. Now we divide 'n' with 'm' and get the number 'k'. Then, the current upright position where we'll read the color is (centreX -k * (centreX -currentX), centreY -k * ( centreY -currentY )).
Current up-right, lower-left and lower-right coordinates can be calculated similar way using the same 'k'...

OK I somehow think I didn't get it correct. But what is this function that takes 'm' and returns 'n'? ??? ???

OpenLayer has reached a random SVN version number ;) | Online manual | Installation video!| MSVC projects now possible with cmake | Now alvailable as a Dev-C++ Devpack! (Thanks to Kotori)

Gabhonga
Member #1,247
February 2001
avatar

hmm...if anyone finds a good sollution/explanation to this problem, please post info here!
however, it might prove interesting too look for people mad about maps...
I've once tried to map a texture onto a sphere using (quite logical...) spherical coordinates, i.e. for each vertex I calculated the latitude and longitude angles and just made them u and v! :D
this "worked" pretty nice, but distorted the texture somewhat, so you have to be careful when drawing it, expecially the area around the poles will be somewhat warped together!! I believe there's other ways to map square textures to a sphere that give better results.
for 2d (without any 3d model in between) you'd have to go raytracing...sounds complicated, but for a simple sphere it's quite easy and can be optimized a lot...
another idea that might be even faster would be to implement one of those classical 2d lens effects (basically an array with texture offsets that warps anything it's applied to a bit like a magnifying glass). by calculating the lens like half of a sphere and scrolling/rotating the texture underneath, it might also do the trick (quite fast even!)

--------------------------------------------------------
sigs suck

Fladimir da Gorf
Member #1,565
October 2001
avatar

"maybe it was plush or p3d"

I searched for both of them and it heavily seems they used 3D code and an older allegro version (and even DJGPP makefiles I think...) So....?

OpenLayer has reached a random SVN version number ;) | Online manual | Installation video!| MSVC projects now possible with cmake | Now alvailable as a Dev-C++ Devpack! (Thanks to Kotori)

Thomas Fjellstrom
Member #476
June 2000
avatar

wasn't p3d slurped up by allegro? Or did it get removed?

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Bob
Free Market Evangelist
September 2000
avatar

Yes, p3d was merged into Allegro. However, p3d did NOT do sphere mapping of any kind.

--
- Bob
[ -- All my signature links are 404 -- ]

Gabhonga
Member #1,247
February 2001
avatar

hmm, it could be that it was called "more3d". yes, it's quite old, but it had this textured sphere rendering code that wasn't actually fast, but worked fine :-)

--------------------------------------------------------
sigs suck

Johan Peitz
Member #9
April 2000
avatar

Ok ppl, here's some results then. :)

1) Paul Pridham mailed me some code by a guy named David Lenhart. I haven't really looked at it that much and I haven't managed to compile it, it uses some old version of allegro I think and I didn't bother. Get it here: [url http://www.dtek.chalmers.se/~d98peitz/junk/earth.zip]

2) My own version. Using lookup tables and is not very mathematically correct, but it's fast enough and looks ok. Instead of sphere-mapping I apply some kind of lens filter to the texture and shows. Get it here: [url http://www.dtek.chalmers.se/~d98peitz/junk/balls.zip]

Feel free ta ask questions and/or come with imporvements.

--
johan peitz :: things I made

Gabhonga
Member #1,247
February 2001
avatar

hehe well, and I finally remembered (& then searched my hd, and even found!!!) the library that was able of doing "3d" spheres!! actually the code is quite messy to read, but it works and it's also quite good commented...the name was AllegPak!! (har, the only one allegro 3d lib I hadn't mentioned yet ::))

--------------------------------------------------------
sigs suck

Steve Terry
Member #1,989
March 2002
avatar

hmm, well anyone got a MingW32 edition of the code. I erased my DJGPP compiler because windowsXP forced me to.... or do you at least have an executable I can look at so maybe I can rewrite it if the effect looks good.

___________________________________
[ Facebook ]
Microsoft is not the Borg collective. The Borg collective has got proper networking. - planetspace.de
Bill Gates is in fact Shawn Hargreaves' ßî+çh. - Gideon Weems

Plucky
Member #1,346
May 2001
avatar

Some years back I played with a spherical texture mapping algorithm using lookup tables that was developed by a Hans Kopp. I apparently don't have his original source, but I found my variation of his algorithm written for Allegro.

His algorithm in a nutshell: It is relatively easy to wrap a texture on a sphere for rotations about the sphere's N-S pole axis. For this you need a lookup table from pixel coordinate to spherical coordinate. Add the rotation (longitude), and finally convert spherical to texture coordinates. The problem is when you want the sphere to rotate on all 3 axis. Hans' solution was to recognize the ease of rotating on a N-S pole axis and so created a lookup table that transformed a spherical coordinate such that one of the E-W axis is rotated to the N-S axis. After the coordinate system transformation, just add the now-longitudinal rotation. Perform this transformation once more, and you have rotation for all 3 axes.

1 /* TEX_SIZE needs to be <=256 due to coord_transform_table holding shorts.
2 In this code, it needs to be a power of 2, but a couple of minor tweaks,
3 and this constraint can go away */
4#define TEX_SIZE 256
5#define ASPECT_RATIO 1.05
6#ifndef M_PI
7#define M_PI 3.14159265
8#endif
9 
10unsigned short *coord_transform_table;
11unsigned short *screen2sphere_table;
12 
13// unsigned short *texture; // Texture table; a 1D array holding the texture; used for convenience because the spherical coordinates are held in one variable.
14 
15void Spherical2Cartesian(int alpha, int beta, double *x, double *y, double *z)
16{
17 /* Convert to radians */
18 double alpha1 = (double)alpha * 2 * M_PI / TEX_SIZE;
19 double beta1 = (double)(beta - TEX_SIZE/2) * M_PI / TEX_SIZE;
20
21 /* Convert to Cartesian */
22 *x = cos(alpha1) * cos(beta1);
23 *y = sin(beta1);
24 *z = sin(alpha1) * cos(beta1);
25}
26 
27void Cartesian2Sphere(double x, double y, double z, int *alpha, int *beta)
28{
29 double beta1, alpha1, w;
30
31 /* convert to Spherical Coordinates */
32 beta1 = asin(y);
33 if (fabs(cos(beta1)) > 0.0) { // we'll be dividing by cos(beta1)
34 w = x / cos(beta1);
35 if (w > 1) w = 1; if (w < -1) w = -1; // Check bounds
36 alpha1 = acos(w);
37 if (z/cos(beta1) < 0) // Check for wrapping around top/bottom of sphere
38 alpha1 = 2 * M_PI - alpha1;
39 }
40 else
41 alpha1 = 0;
42
43 /* Convert to texture coordinates */
44 *alpha = (int)(alpha1 / (M_PI * 2) * SIZEOFTEX);
45 *beta = (int)(beta1 / M_PI * SIZEOFTEX + SIZEOFTEX/2);
46
47 /* 'Clip' the texture coordinates */
48 if (*alpha < 0) *alpha = 0;
49 if (*alpha >= SIZEOFTEX) *alpha = SIZEOFTEX-1;
50 if (*beta < 0) *beta = 0;
51 if (*beta >= SIZEOFTEX) *beta = SIZEOFTEX-1;
52}
53 
54void CreateTextureTable(const char *filename, unsigned short *texture)
55/* unsigned short for 16 bit, can use int for 32 bit, char for 8 bit */
56{
57 int i, j;
58 BITMAP *bmp;
59 
60 bmp = load_bmp(filename, NULL);
61
62 for (i=0; i<TEX_SIZE; i++)
63 for (j=0; j<TEX_SIZE+1; j++)
64 texture[j*TEX_SIZE+i] = getpixel(bmp, i*bmp->w/TEX_SIZE, j*bmp->h/TEX_SIZE);
65}
66 
67void InitLookupTables()
68{
69 int alpha, beta; // Spherical Coordinates
70 int i, j;
71 double x, y, z; // Cartesian coordinates
72 
73 coord_transform_table = (unsigned short *)malloc((TEX_SIZE*(TEX_SIZE+1)*sizeof(short));
74 
75 screen2sphere_table = (unsigned short *)malloc(TEX_SIZE * sizeof(short));
76 
77 /* Compute the Lookup Table for the Switching between
78 the Coordinate-Systems */
79 for(j = 0; j < TEX_SIZE+1; j++)
80 for(i = 0; i < TEX_SIZE; i++) {
81 /* Convert Spherical Coord. to Cartesian Coord. */
82 Spherical2Cartesian(i, j, &x, &y, &z);
83 /* Convert Cartesian Coord. to Spherical Coord.
84 Notice it's not x,y,z but x,z,y */
85 Cartesian2Sphere(x, z, y, &alpha, &beta);
86 /* lower order of bits occupied by alpha,
87 upper order shifted by TEX_SIZE occupied by beta */
88 coord_transform_table[i + j*TEX_SIZE] = alpha + beta*TEX_SIZE;
89 }
90 
91 /* Compute the Lookup Table that is used to
92 convert the 2D Screen Coordinates to the initial
93 Spherical Coordinates */
94 for(i = 0; i < TEX_SIZE; i++) {
95 screen2sphere_table<i> = (int)(acos((double)(i-TEX_SIZE/2+1) * 2/TEX_SIZE)
96 * TEX_SIZE/M_PI);
97 screen2sphere_table<i> %= TEX_SIZE;
98 }
99}
100 
101void DrawSphere(int phi, int theta, int psi, int radius, unsigned short *texture,
102 BITMAP *dest, int center_x, int center_y)
103/* Unoptimized for clarity; */
104{
105 int x, y; // current Pixel-Position
106 int xr; // Half Width of Sphere (pixels) in current scanline
107 int beta1, alpha1; // initial spherical coordinates
108 fixed xinc, xscaled; // auxiliary variables
109 int alpha_beta2,alpha_beta3;
110 /* spherical coordinates of the 2nd and 3rd rotated system
111 (the 2 coordinates are stored in a single integer) */
112 
113 /* For all Scanlines ... */
114 for(y = -radius+1;y < radius; y++) {
115 /* compute the Width of the Sphere in this Scanline */
116 xr = (int)(sqrt(radius*radius - y*y) * ASPECT_RATIO); // Can be turned into fixed point
117 if (xr==0) xr = 1;
118
119 /* computer the first Spherical Coordinate beta */
120 beta1 = screen2sphere_table[(y+radius) * TEX_SIZE/(2*radius)] * TEX_SIZE;
121 xinc = (TEX_SIZE << 16) / (2*xr);
122 xscaled = 0;
123 
124 /* For all Pixels in this Scanline ... */
125 for(x = -xr; x < xr; x++) {
126 /* compute the second Spherical Coordinate alpha */
127 alpha1 = screen2sphere_table[xscaled >> 16] / 2;
128 xscaled += xinc;
129
130 alpha1 = alpha1 + phi;
131 /* Rotate Texture in the first Coordinate-System (alpha,beta);
132 Switch to the next Coordinate-System and rotate there */
133 alpha_beta2 = coord_transform_table[beta1 + alpha1] + theta;
134 /* the same Procedure again ... */
135 alpha_beta3 = coord_transform_table[alpha_beta2] + psi;
136
137 /* draw the Pixel */
138 putpixel(dest, x+center_x, y+center_y, texture[alpha_beta3]);
139 }
140 }
141}

Eric Love
Member #846
December 2000

Just this week I made a small program which draws a globe if you give it a rectangular world map.
circ.zip (245kB) includes code, exe and bmp.
The current orientation of the ball is stored as a matrix, and you can rotate it a few different ways. Lookup tables are used to convert each screen x,y to (x,y,z) which are converted by the matrix to another x,y,z which worked out which pixel of the rectangular map to use. Slow compared to polygons I think, but if you're using a small enough ball, you'll be alright. There may be room more more optimization in my code.

Kloks
Member #943
February 2001
avatar

Hi Johan,
I read your code, very impressive result with so small things :o
Have you found this lookup table ? I'm no sarcastic.
I have not complete understand it (render_ball) but I'm working on it!
I'd like to use some stuff like this in my games.

| K-nion | Weed | Rebound | Web site |

Each One Teach One

Steve Terry
Member #1,989
March 2002
avatar

Pucky's code works great too, and you can use spheres of any size, it also runs a bit slow, but if you cange teh putpixel to a direct memory access and maybe the sqrt() function in draw sprite to a fixed point notation some speed might be improved.. though it ran fast enough for me. I got about 140fps with a radius of 128, and 70fps at radius of 256, that fills the entire screen at 640x480. The effect is good, and you can rotate on all three axis.

Problems with code
- SIZEOFTEX undeclared, define it as 256
- Parse error line blah, add another )
- uncomment the *texture
- malloc your texture before creating the texture table
- FREE all used memory (all three pointers)
- psi, theta, and phi range between 0 and 255, going over will trash the look-up tables and the program.
- texture size must be 256x256x16, but it looks great anyway.

called like:

InitLookupTables();
texture = (unsigned short *)malloc(sizeof(short) * TEX_SIZE * TEX_SIZE);
CreateTextureTable("Earth2.bmp", texture);
do{
    clear(buffer);
    DrawSphere(phi, theta, psi, radius, texture, buffer, SCREEN_W >> 1, SCREEN_H >> 1); 
    blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
}while(!key[KEY_ESC]);

___________________________________
[ Facebook ]
Microsoft is not the Borg collective. The Borg collective has got proper networking. - planetspace.de
Bill Gates is in fact Shawn Hargreaves' ßî+çh. - Gideon Weems

Johan Peitz
Member #9
April 2000
avatar

Kloks, glad you like it. :) I made up the lookup table myself. I had a general idea of what I wanted and then I tweaked it until it looked ok.

It is not by any means a very stringent way to solve the problem. Also, it does not map a texture on a sphere. It's more like a lens funciton on a texture, giving the impression of a ball. This is mostly noticeable on the red/white squared ball. All squares are really squares, and this is not possible on a sphere.

Feel free to use what ever you want of the code.

--
johan peitz :: things I made

Plucky
Member #1,346
May 2001
avatar

Sorry about the sloppy code. I just lifted it out my test code, and obviously spent little time cleaning it up. Thanks Steve for picking through it and cleaning it up.

I had TEX_SIZE and SIZEOFTEX, which are the same.

I commented out *texture because the code could handle more than one.

Quote:

texture size must be 256x256x16, but it looks great anyway.

Actually not quite true. The texture table is size TEX_SIZE x TEX_SIZE x 16. But when loading a texture, the texture could be of any size... it would just get crudely stretched to TEX_SIZE x TEX_SIZE. Also you can change the color depth of the texture table as well. A better stretching algorithm would improve the look of the texture. Also the element size of the texture table need not be 16 bits; it could be 8 bit or 32 bits depending on color depth.

TEX_SIZE is limited to 256 because it is stuffing two numbers into a short. TEX_SIZE can be expanded to 65536 if the lookup table was a 32 bit array. However you run the risk of the table not fitting in memory cache.

Also the code makes no distinction between texture size TEX_SIZE and the number of bits occupied by the low and high order data when stuffing the two values into one. A few tweaks and you can make this distinction such that TEX_SIZE need not be a power of 2.

Quote:

psi, theta, and phi range between 0 and 255, going over will trash the look-up tables and the program.

Should have pointed this out earlier. Somewhere in the code, one should have psi %= TEX_SIZE; or the equivalent.

Quote:

if you cange teh putpixel to a direct memory access and maybe the sqrt() function in draw sprite to a fixed point notation some speed might be improved

You can unroll the loop a bit along with direct memory access to increase speed. For example with 16 bit color:

1 unsigned int color;
2 unsigned int address = bmp_write_line(dest, y+center_y);
3 /* For all Pixels in this Scanline ... */
4 for(x = -xr; x < xr; x+=2) {
5 alpha1 = screen2sphere_table[xscaled >> 16] / 2;
6 xscaled += xinc;
7 alpha1 = alpha1 + phi;
8 alpha_beta2 = coord_transform_table[beta1 + alpha1] + theta;
9 alpha_beta3 = coord_transform_table[alpha_beta2] + psi;
10 color = texture[alpha_beta3] << 16;
11 
12 /* Repeat */
13 alpha1 = screen2sphere_table[xscaled >> 16] / 2;
14 xscaled += xinc;
15 alpha1 = alpha1 + phi;
16 alpha_beta2 = coord_transform_table[beta1 + alpha1] + theta;
17 alpha_beta3 = coord_transform_table[alpha_beta2] + psi;
18 color |= texture[alpha_beta3];
19 
20 /* draw 2 pixels */
21 bmp_write32(address+(x+center_x)*2, color);
22 }

Obviously you can do the same with 8 bit color by unrolling the loop 4 times.

Steve Terry
Member #1,989
March 2002
avatar

hehe, cool, well it doesn't give much of a speed-up using the 32 bit packed way, and oddly it gives some weird fuzzy stuff around teh pixel blocks the other one doesn't have. That's okay anyway. But now i'm wondering if anyone has any tutorials on how to do the packed routines. I've tried incorperating them in other functions I've made and it doesn't quite work. My way was just doing this:
((short *)dest -> line[y+center_y])[x + center_x] = texture[alpha_beta3];
which gave the same amount of speed-up.
Another method I've ran across is this:
color = (color |(color<< 16)) & 0x07E0F81F;
color = ((color * alpha)>> 5) & 0x07E0F81F;
color = color|(color >> 16);
for doing faster blurs, it also does not work:'(
Oh and be sure to check that y+center_y and x+center_x are within dest->w and dest->h or you will murder the program.
I'm kinda new at learning how to optimize in Allegro, but I caught onto fixed point, bitshifts, and vectors fast.
For now i'm forced to use:
r = getr16(color);
r*=a;
g = getg16(color);
g*=a;
b = getb16(color);
b*=a;
((short *)src -> line[y])[x] = makecol16(r, g, b);
which is quite um .. crappy.
[edit]
The color = (color | color << 16... stuff i'm not trying to put that into yoru function, i'm just wondering if there are any tutorials or if someone could help me out on incorperating that into my functions.
[/edit]

___________________________________
[ Facebook ]
Microsoft is not the Borg collective. The Borg collective has got proper networking. - planetspace.de
Bill Gates is in fact Shawn Hargreaves' ßî+çh. - Gideon Weems

Bob
Free Market Evangelist
September 2000
avatar

Steve: I did write an article about this.
Also, this code fades. It doesn't blur. And it only works with 5.6.5 packed pixel formats.

--
- Bob
[ -- All my signature links are 404 -- ]

Steve Terry
Member #1,989
March 2002
avatar

hehe yeah i've seen that code before, yes it takes one pixel and fades it by a factor from 0 to 32. I think it was used in fade16.c which worked ok. I think i'll just have to play with the code a bit more. I also had an example of blending two colors using the same technique, but I misplaced it or something.

___________________________________
[ Facebook ]
Microsoft is not the Borg collective. The Borg collective has got proper networking. - planetspace.de
Bill Gates is in fact Shawn Hargreaves' ßî+çh. - Gideon Weems

 1   2 


Go to: