Jump to content
  • Advertisement
Sign in to follow this  
Hawkblood

How do you make a flat map go over a sphere?

This topic is 1839 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have a planet (sphere) with a noise generated terrain image. I use the same noise values to generate a square height map terrain. Each vertex (regularly spaced) of the terrain is calculated so that it “wraps” on the sphere. This sounds good except it doesn’t exactly work. Initially it looks correct, but when I move the terrain, I notice that the new additions to the terrain are not lining up with the original parts. Here is my theory: take a piece of paper and try to lay it over the surface of a ball. To make it work, the paper will have folds the further the point on the paper is from the central point. This means each vertex at the edges of the spherified plane is closer to its neighbor than the ones near the center of the plane….. How do I correct this problem?

Share this post


Link to post
Share on other sites
Advertisement

You can use 3D noise instead, so when you want height at some point on the surface of the sphere, you pass the x,y,z coordinate to your noise function and use the return value for height.

Share this post


Link to post
Share on other sites
I am using 3d noise. I calculate where on the sphere the vertex falls and generate the 3d noise for that spot then use that value to push the vertex out/in from the center. The problem is that the point on the sphere calculation is giving me what I discribed with the paper on the sphere. It's not that noticable until I move the center of the map, then they don't line up with the old parts. I tried simply generating the whole thing each time I moved, but that caused the points on the sphere, that should be in the same place, to be shifted slightly causing a completely different height map-- this is unacceptible.

Share this post


Link to post
Share on other sites

Although Waterlimon's answer is probably best, the problem as you originally stated it is known as using a map projection. This is important for cartography, so it has been studied to death.

Share this post


Link to post
Share on other sites

I am using 3d noise. I calculate where on the sphere the vertex falls and generate the 3d noise for that spot then use that value to push the vertex out/in from the center. The problem is that the point on the sphere calculation is giving me what I discribed with the paper on the sphere. It's not that noticable until I move the center of the map, then they don't line up with the old parts. I tried simply generating the whole thing each time I moved, but that caused the points on the sphere, that should be in the same place, to be shifted slightly causing a completely different height map-- this is unacceptible.

 

I don't understand. At what point are you mapping your 3D point to a plane? It looks like you don't really need to...

Share this post


Link to post
Share on other sites
On a plane, each point has a distance from the center. I am using the sphere's arc length as a multiplier to determine the pitch and yaw from this center point that each of the other points are located (I hope this makes sense). This may be where I’m going wrong. I have looked at the article, but nothing seems to be accurate. Moreover, it suggests that what I am looking for is actually impossible…..

Share this post


Link to post
Share on other sites

I still don't understand. You have a mesh whose vertices are originally on the surface of a sphere. You then want to compute some noise function to displace those vertices along the direction that passes through the center of the sphere. At what point do you need a 2D representation of any sort?

Share this post


Link to post
Share on other sites

I think I know what you are getting at... I'll try to explain better. This is a space sim game. I want the player to be able to land on any planet. I want the surface of the planet to get more and more detailed as the player gets closer to the surface. I can't generate the entire planet's surface, in any significant detail, in real time so I will generate the section that the player is nearest. First using a rough map (skirt broken into blocks and each block has cells) and then, as the player gets closer, generate more detail using the corners of each cell of the map as a rough topographic map (in 3D). The skirt shows up first, fading in as the player gets closer. If the player changes direction, essentially changing the landing area, the skirt would have to change its center point, generating a new set of blocks at the edge of the map in the direction of the movement. This is where I first noticed the problem. I shifted north and here is what I saw:

[attachment=17275:Untitled.png]

This is WITHOUT ANY perturbation using noise. It's only taking the plane and placing the vertices on the sphere.

The top image shows it before and the bottom shows after. Notice the difference? The new row is not lining up. So, I set up a test. I measured the distance between opposite corners of each block (shown by red arrow). The distance should be the same throughout (if my calculations were correct). I got noticeable variations: the center block was ~324282 and the edges were ~320324 and the ones in between lerped those numbers.
 

I think I can make a multiplier to correct this, but I think it's my calculations that are causing it in the first place. Here is the code I am using to generate each block:

void TERRAINSKIRT::Generate(GAMEENGINE *GE,int x,int z,ISOTERRAIN* it,SKIRTOBJECT* so){
	double vertnumber=so->NumberOfVerts;
	double elementnumber=vertnumber-1;
	double ESize=so->ElementSize;
	double rX=(double(x-4)*elementnumber-elementnumber/2.0)*ESize,rZ=(double(z-4)*elementnumber-elementnumber/2.0)*ESize;//location of the first vertex (upper left)?  THE "ARC DISTANCE" FROM CENTER OF THE MAP

	GE->d3ddev->CreateVertexBuffer(int(vertnumber*vertnumber)*sizeof(E_D3DVERTEX), 0, 
					  E_D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &pVertexObject, NULL);
	GE->d3ddev->CreateIndexBuffer(3*2*int(elementnumber*elementnumber)*sizeof(short),                  
					0,                           
					D3DFMT_INDEX16,                           
					D3DPOOL_MANAGED,                           
					&i_buffer,                           
					NULL);

	void *pVoid;
	pVertexObject->Lock(0, int(vertnumber*vertnumber)*sizeof(E_D3DVERTEX), &pVoid, D3DLOCK_DISCARD);

	E_D3DVERTEX *pVP=(E_D3DVERTEX*)pVoid;

	D3DXMATRIX rotmat,mattmp;
	D3DXQUATERNION rotQuat,qtmp;
	int index=0;
	float rad=it->PlanetRadius/100.0f;

	double yaw=(rX+elementnumber*ESize/2.0)*it->radianFactor;
	double pitch=(rZ+elementnumber*ESize/2.0)*it->radianFactor;

	D3DXQuaternionRotationYawPitchRoll(&rotQuat,float(yaw),float(pitch),0);
	rotQuat*=it->startingQt;
	D3DXMatrixRotationQuaternion(&rotmat,&rotQuat);
	D3DXVECTOR3 centerVec;
	D3DXVec3TransformCoord(&centerVec,&D3DXVECTOR3(0,0,1),&rotmat);
	loc=centerVec;
	loc*=double(it->PlanetRadius);
	centerVec*=it->PlanetRadius;


	for (int z=0;z<vertnumber;z++){
		for (int x=0;x<vertnumber;x++){
			double yaw=(rX+double(x)*ESize)*it->radianFactor;
			double pitch=(rZ+double(z)*ESize)*it->radianFactor;
			D3DXQuaternionRotationYawPitchRoll(&rotQuat,float(yaw),float(pitch),0);
			rotQuat*=it->startingQt;
			D3DXMatrixRotationQuaternion(&rotmat,&rotQuat);
			D3DXVec3TransformCoord(&pVP[index].v,&D3DXVECTOR3(0,0,1),&rotmat);
			D3DXVECTOR3 pvec=pVP[index].v*rad;

			pVP[index].v*= it->PlanetRadius;// + so->SkirtPerlNoise.Get3(pvec.x,pvec.y,pvec.z) ;//+perlin noise value!!!!!!!!!!!!******************
			pVP[index].v-=centerVec;
			pVP[index].n.x=pVP[index].n.y=pVP[index].n.z=0.0f;
			pVP[index].tu=float(x)/float(vertnumber);
			pVP[index].tv=float(z)/float(vertnumber);
			index++;
		}
	}
tp3=D3DXVec3Length(&(pVP[0].v-pVP[index-1].v));//this is how I get the length

	short indices[3*2*3969];//63x63 (3969) is the max size for the number of squares *2 triangles per square *3 verts per triangle
	int pos=0;
	for (int ty=0;ty<int(elementnumber);ty++){
		for (int tx=0;tx<int(elementnumber);tx++){
			indices[pos]=short((ty*int(vertnumber))+tx+1);
			indices[pos+2]=short((ty*int(vertnumber))+tx);
			indices[pos+1]=short(((ty+1)*int(vertnumber))+tx);
							
			pos+=3;
			indices[pos]=short((ty*int(vertnumber))+tx+1);
			indices[pos+2]=short(((ty+1)*int(vertnumber))+tx);
			indices[pos+1]=short(((ty+1)*int(vertnumber))+tx+1);
			pos+=3;
		}
	}

	for (UINT i = 0; i < UINT(elementnumber*elementnumber)*2; i++) {//(elementnumber*elementnumber) is the number of squares and 2 triangles per square
		UINT pos=i*3;
		D3DXVECTOR3 vec1,vec2,normal;
		short id0,id1,id2;
		id0=indices[pos];
		id1=indices[pos+1];
		id2=indices[pos+2];

		vec1=pVP[id1].v-pVP[id0].v;
		vec2=pVP[id2].v-pVP[id0].v;
		normal.x=vec1.z*vec2.y-vec1.y*vec2.z;
		normal.y=vec1.x*vec2.z-vec1.z*vec2.x;
		normal.z=vec1.y*vec2.x-vec1.x*vec2.y;

		pVP[id0].n+=normal;
		pVP[id1].n+=normal;
		pVP[id2].n+=normal;
	}
	// Normalize normals.
	for (UINT i = 0; i < UINT(vertnumber*vertnumber); i++) {
		float length = sqrt(pVP[i].n.x*pVP[i].n.x + pVP[i].n.y*pVP[i].n.y + pVP[i].n.z*pVP[i].n.z);
		pVP[i].n/=length;
	}



	i_buffer->Lock(0, 0, (void**)&pVoid, 0);
	memcpy(pVoid, indices, int(elementnumber*elementnumber)*3*2*sizeof(short));
	i_buffer->Unlock();


	pVertexObject->Unlock();
}

The significant part is:

	for (int z=0;z<vertnumber;z++){
		for (int x=0;x<vertnumber;x++){
			double yaw=(rX+double(x)*ESize)*it->radianFactor;
			double pitch=(rZ+double(z)*ESize)*it->radianFactor;
			D3DXQuaternionRotationYawPitchRoll(&rotQuat,float(yaw),float(pitch),0);
			rotQuat*=it->startingQt;
			D3DXMatrixRotationQuaternion(&rotmat,&rotQuat);
			D3DXVec3TransformCoord(&pVP[index].v,&D3DXVECTOR3(0,0,1),&rotmat);
			D3DXVECTOR3 pvec=pVP[index].v*rad;

			pVP[index].v*= it->PlanetRadius;// + so->SkirtPerlNoise.Get3(pvec.x,pvec.y,pvec.z) ;//+perlin noise value!!!!!!!!!!!!******************
			pVP[index].v-=centerVec;
			pVP[index].n.x=pVP[index].n.y=pVP[index].n.z=0.0f;
			pVP[index].tu=float(x)/float(vertnumber);
			pVP[index].tv=float(z)/float(vertnumber);
			index++;
		}
	}

The "it->radianFactor" is generated when the terrain is initialized. It's basically 1/planetRadius. rX and rZ are where the block is located and ESize is the length between each vertex of the block (if in a plane).

 


You have a mesh whose vertices are originally on the surface of a sphere.

I want to use the vertices of the skirt to be used as the corners of each cell of the detail blocks. If I use the sphere's vertices, I couldn't have square blocks-- neither regular spheres nor geospheres would have regular shaped squares. I need regular squares for the marching cubes of my detail terrain.

Edited by Hawkblood

Share this post


Link to post
Share on other sites

FIXED IT!!!!

void TERRAINSKIRT::Generate(GAMEENGINE *GE,int x,int z,ISOTERRAIN* it,SKIRTOBJECT* so){
	double vertnumber=so->NumberOfVerts;
	double elementnumber=vertnumber-1;
	double ESize=so->ElementSize;
	double rX=(double(x-4)*elementnumber-elementnumber/2.0)*ESize,rZ=(double(z-4)*elementnumber-elementnumber/2.0)*ESize;//location of the first vertex (upper left)?  THE "ARC DISTANCE" FROM CENTER OF THE MAP

	GE->d3ddev->CreateVertexBuffer(int(vertnumber*vertnumber)*sizeof(E_D3DVERTEX), 0, 
					  E_D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &pVertexObject, NULL);
	GE->d3ddev->CreateIndexBuffer(3*2*int(elementnumber*elementnumber)*sizeof(short),                  
					0,                           
					D3DFMT_INDEX16,                           
					D3DPOOL_MANAGED,                           
					&i_buffer,                           
					NULL);

	void *pVoid;
	pVertexObject->Lock(0, int(vertnumber*vertnumber)*sizeof(E_D3DVERTEX), &pVoid, D3DLOCK_DISCARD);

	E_D3DVERTEX *pVP=(E_D3DVERTEX*)pVoid;

	D3DXMATRIX rotmat,mattmp;
	D3DXQUATERNION rotQuat,qtmp;

	D3DXMATRIX start;
	D3DXMatrixRotationQuaternion(&start,&it->startingQt);

	D3DXVECTOR3 up,right;
	D3DXVec3TransformCoord(&up,&D3DXVECTOR3(0,1,0),&start);
	D3DXVec3TransformCoord(&right,&D3DXVECTOR3(1,0,0),&start);

	int index=0;
	float rad=it->PlanetRadius/100.0f;

	double yaw=(rX+elementnumber*ESize/2.0)*it->radianFactor;
	double pitch=(rZ+elementnumber*ESize/2.0)*it->radianFactor;
	D3DXVECTOR3 centerVec,startvec;
			D3DXVec3TransformCoord(&startvec,&D3DXVECTOR3(0,0,1),&start);//the start point (center of the map)
			D3DXQuaternionRotationAxis(&rotQuat,&up,float(yaw));
			D3DXMatrixRotationQuaternion(&rotmat,&rotQuat);
			D3DXVec3TransformCoord(&centerVec,&startvec,&rotmat);
			D3DXQuaternionRotationAxis(&rotQuat,&right,float(pitch));
			D3DXMatrixRotationQuaternion(&rotmat,&rotQuat);
			D3DXVec3TransformCoord(&centerVec,&centerVec,&rotmat);

	loc=centerVec;
	loc*=double(it->PlanetRadius);
	centerVec*=it->PlanetRadius;

	for (int z=0;z<vertnumber;z++){
		for (int x=0;x<vertnumber;x++){
			double yaw=(rX+double(x)*ESize)*it->radianFactor;
			double pitch=(rZ+double(z)*ESize)*it->radianFactor;

			
			D3DXQuaternionRotationAxis(&rotQuat,&up,float(yaw));
			D3DXMatrixRotationQuaternion(&rotmat,&rotQuat);
			D3DXVec3TransformCoord(&pVP[index].v,&startvec,&rotmat);
			D3DXQuaternionRotationAxis(&rotQuat,&right,float(pitch));
			D3DXMatrixRotationQuaternion(&rotmat,&rotQuat);
			D3DXVec3TransformCoord(&pVP[index].v,&pVP[index].v,&rotmat);

			D3DXVECTOR3 pvec=pVP[index].v*rad;

			pVP[index].v*= it->PlanetRadius + so->SkirtPerlNoise.Get3(pvec.x,pvec.y,pvec.z) ;//+perlin noise value!!!!!!!!!!!!******************
			pVP[index].v-=centerVec;
			pVP[index].n.x=pVP[index].n.y=pVP[index].n.z=0.0f;
			pVP[index].tu=float(x)/float(vertnumber);
			pVP[index].tv=float(z)/float(vertnumber);
			index++;
		}
	}

	short indices[3*2*3969];//63x63 (3969) is the max size for the number of squares *2 triangles per square *3 verts per triangle
	int pos=0;
	for (int ty=0;ty<int(elementnumber);ty++){
		for (int tx=0;tx<int(elementnumber);tx++){
			indices[pos]=short((ty*int(vertnumber))+tx+1);
			indices[pos+2]=short((ty*int(vertnumber))+tx);
			indices[pos+1]=short(((ty+1)*int(vertnumber))+tx);
							
			pos+=3;
			indices[pos]=short((ty*int(vertnumber))+tx+1);
			indices[pos+2]=short(((ty+1)*int(vertnumber))+tx);
			indices[pos+1]=short(((ty+1)*int(vertnumber))+tx+1);
			pos+=3;
		}
	}

	for (UINT i = 0; i < UINT(elementnumber*elementnumber)*2; i++) {//(elementnumber*elementnumber) is the number of squares and 2 triangles per square
		UINT pos=i*3;
		D3DXVECTOR3 vec1,vec2,normal;
		short id0,id1,id2;
		id0=indices[pos];
		id1=indices[pos+1];
		id2=indices[pos+2];

		vec1=pVP[id1].v-pVP[id0].v;
		vec2=pVP[id2].v-pVP[id0].v;
		normal.x=vec1.z*vec2.y-vec1.y*vec2.z;
		normal.y=vec1.x*vec2.z-vec1.z*vec2.x;
		normal.z=vec1.y*vec2.x-vec1.x*vec2.y;

		pVP[id0].n+=normal;
		pVP[id1].n+=normal;
		pVP[id2].n+=normal;
	}
	// Normalize normals.
	for (UINT i = 0; i < UINT(vertnumber*vertnumber); i++) {
		float length = sqrt(pVP[i].n.x*pVP[i].n.x + pVP[i].n.y*pVP[i].n.y + pVP[i].n.z*pVP[i].n.z);
		pVP[i].n/=length;
	}



	i_buffer->Lock(0, 0, (void**)&pVoid, 0);
	memcpy(pVoid, indices, int(elementnumber*elementnumber)*3*2*sizeof(short));
	i_buffer->Unlock();


	pVertexObject->Unlock();
}

The significance was this change:

			D3DXVec3TransformCoord(&startvec,&D3DXVECTOR3(0,0,1),&start);//the start point (center of the map)
			D3DXQuaternionRotationAxis(&rotQuat,&up,float(yaw));
			D3DXMatrixRotationQuaternion(&rotmat,&rotQuat);
			D3DXVec3TransformCoord(&centerVec,&startvec,&rotmat);
			D3DXQuaternionRotationAxis(&rotQuat,&right,float(pitch));
			D3DXMatrixRotationQuaternion(&rotmat,&rotQuat);
			D3DXVec3TransformCoord(&centerVec,&centerVec,&rotmat);

Instead of transforming the vector with yaw and pitch together, I tried to do them separately. This did the trick.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!