- Joined
- Aug 1, 2013
- Messages
- 4,658
Ok, this is a bit of a combination of two systems...
1, Terrain (which is basically a way of setting the terrain height using Terrain Deformations.)
2, HeightGenerator (which is a procedural terrain generator using the Terrain API.)
(This one uses Thread because it would otherwise reach the OP limit pretty easily.)
Here is a small test script to try it out: (which is also present in the test map)
There is one problem with this thing... which is that terrain deformations are pretty cpu intense.
Especially because there are probably going to be a few thousand on the map if you use it a lot.
I tested this on
- a 2,048x2,048 terrain, where there is no lag at all.
- a 4,096x4,096 terrain, where you can notice the lag... mostly by not having a smooth camera.
- a 8,192x8,192 terrain, where this lag can be really annoying.
(A 480x480 map is a 61,440x61,440 terrain, which is going to lag so hard that you want to uninstall Warcraf III.)
So for small areas, this would be nice, but apart from that... it is not really useful until there is a better method of changing the height of a point in the terrain.
1, Terrain (which is basically a way of setting the terrain height using Terrain Deformations.)
2, HeightGenerator (which is a procedural terrain generator using the Terrain API.)
JASS:
struct Terrain
readonly static constant real GRID = 128.
readonly static constant integer X_SIZE = 8192 // Configure to map.
readonly static constant integer Y_SIZE = 8192 // Configure to map.
readonly static constant integer X_GRID = .X_SIZE / R2I(.GRID)
readonly static constant integer Y_GRID = .Y_SIZE / R2I(.GRID)
readonly static integer MIN_X_GRID
readonly static integer MIN_Y_GRID
private static constant location LOCATION = Location(0, 0)
private static terraindeformation array terrainDeformations[4096] // [.X_GRID*.Y_GRID]
private static real array originalHeight[4096] // [.X_GRID*.Y_GRID]
private static method create takes nothing returns thistype
return 0
endmethod
public static method getHeight takes real x, real y returns real
call MoveLocation(.LOCATION, x, y)
return GetLocationZ(.LOCATION)
endmethod
public static method reduceHeight takes real x, real y, real z returns nothing
local integer i = R2I(((x/.GRID)-.MIN_X_GRID) * .Y_GRID + ((y/.GRID)-.MIN_Y_GRID))
local real newZ
if .terrainDeformations[i] != null then
call TerrainDeformStop(.terrainDeformations[i], 0)
set z = z - (.getHeight(x, y) - .originalHeight[i])
else
set .originalHeight[i] = .getHeight(x, y)
endif
set .terrainDeformations[i] = TerrainDeformCrater(x, y, .GRID, z, 0, false)
endmethod
public static method addHeight takes real x, real y, real z returns nothing
call .reduceHeight(x, y, -z)
endmethod
public static method setHeight takes real x, real y, real z returns nothing
call .reduceHeight(x, y, .getHeight(x, y) - z)
endmethod
public static method resetHeight takes real x, real y returns nothing
local integer i = R2I(((x/.GRID)-.MIN_X_GRID) * .Y_GRID + ((y/.GRID)-.MIN_Y_GRID))
call TerrainDeformStop(.terrainDeformations[i], 0)
set .terrainDeformations[i] = null
endmethod
private static method onInit takes nothing returns nothing
local rect world = GetWorldBounds()
set .MIN_X_GRID = R2I(GetRectMinX(world) / .GRID)
set .MIN_Y_GRID = R2I(GetRectMinY(world) / .GRID)
call RemoveRect(world)
set world = null
call TerrainDeformStop(TerrainDeformCrater(0, 0, 0, 0, 0, false), 0)
endmethod
endstruct
(This one uses Thread because it would otherwise reach the OP limit pretty easily.)
JASS:
struct HeightGenerator
private static thistype looping = 0
private static real ty = 0
private static real tx = 0
public integer seed
public real minHeight
public real maxHeight
public real minX
public real minY
public real maxX
public real maxY
public real smoothFactor
public static method create takes integer seed returns thistype
local thistype this = .allocate()
set .seed = seed
return this
endmethod
private method getNoise takes real x, real y returns real
call SetRandomSeed(R2I(x*49632 + y*325176) + .seed)
return GetRandomReal(.minHeight, .maxHeight)
endmethod
private method getSmoothNoise takes real x, real y returns real
local real corners = (.getNoise(x-1, y-1) + .getNoise(x-1, y+1) + .getNoise(x+1, y-1) + .getNoise(x+1, y+1)) / 16.
local real sides = (.getNoise(x-1, y) + .getNoise(x+1, y) + .getNoise(x, y-1) + .getNoise(x, y+1)) / 8.
local real center = .getNoise(x, y) / 4.
return corners + sides + center
endmethod
private static method interpolate takes real a, real b, real blend returns real
local real theta = blend * bj_PI
local real r = (1 - Cos(theta)) * 0.5
return a * (1 - r) + b * r
endmethod
private method getInterpolatedNoise takes real x, real y returns real
local integer intX = R2I(x)
local integer intY = R2I(y)
local real fracX = x - intX
local real fracY = y - intY
local real v1 = .getSmoothNoise(intX, intY)
local real v2 = .getSmoothNoise(intX+1, intY)
local real v3 = .getSmoothNoise(intX, intY+1)
local real v4 = .getSmoothNoise(intX+1, intY+1)
local real i1 = .interpolate(v1, v2, fracX)
local real i2 = .interpolate(v3, v4, fracX)
return .interpolate(i1, i2, fracY)
endmethod
private method generateHeight takes real x, real y returns real
return .getInterpolatedNoise((x / Terrain.GRID - Terrain.MIN_X_GRID) / .smoothFactor, (y / Terrain.GRID - Terrain.MIN_Y_GRID) / .smoothFactor)
endmethod
private static method generateTerrainLoop takes nothing returns nothing
local thistype this = .looping
loop
exitwhen .ty >= .maxY
loop
exitwhen .tx >= .maxX
if Thread.count == 100 then
call Thread.new(function thistype.generateTerrainLoop)
return
endif
call Terrain.setHeight(.tx, .ty, .generateHeight(.tx, .ty))
set .tx = .tx + Terrain.GRID
endloop
set .tx = .minX
set .ty = .ty + Terrain.GRID
endloop
endmethod
public method generateTerrain takes nothing returns nothing
set .looping = this
set .ty = .minY
set .tx = .minX
call Thread.new(function thistype.generateTerrainLoop)
endmethod
private static method resetTerrainLoop takes nothing returns nothing
local thistype this = .looping
loop
exitwhen .ty >= .maxY
loop
exitwhen .tx >= .maxX
if Thread.count == 100 then
call Thread.new(function thistype.resetTerrainLoop)
return
endif
call Terrain.resetHeight(.tx, .ty)
set .tx = .tx + Terrain.GRID
endloop
set .tx = .minX
set .ty = .ty + Terrain.GRID
endloop
endmethod
public method resetTerrain takes nothing returns nothing
set .looping = this
set .ty = .minY
set .tx = .minX
call Thread.new(function thistype.resetTerrainLoop)
endmethod
endstruct
Here is a small test script to try it out: (which is also present in the test map)
JASS:
globals
HeightGenerator hg
endglobals
function Test2 takes nothing returns nothing
call hg.resetTerrain()
endfunction
function Test1 takes nothing returns nothing
local rect world = GetWorldBounds()
set hg = HeightGenerator.create(GetRandomInt(0, 1000))
set hg.minHeight = -100
set hg.maxHeight = 700
set hg.minX = -2048 // GetRectMinX(world)
set hg.minY = -2048 // GetRectMinY(world)
set hg.maxX = 2048 // GetRectMaxX(world)
set hg.maxY = 2048 // GetRectMaxY(world)
set hg.smoothFactor = 4
call hg.generateTerrain()
call RemoveRect(world)
set world = null
endfunction
//===========================================================================
function InitTrig_Test takes nothing returns nothing
call TimerStart(CreateTimer(), 0, false, function Test1)
//call TimerStart(CreateTimer(), 5, false, function Test2)
endfunction
There is one problem with this thing... which is that terrain deformations are pretty cpu intense.
Especially because there are probably going to be a few thousand on the map if you use it a lot.
I tested this on
- a 2,048x2,048 terrain, where there is no lag at all.
- a 4,096x4,096 terrain, where you can notice the lag... mostly by not having a smooth camera.
- a 8,192x8,192 terrain, where this lag can be really annoying.
(A 480x480 map is a 61,440x61,440 terrain, which is going to lag so hard that you want to uninstall Warcraf III.)
So for small areas, this would be nice, but apart from that... it is not really useful until there is a better method of changing the height of a point in the terrain.