Posted: 13th Sep 2013 3:06
Updated 22/10/2013!
Ok, so this is a little bit longer than a code 'snippet', but it's probably better off here than Program Announcements
Basically, I made this program for a project with EVOLVED's Advanced Lighting, since 3D World Studio terrain doesn't seem to work well with it. It has most of the features of the 3D World Studio terrain editor, apart from texture painting, because I have NO idea how to do that
Feel free to play around with the code, edit it, use it however you want, etc! If you just want to use it to make terrains, you can just copy and paste it into a new project, and it should work fine. You can export terrains you've created as .dbo or .x objects.
Hope people find it useful
You'll need Matrix1Utils to compile it

Changelog:
+ Code Snippet
V1.2: The editor can now save in it's own format for loading a level later

V1.1: Main change is the terrain is now split into limbs, meaning you can create large terrains while still keeping a reasonable polygon count!
I've kept the old version for making small terrains, since the the new one is slightly more irritating to work with, and not needed if you only want a small terrain


Current version:
+ Code Snippet
Rem Project: Terrain editor
Rem Created: Monday, September 09, 2013

Rem ***** Main Source File *****

rem CHANGE THESE TO ALTER THE TERRAIN SIZE/VERTEX COUNT!
global terrainwidth# = 1000.0
global terrainheight# = 1000.0
rem Note: Trying to save a terrain as .x with a row/column ratio of anything over 52x52 will probably cause a crash!
global terrainrows = 70
global terraincolumns = 70
rem loadname$ can be changed to a filename to load a saved .ter file, instead of creating a new terrain
loadname$ = ""
sync on : sync rate 375
autocam off
disable escapekey
oldcamangle = make vector3(1)
newcamangle = make vector3(2)
objposition = make vector3(3)
global pensize = 25
global mousex#
global mousey#
global movecamposx#
global movecamposz#
global yadjustvertex
global camangle# = 0
global camdistance# = 150.0
global camheight# = 100.0
global datainput
global xwidth
global zwidth
global rows
global columns
global currentlimb = 1
global limbx#
global limbz#
global xdifference
global zdifference
global changelimbtext
global disablecltext as boolean
intensity# = 5
set text font "Arial Bold"
set text size 40
coordimages(42, 42)
set text size 15
terrainmode = 1
camfollow = 1
enablethis = 0
wframe = 1
rem Initialise the array for smoothing the limb edges
dim vertexxz1#()
dim vertexy1#()
dim vertexxz2#()
dim vertexy2#()

if loadname$ = ""
terrainwidth# = terrainwidth# / 2
terrainheight# = terrainheight# / 2
else
open datafile to read 1, loadname$
terrainwidth# = datafile float(1)
terrainheight# = datafile float(1)
terrainrows = datafile integer(1)
terraincolumns = datafile integer(1)
endif
make object plain 1, terrainwidth#, terrainheight#, terrainrows, terraincolumns
make mesh from object 2, 1
delete object 1
rem Add the other 3 parts of the terrain
make object sphere 1, 0
for i = 1 to 4
add limb 1, i, 2
next i
offset limb 1, 1, -terrainwidth# / 2, 0, -terrainheight# / 2
offset limb 1, 2, -terrainwidth# / 2, 0, terrainheight# / 2
offset limb 1, 3, terrainwidth# / 2, 0, -terrainheight# / 2
offset limb 1, 4, terrainwidth# / 2, 0, terrainheight# / 2
scale limb 1, 0, 0, 0, 0
rem Each limb has it's own colour to distinguish it - Green is always the 'selected limb' colour
color limb 1, 1, RGB(0,192,0)
color limb 1, 2, RGB(255,0,255)
color limb 1, 3, RGB(0,255,255)
color limb 1, 4, RGB(255,0,0)
limbx# = limb offset x(1, currentlimb)
limbz# = limb offset z(1, currentlimb)
set object wireframe 1, 1
make object sphere 2, 100, 100, 100
scale object 2, pensize, 5, pensize
color object 2, RGB(255,192,0)
set alpha mapping on 2, 48
position object 2, -terrainwidth# / 2, 0, -terrainheight# / 2
make object sphere 7, 6
color object 7, rgb(255, 0, 0)
rem Make and position the coord plains
for i = 3 to 6
make object plain i, 35, 35
texture object i, i-2
set object light i, 0
next i
position object 3, limbx#, 30, limbz# + (terrainheight# / 2) + 20
position object 4, limbx#, 30, limbz# + (terrainheight# / -2) - 20
position object 5, limbx# + (terrainwidth# / 2) + 20, 30, limbz#
position object 6, limbx# + (terrainwidth# / -2) - 20, 30, limbz#
lock vertexdata for limb 1, currentlimb
meshno = get vertexdata vertex count()
calculatexcoords(meshno)
rem Load the terrain height data from a saved file
if loadname$ > ""
unlock vertexdata
for k = 1 to 4
lock vertexdata for limb 1, k
for i = 0 to meshno
vheight# = datafile float(1)
set vertexdata position i, get vertexdata position x(i), vheight#, get vertexdata position z(i)
next i
unlock vertexdata
next k
close datafile 1
lock vertexdata for limb 1, currentlimb
endif
position camera -100, 50, 0
do
position mouse screen width() / 2, screen height() / 2
if editmode <> 0
speed# = 0.40
if shiftkey() = 1 then speed# = 0.9
if keystate(17) = 1 then move camera speed#
if keystate(31) = 1 then move camera -speed#
if keystate(30) = 1 then move camera left speed#
if keystate(32) = 1 then move camera right speed#
else
rem Control the chase camera in Edit Mode
if camfollow = 1
if keystate(17) = 1 then dec camdistance#, 0.25
if keystate(31) = 1 then inc camdistance#, 0.25
if camdistance# < 1 then camdistance# = 1
if keystate(30) = 1 and keyhold = 0 then camside = wrapvalue(camside + 90) : keyhold = scancode()
if keystate(32) = 1 and keyhold = 0 then camside = wrapvalue(camside - 90) : keyhold = scancode()
camangle# = curveangle(camside, camangle#, 30)
if shiftkey() = 1 then inc camheight#, 0.22
if controlkey() = 1 then dec camheight#, 0.22
endif
endif
mousex# = mousemovex()
mousey# = mousemovey()
mousez# = mousemovez()
if val(inkey$()) = 1 then editmode = 0
if val(inkey$()) = 2 then editmode = 1
if mousez# <> 0
pensize = clamp(pensize + clamp(mousez#, -2, 2), 5, 120)
scale object 2, pensize * 2, 5, pensize * 2
endif
if editmode = 1 rem Start of the Edit Mode if block
text 0, 0, "Cam mode(1-2): Free flight mode"
rotate camera camera angle x() + mousey# / 4, camera angle y() + mousex# / 4, camera angle z()
else
text 0, 0, "Cam mode(1-2): Edit mode"
text 0, 20, "Shift-Ctrl to adjust cam height"
if camfollow = 1 : text 0, 40, "Cam follow on (f)" : else : text 0, 40, "Cam follow off (f)" : endif
text 0, 60, "Pen size(mousewheel): " + str$(pensize)
if penmode = 0 : text 0, 80, "Pen(Tabs): Round" : else : text 0, 80, "Pen(Tabs): Square" : endif
if keystate(33) = 1 and keyhold = 0 then camfollow = abs(camfollow - 1) : keyhold = scancode()
rem Change the pen from a circle to a square, and back again... Magic :3
if keystate(15) = 1 and keyhold = 0
set vector3 to object position 3, 2
delete object 2
if penmode = 0
make object box 2, 100, 100, 100 : penmode = 1
yrotate object 2, 45
fix object pivot 2
else
make object sphere 2, 100, 100, 100 : penmode = 0
endif
scale object 2, pensize * 2, 5, pensize * 2
color object 2, RGB(255,192,0)
set alpha mapping on 2, 48
position object 2, x vector3(3), y vector3(3), z vector3(3)
keyhold = scancode()
endif
rem Calculate what angle to move the selector from
if camside / 90 = 0 or camside / 90 = 2
if camside / 90 = 0 then mousey# = mousey# * -1
if camside / 90 = 2 then mousex# = mousex# * -1
position object 2, object position x(2) + mousex# / 4, object position y(2), object position z(2) + mousey# / 4
else
if camside / 90 = 1 then mousex# = mousex# * -1 : mousey# = mousey# * -1
position object 2, object position x(2) + mousey# / 4, object position y(2), object position z(2) + mousex# / 4
endif
limbselect(colourmode)
pointcamera(2, 20, camfollow)
rem Calculate the selector's height on the terrain
getcurrentheight(meshno)
if mouseclick() = 1 or mouseclick() = 2
position object 2, object position x(2), curvevalue(get vertexdata position y(yadjustvertex), object position y(2), 15), object position z(2)
else
position object 2, object position x(2), curvevalue(get vertexdata position y(yadjustvertex), object position y(2), 45), object position z(2)
endif
position object 7, get vertexdata position x(yadjustvertex) + limbx#, get vertexdata position y(yadjustvertex), get vertexdata position z(yadjustvertex) + limbz#
if terrainmode = 1
text 10, screen height() - 25, "Edit tool(z-x to change): Sculpt"
if mouseclick() = 1 or mouseclick() = 2
for i = 0 to meshno
x# = get vertexdata position x(i) + limbx#
z# = get vertexdata position z(i) + limbz#
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if penmode = 0
point object 2, x#, object position y(2), z#
distcompare = 0
distcompare = iswithinlimits(distance#, pensize)
endif
if (penmode = 0 and distcompare > 0) or (penmode = 1 and distance# < pensize)
if mouseclick() = 1
set vertexdata position i, get vertexdata position x(i), get vertexdata position y(i) + intensity# * 0.03, get vertexdata position z(i)
else
set vertexdata position i, get vertexdata position x(i), get vertexdata position y(i) - intensity# * 0.03, get vertexdata position z(i)
endif
endif
next i
if penmode = 0 then yrotate object 2, 0
endif
endif
if terrainmode = 2
text 10, screen height() - 25, "Edit tool(z-x to change): Sculpt(smooth)"
if mouseclick() = 1 or mouseclick() = 2
for i = 0 to meshno
x# = get vertexdata position x(i) + limbx#
z# = get vertexdata position z(i) + limbz#
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if penmode = 0
point object 2, x#, object position y(2), z#
distcompare = 0
distcompare = iswithinlimits(distance#, pensize)
endif
if (penmode = 0 and distcompare > 0) or (penmode = 1 and distance# < pensize)
pensize# = pensize
dividenumber# = (pensize# - pensize# * 0.1) / 90
smoothsculpt# = sin(90 - distance# / dividenumber#) * (intensity# * 0.03)
if smoothsculpt# < 0 then smoothsculpt# = 0
if mouseclick() = 1
set vertexdata position i, get vertexdata position x(i), get vertexdata position y(i) + smoothsculpt#, get vertexdata position z(i)
else
set vertexdata position i, get vertexdata position x(i), get vertexdata position y(i) - smoothsculpt#, get vertexdata position z(i)
endif
endif
next i
if penmode = 0 then yrotate object 2, 0
endif
endif
if terrainmode = 3
text 10, screen height() - 25, "Edit tool(z-x to change): Add noise"
if mouseclick() = 1
for i = 0 to meshno
x# = get vertexdata position x(i) + limbx#
y# = get vertexdata position y(i)
z# = get vertexdata position z(i) + limbz#
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if penmode = 0
point object 2, x#, object position y(2), z#
distcompare = 0
distcompare = iswithinlimits(distance#, pensize)
endif
if (penmode = 0 and distcompare > 0) or (penmode = 1 and distance# < pensize) then set vertexdata position i, get vertexdata position x(i), y# + sin((timer() + (i*(500 + (i * i)))) / 1.5) * (intensity# * 0.03), get vertexdata position z(i)
next i
if penmode = 0 then yrotate object 2, 0
endif
endif
if terrainmode = 4
text 10, screen height() - 25, "Edit tool(z-x to change): Flatten"
if mouseclick() = 1
if lockedheight = 0 then lockedheight = yadjustvertex
for i = 0 to meshno
x# = get vertexdata position x(i) + limbx#
y# = get vertexdata position y(i)
z# = get vertexdata position z(i) + limbz# 
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if penmode = 0
point object 2, x#, object position y(2), z#
distcompare = 0
distcompare = iswithinlimits(distance#, pensize)
endif
if (penmode = 0 and distcompare > 0) or (penmode = 1 and distance# < pensize) then set vertexdata position i, get vertexdata position x(i), curvevalue(get vertexdata position y(lockedheight), y#, 106 - (intensity# * 3)), get vertexdata position z(i)
next i
if penmode = 0 then yrotate object 2, 0
endif
if mouseclick() = 0 then lockedheight = 0
endif
if terrainmode = 5
text 10, screen height() - 25, "Edit tool(z-x to change): Smooth"
if mouseclick() = 1
if zgap# = 0
if xdifference > 0 then xgap# = abs(get vertexdata position x(meshno / 2) - get vertexdata position x((meshno / 2) + 1))
zgap# = abs(get vertexdata position z(meshno / 2) - get vertexdata position z(meshno / 2 + xdifference))
endif
for i = 0 to meshno
x# = get vertexdata position x(i) + limbx#
y# = get vertexdata position y(i)
z# = get vertexdata position z(i) + limbz#
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if penmode = 0
point object 2, x#, object position y(2), z#
distcompare = 0
distcompare = iswithinlimits(distance#, pensize)
endif
if (penmode = 0 and distcompare > 0) or (penmode = 1 and distance# < pensize)

rem Decide the smoothing method to be used, based on how many verticies are available to compare against
smoothmode = 0 : meanvertexheight# = 0
for n = 1 to 4
if n = 1 then vertex = i - xdifference
if n = 2 then vertex = i + xdifference
if n = 3 then vertex = i - 1
if n = 4 then vertex = i + 1
rem If the vertex is close enough, and actually exists...
if (xdifference > 0 and i > xdifference) or n > 2
if vertex => 0 and vertex <= meshno
if abs(get vertexdata position x(vertex) - get vertexdata position x(i)) < xgap# * 1.75 and abs(get vertexdata position z(vertex) - get vertexdata position z(i)) < zgap# * 1.75
meanvertexheight# = meanvertexheight# + get vertexdata position y(vertex) : inc smoothmode
endif
endif
endif
next n
meanvertexheight# = meanvertexheight# / smoothmode
if smoothmode > 0 then set vertexdata position i, get vertexdata position x(i), curvevalue(meanvertexheight#, y#, 110 - (intensity# * 3)), get vertexdata position z(i)
endif
next i
if penmode = 0 then yrotate object 2, 0
endif
endif

rem Join the edges of the limbs to make smooth terrain
text 0, 100, "F1 to join limb edges"
if keystate(59) = 1 and keyhold = 0
for i = 1 to 2
smoothlimbedges(meshno, 0, 1, 2)
smoothlimbedges(meshno, 1, 1, 3)
smoothlimbedges(meshno, 1, 2, 4)
smoothlimbedges(meshno, 0, 3, 4)
next i
keyhold = 59
endif

text 250, screen height() - 50, "Limb: " + str$(currentlimb)
text 250, screen height() - 25, "Vertex: " + str$(yadjustvertex)
endif rem End of the Edit Mode if block

text 10, screen height() - 50, "Intensity(+/- keys to change): " + str$(intensity#)
if colourmode = 0 : text 450, screen height() - 25, "Limb colours(l): Multiple" : else : text 450, screen height() - 25, "Limb colours(l): Single" : endif
text 250, 0, "Press Escape to pause/save"
if wframe = 1 : text 250, 20, "Wireframe on (F5 to change)" : else : text 250, 20, "Wireframe off (F5 to change)" : endif
if keystate(44) = 1 and keyhold = 0 then terrainmode = wrap(terrainmode - 1, 1, 5) : keyhold = scancode()
if keystate(45) = 1 and keyhold = 0 then terrainmode = wrap(terrainmode + 1, 1, 5) : keyhold = scancode()

rem Change the editing intensity
if keystate(12) = 1 or keystate(13) = 1
if holdingkey = 0 then holdingkey = timer()
if disablekey = 0
if keystate(12) = 1 : intensity# = clamp(intensity# - 1, 1, 35) : else : intensity# = clamp(intensity# + 1, 1, 35) : endif
endif
disablekey = 1
if timer() - holdingkey > 400 and heldkey = 0 then heldkey = timer() : disablekey = 0
if heldkey > 0 and timer() - heldkey > 50 then disablekey = 0 : heldkey = timer()
else
if holdingkey > 0 then holdingkey = 0 : heldkey = 0 : disablekey = 0
endif

if keystate(63) = 1 and keyhold = 0 then wframe = abs(wframe - 1) : set object wireframe 1, wframe : keyhold = scancode()
if keystate(38) = 1 and keyhold = 0
colourmode = abs(colourmode - 1)
rem Recolour the limbs
if colourmode = 1
for i = 1 to 4
if i <> currentlimb then color limb 1, i, rgb(255, 40, 10)
next i
else
for i = 1 to 4
if i <> currentlimb
if i = 1 then color limb 1, i, RGB(255,255,0)
if i = 2 then color limb 1, i, RGB(255,0,255)
if i = 3 then color limb 1, i, RGB(0,255,255)
if i = 4 then color limb 1, i, RGB(255,0,0)
endif
next i
endif
keyhold = 38
endif
if escapekey() = 1 then saveandquit()
if timer() - checkfocus > 1300
if has focus() = 0 then saveandquit()
checkfocus = 0
endif
rem Disables a key until you've let it go
if keyhold > 0 and keystate(keyhold) = 0 then keyhold = 0
rem Point the coord icons so the camera can see them
for i = 3 to 6
point object i, camera position x(), camera position y(), camera position z()
next i
sync
loop

function pointcamera(object, speed, camfollow)
set vector3 1, camera angle x(), camera angle y(), camera angle z()
point camera object position x(object), object position y(object), object position z(object)
set vector3 2, camera angle x(), camera angle y(), camera angle z()
if mouseclick() = 1 or mouseclick() = 2 then speed = speed / 4
rotate camera curveangle(x vector3(2), x vector3(1), speed), curveangle(y vector3(2), y vector3(1), speed), z vector3(1)
if camfollow = 1 then set camera to follow object position x(2), object position y(2), object position z(2), camangle#, camdistance#, camheight#, speed * 4, 0
endfunction

function getcurrentheight(meshno)
olddistance# = 100
for i = 0 to meshno
x# = get vertexdata position x(i) + limbx#
z# = get vertexdata position z(i) + limbz#
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if distance# < olddistance# then : olddistance# = distance# : yadjustvertex = i
next i
endfunction

function coordimages(sizex, sizey)
for i = 1 to 4
if i = 1 then print "+Z"
if i = 2 then print "-Z"
if i = 3 then print "+X"
if i = 4 then print "-X"
for k = 1 to 2
sync
next k
get image i, 0.5, 0.5, sizex, sizey
cls
next i
endfunction

function calculatexcoords(meshno)
for k = meshno / 2 to meshno
if get vertexdata position x(k) = get vertexdata position x(meshno / 2) and k <> meshno / 2
xdifference = abs(meshno / 2 - k)
exit
endif
next k
endfunction

function saveandquit()
justsaved = 0
for i = 1 to 7
hide object i
next i
do
if screen = 0
text 50, 50, "F1 to save"
text 50, 100, "F2 to quit (discards unsaved changes)"
text 50, 150, "F3 to cancel"
if keystate(59) = 1 then pausekey(59) : screen = 1 : clear entry buffer
if keystate(60) = 1
if justsaved = 1 then end
do
text 50, 100, "Are you sure you want to quit without saving? TOTALLY sure? :P"
text 50, 125, "Y to quit, N to stay"
if keystate(21) = 1 then pausekey(21) : end
if keystate(49) = 1 then pausekey(49) : exit
sync
loop
endif
if keystate(61) = 1
for i = 1 to 7
show object i
next i
exitfunction
endif
endif
if screen = 1
saveinput$ = entry$(1)
text 50, 50, "Enter the filename to save the terrain as!"
text 50, 75, "Enter just the filename (with extension), or an absolute path"
text 50, 100, "You can export as either .x or .dbo, or save the project as .ter"
text 50, 125, saveinput$
text 50, 175, "Press F3 to go back"
if returnkey() = 1 and saveerror = 0
rem Check the string to make sure it's ok!
saveinput$ = trim$(saveinput$)
saveinput$ = replace all$(saveinput$, "\", "/")
stringdot = last instr(saveinput$, ".")
if stringdot = 0 then saveerror = timer() : reason$ = "Remember to put the file extension!"
if saveerror = 0 and right$(saveinput$, len(saveinput$) - stringdot) <> "x" and right$(saveinput$, len(saveinput$) - stringdot) <> "dbo" and right$(saveinput$, len(saveinput$) - stringdot) <> "ter" then saveerror = timer() : reason$ = "Wrong file extension!"
stringdash = last instr(saveinput$, "/")
if stringdash > 0 and path exist(left$(saveinput$, stringdash)) = 0 then saveerror = timer() : reason$ = "Path does not exist!"
pausekey(28)
rem If it was ok, save the terrain
if saveerror = 0
unlock vertexdata
sync
if file exist(saveinput$) = 1 then delete file saveinput$
make mesh from object 1, 1
if right$(saveinput$, 2) = ".x"
save mesh saveinput$, 1
endif
if right$(saveinput$, 4) = ".dbo"
save object saveinput$, 1
endif
rem Save terrain so it can be opened later
if right$(saveinput$, 4) = ".ter"
saveterrain(saveinput$)
endif
delete mesh 1
lock vertexdata for limb 1, currentlimb
if file exist(saveinput$) = 1
clear entry buffer
filesaved = timer()
justsaved = 1
else
saveerror = timer() : reason$ = "Can't seem to save for some reason :s"
endif

endif
endif
if saveerror > 0
text 50, 150, "Error saving file(" + reason$ + ")"
if timer() - saveerror > 2000 then saveerror = 0
endif
if filesaved > 0
text 50, 150, "File successfully saved!"
if timer() - filesaved > 2500 then filesaved = 0
endif
if keystate(61) = 1 then screen = 0 : pausekey(61)
endif

sync
loop
endfunction

function pausekey(key)
while keystate(key) = 1
endwhile
endfunction

function iswithinlimits(dist#, pensize)
distcompare = 0
pen# = pensize
sindist# = sin(object angle y(2)) * pen#
cosdist# = cos(object angle y(2)) * pen#
if dist# < sindist# + cosdist# or dist# < sindist# - cosdist# or dist# < sindist#*-1 + cosdist#*-1 or dist# < sindist#*-1 - cosdist#*-1 then inc distcompare
endfunction distcompare

function limbselect(colourmode)
objx# = object position x(2)
objy# = object position y(2)
objz# = object position z(2)
oldcurrentlimb = currentlimb
if objx# > limbx# + terrainwidth# / 2
if spacekey() = 0 or currentlimb > 2
if (currentlimb = 1 or currentlimb = 2) and disablecltext = 0 then changelimbtext = timer()
position object 2, limb offset x(1, currentlimb) + terrainwidth# / 2, objy#, objz#
else
inc currentlimb, 2
endif
endif
if objx# < limbx# - terrainwidth# / 2
if spacekey() = 0 or currentlimb < 3
if (currentlimb = 3 or currentlimb = 4) and disablecltext = 0 then changelimbtext = timer()
if not currentlimb < 2 then p = 1
position object 2, limbx# - terrainwidth# / 2, objy#, objz#
else
dec currentlimb, 2
endif
endif
if objz# > limbz# + terrainheight# / 2
if spacekey() = 0 or currentlimb = 2 or currentlimb = 4
if (currentlimb = 1 or currentlimb = 3) and disablecltext = 0 then changelimbtext = timer()
position object 2, objx#, objy#, limb offset z(1, currentlimb) + terrainheight# / 2
else
inc currentlimb
endif
endif
if objz# < limbz# - terrainheight# / 2
if spacekey() = 0 or currentlimb = 1 or currentlimb = 3
if (currentlimb = 2 or currentlimb = 4) and disablecltext = 0 then changelimbtext = timer()
position object 2, objx#, objy#, limb offset z(1, currentlimb) - terrainheight# / 2
else
dec currentlimb
endif
endif
if spacekey() = 1 then disablecltext = 1
rem Show text telling you how to change the current limb
if changelimbtext > 0
text 220, screen height() - 75, "Hold Spacebar to move between limbs!"
if timer() - changelimbtext > 900 or spacekey() = 1 then changelimbtext = 0
endif
if currentlimb <> oldcurrentlimb
unlock vertexdata
rem Colour the limbs
if colourmode = 0
select oldcurrentlimb
case 1
color limb 1, oldcurrentlimb, RGB(255,255,0)
endcase
case 2
color limb 1, oldcurrentlimb, RGB(255,0,255)
endcase
case 3
color limb 1, oldcurrentlimb, RGB(0,255,255)
endcase
case 4
color limb 1, oldcurrentlimb, RGB(255,0,0)
endcase
endselect
else
color limb 1, oldcurrentlimb, rgb(255, 40, 10)
endif
color limb 1, currentlimb, RGB(0,192,0)
lock vertexdata for limb 1, currentlimb
limbx# = limb offset x(1, currentlimb)
limbz# = limb offset z(1, currentlimb)
position object 3, limbx#, 30, limbz# + (terrainheight# / 2) + 20
position object 4, limbx#, 30, limbz# + (terrainheight# / -2) - 20
position object 5, limbx# + (terrainwidth# / 2) + 20, 30, limbz#
position object 6, limbx# + (terrainwidth# / -2) - 20, 30, limbz#
endif
endfunction

function smoothlimbedges(meshno, xorz, limb1, limb2)
empty array vertexxz1#()
empty array vertexy1#()
empty array vertexxz2#()
empty array vertexy2#()
if xdifference > 0 then xgap# = abs(get vertexdata position x(meshno / 2) - get vertexdata position x((meshno / 2) + 1))
zgap# = abs(get vertexdata position z(meshno / 2) - get vertexdata position z(meshno / 2 + xdifference))
for mode = 0 to 1
unlock vertexdata
lock vertexdata for limb 1, limb1
for k = 0 to meshno
if (xorz = 0 and get vertexdata position z(k) > (terrainheight# / 2) - zgap#) or (xorz = 1 and get vertexdata position x(k) > (terrainwidth# / 2) - xgap#)
if mode = 0
array insert at bottom vertexxz1#()
array insert at bottom vertexy1#()
if xorz = 0 : vertexxz1#() = round(get vertexdata position x(k), 0) : else : vertexxz1#() = round(get vertexdata position z(k), 0) : endif
vertexy1#() = get vertexdata position y(k)
else
array index to top vertexxz2#()
array index to top vertexy2#()
while array index valid(vertexxz2#()) = 1
if xorz = 0
if vertexxz2#() = round(get vertexdata position x(k), 0) then set vertexdata position k, get vertexdata position x(k), (vertexy2#() + get vertexdata position y(k)) / 2, get vertexdata position z(k)
else
if vertexxz2#() = round(get vertexdata position z(k), 0) then set vertexdata position k, get vertexdata position x(k), (vertexy2#() + get vertexdata position y(k)) / 2, get vertexdata position z(k)
endif
next array index vertexxz2#()
next array index vertexy2#()
endwhile
endif
endif
next k
unlock vertexdata
lock vertexdata for limb 1, limb2
for k = 0 to meshno
if (xorz = 0 and get vertexdata position z(k) < (terrainheight# / -2) + zgap#) or (xorz = 1 and get vertexdata position x(k) < (terrainwidth# / -2) + xgap#)
if mode = 0
array insert at bottom vertexxz2#()
array insert at bottom vertexy2#()
if xorz = 0 : vertexxz2#() = round(get vertexdata position x(k), 0) : else : vertexxz2#() = round(get vertexdata position z(k), 0) : endif
vertexy2#() = get vertexdata position y(k)
else
array index to top vertexxz1#()
array index to top vertexy1#()
while array index valid(vertexxz1#()) = 1
if xorz = 0
if vertexxz1#() = round(get vertexdata position x(k), 0) then set vertexdata position k, get vertexdata position x(k), (vertexy1#() + get vertexdata position y(k)) / 2, get vertexdata position z(k)
else
if vertexxz1#() = round(get vertexdata position z(k), 0) then set vertexdata position k, get vertexdata position x(k), (vertexy1#() + get vertexdata position y(k)) / 2, get vertexdata position z(k)
endif
next array index vertexxz1#()
next array index vertexy1#()
endwhile
endif
endif
next k
next mode
unlock vertexdata
lock vertexdata for limb 1, currentlimb
endfunction

function round(number#, upordown)
if upordown = 0
tempno = ceil(abs(number#))
else
tempno = floor(abs(number#))
endif
if number# < 0 then tempno = tempno * -1
endfunction tempno

function saveterrain(filename$)
open datafile to write 1, filename$
write datafile float 1, terrainwidth#
write datafile float 1, terrainheight#
write datafile integer 1, terrainrows
write datafile integer 1, terraincolumns
for i = 1 to 4
lock vertexdata for limb 1, i
if tempmeshno = 0 then tempmeshno = get vertexdata vertex count()
for k = 0 to tempmeshno
write datafile float 1, get vertexdata position y(k)
next k
unlock vertexdata
next i
close datafile 1
endfunction


Old version:
+ Code Snippet
Rem Project: Terrain editor
Rem Created: Monday, September 09, 2013

Rem ***** Main Source File *****


rem CHANGE THESE TO ALTER THE TERRAIN SIZE/VERTEX COUNT!
terrainwidth# = 1024.0
terrainheight# = 1024.0
terrainrows = 80
terraincolumns = 80

sync on
autocam off
disable escapekey
oldcamangle = make vector3(1)
newcamangle = make vector3(2)
objposition = make vector3(3)
global pensize = 25
global mousex#
global mousey#
global movecamposx#
global movecamposz#
global yadjustvertex
global camangle# = 0
global camdistance# = 150.0
global camheight# = 100.0
global datainput
global xwidth
global zwidth
global rows
global columns
intensity# = 5
set text font "Arial Bold"
set text size 40
coordimages(42, 42)
set text size 15
terrainmode = 1
camfollow = 1
make object plain 1, terrainwidth#, terrainheight#, terrainrows, terraincolumns
color object 1, rgb(255, 100, 50)
set object wireframe 1, 1
make object sphere 2, 100, 100, 100
scale object 2, pensize, 5, pensize
color object 2, rgb(0, 255, 0)
ghost object on 2
make object sphere 7, 6
color object 7, rgb(255, 0, 0)
for i = 3 to 6
make object plain i, 35, 35
if i = 3 then position object i, 0, 30, (terrainheight# / 2) + 20
if i = 4 then position object i, 0, 30, (terrainheight# / -2) - 20
if i = 5 then position object i, (terrainwidth# / 2) + 20, 30, 0
if i = 6 then position object i, (terrainwidth# / -2) - 20, 30, 0
texture object i, i-2
set object light i, 0
next i
lock vertexdata for limb 1, 0
meshno = get vertexdata vertex count()
xdifference = calculatexcoords(meshno)
position camera -100, 50, 0
do
position mouse screen width() / 2, screen height() / 2
if editmode <> 0
speed# = 0.08
if shiftkey() = 1 then speed# = 0.25
if keystate(17) = 1 then move camera speed#
if keystate(31) = 1 then move camera -speed#
if keystate(30) = 1 then move camera left speed#
if keystate(32) = 1 then move camera right speed#
else
rem Control the chase camera in Edit Mode
if camfollow = 1
if keystate(17) = 1 then dec camdistance#, 0.25
if keystate(31) = 1 then inc camdistance#, 0.25
if camdistance# < 1 then camdistance# = 1
if keystate(30) = 1 and keyhold = 0 then camside = wrapvalue(camside + 90) : keyhold = scancode()
if keystate(32) = 1 and keyhold = 0 then camside = wrapvalue(camside - 90) : keyhold = scancode()
camangle# = curveangle(camside, camangle#, 30)
if shiftkey() = 1 then inc camheight#, 0.22
if controlkey() = 1 then dec camheight#, 0.22
endif
endif
mousex# = mousemovex()
mousey# = mousemovey()
mousez# = mousemovez()
if val(inkey$()) = 1 then editmode = 0
if val(inkey$()) = 2 then editmode = 1
if mousez# <> 0
pensize = clamp(pensize + clamp(mousez#, -2, 2), 5, 120)
scale object 2, pensize * 2, 5, pensize * 2
endif
if editmode = 1 rem Start of the Edit Mode if block
text 0, 0, "Cam mode(1-2): Free flight mode"
rotate camera camera angle x() + mousey# / 4, camera angle y() + mousex# / 4, camera angle z()
else
text 0, 0, "Cam mode(1-2): Edit mode"
text 0, 20, "Shift-Ctrl to adjust cam height"
if camfollow = 1 : text 0, 40, "Cam follow on (f key)" : else : text 0, 40, "Cam follow off (f to toggle)" : endif
text 0, 60, "Pen size(mousewheel): " + str$(pensize)
if penmode = 0 : text 0, 80, "Pen shape (Tabs key): Round" : else : text 0, 80, "Pen shape (Tabs key): Square" : endif
if keystate(33) = 1 and keyhold = 0 then camfollow = abs(camfollow - 1) : keyhold = scancode()

rem Change the pen from a circle to a square, and back again... Magic :3
if keystate(15) = 1 and keyhold = 0
set vector3 to object position 3, 2
delete object 2
if penmode = 0
make object box 2, 100, 100, 100 : penmode = 1
yrotate object 2, 45
fix object pivot 2
else
make object sphere 2, 100, 100, 100 : penmode = 0
endif
scale object 2, pensize * 2, 5, pensize * 2
color object 2, rgb(0, 255, 0)
ghost object on 2
position object 2, x vector3(3), y vector3(3), z vector3(3)
keyhold = scancode()
endif
rem Calculate what angle to move the selector from
if camside / 90 = 0 or camside / 90 = 2
if camside / 90 = 0 then mousey# = mousey# * -1
if camside / 90 = 2 then mousex# = mousex# * -1
position object 2, clamp(object position x(2) + mousex# / 4, terrainwidth# / -2, terrainwidth# / 2), object position y(2), clamp(object position z(2) + mousey# / 4, terrainheight# / -2, terrainheight# / 2)
else
if camside / 90 = 1 then mousex# = mousex# * -1 : mousey# = mousey# * -1
position object 2, clamp(object position x(2) + mousey# / 4, terrainwidth# / -2, terrainwidth# / 2), object position y(2), clamp(object position z(2) + mousex# / 4, terrainheight# / -2, terrainheight# / 2)
endif
pointcamera(2, 20, camfollow)
rem Calculate the selector's height on the terrain
getcurrentheight(meshno)
if mouseclick() = 1 or mouseclick() = 2
position object 2, object position x(2), curvevalue(get vertexdata position y(yadjustvertex), object position y(2), 15), object position z(2)
else
position object 2, object position x(2), curvevalue(get vertexdata position y(yadjustvertex), object position y(2), 45), object position z(2)
endif
position object 7, get vertexdata position x(yadjustvertex), get vertexdata position y(yadjustvertex), get vertexdata position z(yadjustvertex)
if terrainmode = 1
text 50, screen height() - 25, "Edit tool(z-x to change): Sculpt"
if mouseclick() = 1 or mouseclick() = 2
for i = 0 to meshno
x# = get vertexdata position x(i)
z# = get vertexdata position z(i)
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if penmode = 0
point object 2, x#, object position y(2), z#
distcompare = 0
distcompare = iswithinlimits(distance#, pensize)
endif
if (penmode = 0 and distcompare > 0) or (penmode = 1 and distance# < pensize)
if mouseclick() = 1
set vertexdata position i, x#, get vertexdata position y(i) + intensity# * 0.03, z#
else
set vertexdata position i, x#, get vertexdata position y(i) - intensity# * 0.03, z#
endif
endif
next i
if penmode = 0 then yrotate object 2, 0
endif
endif
if terrainmode = 2
text 50, screen height() - 25, "Edit tool(z-x to change): Sculpt(smooth)"
if mouseclick() = 1 or mouseclick() = 2
for i = 0 to meshno
x# = get vertexdata position x(i)
z# = get vertexdata position z(i)
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if penmode = 0
point object 2, x#, object position y(2), z#
distcompare = 0
distcompare = iswithinlimits(distance#, pensize)
endif
if (penmode = 0 and distcompare > 0) or (penmode = 1 and distance# < pensize)
pensize# = pensize
dividenumber# = (pensize# - pensize# * 0.1) / 90
smoothsculpt# = sin(90 - distance# / dividenumber#) * (intensity# * 0.03)
if smoothsculpt# < 0 then smoothsculpt# = 0
if mouseclick() = 1
set vertexdata position i, x#, get vertexdata position y(i) + smoothsculpt#, z#
else
set vertexdata position i, x#, get vertexdata position y(i) - smoothsculpt#, z#
endif
endif
next i
if penmode = 0 then yrotate object 2, 0
endif
endif
if terrainmode = 3
text 50, screen height() - 25, "Edit tool(z-x to change): Add noise"
if mouseclick() = 1
for i = 0 to meshno
x# = get vertexdata position x(i)
y# = get vertexdata position y(i)
z# = get vertexdata position z(i)
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if penmode = 0
point object 2, x#, object position y(2), z#
distcompare = 0
distcompare = iswithinlimits(distance#, pensize)
endif
if (penmode = 0 and distcompare > 0) or (penmode = 1 and distance# < pensize) then set vertexdata position i, x#, y# + sin((timer() + (i*(500 + (i * i)))) / 1.5) * (intensity# * 0.03), z#
next i
if penmode = 0 then yrotate object 2, 0
endif
endif
if terrainmode = 4
text 50, screen height() - 25, "Edit tool(z-x to change): Flatten"
if mouseclick() = 1
if lockedheight = 0 then lockedheight = yadjustvertex
for i = 0 to meshno
x# = get vertexdata position x(i)
y# = get vertexdata position y(i)
z# = get vertexdata position z(i)
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if penmode = 0
point object 2, x#, object position y(2), z#
distcompare = 0
distcompare = iswithinlimits(distance#, pensize)
endif
if (penmode = 0 and distcompare > 0) or (penmode = 1 and distance# < pensize) then set vertexdata position i, x#, curvevalue(get vertexdata position y(lockedheight), y#, 106 - (intensity# * 3)), z#
next i
if penmode = 0 then yrotate object 2, 0
endif
if mouseclick() = 0 then lockedheight = 0
endif
if terrainmode = 5
text 50, screen height() - 25, "Edit tool(z-x to change): Smooth"
if mouseclick() = 1
for i = 0 to meshno
x# = get vertexdata position x(i)
y# = get vertexdata position y(i)
z# = get vertexdata position z(i)
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if penmode = 0
point object 2, x#, object position y(2), z#
distcompare = 0
distcompare = iswithinlimits(distance#, pensize)
endif
if (penmode = 0 and distcompare > 0) or (penmode = 1 and distance# < pensize)
rem If the xdifference variable hasn't been worked out, go for simple smoothing instead
if xdifference > 0 and i > xdifference
meanvertexheight# = (get vertexdata position y(i - 1) + get vertexdata position y(i + 1) + get vertexdata position y(i + xdifference) + get vertexdata position y(i - xdifference)) / 4
else
meanvertexheight# = (get vertexdata position y(i - 1) + get vertexdata position y(i + 1)) / 2
endif
set vertexdata position i, x#, curvevalue(meanvertexheight#, y#, 110 - (intensity# * 3)), z#
endif
next i
if penmode = 0 then yrotate object 2, 0
endif
endif

endif rem End of the Edit Mode if block
text 50, screen height() - 50, "Intensity(+/- keys to change): " + str$(intensity#)
text 250, 0, "Press Escape to pause/save"
if keystate(44) = 1 and keyhold = 0 then terrainmode = wrap(terrainmode - 1, 1, 5) : keyhold = scancode()
if keystate(45) = 1 and keyhold = 0 then terrainmode = wrap(terrainmode + 1, 1, 5) : keyhold = scancode()
if keystate(12) = 1 and keyhold = 0 then intensity# = clamp(intensity# - 1, 1, 35) : keyhold = scancode()
if keystate(13) = 1 and keyhold = 0 then intensity# = clamp(intensity# + 1, 1, 35) : keyhold = scancode()
if escapekey() = 1 then saveandquit()
rem Disables a key until you've let it go
if keyhold > 0 and keystate(keyhold) = 0 then keyhold = 0
rem Point the coord icons so the camera can see them
for i = 3 to 6
point object i, camera position x(), camera position y(), camera position z()
next i
sync
loop

function pointcamera(object, speed, camfollow)
set vector3 1, camera angle x(), camera angle y(), camera angle z()
point camera object position x(object), object position y(object), object position z(object)
set vector3 2, camera angle x(), camera angle y(), camera angle z()
if mouseclick() = 1 or mouseclick() = 2 then speed = speed / 4
rotate camera curveangle(x vector3(2), x vector3(1), speed), curveangle(y vector3(2), y vector3(1), speed), z vector3(1)
if camfollow = 1 then set camera to follow object position x(2), object position y(2), object position z(2), camangle#, camdistance#, camheight#, speed * 4, 0
endfunction

function getcurrentheight(meshno)
olddistance# = 100
for i = 0 to meshno
x# = get vertexdata position x(i)
z# = get vertexdata position z(i)
distance# = abs(x# - object position x(2)) + abs(z# - object position z(2))
if distance# < olddistance# then : olddistance# = distance# : yadjustvertex = i
next i
endfunction

function coordimages(sizex, sizey)
for i = 1 to 4
if i = 1 then print "+Z"
if i = 2 then print "-Z"
if i = 3 then print "+X"
if i = 4 then print "-X"
for k = 1 to 2
sync
next k
get image i, 0.5, 0.5, sizex, sizey
cls
next i
endfunction

function calculatexcoords(meshno)
for k = meshno / 2 to meshno
if get vertexdata position x(k) = get vertexdata position x(meshno / 2) and k <> meshno / 2
xdifference = abs(meshno / 2 - k)
exit
endif
next k
endfunction xdifference

function saveandquit()
justsaved = 0
for i = 1 to 7
hide object i
next i
do
if screen = 0
text 50, 50, "F1 to save"
text 50, 100, "F2 to quit (discards unsaved changes)"
text 50, 150, "F3 to cancel"
if keystate(59) = 1 then pausekey(59) : screen = 1 : clear entry buffer
if keystate(60) = 1
if justsaved = 1 then end
do
text 50, 100, "Are you sure you want to quit without saving? TOTALLY sure? :P"
text 50, 125, "Y to quit, N to stay"
if keystate(21) = 1 then pausekey(21) : end
if keystate(49) = 1 then pausekey(49) : exit
sync
loop
endif
if keystate(61) = 1
for i = 1 to 7
show object i
next i
exitfunction
endif
endif
if screen = 1
saveinput$ = entry$(1)
text 50, 50, "Enter the filename to save the terrain as!"
text 50, 75, "Enter just the filename (with extension), or an absolute path"
text 50, 100, "You can save as either .x or .dbo"
text 50, 125, saveinput$
text 50, 175, "Press F3 to go back"
if returnkey() = 1 and saveerror = 0
rem Check the string to make sure it's ok!
saveinput$ = trim$(saveinput$)
saveinput$ = replace all$(saveinput$, "\", "/")
stringdot = last instr(saveinput$, ".")
if stringdot = 0 then saveerror = timer() : reason$ = "Remember to put the file extension!"
if saveerror = 0 and right$(saveinput$, len(saveinput$) - stringdot) <> "x" and right$(saveinput$, len(saveinput$) - stringdot) <> "dbo" then saveerror = timer() : reason$ = "Wrong file extension!"
stringdash = last instr(saveinput$, "/")
if stringdash > 0 and path exist(left$(saveinput$, stringdash)) = 0 then saveerror = timer() : reason$ = "Path does not exist!"
pausekey(28)
rem If it was ok, save the terrain
if saveerror = 0
unlock vertexdata
sync
if file exist(saveinput$) = 1 then delete file saveinput$
make mesh from object 1, 1
if right$(saveinput$, 2) = ".x"
save mesh saveinput$, 1
else
make object 50, 1
save object saveinput$, 50
delete object 50
endif
delete mesh 1
lock vertexdata for limb 1, 0
if file exist(saveinput$) = 1
clear entry buffer
filesaved = timer()
justsaved = 1
else
saveerror = timer() : reason$ = "Can't seem to save for some reason :s"
endif

endif
endif
if saveerror > 0
text 50, 150, "Error saving file(" + reason$ + ") - check what you've written :P"
if timer() - saveerror > 2000 then saveerror = 0
endif
if filesaved > 0
text 50, 150, "File successfully saved!"
if timer() - filesaved > 2500 then filesaved = 0
endif
if keystate(61) = 1 then screen = 0 : pausekey(61)
endif

sync
loop
endfunction

function pausekey(key)
while keystate(key) = 1
endwhile
endfunction

function iswithinlimits(dist#, pensize)
distcompare = 0
pen# = pensize
sindist# = sin(object angle y(2)) * pen#
cosdist# = cos(object angle y(2)) * pen#
if dist# < sindist# + cosdist# or dist# < sindist# - cosdist# or dist# < sindist#*-1 + cosdist#*-1 or dist# < sindist#*-1 - cosdist#*-1 then inc distcompare
endfunction distcompare
Posted: 14th Sep 2013 13:14
@SamKM Great editor with so many options...I like it. You could add your terrain appearance using colour diffuse and converting object fvf and vertexdata...I will keep it
Posted: 15th Sep 2013 3:09
I had a problem with the following code (around line 102:

rem Change the pen from a circle to a square, and back again... Magic :3
if keystate(15) = 1 and keyhold = 0
set vector3 to object position 3, 2
delete object 2

Should the bolded line be changed to:

rem Change the pen from a circle to a square, and back again... Magic :3
if keystate(15) = 1 and keyhold = 0
set vector3 to matrix position 3, 2
delete object 2

Thanks and very nice.
Posted: 15th Sep 2013 5:01
Thanks for the comments
Are you sure about the vector thing? You could be right, but... The vector there is supposed to hold the position of object 2 (the pen/editing object), so when it's deleted then recreated, it can be put back in the same place
Posted: 16th Sep 2013 3:39
I found the problem. I had an older version of the matrix1 utilities. No changes needed.

Thanks.
Posted: 22nd Oct 2013 4:21
Updated the code so you can now create terrains that are actually a usable size, rather than tiny terrains that look low-poly if you stretch them to anything larger!
Posted: 23rd Oct 2013 0:02
Definitely a good start. But the camera needs some work. I found it difficult to navigate since the direction the mouse moves doesn't feel natural unless I'm facing directly down the Z-axis. In other words, it's not rotating with the camera, making the controls feel reversed depending on where I'm looking. It's more noticable when moving between limbs.

Also, it's hard to match up the terrain across limbs since you can only update one at a time. Editing along the edges makes gaps.
Posted: 23rd Oct 2013 4:32
Thanks for trying it out! You're right about the camera controls - they're not great :/
I swear there's an easy way to convert mouse movement into 3d, allowing you to drag an object, but I wasn't sure what it was, so I improvised a bit...
As for the gaps at the limb edges, press F1 to join the edges together
Posted: 23rd Oct 2013 19:57
As for the gaps at the limb edges, press F1 to join the edges together

I must've missed that.

If you search the forum, you should find 2d mouse to 3D code. Since your terrain is an object, you could just use pick object and then x# = camera position x() + get pick vector x().
Posted: 23rd Nov 2013 20:24
@SamKM

I tried to run you example as just a past code in DBP

but I got a error which I'm guessing is because of conflict between
one of the additions I have


it showed up in line 483
saveinput$ = trim$(saveinput$)

subscript must be integer of DWORD when referring an array at line 783.

that was the error message

one thing i noticed the word " trim$ " was not highlighted
like other commands were

I've collected every addition I thought I would need
the other thing is one of the additions is newer and dose not contain
or could be older but that seems less likely
Posted: 26th Nov 2013 3:45
Hmm, that's strange
If you remove that line, do the commands 'replace all$' and 'last instr$' underneath it work? If not, you might want to try re installing Matrix1Utils
Posted: 26th Nov 2013 19:45
@SamKM

ok it works ;o)

this could be expanded for placing objects
and the code saved for such

but when I try to save it keeps saying wrong file extension

i tried x and dbo and ter

nun worked

the color of the text get's a bit washed out
it could have a faint back ground so that it would easier to read

ok because I have anther dll that use the trim$
I changed that in the ini file

so my only problem now is saving
Posted: 26th Nov 2013 20:02
@SamKM

the space bar thing is understandable why it's used
but would be nice to have it auto shift over to the next section

ok dbo and .ter file saving works
but when I do .x I get

Runtime error 7029 - 3d memory error at line 494

the files that were made load perfectly

now all that needed is way to paint on them
and to have a larger land mass