Texture Map Correction for spherical mapping

Blitz3D Forums/Blitz3D Programming/Texture Map Correction for spherical mapping

Hi,

I have a generated texture map (planet surface) in mercator-style projection applied to a sphere. The problem is that the texture distorts at the poles. I have seen textures of planets/moons of our solar system where the texture has been modified and there is no more distortion at the poles (like here: http://www.mmedia.is/bjj/planetary_maps.html ).

Any Idea how to do this modification in blitzbasic? The idea behind is a procedural universe with random planets / no file-based heightmaps, everything is being generated on the fly.

Here is a example planet texture:


The algorithm should produce a map like this:


The examples are generated with a DOS tool by Torben Mogensen (grab it here: http://www.diku.dk/~torbenm/ ), which can generate a "square projection" map (Example 2) which fits my needs, but unfortunately the planet.exe must be in a path only with DOS compatible 8.3 parent directories :-( But i won't use this tool, I have my own perlin-based algorithm for planet surface generation which is way faster.

Can anybody read/transform the square projection C-Code from this planet generator into Blitz code? I really don't understand Torbens code and need help. Thanks.

If you have Photoshop here's a plug-in that does the same thing:


Spherical Mapping Corrector

This filter produces texture map correction for spherical texture mapping. Special thanks to Paul Bourke for allowing his algorithm to be ported to this plugin. For more information, please visit Mr. Bourke's site at Mr. Bourke'sCurrent Version v1.2



http://www.richardrosenman.com/software/downloads/


It stretches the top and bottom of an image horizontally more than the center of the image so that the top and bottom don't create a "pinched" look at the poles. The results are not necessarily mathematically accurate.



I know this tool, but I won't preprocess the heightmaps, I want to calculate the new maps in BB (imagine 100 stars with approx. 5 planets each = 500 heightmaps, doh!). And if you try to stretch the upper map with this tool and put the new map on a sphere it won't fit anymore at the sides.

Torbens planet generator has the solution but I don't understand his source code (the squarep() function).

Here's some pseudocode from: http://local.wasp.uwa.edu.au/~pbourke/texture/polargrid/

   double theta,phi,phi2;
   int i,i2,j;
   BITMAP *imagein,*imageout;

   Form the input and output image arrays
   Read an input image from a file

   for (j=0;j<image.height;j++) {
      theta = PI * (j - (image.height-1)/2.0) / (double)(image.height-1);
      for (i=0;i<image.width;i++) {
         phi  = TWOPI * (i - image.width/2.0) / (double)image.width;
         phi2 = phi * cos(theta);
         i2  = phi2 * image.width / TWOPI + image.width/2;
         if (i2 < 0 || i2 > image.width-1) {
            newpixel = red;                         /* Should not happen */
         } else {
            newpixel = imagein[j*image.width+i2];
         }
         imageout[j*image.width+i] = image.newpixel;
      }
   }

   Do something with the output image
Share your code when you're done!

Also, you could combine your map generation and polar correction in the same loop to speed things up. In other words calculate each map pixel and polar-correct it as you go.

Mike

Morbius, sorry I forgot to mention that I know this site and tried to write the code in Blitzbasic. But there is something wrong, running my code gives an exact copy of the original image, and I dont know why LOL!

On the other hand, if I take an image from this site and put it on a sphere, the X axis sides doesn't match anymore.

Here's my code attempt:

Graphics 512,513,32,2 

width=512
height=256
twopi#=2*Pi


;read image to array
Dim col(width,height) 


;load source image
raster=LoadImage("raster.png") 


;get color data from source image
SetBuffer ImageBuffer(raster) 
LockBuffer ImageBuffer(raster) 
For x=0 To width-1 
	For y=0 To height-1 
		col(x,y)=ReadPixelFast(x,y)
	Next 
Next 
UnlockBuffer ImageBuffer(raster) 


;create a new image
test=CreateImage(width,height)


;algorithm
SetBuffer ImageBuffer(test) 
LockBuffer ImageBuffer(test) 
For j=0 To height-1 
	theta#=Pi*(j-(height-1)/2.0)/(height-1) 
	For i=0 To width-1 
		phi#=twopi#*(i-width/2.0)/width 
		phi2#=phi#*Cos(theta#) 
		i2#=phi2#*width/twopi#+width/2 
		rgb=col(i,j) 
		WritePixelFast i2#,j,rgb 
	Next 
Next 
UnlockBuffer ImageBuffer(test) 
SetBuffer FrontBuffer() 


;draw results
DrawImage raster,0,0 
DrawImage test,0,height+1 


;red divider line
Color 255,0,0 
Line 0,256,512,256 


WaitKey 


End


The raster image i used is here:



I think the problem itself is the difference between blitz' cos/sin and c++ cos/sin.
In Blitz, cos/sin use degrees and in C++, radians.
The conversion between them is * pi / 180, but I don't remeber which way around, so you might need to invert it.
And I would make all variables float, so that when you use them in a division, the outcome will not become an integer.

You switched two variables, and forgot to convert from radians to degrees.

Anyway, here you go:
Graphics 512,513,32,2 

imagefile$="raster.png"

;load source image
raster=LoadImage(imagefile$) 

width=ImageWidth(raster)
height=ImageHeight(raster)
twopi#=2*Pi

;read image to array
Dim col(width,height) 

;get color data from source image
SetBuffer ImageBuffer(raster) 
LockBuffer ImageBuffer(raster) 
For x=0 To width-1 
	For y=0 To height-1 
		col(x,y)=ReadPixelFast(x,y)
	Next 
Next 
UnlockBuffer ImageBuffer(raster) 


;create a new image
test=CreateImage(width,height)


;algorithm
SetBuffer ImageBuffer(test) 
LockBuffer ImageBuffer(test) 
For j=0 To height-1 
	theta#=Pi*(j-(height-1)/2.0)/(height-1) 
	For i=0 To width-1 
		phi#=twopi#*(i-width/2.0)/width 
		phi2#=phi#*Cos(theta#*(180.0/Float(Pi))) 
		i2#=phi2#*width/twopi#+width/2 
		rgb=col(i2,j)
		WritePixelFast i,j,rgb 
	Next 
Next 
UnlockBuffer ImageBuffer(test) 
SetBuffer FrontBuffer() 


;draw results
DrawImage raster,0,0 
DrawImage test,0,height+1 


;red divider line
Color 255,0,0 
Line 0,256,512,256 


WaitKey 


End


Jeppe, nice one :-) There is still one issue i mentioned before: the X axis sides doesn't match, here is a 3D test for it:

Graphics3D 512,513,32,2 

imagefile$="planet1.png"

;load source image
raster=LoadImage(imagefile$) 

width=ImageWidth(raster)
height=ImageHeight(raster)
twopi#=2*Pi

;read image to array
Dim col(width,height) 

;get color data from source image
SetBuffer ImageBuffer(raster) 
LockBuffer ImageBuffer(raster) 
For x=0 To width-1 
	For y=0 To height-1 
		col(x,y)=ReadPixelFast(x,y)
	Next 
Next 
UnlockBuffer ImageBuffer(raster) 


;create a new image
test=CreateImage(width,height)


;algorithm
SetBuffer ImageBuffer(test) 
LockBuffer ImageBuffer(test) 
For j=0 To height-1 
	theta#=Pi*(j-(height-1)/2.0)/(height-1) 
	For i=0 To width-1 
		phi#=twopi#*(i-width/2.0)/width 
		phi2#=phi#*Cos(theta#*(180.0/Float(Pi))) 
		i2#=phi2#*width/twopi#+width/2 
		rgb=col(i2,j)
		WritePixelFast i,j,rgb 
	Next 
Next 
UnlockBuffer ImageBuffer(test) 
SetBuffer FrontBuffer() 


;draw results
;DrawImage raster,0,0
;DrawImage test,0,height+1 


;red divider line
;Color 255,0,0 
;Line 0,256,512,256 

SaveImage test,"planet.bmp"

planet=CreateSphere(32)
tex=LoadTexture("planet.bmp")
;test it with this line
;tex=LoadTexture("planet2.png")
EntityTexture planet,tex

cam=CreateCamera()
PositionEntity cam,0,0,2
MoveMouse 256,256
PointEntity cam,planet

While Not KeyHit(1)

TurnEntity planet,0.5,0.25,0.5

RenderWorld

Flip

Wend


End


to see how it should look just remove the comment before entitytexture (you need planet1.png / planet2.png, the two examples from the beginning)

I'll have a look tonight. I'm sure you're on the right track.

Ah, which projection are you using to make the original map? And the "corrected" map?

Give me the commandline arguments you are using if you can.

The original flat map you posted appears to be truncated at the top and bottom. Entire sections of map are missing that do not appear in the spherical version. This is what is leading you to conclude that your code is wrong when in fact it is fine.

Thanks

Mike

this is what i got. it basically lowers the amount of distortion, but results in problems at the poles.

Graphics3D 512,513,32,2 

imagefile$="planet1.png"

;load source image
raster=LoadImage(imagefile$) 

width=ImageWidth(raster)
height=ImageHeight(raster)
twopi#=2*Pi

;read image to array
Dim col(width,height) 

;get color data from source image
SetBuffer ImageBuffer(raster) 
LockBuffer ImageBuffer(raster) 
For x=0 To width-1 
	For y=0 To height-1 
		col(x,y)=ReadPixelFast(x,y)
	Next 
Next 
UnlockBuffer ImageBuffer(raster) 


;create a new image
test=CreateImage(width,height)


;algorithm
SetBuffer ImageBuffer(test) 
LockBuffer ImageBuffer(test) 
For j=0 To height-1 
	theta#=Pi*(j-(height-1)/2.0)/(height-1) 
	For i=0 To width-1 
		chheight#=Cos(j/(height/Float(180))) ;1..0..1
		chwidth#=Sin(i/(width/Float(180))) ;0..1..0
		chd#=180.0*(chheight#*chwidth#) ;change distortion amount
		phi#=twopi#*(i-width/2.0)/width 
		phi2#=phi#*Cos(theta#*(chd#/Float(Pi))) 
		i2#=phi2#*width/twopi#+width/2 
		rgb=col(i2,j)
		WritePixelFast i,j,rgb 
	Next 
Next 
UnlockBuffer ImageBuffer(test) 
SetBuffer FrontBuffer() 


;draw results
;DrawImage raster,0,0
;DrawImage test,0,height+1 


;red divider line
;Color 255,0,0 
;Line 0,256,512,256 

SaveImage test,"planet.bmp"

planet=CreateSphere(32)
tex=LoadTexture("planet.bmp")
;test it with this line
;tex=LoadTexture("planet2.png")
EntityTexture planet,tex

cam=CreateCamera()
PositionEntity cam,0,0,2
MoveMouse 256,256
PointEntity cam,planet

While Not KeyHit(1)

TurnEntity planet,0.3,0.1,0.1

RenderWorld

Flip

Wend


End


Morbius, the commandline for this planet is exactly

planet.exe -pq -M terra.rgb -i -0.01 -s 0.1 -w 512 -h 256 -o planet.bmp

the switch -pq calculates the square projection (example 2), changing this to -pm you'll get mercator projection (example 1), oh and terra.rgb is a simple text file:

#333399
#6666cc
#009900
#E8A45A
#F8B45C
#A5806C
#FFFFFF
#000000
#000000

EDIT: here is an animation of the two maps - Morbius you're right, it seems that the map is cut off slightly at the poles in mercator projection mode. The distortion looks like if it increases from equator to poles in an exponential?!? curve:



But even if I take another map it doesn't gets seamless. I've made a small GIF animation to see the difference:



markcw, if I take your code and use the same image we can see the difference, the distortion is not strong enough:



I think we should extract the wisdom from Torbens C code because he knew how to do the trick :-|

My final pseudorandom generated world could look like this (needs more finetuning, first attempt of atmosphere):



hi Krischan, yes with that algorithm it seems to "push" the data off the map, as can be seen in your gifs. so i guess you're right, it needs the squarep algorithm. i'll have a go at it. what is that algorithm by the way?

edit: sorry missed the link to that article.

Hi markcw,

the algorithm is this C code (include in Torbens planet generator, see the link in my first post):

void squarep()
{
double y,scale1,theta1,cos2, log_2();
int k,i,j, planet0();

k = (int)(lat*Width*scale/PI);
for (j = 0; j < Height; j++) {
if (debug && ((j % (Height/25)) == 0)) {fprintf (stderr, "%c", view); fflush(stderr);}
y = (2.0*(j-k)-Height)/Width/scale*PI;
if (fabs(y)>=0.5*PI) for (i = 0; i < Width ; i++) {
col[i][j] = BACK;
if (doshade) shades[i][j] = 255;
} else {
cos2 = cos(y);
if (cos2>0.0) {
scale1 = scale*Width/Height/cos2/PI;
Depth = 3*((int)(log_2(scale1*Height)))+3;
for (i = 0; i < Width ; i++) {
theta1 = longi-0.5*PI+PI*(2.0*i-Width)/Width/scale;
col[i][j] = planet0(cos(theta1)*cos2,sin(y),-sin(theta1)*cos2);
if (doshade) shades[i][j] = shade;
}
}
}
}
if (hgrid != 0.0) { /* draw horisontal gridlines */
for (theta1 = 0.0; theta1>-90.0; theta1-=hgrid);
for (theta1 = theta1; theta1<90.0; theta1+=hgrid) {
y = DEG2RAD*theta1;
j = Height/2+(int)(0.5*y*Width*scale/PI)+k;
if (j>=0 && j<Height) for (i = 0; i < Width ; i++) col[i][j] = BLACK;
}
}
if (vgrid != 0.0) { /* draw vertical gridlines */
for (theta1 = 0.0; theta1>-360.0; theta1-=vgrid);
for (theta1 = theta1; theta1<360.0; theta1+=vgrid) {
i = (int)(0.5*Width*(1.0+scale*(DEG2RAD*theta1-longi)/PI));
if (i>=0 && i<Width)
for (j = max(0,Height/2-(int)(0.25*PI*Width*scale/PI)+k);
j < min(Height,Height/2+(int)(0.25*PI*Width*scale/PI)+k); j++)
col[i][j] = BLACK;
}
}
}

Notes on this code I found out:

- lat/longi is 0.0 (I guess)
- scale is 1.0
- log_2 is log(x)/log(2.0)
- the "Depth" isn't used and makes no sense
- col[x][y] contains the final map data (is being calculated by squarep)
- we don't need shades
- we don't need grids

So we can shorten the code to:

void squarep()
{
double y,scale1,theta1,cos2, log_2();
int k,i,j, planet0();

k = (int)(lat*Width*scale/PI);
for (j = 0; j < Height; j++) {
y = (2.0*(j-k)-Height)/Width/scale*PI;
if (fabs(y)>=0.5*PI) for (i = 0; i < Width ; i++) {
col[i][j] = BACK;
cos2 = cos(y);
if (cos2>0.0) {
scale1 = scale*Width/Height/cos2/PI;
for (i = 0; i < Width ; i++) {
theta1 = longi-0.5*PI+PI*(2.0*i-Width)/Width/scale;
col[i][j] = planet0(cos(theta1)*cos2,sin(y),-sin(theta1)*cos2);
}
}
}
}

What I don't understand is the call of planet0(x,y,z), I think the code calculates somehow the planet surface color for a given x,y,z coordinate and distorts the value from this position to the final image. But this function is very confusing to me.

Thanks for posting. I'll have a look tonight. Judging by the animated gif, (very helpful!) it appears that it is just squeezing the lattitude as a proportion of the circumference of the circle at some lattitude to the circumference of the equator.

To find the circumference at a given lattitude: C = E * Cos(L)

Where:

E is the circumference at the equator
L is the lattitude (O at the equator, 1 at the poles)

It's hard to tell exactly what is going on at the poles since they are truncated in the original map. I'll check out the algorithm above and see if I can get a clue. It looks like it's squeezing (vertical) as described above, then doing a little stretching (horizontal) (near poles?)

Thanks

Mike

hi Krischan, yeah i looked at the planet.c code but i couldn't understand the planet functions either. but i went back to the other algorithm and ended up rewriting it. the theory is you distort according to an arc, but a standard sin arc isn't enough, you need a steep arc. the result of some further tweaking is enough to get what you need i think.

i have two codes, one for the planet sphere and one for the image to show how the custom sin curve is obtained.

Graphics3D 512,512,0,2

imagefile$="planet1.png"

;load source image
raster=LoadImage(imagefile$) 

width=ImageWidth(raster)
height=ImageHeight(raster)
twopi#=2*Pi

;read image to array
Dim col(width,height) 

;get color data from source image
SetBuffer ImageBuffer(raster) 
LockBuffer ImageBuffer(raster) 
For x=0 To width-1 
	For y=0 To height-1 
		col(x,y)=ReadPixelFast(x,y)
	Next 
Next 
UnlockBuffer ImageBuffer(raster) 

;create a new image
test=CreateImage(width,height)

;algorithm2
SetBuffer ImageBuffer(test) 
LockBuffer ImageBuffer(test)
For j=0 To height-1 
 theta#=Sin(j/(height/Float(180))) ;y_sin 0..1..0
 For i=0 To width-1
  phi#=64 ;level of sin curve distortion
  sin1#=1-((1-theta#)^phi#) ;inverse(inverse_sin^n)
  sin2#=(theta#*((1-theta#)^(phi#/2)))*phi# ;sin*(inverse_sin^n)*n
  i2#=((i-(width/2))*(sin1#-sin2#))+(width/2) ;offset*(sin_product)+mid
  rgb=col(i2,j)
  WritePixelFast i,j,rgb
 Next
Next
UnlockBuffer ImageBuffer(test) 
SetBuffer FrontBuffer() 

;draw results
;DrawImage raster,0,0
;DrawImage test,0,height+1 

;red divider line
;Color 255,0,0 
;Line 0,256,512,256 

SaveImage test,"planet.bmp"

planet=CreateSphere(32)
tex=LoadTexture("planet.bmp")
;test it with this line
;tex=LoadTexture("planet2.png")
EntityTexture planet,tex

cam=CreateCamera()
PositionEntity cam,0,0,2
MoveMouse 256,256
PointEntity cam,planet

While Not KeyHit(1)

 TurnEntity planet,0.2,0.1,0

 RenderWorld

 Flip
Wend
End


Graphics 512,512,0,2

imagefile$="raster.png"
imagefile$="planet1.png"

;load source image
raster=LoadImage(imagefile$) 

width=ImageWidth(raster)
height=ImageHeight(raster)
twopi#=2*Pi

;read image to array
Dim col(width,height) 

;get color data from source image
SetBuffer ImageBuffer(raster) 
LockBuffer ImageBuffer(raster) 
For x=0 To width-1 
	For y=0 To height-1 
		col(x,y)=ReadPixelFast(x,y)
	Next 
Next 
UnlockBuffer ImageBuffer(raster) 

;create a new image
test=CreateImage(width,height)

;algorithm2
SetBuffer ImageBuffer(test) 
LockBuffer ImageBuffer(test)

For j=0 To height-1 
 theta#=Sin(j/(height/Float(180))) ;y_sin 0..1..0
 For i=0 To width-1
  phi#=64 ;level of sin curve distortion
  sin1#=1-((1-theta#)^phi#) ;inverse(inverse_sin^n)
  sin2#=(theta#*((1-theta#)^(phi#/2)))*phi# ;sin*(inverse_sin^n)*n
  i2#=((i-(width/2))*(sin1#-sin2#))+(width/2) ;offset*(sin_product)+mid
  rgb=col(i2,j)
  WritePixelFast i,j,rgb
 Next
Next
;draw sin curves
For j=0 To height-1
 theta#=Sin(j/(height/Float(180))) ;y_sin 0..1..0
 phi#=64 ;level of sin curve distortion
 sin1#=1-((1-theta#)^phi#) ;inverse(inverse_sin^n)
 sin2#=(theta#*((1-theta#)^(phi#/2)))*phi# ;sin*(inverse_sin^n)*n
 WritePixelFast theta#*256,j,$00FF00 ;green
 WritePixelFast sin1#*256,j,$0000FF ;blue
 WritePixelFast sin2#*256,j,$FF0000 ;red
 WritePixelFast (sin1#-sin2#)*256,j,$00FFFF ;cyan
Next

UnlockBuffer ImageBuffer(test)
SetBuffer FrontBuffer()

;draw results
DrawImage raster,0,0
DrawImage test,0,height+1

;red divider line
Color 255,0,0
Line 0,256,512,256

WaitKey
End


Hmmmm, seems like a lot of bother... Why not just use a geosphere? That would solve your problems :)

markcw, thanks for your nice demonstration, the result is here:



I used a different map (perlin noise with a shaded relief) with more land mass at the north pole, and you can clearly see that the distortion looks strange there. A simple solution would be to have an ocean at the poles, so that the distortion seems to disappear in a single color, but this is only useful for creating Class M planets (think about a pluto-style planet or even jupiter with its clouds).

Ross C, I already thought about it but the result looks like on a sphere, or do you have another solution or example code/mesh for it? Here is a screenshot with a 32 segment Icosaeder geosphere:



For both examples I used this new colormap:



I think the only useful solution is to translate the *complete* source from Torbens planet generator to Blitzbasic and see if it is working there (without knowing *why* it's working LOL). But I'm afraid that Blitzbasic is not fast enough to calculate the planet in an appropriate time but we'll see (the DOS program is not that fast already).

Any suggestions / hints concerning known traps if I translate C code to Blitzbasic? Never did that, beside Basic I know PHP and a little bit Java... never coded in C but it looks a little bit like Java syntax.

Well, the problem using sphereical mapping, is the mesh's UV co-ords are always going to be squeezed at the top. Have you tried using a cubemap? I'm not sure how you would go about creating a cubemap from this...

hi Krischan, i have been translating the planet.c code. thought you might like to see what i have so far. it is now functioning.

edit: just an update on what i have now.
edit2: ok, another update, it now prints the heightmap, i think.
edit3: added the mercator function.
edit4: added 5 more of the projection functions.
edit5: added the last 4 projections.
edit6: fixed sinusoid map projection, added ppm output.

code is now here:
http://www.blitzbasic.com/codearcs/codearcs.php?code=1831

Amazing! It would take me a week to get that far. I've done a little further research and found that what we're looking for is called a geographic lat-lon projection. It really isn't a projection at all, and the cartographers don't recognize it as such.

The trick, as I understand it so far, is to treat lats and lons directly as if they were x and y coordinates with lat -90 to 90 and lon -180 to 180. If I get anywhere, I'll share it.

Good luck to you both!

Mike

markcw, I'm deeply impressed how far you got, I didn't even start my own code! I would love to see the final result and hope that we'll get it to work in BB!

Ross, I've hade some experiments with geospheres but I don't get it how to put a texture on it. Only a premade UV-mapped geosphere worked, but none of the codes here which create a geosphere in BB. The sphere gets some color from the texture but that's all.

Morbius, I think the correct name for this is "equidistant cylindrical projection", read this:

The simplest of all map graticules belongs to the equatorial aspect of the equirrectangular projection, referred to by many names like equidistant cylindrical, plane chart, plain chart and rectangular. It is a cylindrical projection with standard meridians: all have constant scale, equal to the standard parallels's, therefore all parallels are equally spaced. It was credited to Erathostenes (ca. 200 b.c.) and to Marinus of Tyre (ca. 100). Its trivial construction made it widely used, even for navigation, until the Modern Age.

A special case of the equirrectangular projection is called Plate Carrée, or simple cylindrical: the Equator is a standard parallel, so it is twice as long as all meridians, making the map a 2 : 1 rectangle and the graticule's grid square.

Fast, trivial equations led to its resurgence in rough computer-drawn maps, with early machines or real-time graphics. It is still commonly used in digitized textures ("skins") of earthly and planetary features.

From http://www.progonos.com/furuti/MapProj/Normal/ProjCyl/projCyl.html

There is a nice overview of projection techniques, too:
http://www.progonos.com/furuti/MapProj/Normal/TOC/cartTOC.html

We're learning more about cartography than we ever imagined, aren't we!

i'm having problems. i have the mercator function translated and the "commandline" is set to do that function, but i think there is a problem with converting from radians to degrees, as the output is just blank.

it draws the vertical gridlines ok but not the horizontal ones, there's a commented line on what it was, and then my attempt at converting radians to degrees, but i don't know how to do that, maybe you could fix it Krischan/anyone?

i'll move on a translate the other ones now.

Degrees = Radians * (Pi / 180) ?

You're conveting theta1 to radians then back to degrees?

y = Sin(DEG2RAD*theta1 * Rad2Deg) ;y = Sin(DEG2RAD*theta1)


Back to cartography for a moment - There's a nice illustration of the geographic projection here:

http://www.3dsoftware.com/Cartography/USGS/MapProjections/Cylindrical/PlateCarree/

And here's some fantastic info:

http://www.lepp.cornell.edu/~seb/celestia/textures.html#2.1



hi Morbius,

well, while translating the sinusoid projection i found the gridline projections worked when i did sin or cos(value*Rad2Deg) where Rad2Deg = 180/Pi. And when i went back to the mercator function i found i could do:
Sin(theta1) or Sin(DEG2RAD*theta1*Rad2Deg)
but i still had to edit the original code before it worked.

i still can't get anything working for the actual map projections though, but i'm thinking it must be something wrong in the planet functions, but it's confusing because they don't have any sin/cos commands.

i will just ignore it for now and get the rest of the projections translated.

hi Krischan,

that's all the important stuff translated now. the only stuff left is the output to ppm or xpm formats, which i'll do later.

i have tested all the projections and compared them to identical commandlines in planet.exe, and all the gridlines match now that i converted sin/cos to rad2deg.

the last 5 projections don't have any lat/longi specifications so they are at 0,0 always. strange how it isn't finished.

also there's something funny going on in the conical projection one, most of it comes out black. so i've edited it to force blue to see the gridlines.

so that's the gridlines working but still no actual map data. :/

edit: ok, found the problem, it was the initial altitude -i or M in the code which needs to be around 0.3 for some reason. it works now, yippee!

Wow! I'm blown away. Really amazing work there!

markcw, that are really good news today. I would love to see it running in Blitzbasic, by the way: how fast is it calculating a 1024x512 map in square projection? And did you notice, that when you apply lat/lon data and a magnification level of 10 or 100, the details get finer and finer, shores and small islands appear :-)

Hi Krischan,
i have moved the code to here: http://www.blitzbasic.com/codearcs/codearcs.php?code=1831
i had to remove a bit so it would fit. i will edit it some more.

can you not run the code? i haven't tried 1024x512 but it is slow, especially when you increase the -m (magnification) option.

Hi markcw,

sorry I didn't see that you've posted the code to the code archive. Huh, you really translated the complete source, respect! It works very good, I played a little bit around but still don't get it how the square projection works, I'm even more confused now. The "planet1" function returns incredible small numbers (smaller than 0.0000xxx) and I think I'm just too stupid to understand the technique behind it. Unfortunately, the planet generation in higher dimensions (1024x512) is way too slow for nearly realtime calculation (for ex. when entering a solar system, screen message: "Scanning system..." for ~ 3-5 seconds)

Calculating a perlin noise cloud is ~ 100 times faster than Torben's algorithm (ok, Torbens planet looks nicer, so it should take longer). But I don't know how to distort such a perlin noise image with torbens squarep algorithm, because we have X/Y/Z coordinates, and Torbens code calculates the height (=alt) out of these coordinates, but my perlin cloud is only 2D. Huh? Why does it need a Y coordinate?

So "my" holy grail of planet generation would be a mixture of a fast perlin cloud and its distortion with squarep.

When you found a method that works, but is to slow, maybe you could precalculate a table. Say, two arrays, containing the projected coordinates ie. newx(x, y) and newy(x, y)

hi Krischan,

well knowing a bit about calculating stuff i can say there is a rule that usually works, which is: quality is relative to time. so if you want quality AND speed then you'd need to precalculate the maps ie. save out the maps to be loaded into the project later.

looking at the planet functions i can't see anything you could do to speed them up, they are already optimized.

Krischan: Here's a link to a very complete projection system with source http://www.users.globalnet.co.uk/~arcus/mmps/

markcw: There's an interesting projection in this code called a perspective projection, which basically maps the texture around an imaginary sphere, complete with shadow casting. You could use this to render a small quick billboard imposter of the planet and save the heavy calculation for close ranges.

I have another idea how to distort the texture, maybe it is working. The idea is to convert the cylindrical map into a sinusoid projection, so that the poles get pinched to a singularity and remap this sinusoid back to a cylindrical projection.

Here is my example code using the planet1.png (first image in this post) which converts the image into a sinusoid-like one. But now there is a problem: I dunno how to convert this image back to a flat image :-(

Graphics 512,512,32,2

;define color field
Dim col(512,256)

;read source image and its colors
image=LoadImage("planet1.png")
SetBuffer ImageBuffer(image)
LockBuffer ImageBuffer(image)
For x=0 To 511
	For y=0 To 255
		col(x,y)=ReadPixelFast(x,y)
	Next
Next
UnlockBuffer ImageBuffer(image)

;create new image
sinusoid=CreateImage(512,256)
SetBuffer ImageBuffer(sinusoid)
LockBuffer ImageBuffer(sinusoid)

;image/sinus factor
factor#=360.0/512.0

;calulate sinusoid
For yy#=0 To 255 Step 1
	For xx#=0 To 511 Step 0.1

		;read current color
		rgb=col(Int(xx),Int(yy))

		;calulate new x
		theta#=Cos((1+xx)/2*factor)*Sin((1+yy)*factor)*256

		;output
		If theta>256 Then xxx=256-theta Else xxx=256+theta
		WritePixelFast 512-xxx,yy,rgb

	Next
Next
UnlockBuffer ImageBuffer(sinusoid)

;output both images
SetBuffer FrontBuffer()
While Not KeyHit(1)
	DrawImage image,0,0
	DrawImage sinusoid,0,256
	Flip
Wend

End


source:


output:


If you want more details, do re-calculate the uv coords instead edit the image from texture.

ok, here is my "solution" but I'm not very happy with it, still pinching at the poles and the code looks messy:



Graphics 512,768,32,2

;define color field
Dim col(512,256),col2(512,256)

Dim the#(256)

;read source image and its colors
image=LoadImage("planet1.png")
SetBuffer ImageBuffer(image)
LockBuffer ImageBuffer(image)
For x=0 To 511
	For y=0 To 255
		col(x,y)=ReadPixelFast(x,y)
	Next
Next
UnlockBuffer ImageBuffer(image)

;create new image
sinusoid=CreateImage(512,256)
SetBuffer ImageBuffer(sinusoid)
LockBuffer ImageBuffer(sinusoid)

;image/sinus factor
factor#=360.0/512.0

;calulate sinusoid
For yy#=0 To 255 Step 1
	For xx#=0 To 511 Step 0.5

		;read current color
		rgb=col(Int(xx),Int(yy))

		;calulate new x
		theta#=Cos((1+xx)/2*factor)*Sin((yy)*factor)*256
		
		;output
		If theta>256 Then xxx=256-theta Else xxx=256+theta
		WritePixelFast 512-xxx,yy,rgb
		
		col2(512-Int(xxx),Int(yy))=rgb
		
	Next
	
	the#(yy)=Abs(theta)
	
Next
UnlockBuffer ImageBuffer(sinusoid)

;convert sinusoid to rectangular
spherical=CreateImage(512,256)
SetBuffer ImageBuffer(spherical)
LockBuffer ImageBuffer(spherical)

For yy=0 To 255

	;get y distortion
	theta=the(yy)

	;divider
	div#=theta/256.0

	;left side
	For xx=0 To 255
		rgb=col2(256-(xx*div),yy)
		WritePixelFast 255-xx,yy,rgb
	Next
	
	;right side
	For xx=0 To 255
		rgb=col2(256+(xx*div),yy)
		WritePixelFast 256+xx,yy,rgb
	Next
Next
	
UnlockBuffer ImageBuffer(spherical)

;output all images
SetBuffer FrontBuffer()
While Not KeyHit(1)

	Cls

	DrawImage image,0,0
	DrawImage sinusoid,0,256
	DrawImage spherical,0,512
	
	Flip
Wend

End


This tech demo shows all three maps in a row

Well folks, after two years I found the solution to this, but unfortunately it is written in Dark Basic Pro. It is a custom Perlin3D noise function which produces a smooth and spherecompatible texture. I tried to translate this to BB but wasn't successfully. Is anybody able to locate the error or rewrite my attempt that we get the same results in BB?

Dark Basic Pro output:


My BB attempt output:


The Dark Basic Code
Rem ***** Main Source File *****
set window on
set window layout 0,0,0
set display mode 512,256,32
hide mouse
sync off
sync rate 0

rem create arrays for perlin generator
dim s#(15,2)
dim r#(63,63,63)

rem prepare the perlin
prepare_perlin(1,0.5)

rem multisampling controller
multi = 1
milti = multi - 1
malt# = multi ^ 2
malt# = 1 / malt#

xsize = 512
ysize = 256

rem get the base sizes
xsize# = xsize * multi
ysize# = ysize * multi

rem get the scalers
xscaler# = 360
yscaler# = 180

xscaler# = xscaler# / xsize#
yscaler# = yscaler# / ysize#

width# = 2

rem piece details
pxs = 511
pys = 255

xof = 0
yof = 0

rem loop the y position
for posx = 0 to pxs step 1

   rem prepare screen for drawing
   lock pixels

   rem loop the x
   for posy = 0 to pys step 1


      rem reset colours
      g = 0
      b = 0

      rem get the scaled sizes
      xp = (posx + xof) * multi
      yp = (posy + yof) * multi

      rem do the multisampling
      for posa = 0 to milti
         rem get the baring around the sphere
         ba# = xp + posa
         ba# = (ba# + 0.5) * xscaler#
         xp# = cos(ba#)
         zp# = sin(ba#)
         for s = 0 to milti
            rem get the pitch around the sphere
            pa# = yp + poss
            pa# = ((pa# + 0.5) * yscaler#)
            po# = sin(pa#) * width#
            rem get the positions in the space
            x# = (xp# * po#) + 5
            y# = (cos(pa#) * width#) + 5
            z# = (zp# * po#) + 5
            rem get the perlin result for that part
            h = int(perl(x#,y#,z#,8) * 255)
            rem cap the value
            if h => 255 then h = 255
            if h <= 0 then h = 0

            rem add appropriate colour
            if h => 136
               g=g+h
            else
               b=b+h
            endif

            rem failsafe quit
            if spacekey() then end

         next poss
      next posa

      rem scale down the colours
      g = g * malt#
      b = b * malt#

      rem get the colour
      ink rgb(0,g,b),0

      rem put a dot there
      dot posx,posy

   next posy

   unlock pixels
   sync

next posx

do

 if spacekey() then end

loop

rem end

rem perlin function
function perl(x#,y#,z#,octaves)
   rem make sure the pass value is zerod
   h# = 0
   rem shift octaves down to input works from 1 but system works from 0
   octaves=octaves-1
   rem make sue octaves are an ecceptable value
   if octaves <= 0 then octaves = 0
   if octaves => 15 then octaves = 15
   rem loop the octaves
   for oct = 0 to octaves
      rem grab the frequency and amplitude for this
      fre# = s#(oct,0)
      amp# = s#(oct,1)
      rem convert the co-ordinates into steps
      x = int(x# * fre#)
      y = int(y# * fre#)
      z = int(z# * fre#)
      rem get the inbetween co-ords
      xb# = sine((x# * fre#) - flo(x))
      yb# = sine((y# * fre#) - flo(y))
      zb# = sine((z# * fre#) - flo(z))
      xa# = 1 - xb#
      ya# = 1 - yb#
      za# = 1 - zb#
      rem get the values for the 8 corners
      v000# = vil(x,y,z) * xa# * ya# * za#
      v100# = vil(x+1,y,z) * xb# * ya# * za#
      v010# = vil(x,y+1,z) * xa# * yb# * za#
      v001# = vil(x,y,z+1) * xa# * ya# * zb#
      v101# = vil(x+1,y,z+1) * xb# * ya# * zb#
      v110# = vil(x+1,y+1,z) * xb# * yb# * za#
      v011# = vil(x,y+1,z+1) * xa# * yb# * zb#
      v111# = vil(x+1,y+1,z+1) * xb# * yb# * zb#
      rem add it on
      h#=h#+(v000# + v100# + v010# + v001# + v101# + v110# + v011# + v111#) * amp#
   next oct
   rem scale it down
   h# = h# * s#(octaves,2)
endfunction h#

rem function to get the random value of a co-ordinate
function vil(x,y,z)
   rem control edges
   if x < 0 then x = x - (int((x / 64) - 1) * 64) else x = x - (int(x/64) * 64)
   if y < 0 then y = y - (int((y / 64) - 1) * 64) else y = y - (int(y/64) * 64)
   if z < 0 then z = z - (int((z / 64) - 1) * 64) else z = z - (int(z/64) * 64)
   rem get the number
   v# = r#(x,y,z)
endfunction v#

rem return an integer as a floating point
function flo(a)
   b# = a
endfunction b#

rem function to turn a straight 0 - 1 into a sine curved 0 - 1
function sine(v#)
   rem perform the change
   v# = (1 - cos(v# * 180)) * 0.5
endfunction v#

rem function to prepare data for perlin noise
function prepare_perlin(seed,persistance#)
   rem set the seed value
   randomize seed
   rem create seed data
   for x = 0 to 63
      for y = 0 to 63
         for z = 0 to 63
            z# = rnd(10000)
            r#(x,y,z) = (z# * 0.0001)
         next z
      next y
   next z
   rem prepare octave data
   for z = 0 to 15
      rem work out the frequence of the octave
      s#(z,0) = 2 ^ z
      rem get the amplitude
      s#(z,1) = persistance# ^ z
      rem work out the maximum amplitude of
      s#(z,2) = 0
      for x = 0 to z
         s#(z,2)=s#(z,2)+s#(x,1)
      next x
      s#(z,2) = 1 / s#(z,2)
   next z
endfunction


My BB attempt Code
Graphics 512,256,32,2

; create arrays For perlin generator
	Dim s#(15,2)
	Dim r#(63,63,63)
	
	; prepare the perlin
	prepare_perlin(1,0.5)
	
	; multisampling controller
	multi = 1
	milti = multi - 1
	malt# = multi ^ 2
	malt# = 1.0 / malt#
	
	xsize# = 512
	ysize# = 256
	
	; get the base sizes
	xsize# = xsize * multi
	ysize# = ysize * multi
	
	; get the scalers
	xscaler# = 360
	yscaler# = 180
	
	xscaler# = xscaler# / xsize#
	yscaler# = yscaler# / ysize#
	
	width# = 2
	
	; piece details
	pxs = 511
	pys = 255
	
	xof = 0
	yof = 0
	
	LockBuffer GraphicsBuffer()
	
	; loop the y position
	For posx = 0 to pxs Step 1
		
			; loop the x
			For posy = 0 to pys Step 1
				
				
				; reset colours
				g = 0
				b = 0
				
				; get the scaled sizes
				xp# = (posx + xof) * multi
				yp# = (posy + yof) * multi
				
; do the multisampling
				For posa = 0 to milti
					; get the baring around the sphere
					ba# = xp + posa
					ba# = (ba# + 0.5) * xscaler#
					xp# = Cos(ba#)
					zp# = Sin(ba#)
					For poss = 0 To milti
						; get the pitch around the sphere
						pa# = yp + poss
						pa# = ((pa# + 0.5) * yscaler#)
						po# = Sin(pa#) * width#
						; get the positions in the space
						x# = (xp# * po#) + 5
						y# = (Cos(pa#) * width#) + 5
						z# = (zp# * po#) + 5
						; get the perlin result For that part
						h = Int(perl(x#,y#,z#,8) * 255)
						
						;Print h : WaitKey : End
						
							; cap the value
							If h => 255 Then h = 255
							If h <= 0 Then h = 0
							
							; add appropriate colour
							If h => 136
								g=g+h
							Else
								b=b+h
							EndIf
							
							; failsafe quit
							If KeyHit(1) Then End
							
						Next
					Next
					
					; scale down the colours
					g = g * malt#
					b = b * malt#
					
					; get the colour
					rgb=0*$10000+g*$100+b
					WritePixelFast posx,posy,rgb
					; put a dot there
					;Plot posx,posy
					
				Next
				
			Next
			
			UnlockBuffer GraphicsBuffer()
			
			SaveBuffer(GraphicsBuffer(),"bbperlin.bmp")
			
			While Not KeyHit(1)
				
				Flip
				
			Wend
			
			End
			
			; perlin Function
Function perl#(x#,y#,z#,octaves)
	; make sure the pass value is zerod
	h# = 0
	; shift octaves down to Input works from 1 but system works from 0
	octaves=octaves-1
	; make sue octaves are an ecceptable value
	If octaves <= 0 Then octaves = 0
	If octaves => 15 Then octaves = 15
	; loop the octaves
	For oct = 0 to octaves
		; grab the frequency And amplitude For this
			fre# = s#(oct,0)
			amp# = s#(oct,1)
			; convert the co-ordinates into steps
			x = Int(x# * fre#)
			y = Int(y# * fre#)
			z = Int(z# * fre#)
			; get the inbetween co-ords
			xb# = sine((x# * fre#) - Floor(x))
			yb# = sine((y# * fre#) - Floor(y))
			zb# = sine((z# * fre#) - Floor(z))
			xa# = 1.0 - xb#
			ya# = 1.0 - yb#
			za# = 1.0 - zb#
			; get the values For the 8 corners
				v000# = vil(x,y,z) * xa# * ya# * za#
				v100# = vil(x+1,y,z) * xb# * ya# * za#
				v010# = vil(x,y+1,z) * xa# * yb# * za#
				v001# = vil(x,y,z+1) * xa# * ya# * zb#
				v101# = vil(x+1,y,z+1) * xb# * ya# * zb#
				v110# = vil(x+1,y+1,z) * xb# * yb# * za#
				v011# = vil(x,y+1,z+1) * xa# * yb# * zb#
				v111# = vil(x+1,y+1,z+1) * xb# * yb# * zb#
				; add it on
				h#=h#+(v000# + v100# + v010# + v001# + v101# + v110# + v011# + v111#) * amp#
			Next
			; scale it down
			h# = h# * s#(octaves,2)
			Return h#
End Function

; Function to get the random value of a co-ordinate
Function vil#(x,y,z)
	; control edges
	If x < 0 Then x = x - (Int((x / 64) - 1) * 64) Else x = x - (Int(x/64) * 64)
	If y < 0 Then y = y - (Int((y / 64) - 1) * 64) Else y = y - (Int(y/64) * 64)
	If z < 0 Then z = z - (Int((z / 64) - 1) * 64) Else z = z - (Int(z/64) * 64)
	; get the number
	v# = r#(x,y,z)
	Return v#
End Function

	; Return an integer as a floating point
Function flo#(a)
	b# = a
	Return b#
End Function

; Function to turn a straight 0 - 1 into a sine curved 0 - 1
Function sine#(v#)
	; perform the change
	v# = (1 - Cos(v# * 180)) * 0.5
	
	Return v#
End Function

; Function to prepare Data For perlin noise
Function prepare_perlin(seed,persistance#)
	; set the seed value
	SeedRnd seed
	; create seed Data
	For posx = 0 To 63
		For posy = 0 To 63
			For posz = 0 To 63
				z# = Rnd(0,10000)
				r#(posx,posy,posz) = (z# * 0.0001)
				
			Next
		Next
	Next
	
	; prepare octave Data
	For posz = 0 To 15
		; work out the frequence of the octave
		s#(posz,0) = 2 ^ posz
		; get the amplitude
		s#(posz,1) = persistance# ^ posz
		; work out the maximum amplitude of
		s#(posz,2) = 0
		For posx = 0 To posz
			s#(posz,2)=s#(posz,2)+s#(posx,1)
		Next
		s#(posz,2) = 1.0 / s#(posz,2)
		
		
		
	Next
End Function
	


couldn't you use a sphere with pre-done uv coords and then generate the map and then place it on the spherein the game?
also how did you get that atmosphere?? its awesome looking

This one is seamless and easy to use. The Atmosphere - dunno - i have to look in my archives. But I worked further and got a different pic now, still not working correct. Looks like something has turned upside down with Sinus and Cosinus coordinates. The main problem was that you can declare in Dark Basic variables twice so I renamed them all that they can't collide in BB. At least I can see some parts of a shoreline now :-)

But I still need help here:



Graphics 512,256,32,2

	; create arrays For perlin generator
	Dim s#(15,2)
	Dim r#(63,63,63)
	
	; prepare the perlin
	prepare_perlin(1,0.5)
	
	; multisampling controller
	multi = 1
	milti = multi - 1
	malt# = multi ^ 2
	malt# = 1 / malt#
	
	xsize1 = 512
	ysize1 = 256
	
	; get the base sizes
	xsize2# = xsize1 * multi
	ysize2# = ysize1 * multi
	
	; get the scalers
	xscaler1# = 360
	yscaler1# = 180
	
	xscaler2# = xscaler1# / xsize2#
	yscaler2# = yscaler1# / ysize2#
	
	width# = 2
	
	; piece details
	pxs = 511
	pys = 255
	
	xof = 0
	yof = 0
	
	; loop the y position
	For posx = 0 To pxs Step 1
		
			; loop the x
			For posy = 0 To pys Step 1
				
				; reset colours
				g = 0
				b = 0
				
				; get the scaled sizes
				xp1 = (posx + xof) * multi
				yp1 = (posy + yof) * multi
				
				; do the multisampling
				For posa = 0 To milti
					
					; get the baring around the sphere
					ba# = xp1 + posa
					ba# = (ba# + 0.5) * xscaler2#
					xp2# = Cos(ba#)
					zp2# = Sin(ba#)
					
					For poss = 0 To milti
						
						; get the pitch around the sphere
						pa# = yp1 + poss
						pa# = ((pa# + 0.5) * yscaler2#)
						po# = Sin(pa#) * width#
						
						; get the positions in the space
						x# = (xp2# * po#) + 5
						y# = (Cos(pa#) * width#) + 5
						z# = (zp2# * po#) + 5
						
						; get the perlin result For that part
						h = Int(perl(x#,y#,z#,8) * 255)
							
						; cap the value
						If h => 255 Then h = 255
						If h <= 0 Then h = 0
							
						; add appropriate colour
						If h => 136
							g=g+h
						Else
							b=b+h
						EndIf
							
						; failsafe quit
						If KeyHit(1) Then End
							
					Next
					
				Next	
				
				; scale down the colours
				g = g * malt#
				b = b * malt#
					
				; get the colour
				Color 0,g,b
					
				; put a dot there
				Plot posx,posy
					
			Next
				
		Next
			
		WaitKey
		
		End
		
; perlin Function
Function perl#(x#,y#,z#,octaves)
	
	; make sure the pass value is zerod
	h# = 0
	
	; shift octaves down to Input works from 1 but system works from 0
	octaves=octaves-1
	
	; make sue octaves are an ecceptable value
	If octaves <= 0 Then octaves = 0
	If octaves => 15 Then octaves = 15
	
	; loop the octaves
	For oct = 0 To octaves
		
		; grab the frequency And amplitude For this
		fre# = s#(oct,0)
		amp# = s#(oct,1)
			
		; convert the co-ordinates into steps
		xx = Int(x# * fre#)
		yy = Int(y# * fre#)
		zz = Int(z# * fre#)
			
		; get the inbetween co-ords
		xb# = sine((x# * fre#) - flo(xx))
		yb# = sine((y# * fre#) - flo(yy))
		zb# = sine((z# * fre#) - flo(zz))
		xa# = 1.0 - xb#
		ya# = 1.0 - yb#
		za# = 1.0 - zb#
			
		; get the values For the 8 corners
		v000# = vil(xx,yy,zz) * xa# * ya# * za#
		v100# = vil(xx+1,yy,zz) * xb# * ya# * za#
		v010# = vil(xx,yy+1,zz) * xa# * yb# * za#
		v001# = vil(xx,yy,zz+1) * xa# * ya# * zb#
		v101# = vil(xx+1,yy,zz+1) * xb# * ya# * zb#
		v110# = vil(xx+1,yy+1,zz) * xb# * yb# * za#
		v011# = vil(xx,yy+1,zz+1) * xa# * yb# * zb#
		v111# = vil(xx+1,yy+1,zz+1) * xb# * yb# * zb#
				
		; add it on
		h#=h#+(v000# + v100# + v010# + v001# + v101# + v110# + v011# + v111#) * amp#
				
	Next
			
	; scale it down
	h# = h# * s#(octaves,2)
			
	Return h#
			
End Function
			
; Function to get the random value of a co-ordinate
Function vil#(x,y,z)
	
	; control edges
	If x < 0 Then x = x - (Int((x / 64) - 1) * 64) Else x = x - (Int(x/64) * 64)
	If y < 0 Then y = y - (Int((y / 64) - 1) * 64) Else y = y - (Int(y/64) * 64)
	If z < 0 Then z = z - (Int((z / 64) - 1) * 64) Else z = z - (Int(z/64) * 64)
	
	; get the number
	v# = r#(x,y,z)
	
	Return v#
	
End Function

	
	; Return an integer as a floating point
Function flo#(a)
	
	b# = a*1.0
	
	Return b#
	
End Function
	
; Function to turn a straight 0 - 1 into a sine curved 0 - 1
Function sine#(v#)
	
	; perform the change
	v# = (1 - Cos(v# * 180)) * 0.5
	
	Return v
	
End Function

	
; Function to prepare Data For perlin noise
Function prepare_perlin(seed,persistance#)
	
	; set the seed value
	SeedRnd seed
	
	; create seed Data
	For x = 0 To 63
		
		For y = 0 To 63
			
			For z = 0 To 63
				
				zz# = Rnd(10000)
				r#(x,y,z) = (zz# * 0.0001)
				
			Next
			
		Next
		
	Next
	
	; prepare octave Data
	For i = 0 To 15
		
		; work out the frequence of the octave
		s#(i,0) = 2 ^ i
		
		; get the amplitude
		s#(i,1) = persistance# ^ i
		
		; work out the maximum amplitude of
		s#(i,2) = 0.0
		
		For j = 0 To i
			
			s#(i,2)=s#(i,2)+s#(j,1)
			
		Next
		
		s#(i,2) = 1.0 / s#(i,2)
		
	Next
	
End Function


I think the remaining problem is Int(), which is misnamed in Blitz3D. It should be called Round() since it rounds to the nearest integer. Every other language I know uses Int() to mean "integer part of", i.e. discard the fractional part.

So here is a quick fix. I did a search and replace to change every Int( to DBInt(, which chops off everything after the decimal point.

Function DBInt( x# )
	If x >= 0 
		Return Floor(x)
	Else
		Return Ceil(x)
	End If
End Function

Graphics 512,256,32,2

	; create arrays For perlin generator
	Dim s#(15,2)
	Dim r#(63,63,63)
	
	; prepare the perlin
	prepare_perlin(1,0.5)
	
	; multisampling controller
	multi = 1
	milti = multi - 1
	malt# = multi ^ 2
	malt# = 1 / malt#
	
	xsize1 = 512
	ysize1 = 256
	
	; get the base sizes
	xsize2# = xsize1 * multi
	ysize2# = ysize1 * multi
	
	; get the scalers
	xscaler1# = 360
	yscaler1# = 180
	
	xscaler2# = xscaler1# / xsize2#
	yscaler2# = yscaler1# / ysize2#
	
	width# = 2
	
	; piece details
	pxs = 511
	pys = 255
	
	xof = 0
	yof = 0
	
	; loop the y position
	For posx = 0 To pxs Step 1
		
			; loop the x
			For posy = 0 To pys Step 1
				
				; reset colours
				g = 0
				b = 0
				
				; get the scaled sizes
				xp1 = (posx + xof) * multi
				yp1 = (posy + yof) * multi
				
				; do the multisampling
				For posa = 0 To milti
					
					; get the baring around the sphere
					ba# = xp1 + posa
					ba# = (ba# + 0.5) * xscaler2#
					xp2# = Cos(ba#)
					zp2# = Sin(ba#)
					
					For poss = 0 To milti
						
						; get the pitch around the sphere
						pa# = yp1 + poss
						pa# = ((pa# + 0.5) * yscaler2#)
						po# = Sin(pa#) * width#
						
						; get the positions in the space
						x# = (xp2# * po#) + 5
						y# = (Cos(pa#) * width#) + 5
						z# = (zp2# * po#) + 5
						
						; get the perlin result For that part
						h = DBInt(perl(x#,y#,z#,8) * 255)
							
						; cap the value
						If h => 255 Then h = 255
						If h <= 0 Then h = 0
							
						; add appropriate colour
						If h => 136
							g=g+h
						Else
							b=b+h
						EndIf
							
						; failsafe quit
						If KeyHit(1) Then End
							
					Next
					
				Next	
				
				; scale down the colours
				g = g * malt#
				b = b * malt#
					
				; get the colour
				Color 0,g,b
					
				; put a dot there
				Plot posx,posy
					
			Next
				
		Next
			
		WaitKey
		
		End
		
; perlin Function
Function perl#(x#,y#,z#,octaves)
	
	; make sure the pass value is zerod
	h# = 0
	
	; shift octaves down to Input works from 1 but system works from 0
	octaves=octaves-1
	
	; make sue octaves are an ecceptable value
	If octaves <= 0 Then octaves = 0
	If octaves => 15 Then octaves = 15
	
	; loop the octaves
	For oct = 0 To octaves
		
		; grab the frequency And amplitude For this
		fre# = s#(oct,0)
		amp# = s#(oct,1)
			
		; convert the co-ordinates into steps
		xx = DBInt(x# * fre#)
		yy = DBInt(y# * fre#)
		zz = DBInt(z# * fre#)
			
		; get the inbetween co-ords
		xb# = sine((x# * fre#) - flo(xx))
		yb# = sine((y# * fre#) - flo(yy))
		zb# = sine((z# * fre#) - flo(zz))
		xa# = 1.0 - xb#
		ya# = 1.0 - yb#
		za# = 1.0 - zb#
			
		; get the values For the 8 corners
		v000# = vil(xx,yy,zz) * xa# * ya# * za#
		v100# = vil(xx+1,yy,zz) * xb# * ya# * za#
		v010# = vil(xx,yy+1,zz) * xa# * yb# * za#
		v001# = vil(xx,yy,zz+1) * xa# * ya# * zb#
		v101# = vil(xx+1,yy,zz+1) * xb# * ya# * zb#
		v110# = vil(xx+1,yy+1,zz) * xb# * yb# * za#
		v011# = vil(xx,yy+1,zz+1) * xa# * yb# * zb#
		v111# = vil(xx+1,yy+1,zz+1) * xb# * yb# * zb#
				
		; add it on
		h#=h#+(v000# + v100# + v010# + v001# + v101# + v110# + v011# + v111#) * amp#
				
	Next
			
	; scale it down
	h# = h# * s#(octaves,2)
			
	Return h#
			
End Function
			
; Function to get the random value of a co-ordinate
Function vil#(x,y,z)
	
	; control edges
	If x < 0 Then x = x - (DBInt((x / 64) - 1) * 64) Else x = x - (DBInt(x/64) * 64)
	If y < 0 Then y = y - (DBInt((y / 64) - 1) * 64) Else y = y - (DBInt(y/64) * 64)
	If z < 0 Then z = z - (DBInt((z / 64) - 1) * 64) Else z = z - (DBInt(z/64) * 64)
	
	; get the number
	v# = r#(x,y,z)
	
	Return v#
	
End Function

	
	; Return an integer as a floating point
Function flo#(a)
	
	b# = a*1.0
	
	Return b#
	
End Function
	
; Function to turn a straight 0 - 1 into a sine curved 0 - 1
Function sine#(v#)
	
	; perform the change
	v# = (1 - Cos(v# * 180)) * 0.5
	
	Return v
	
End Function

	
; Function to prepare Data For perlin noise
Function prepare_perlin(seed,persistance#)
	
	; set the seed value
	SeedRnd seed
	
	; create seed Data
	For x = 0 To 63
		
		For y = 0 To 63
			
			For z = 0 To 63
				
				zz# = Rnd(10000)
				r#(x,y,z) = (zz# * 0.0001)
				
			Next
			
		Next
		
	Next
	
	; prepare octave Data
	For i = 0 To 15
		
		; work out the frequence of the octave
		s#(i,0) = 2 ^ i
		
		; get the amplitude
		s#(i,1) = persistance# ^ i
		
		; work out the maximum amplitude of
		s#(i,2) = 0.0
		
		For j = 0 To i
			
			s#(i,2)=s#(i,2)+s#(j,1)
			
		Next
		
		s#(i,2) = 1.0 / s#(i,2)
		
	Next
	
End Function


Int() also has this strange behaviour:

Int converts floating point numbers by rounding to the nearest integer.
NOTE: This is not the traditional meaning of Int in Basic.

What about numbers exactly halfway between integers?
The rounding is to the nearest even integer:

Int( 2.5 ) ... produces 2
Int( 3.5 ) ... produces 4



Just use Int( Left( Number,1) ).

Kryzon, that wont work for numbers with more than 1 digit or negative numbers.

True. Nevermind that.

To bypass Blitz's rounding system, use strings (since blitz doesn't round strings, it just returns the number until the "."):
Print Int( 2.9 )  ;result is 3
Print Int( Str(2.9) ) ;result is 2, which is what it should be in the first place.


Thanks to all, that is the solution! I wonder if I could speed up the whole code since it is a little bit slow for on-the-fly generating a planet. Perhaps pre-calculated sin/cos tables?

For a decent speed up, lock the buffer and use writepixelfast, instead of plot.

Also, just use the native float command rather than this ..

Function flo#(a)
	
	b# = a*1.0
	
	Return b#
	
End Function


Just an alternate idea. Have you tried a geosphere, with a cubemapped planet texture?

Stevie G - yeah "Plot" was used just for test purposes, sure "Writepixelfast" is faster for output. I was talking about speed up the calculation and my tests results showed that Float is slower than x*1.0.

Ross C - I only read about this but I am too dumb to realise it, are you talking about a "smoothed cube"? At the moment I am using a geosphere mesh instead of a sphere, but the texture only mapped to it the standard way. My goal is a planet generated by a seed which looks like the planets in "Freelancer" - they were simple but looked fine from a certain distance. And do you remember the game "Starflight"? There were nice but low resolution planets where you could land and drive over the surface (in 2D). I dunno how they did it (both the Freelancer planets and these in Starflight).

Another thing I was thinking about is creating and coloring a geosphere direct from the 3D Perlin cloud:

- generate a 3D Perlin Noise cloud
- calc the X/Y/Z Coordinates of Geosphere vertices
- Vertexcolor these vertices with the "color" from the same position in this cloud
- or even apply a texture depending on the "height" of the vertex, e.g. sea/grass/mountain
- realtime subdivide the visible patches if you get closer and modify the geosphere by the Perlin cloud data to get more detail

A very advanced engine using this technique (as far as I understood) is "Infinity":

http://www.infinity-universe.com/Infinity/index.php?option=com_zoom&Itemid=90&catid=4&PageNo=2
http://www.youtube.com/watch?v=DCzDKj3hjOE&feature=related

In Darkbasic there is a simple example how they did it, and even untextured it looks awesome!
http://forumfiles.thegamecreators.com/download/1527504

Ah i see what you mean now.