# Terrain Building API in Leadwerks 5 Beta

534 views

An often-requested feature for terrain building commands in Leadwerks 5 is being implemented. Here is my script to create a terrain. This creates a 256 x 256 terrain with one terrain point every meter, and a maximum height of +/- 50 meters:

```--Create terrain
local terrain = CreateTerrain(world,256,256)
terrain:SetScale(256,100,256)```

Here is what it looks like:

A single material layer is then added to the terrain.

```--Add a material layer

We don't have to do anything else to make the material appear because by default the entire terrain is set to use the first layer, if a material is available there:

Next we will raise a few terrain points.

```--Modify terrain height
for x=-5,5 do
for y=-5,5 do
h = (1 - (math.sqrt(x*x + y*y)) / 5) * 20
terrain:SetElevation(127 + x, 127 + y, h)
end
end```

And then we will update the normals for that whole section, all at once. Notice that we specify a larger grid for the normals update, because the terrain points next to the ones we modified will have their normals affected by the change in height of the neighboring pixel.

```--Update normals of modified and neighboring points
terrain:UpdateNormals(127 - 6, 127 - 6, 13, 13)```

Now we have a small hill.

Next let's add another layer and apply it to terrain points that are on the side of the hill we just created:

```--Add another layer

--Apply layer to sides of hill
for x=-5,5 do
for y=-5,5 do
slope = terrain:GetSlope(127 + x, 127 + y)
alpha = math.min(slope / 15, 1.0)
terrain:SetMaterial(rockLayerID, 127 + x, 127 + y, alpha)
end
end```

We could improve the appearance by giving it a more gradual change in the rock layer alpha, but it's okay for now.

This gives you an idea of the basic terrain building API in Leadwerks 5, and it will serve as the foundation for more advanced terrain features. This will be included in the next beta.

• 4

Can we manipulate the terrain in real time? If so, that reminds me of a feature of the Source engine that was removed sometime after HL2: Episode 1.

Not sure how efficient it would be though.

Yes, you can manipulate the terrain in real-time with no problems. Collision and everything will work just fine.

And what about Navmesh ? This should be updated too, the problem was regarding performances on bigger maps....

1 hour ago, Marcousik said:

And what about Navmesh ? This should be updated too, the problem was regarding performances on bigger maps....

A static cache will be used for the terrain and other static objects, instead of reprocessing everything every time something changes.

2 hours ago, Josh said:

A static cache will be used for the terrain and other static objects, instead of reprocessing everything every time something changes.

Does this mean you're not planning on implementing a dynamic navmesh, like for doors that open to let characters through?  Or is that something different?

Objects are separated into static and dynamic. When a dynamic object moves only the dynamic objects gets recalculated. The static data stays the same.

Shadow maps work with the same principle, so this is actually convenient because static objects can be marked as such for both purposes.

Quick perlin noise example, but there is a seam issue you can see:

Here is the code for terrain.lua

```--Get the primary display
local displaylist = ListDisplays()
local display = displaylist[1];
local displayscale = display:GetScale()

--Create a window
local window = CreateWindow(display, "Terrain", 0, 0, math.min(1280 * displayscale.x, display.size.x), math.min(720 * displayscale.y, display.size.y), WINDOW_TITLEBAR + WINDOW_CENTER)

--Create a rendering framebuffer
local framebuffer = CreateFramebuffer(window);

--Create a world
local world = CreateWorld()

--Create a camera
local camera = CreateCamera(world)
--camera:SetClearColor(0.25)
camera:Turn(35,0,0)
camera:Move(0,12,-33)

--Create a light
local light = CreateLight(world,LIGHT_DIRECTIONAL)
light:SetRotation(45,-55,0)
light:SetColor(4,4,4,1)

--Create terrain
local terrain = CreateTerrain(world,256,256)
terrain:SetScale(256,200,256)

terrain:SetMaterial(layerID, 0, 0, 256, 256, 1)

--PERLIN NOISE

local p = {}

local permutation = {151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
}

for i = 0, 255 do
p[i] = permutation[i + 1]
p[256 + i] = permutation[i + 1]
end

return t * t * t * (t * (t * 6 - 15) + 10)
end

local function lerp(t, a, b)
return a + t * (b - a)
end

local function grad(hash, x, y, z)
local h, u, v = hash % 16
if h < 8 then u = x else u = y end
if h < 4 then v = y elseif h == 12 or h == 14 then v = x else v = z end
local r
if h % 2 == 0 then r = u else r = -u end
if h % 4 == 0 then r = r + v else r = r - v end
return r
end

local function perlin(x, y, z)
y = y or 0
z = z or 0
local X = math.floor(x % 255)
local Y = math.floor(y % 255)
local Z = math.floor(z % 255)
x = x - math.floor(x)
y = y - math.floor(y)
z = z - math.floor(z)
A   = p[X     ] + Y
AA  = p[A     ] + Z
AB  = p[A + 1] + Z
B   = p[X + 1] + Y
BA  = p[B     ] + Z
BB  = p[B + 1] + Z
return lerp(w, lerp(v, lerp(u, grad(p[AA      ], x    , y     , z     ),
grad(p[BA      ], x - 1, y     , z     )),
lerp(u, grad(p[AB      ], x    , y - 1, z      ),
grad(p[BB      ], x - 1, y - 1, z      ))),
lerp(v, lerp(u, grad(p[AA + 1], x      , y     , z - 1),
grad(p[BA + 1], x - 1, y       , z - 1)),
lerp(u, grad(p[AB + 1], x      , y - 1, z - 1),
grad(p[BB + 1], x - 1, y - 1, z - 1))))
end

function fbm(x, y, z, octaves, lacunarity, gain)
octaves = octaves or 8
lacunarity = lacunarity or 2
gain = gain or 0.5
local amplitude = 1.0
local frequency = 1.0
local sum = 0.0
for i = 0, octaves do
sum = sum + amplitude * perlin(x * frequency, y * frequency, z * frequency)
amplitude = amplitude * gain
frequency = frequency * lacunarity
end
return sum
end

--- ### This should match terrain created
local terrainsize=256
local octaves=12  		-- more is finer noise but slower
local lacunarity=1.9		-- size of noise
local gain=0.6		-- height gain
for i=-127,127 do
for j=-127,127 do
local noise=fbm(i/terrainsize, 11, j/terrainsize, octaves, lacunarity, gain)
terrain:SetElevation(127+i,127+j, 15+noise*20)
end
end

--Update normals of modified and neighboring points
terrain:UpdateNormals(0, 0, 256, 256)

--Apply layer to sides of hill
for x=-127,127 do
for y=-127,127 do
slope = terrain:GetSlope(127 + x, 127 + y)
alpha = math.min(slope / 15, 1.0)
terrain:SetMaterial(rockLayerID, 127 + x, 127 + y, alpha)
end
end

--Main loop
while window:Closed() == false do

world:Update()
world:Render(framebuffer)

end```

## Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

×   Pasted as rich text.   Paste as plain text instead

Only 75 emoji are allowed.

×   Your previous content has been restored.   Clear editor

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

×
×

• Pages

• Back
• Store

• #### Support

• Projects
×
• Create New...