Jump to content

Plane to hug terrain


Rick
 Share

Recommended Posts

What's the general high level idea behind trying to make a plane "hug" or "blanket" the terrain? I want to make a plane that will follow my mouse around the terrain but would love for the plane to morph itself to hug the terrain under it. I guess I would assume you could take every vertex in the plane and set it's height .1 unit up from the terrain directly below it? The only issue seems to be that the CreatePlane() doesn't seem to let you decide how many vertices to have so it looks like you just have enough to make 2 triangles, which wouldn't be enough to morph around bumpy terrain.

Link to comment
Share on other sites

I would try to make my own CreatePlane function. Somewhere in this forum was a sample function posted by Josh (maybe the old forum).

Also you may have a look at the road script. there you find code how to morph / align vertices to the terrain height. for optimal results you have to adjust the vertex spacing to fit the terrain resolution.

 

Also there was a thread about terrain decals (by TylerH if i remember correctly) which looks like the thing you want.

  • Windows 10 Pro 64-Bit-Version
  • NVIDIA Geforce 1080 TI
Link to comment
Share on other sites

I have an idea of how I would solve this issue and would be interested in what people think about this solution. Remember that I would require this plane to move with either characters or the mouse. It won't be static on a surface.

 

1. I would create a plane with x number of vertices.

2. Each cycle I would position the plane's x & z position with the values of either the

mouse on the terrain or the character on the terrain. The plane would still be

flat at this point.

3. I would set the plane's y vlaue to some large number making it well above the terrain.

4. Then cycle through each vertice in the plane and do a raycast down to the terrain.

5. Get the collision point of that raycast with the terrain, and use that y value to set that planes

vertice y value + .01 so no fighting happens.

 

It seems like this solution would work, but I have to wonder if the speed would be bad? This would happen with

at most 11 planes at a time.

Link to comment
Share on other sites

Doesn't sound bad but doing a raycast for all vertices may cost lots of cpu power. Remember that you need 2 raycasts with short ranges or 1 with a range of maximum terrainheight - vertexY because you don't know if the point is above or below the terrain.

 

I believe you have 2 options here:

1. Use directly the Terrainheight function which interpolates the height at given points / may be also cpu heavy and depends highly on used vertices.

2. Write a meshshader which aligns the vertices on thy fly to the terrainheight. Terrainheightmap and everything else needed for this should be available in the shader pipeline.

  • Windows 10 Pro 64-Bit-Version
  • NVIDIA Geforce 1080 TI
Link to comment
Share on other sites

Remember that you need 2 raycasts with short ranges or 1 with a range of maximum terrainheight - vertexY because you don't know if the point is above or below the terrain.

 

I'm not following what you are saying here. Why would I need 2 raycasts with short range or 1 with max range? Let's use the mouse pointer for now as an example. If I set the Y value of the plane a set distance from the Y value of the mouse position on the terrain that would basically make the plane following the mouse but be higher. Then loop through each vertex on the plane (the number of vertices the plane has would determine how precise it morphs and this could be variable), and do a raycast from that point down past the Y distance I went up and maybe add a few more units for good measure (or if there is a way to get the min terrain height value would only need to go slightly past that). Then once the collision is found with the terrain I use that Y value for that vertex in the plane. That would mean 1 raycast per vertex in the plane. These planes wouldn't be very large so I'm thinking maybe 20 some vertices would be enough to make it look good. So for each plane (there would be 11 at max) loop through 20 vertices each, which would end up being 220 raycasts each cycle. I guess I could even break that down and say if a character isn't moving then there is no need to redo that plane. Because the game is turn based only 1 unit would be moving at a time which would mean only 1 plane would need to do this each cycle, so maybe 20 raycasts when the character is moving. I've not messed to much with raycasts so I'm not sure how expensive they are, but I would assume 20 each cycle would be doable.

 

I might be missing something, so that's why I step through how I'm thinking. If you can see where I'm missing something let me know. I also don't have to worry about huge peaks or valleys in my game.

Link to comment
Share on other sites

I missed your 3rd point where you handle the height issue (which will result in the 1 long raycast I've mentioned). As said: I would go the "terrainheight" way or the shader way. But there is also a cheaper raycast method: Do it the other way round ! Do a raycast from the vertex position at zero level (x,<=0, z) and do raycast up to the real vertexposition. Now just add an offset and you will have the same result but with generally lower raycast lengths.

 

Well the number of raycasts depends on the pc the application is running on. It can run smooth with yours but can be very slow on other machines.

  • Windows 10 Pro 64-Bit-Version
  • NVIDIA Geforce 1080 TI
Link to comment
Share on other sites

As said: I would go the "terrainheight" way or the shader way.

 

I'm not really interested in getting into shader programmer at the moment. I want to focus more on the game and not learning how shader programming works right now.

 

The terrain height way seems to be the way to do this. Since I know the point of each vertex in the plane, I can use the vertex x & z values as the terrain point and that could give me the height. My only 3 questions would be about the function.

 

http://www.leadwerks.com/wiki/index.php?title=Terrain#TerrainHeight

 

1. The parameter names are x & y. I assume y is really the z value?

2. Also it returns a value between 0 and 1. How is that the height of the terrain at that location? That doesn't make sense to me because the terrain value in the world could be any size.

3. Because this interpolates points does that mean my plane could end up a different size? Does the raycast also interpolate the collision on the terrain to get the point of collision which would give the same effect of the plane being different sizes once it's placed over the terrain?

 

 

But there is also a cheaper raycast method: Do it the other way round ! Do a raycast from the vertex position at zero level (x,<=0, z) and do raycast up to the real vertexposition. Now just add an offset and you will have the same result but with generally lower raycast lengths.

 

That's a good point. That would make it

Link to comment
Share on other sites

There is another Terrainfunction called TerrainEvaluation, this will give you the correct height value. the terrainheight function must be muliplied by the terrain y-scale value. x,y = x,z it is just y because you actually read from a texture in 2d space not 3d. the interpolation is because calculating the correct value (eg: fining the correct chunk, finding the correct triangle and then finding the point in triangle) is far to slow for these kind of things. The interpolation takes the 4 nearest full pixel values of the point (the point itself is a float) and performs a cubic interpolation between these points (weighting is based on distance from the real point to the full pixel). This technique is much faster and as long you don't have really oversized tilesizes it is accurate enough.

  • Windows 10 Pro 64-Bit-Version
  • NVIDIA Geforce 1080 TI
Link to comment
Share on other sites

Thought I'd supply code anyway because I'm away from the forum for a few days.

 

Code snipit: -

float mapSize = 1024;

float x;

float z;

float mapHalfSize = 512;

float height;

for(x = -mapHalfSize; x<mapHalfSize - 1; x++)

{

for(z = -mapHalfSize; z<mapHalfSize - 1; z++)

{

height = TerrainElevation( terrain, x, z);

if ((height != 0) || ((x>=minx && x<=maxx) && (z>=minz && z <= maxz)))

{

minx = __min(minx, x);

minz = __min(minz, z);

maxx = __max(maxx, x);

maxz = __max(maxz, z);

//br

height = TerrainElevation( terrain, (float)x+1, (float)z);

.

.

.

// Start making plane for terrain square

 

 

It uses the height and min/max frig to cope with different size of terrains.

Link to comment
Share on other sites

I'll try the TerrainElevation tonight. I should be able to figure that out. Is there some function already setup to allow me to create a terrain that will have x number of vertices to it, or do I have to create a surface and ad vertices manually and all of that? I hate doing that, it's so tedious. It would be nice to have a create plane function and tell it how many triangles I want.

 

 

So I'm confused about one thing about your code. At the end after you find this information is when you say create the plane. I was thinking create the plane first and then manipulate it's vertices to match the terrain height. Since this plane would be moving along the terrain with the mouse I would think it's cheaper to create the plane once and just manipulate it's vertices than to create a plane each cycle with it's vertices at the location on the terrain height around my mouse.

Link to comment
Share on other sites

hehe Rick :lol: i made this plain and Image is in general disscusion. There is a small problem. Sometimes, Visual side of terrain is different from physical terrain, so, decal is under terrain in someplaces, don't know why.

 

I am using this technique for decals. i have plain, i aling it to grid of terrain and i can set vertices height :)

 

ftp://78.102.70.147/Images/decal.jpg

 

It's fast, why not. you set height only once. No need for raycast, I am using TerrainElevation. Just convert plain coords to global, read height and return coord to locals for your plain .. that's all

-= Phenom II X4 965 3.4Ghz - ATI HD5870 - 6 GB DDR3 RAM - Windows 8 Pro 64x=-

Website: http://www.flamewarestudios.com

Link to comment
Share on other sites

I have put together a small prototype in my test application (Blitzmax) :

 

Alignedplane.png

 

And thats the code of the class:

 

Type TTerrainPlane
Global list:TList = New TList
Global terrain:TTerrain

Field Mesh:TMesh

Method New()
	list.AddLast(Self)
End Method

Function Create:TTerrainPlane(parent:TEntity, Scale:Float = 1.0)
	Local p:TTerrainPlane = New TTerrainPlane
	p.Mesh = CreatePatch(32, 32, parent)
	ScaleMesh(p.Mesh, [scale, 1.0, Scale])
	MoveEntity(p.Mesh, [- Scale / 2.0, 0.0, -Scale / 2.0])
	Return p
End Function

Function SetTerrain(t:TTerrain)
	TTerrainPlane.terrain = t
End Function

Function Update()
	If terrain = Null Then Return

	For Local Plane:TTerrainPlane = EachIn list
		Plane.Calculate()
	Next
End Function

Method Calculate()
	For Local Si:Int = 1 To Mesh.CountSurfaces()
		Local surf:TSurface = Mesh.GetSurface(Si)
		For Local Vi:Int = 0 To surf.CountVertices() - 1
			Local vertex:TVec3 = TFormPoint(surf.GetVertexPosition(Vi), mesh, Null) ;
			Local newpos:TVec3 = Vec3(vertex.X, TerrainElevation(terrain, vertex.X, vertex.Z) + 0.01, vertex.Z)
			surf.SetVertexPosition(Vi, TFormPoint(newpos, Null, Mesh))
		Next
	Next
End Method
End Type

Function CreatePatch:TMesh(xsegs:Int = 1, zsegs:Int = 1, parent:TEntity = Null)
  Local x:Int,z:Int
  Local mesh:TMesh
  Local count:Int
  Local surf:TSurface

  mesh = CreateMesh(parent)
  surf=mesh.AddSurface()
  For z=0 To zsegs
     For x=0 To xsegs
        count=surf.AddVertex([Float(x)/Float(xsegs),0.0,Float(z)/Float(zsegs)],[0.0,1.0,0.0],[Float(x)/Float(xsegs),1.0-Float(z)/Float(zsegs)])
        If x>0 And z>0
           surf.AddTriangle(count-1,count,count-xsegs-1)
           surf.AddTriangle(count-1,count-xsegs-1,count-xsegs-2)
        EndIf
     Next
  Next
  mesh.Update()
  Return mesh
EndFunction

 

Maybe this helps.

  • Windows 10 Pro 64-Bit-Version
  • NVIDIA Geforce 1080 TI
Link to comment
Share on other sites

klepto2, that is perfect! That's exactly what I want. I assume I can paint a material to the plane then that has some transparency. This is really nice of you to do this. I'll convert it over to C++ and you should public this code on the download section because I think it could be useful for others.

Link to comment
Share on other sites

Of couse you can paint it with a material :lol:

 

decaltextured.jpg

 

I will work on it a bit more and then I will post it in the archives.(I planned this feature for later but now i can also finish it;) ) there are some issues if the terrainelavtion is not giving exact values.

Also you may need to set it to the transparent world if you need to use alpha textures like the above.

  • Windows 10 Pro 64-Bit-Version
  • NVIDIA Geforce 1080 TI
Link to comment
Share on other sites

Also you may need to set it to the transparent world if you need to use alpha textures like the above.

 

Why would that be? I've had transparent textures in the same world before. I wouldn't think would be any different?

 

 

Also, what does your mat file look like for the above screenshot you have?

Link to comment
Share on other sites

Yeah, you can put transparent meshes into the main world fine as long as you don't need to be able to see (the shading) through them.

Depending on what you want to use these for, you should add normals calculation to the code (but for a selection highlight, no normals are fine).

 

The whole thing including perfect alignment of the vertices to the terrain vertices can in fact be done very efficiently in a shader:

http://forum.leadwerks.com/viewtopic.php?f=32&t=4254,

...

But if you don't need GPU mesh instancing to work on the patches and don't have too much, too large, too often moving/updating patches then a CPU mesh does fine as well.

Link to comment
Share on other sites

material:

texture0="abstract::sun.dds"
clamp0=0,0,0
blend=1
depthmask=0
depthtest=1
overlay=1
zsort=1
cullface=1
castshadows=0
specular=1.00000000
bumpscale=1.00000000
gloss=0.500000000
shader="abstract::mesh_diffuse.vert","abstract::mesh_diffuse_alphablend.frag"
shadowshader="abstract::mesh_shadow.vert",""

 

As MasterXilo already said, if you use alphablend textures within the mainworld it can produce unwanted effects. eg if you use this material in the mainworld you would just see brighter legs of the model but nothing on the terrain. Building it as a shader is my next step. I have done it with my own engine previously and i know it is more accurate then the non gpu approach.

  • Windows 10 Pro 64-Bit-Version
  • NVIDIA Geforce 1080 TI
Link to comment
Share on other sites

Did you have this code somewhere? I guess I didn't see a link on the old forums that pointed to where this is.

 

yeah, me too, i think, shader will be more accurate and faster, but .. it will calculate everytime ? when i set height by command in LE, IT's only once.

-= Phenom II X4 965 3.4Ghz - ATI HD5870 - 6 GB DDR3 RAM - Windows 8 Pro 64x=-

Website: http://www.flamewarestudios.com

Link to comment
Share on other sites

klepto2, just one question on the code. You have surf=mesh.AddSurface() but I can't seem to find that in the wiki. I see a CreateSurface() and I assume that's the same thing? Is the wiki not right for Bmax on that? A search for AddSurface() didn't bring up anything, not even for BMax.

Link to comment
Share on other sites

I made a terrain decal patch a while back, in Lua, that was a thingoid that could be dragged around with the mouse.

 

Sounds like exactly what you are trying to do...and it was optimized over a week of trials by myself and others, so it renders with little effect on FPS, if any.

52t__nvidia.png nVidia 530M cpu.gif Intel Core i7 - 2.3Ghz 114229_30245_16_hardware_memory_ram_icon.png 8GB DDR3 RAM Windows7_Start.gif Windows 7 Ultimate (64x)

-----

IconVisualStudio16.png Visual Studio 2010 Ultimate google-Chrome.png Google Chrome PhotoshopLinkIndicator.png Creative Suite 5 icon28.gif FL Studio 10 MicrosoftOfficeLive.png Office 15

-----

csharp.png Expert cpp.png Professional lua_icon.png Expert BMX Programmer

-----

i-windows-live-messenger-2009.pngskype-icon16.pngaim_online.pnggmail.pngicon_48x48_prism-facebook.pngtunein-web.pngyahoo.giftwitter16.png

Link to comment
Share on other sites

I'm not sure where I ultimately uploaded it to, so here is the Lua Script (I think it works from my last trial...)

 

EDIT: Yep, works nicely. From what I can tell, with a higher than 1 tessellation (in the properties) it will match terrain perfectly. Choose tessellation based on the resolution and meters per tile of your terrain.

Decal.zip

52t__nvidia.png nVidia 530M cpu.gif Intel Core i7 - 2.3Ghz 114229_30245_16_hardware_memory_ram_icon.png 8GB DDR3 RAM Windows7_Start.gif Windows 7 Ultimate (64x)

-----

IconVisualStudio16.png Visual Studio 2010 Ultimate google-Chrome.png Google Chrome PhotoshopLinkIndicator.png Creative Suite 5 icon28.gif FL Studio 10 MicrosoftOfficeLive.png Office 15

-----

csharp.png Expert cpp.png Professional lua_icon.png Expert BMX Programmer

-----

i-windows-live-messenger-2009.pngskype-icon16.pngaim_online.pnggmail.pngicon_48x48_prism-facebook.pngtunein-web.pngyahoo.giftwitter16.png

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...