A quick Work In Progress sample of were I'm at. Uses Sparkys as well as Matrix1 Utils:
Uses keys - WASD+Space, Return-Jump, Mouse-Look
trace_init("trace.txt")
init_screen()
MG_init()
MG_generate(0)
MG_create_level("","","") ` don't load textures, generate some instead
init_controls()
position camera 0,500,0 : point camera 0,0,25
disable escapekey
while escapekey() = 0
do_controls()
if keystate(28)
make_a_new_level()
endif
sync
endwhile
trace_close()
end
` ====================
` The types (structures)
type _VERT
x as float
y as float
endtype
type _EDGE
v1 as integer
v2 as integer
endtype
type _FACE
v1 as integer
v2 as integer
v3 as integer
endtype
` ====================
` Initialise all the globals used by the generation algorithm
function MG_init()
global dim _VERTS() as _VERT ` main verts list
global dim _OUT() as _EDGE ` outer polygon edges
global dim _IN() as _EDGE ` inner edges
global dim _FACES() as _FACE ` floor faces
global dim _OBJ() as integer ` object ID's list
global dim _IMG() as integer ` texture list ID's
global MG_minx as float ` maps extents
global MG_miny as float
global MG_maxx as float
global MG_maxy as float
global MG_rnd_mod as integer ` map random modulus values
global MG_rnd_add as integer
global MG_rnd_div as float
global MG_edge_length as float ` edges default length
global MG_edge_angle as float
global MG_max_faces as integer
MG_edge_length = 100.0
MG_edge_angle = 45
MG_rnd_mod = 50
MG_rnd_add = 100
MG_rnd_div = 100
MG_max_faces = 750
` Map extents globals
global MG_map_xmin as float
global MG_map_ymin as float
global MG_map_xmax as float
global MG_map_ymax as float
` main level object
global MG_level_obj as integer
endfunction
` ====================
` put this into a function to make the main() look dead tiny
function init_screen()
autocam off
sync rate 60
sync on
set display mode 1024,768,32
set window off
endfunction
` ====================
` a failed attempt at getting a C fmod function working
` I've got the code working anyway by using the maps extents
` to make to passed values positive...
function fmodold(a as float,b as float)
local v as float
if a < 0
a = abs( a )
v = b - ( a - int( a / b ) * b )
else
v = ( a - int( a / b ) * b )
endif
endfunction v
function fmod(a#, b#)
c#=a# %% b#
endfunction c#
` ====================
` my standard trace function while I'm debugging and getting everything working
function trace_init(file$)
global WL_TRACE_FP as integer
c as integer : c=1
while ( c <= 32 and file open( c ) )
inc c
endwhile
if ( c <= 32 )
WL_TRACE_FP=c
else
exitfunction
endif
if ( file exist(file$) ) then delete file file$
open to write WL_TRACE_FP,file$
write string WL_TRACE_FP,"Trace file created : "+get date$()+", "+get time$()
write string WL_TRACE_FP,"File handle used : "+str$(WL_TRACE_FP)
endfunction
function trace_close()
write string WL_TRACE_FP,"EOF Trace file (safe program exit)"
close file WL_TRACE_FP
endfunction
function trace_write(t$)
write string WL_TRACE_FP,t$
endfunction
` ====================
` mouse and keyboard controls to free fly around to view the map
function init_controls()
global objc as integer
global objcsize as integer
objcsize=30
objc = find free object()
make object sphere objc,objcsize
position object objc,MG_center_x_in(0),objcsize,MG_center_y_in(0)
lightc = find free light()
make light lightc
set spot light lightc, 5, 20
set light range lightc, 1000
` players gravity and controls
global pgrav# = 0.0
global pground = 1
endfunction
function do_controls()
mx# = mousemovex() / 4.0
my# = mousemovey() / 4.0
mc = mouseclick()
yrotate object objc, wrapvalue(object angle y(objc) + mx#)
xrotate object objc, wrapvalue(object angle x(objc) + my#)
ox# = object position x(objc)
oy# = object position y(objc)
oz# = object position z(objc)
if spacekey() and pground = 1
pgrav# = 5.0
endif
if pgrav# > -2.0
pgrav# = pgrav# - 0.35
pground = 0
endif
position object objc, ox#, oy# + pgrav#, oz#
move object objc, ( keystate(17) - keystate(31) ) * 5
move object right objc, ( keystate(32) - keystate(30) ) * 5
nx# = object position x(objc)
ny# = object position y(objc)
nz# = object position z(objc)
c = sc_spherecastgroup( 1, ox#,oy#,oz#, nx#,ny#,nz#, objcsize,0 )
if c
nx# = sc_getcollisionslidex()
ny# = sc_getcollisionslidey()
nz# = sc_getcollisionslidez()
position object objc, nx#, ny#, nz#
if sc_getcollisionnormaly() > 0.78 ` hit ground?
pground = 1
endif
endif
position camera object position x(objc), object position y(objc),object position z(objc)
set camera to object orientation objc
set light to object position lightc, objc
set light to object orientation lightc, objc
endfunction
function make_a_new_level()
MG_clear_level()
MG_init()
randomize timer()
MG_generate(rnd(100000))
MG_create_level("","","")
sc_setupcomplexobject MG_level_obj, 1, 2
position object objc,MG_center_x_in(0),objcsize,MG_center_y_in(0)
endfunction
` ====================
` returns 1 if line x1,y1,x2,y2 intersects x3,y3,x4,y4
` THE MOST IMPORTANT PIECE OF THIS COMPLETE ALGORITHM
function MG_intersect(x1#,y1#,x2#,y2#,x3#,y3#,x4#,y4#)
d# = ( x1# - x2# ) * ( y3# - y4# ) - ( y1# - y2# ) * ( x3# - x4# )
if d#=0 then exitfunction 0
` Get the x and y
pre# = x1# * y2# - y1# * x2#
post#= x3# * y4# - y3# * x4#
x#=( pre# * (x3# - x4#) - (x1# - x2#) * post# ) / d#
y#=( pre# * (y3# - y4#) - (y1# - y2#) * post# ) / d#
` Check if x and y are within both lines
if ( x# < min(x1#, x2#) or x# > max(x1#, x2#) or x# < min(x3#, x4#) or x# > max(x3#, x4#) ) then exitfunction 0
if ( y# < min(y1#, y2#) or y# > max(y1#, y2#) or y# < min(y3#, y4#) or y# > max(y3#, y4#) ) then exitfunction 0
endfunction 1
` ====================
` add a new vertex
function MG_add_vert(x#,y#)
array insert at bottom _VERTS()
_VERTS().x = x#
_VERTS().y = y#
endfunction
` ====================
` add an outer edge
function MG_add_out(v1,v2)
array insert at bottom _OUT()
_OUT().v1 = v1
_OUT().v2 = v2
endfunction
` ====================
` add an inner edge
function MG_add_in(v1,v2)
array insert at bottom _IN()
_IN().v1 = v1
_IN().v2 = v2
endfunction
` ====================
` add face to the _FACES() array
function MG_add_face(v1,v2,v3)
array insert at bottom _FACES()
_FACES().v1 = v1
_FACES().v2 = v2
_FACES().v3 = v3
endfunction
` ====================
` Move outer edge to the inner edge list
function MG_move_edge(edge)
array insert at bottom _IN()
_IN().v1 = _OUT(edge).v1
_IN().v2 = _OUT(edge).v2
array delete element _OUT(), edge
endfunction
` ====================
` hard code the first 2 triangles
function MG_make_first_poly()
` first triangles verts
MG_add_vert(0,0)
MG_add_vert(MG_edge_length,0)
MG_add_vert(0,MG_edge_length)
` store its edges
MG_add_out(0,1)
MG_add_in(1,2) ` hide in the inner list
MG_add_out(2,0)
` store as a face
MG_add_face(0,1,2)
` add another vertex for 2nd triangle
MG_add_vert(MG_edge_length,MG_edge_length)
` add the new edges to outer list
MG_add_out(1,3)
MG_add_out(3,2)
` add the new face
MG_add_face(1,3,2)
endfunction
` ====================
` The main generation algorithm
function MG_generate(seed)
` get this bit out of the way
randomize seed
` create the first polygon (2 triangles)
MG_make_first_poly()
` init current face count
face = 2
while face < MG_max_faces
outsize = array count( _OUT() )
if outsize < 10
edge = outsize - rnd( 1 )
else
edge = outsize - ( ( rnd( outsize ) * 12.0 ) / 100.0 )
endif
v1 = _OUT(edge).v1
v3 = _OUT(edge).v2
angle# = atanfull(_VERTS(v3).y - _VERTS(v1).y, _VERTS(v3).x - _VERTS(v1).x)
angle# = wrapvalue(angle# - MG_edge_angle)
edgelen# = MG_edge_length * ( rnd(MG_rnd_mod) + MG_rnd_add ) / MG_rnd_div
`edgelen#=MG_edge_length
x# = _VERTS(v1).x + ( cos(angle#) * edgelen# )
y# = _VERTS(v1).y + ( sin(angle#) * edgelen# )
if MG_face_in_poly(edge,x#,y#) = 0
inc face
MG_add_vert(x#,y#)
MG_move_edge(edge)
v2 = array count( _VERTS() )
MG_add_out(v1,v2)
MG_add_out(v2,v3)
MG_add_face(v1,v2,v3)
` trace_write("Face count = "+str$(face))
` trace_write("-> face verts - "+str$(v1)+","+str$(v2)+","+str$(v3)+" - "+str$(x#)+","+str$(y#)+" - Edge: "+str$(edge))
`else
` trace_write("Skipped")
endif
endwhile
MG_get_map_extents()
`trace_write("Totals: (Verts-"+str$(array count(_VERTS()))+"), (Faces-"+str$(array count(_FACES()))+")")
endfunction
` ====================
` Test if faces crosses inside the current main polygon
function MG_face_in_poly(edge,x#,y#)
ostart = _OUT(edge).v1
oend = _OUT(edge).v2
if MG_test_outer(x#,y#,ostart) = 0
if MG_test_outer(x#,y#,oend) = 0
if MG_test_inner(x#,y#,ostart) = 0
if MG_test_inner(x#,y#,oend) = 0
exitfunction 0
endif
endif
endif
endif
endfunction 1
` ====================
` test outer intersect
function MG_test_outer(x#,y#,v)
curr_edge = array count( _OUT() )
startx# = _VERTS(v).x
starty# = _VERTS(v).y
flag = 0
while curr_edge >= 0 and flag = 0
edge_start = _OUT(curr_edge).v1
edge_end = _OUT(curr_edge).v2
if v = edge_end or v = edge_start
angle# = atanfull(y#-starty#, x#-startx#)
linex1# = startx# + cos(angle#) * 1.0
liney1# = starty# + sin(angle#) * 1.0
else
linex1# = startx#
liney1# = starty#
endif
edge_x1# = _VERTS(edge_start).x
edge_y1# = _VERTS(edge_start).y
edge_x2# = _VERTS(edge_end).x
edge_y2# = _VERTS(edge_end).y
flag = MG_intersect(linex1#,liney1#,x#,y#,edge_x1#,edge_y1#,edge_x2#,edge_y2#)
dec curr_edge
endwhile
endfunction flag
` ====================
` test inner intersect
function MG_test_inner(x#,y#,v)
curr_edge = array count( _IN() )
startx# = _VERTS(v).x
starty# = _VERTS(v).y
flag = 0
while curr_edge >= 0 and flag = 0
edge_start = _IN(curr_edge).v1
edge_end = _IN(curr_edge).v2
if v = edge_end or v = edge_start
angle# = atanfull(y#-starty#, x#-startx#)
linex1# = startx# + cos(angle#) * 1.0
liney1# = starty# + sin(angle#) * 1.0
else
linex1# = startx#
liney1# = starty#
endif
edge_x1# = _VERTS(edge_start).x
edge_y1# = _VERTS(edge_start).y
edge_x2# = _VERTS(edge_end).x
edge_y2# = _VERTS(edge_end).y
flag = MG_intersect(linex1#,liney1#,x#,y#,edge_x1#,edge_y1#,edge_x2#,edge_y2#)
dec curr_edge
endwhile
endfunction flag
` ====================
` OUTER EDGE FUNCTIONS
function MG_center_x_out(edge)
x# = ( _VERTS(_OUT(edge).v1).x + _VERTS(_OUT(edge).v2).x ) / 2.0
endfunction x#
function MG_center_y_out(edge)
y# = ( _VERTS(_OUT(edge).v1).y + _VERTS(_OUT(edge).v2).y ) / 2.0
endfunction y#
function MG_edge_angle_out(edge)
s = _OUT(edge).v1 : e = _OUT(edge).v2
a# = atanfull(_VERTS(e).y - _VERTS(s).y, _VERTS(e).x - _VERTS(s).x)
endfunction a#
` ====================
` INNER EDGE FUNCTIONS
function MG_center_x_in(edge)
x# = ( _VERTS(_IN(edge).v1).x + _VERTS(_IN(edge).v2).x ) / 2.0
endfunction x#
function MG_center_y_in(edge)
y# = ( _VERTS(_IN(edge).v1).y + _VERTS(_IN(edge).v2).y ) / 2.0
endfunction y#
function MG_edge_angle_in(edge)
s = _IN(edge).v1 : e = _IN(edge).v2
a# = atanfull(_VERTS(e).y - _VERTS(s).y, _VERTS(e).x - _VERTS(s).x)
endfunction a#
` ====================
` calc the minimum x and y values from all the vertices
function MG_get_map_extents()
MG_map_xmin = 0 : MG_map_ymin = 0
MG_map_xmax = 0 : MG_map_ymax = 0
vsize = array count( _VERTS() )
while vsize >= 0
currx# = _VERTS( vsize ).x : curry# = _VERTS( vsize ).y
if MG_map_xmin > currx# then MG_map_xmin = currx#
if MG_map_xmax < currx# then MG_map_xmax = currx#
if MG_map_ymin > curry# then MG_map_ymin = curry#
if MG_map_ymax < curry# then MG_map_ymax = curry#
dec vsize
endwhile
`trace_write("Map extents: Min-"+str$(MG_map_xmin)+","+str$(MG_map_ymin)+" - Max-"+str$(MG_map_xmax)+","+str$(MG_map_ymax))
endfunction
` ====================
` Create the actual 3d object (it's ID is then put into MG_level_obj)
` if no texture files are passed then the program will generate one
function MG_create_level(t_floor$,t_wall$,t_roof$)
MG_create_floor()
obj = MG_create_roof() : mesh = find free mesh()
make mesh from object mesh, obj : add limb MG_level_obj,1,mesh
delete object obj : delete mesh mesh
obj = MG_create_walls()
make mesh from object mesh, obj : add limb MG_level_obj,2,mesh
delete object obj : delete mesh mesh
set object normals MG_level_obj
calculate object bounds MG_level_obj
if t_floor$=""
img = MG_make_random_texture("F")
else
img = MG_load_texture(t_floor$)
endif
texture limb MG_level_obj, 0, img
if t_wall$=""
img = MG_make_random_texture("W")
else
img = MG_load_texture(t_wall$)
endif
texture limb MG_level_obj, 2, img
if t_roof$=""
img = MG_make_random_texture("R")
else
img = MG_load_texture(t_roof$)
endif
texture limb MG_level_obj, 1, img
` init collision (group 1)
sc_setupcomplexobject MG_level_obj,1,2
` Decorate the floor of the level
MG_decorate_floor()
endfunction
function MG_create_floor()
MG_level_obj = find free object()
vsize = array count( _VERTS() )
fsize = array count( _FACES() )
`trace_write("vsize="+str$(vsize)+" ,fsize="+str$(fsize))
make object new MG_level_obj, vsize + 1, ( fsize + 1 ) * 3
lock vertexdata for limb MG_level_obj, 0 ` open up object for editing
for a = 0 to vsize
x# = _VERTS(a).x : y# = _VERTS(a).y
set vertexdata position a, x#, 0.0, y#
` I couldn't get the fmod() function working correctly so I've used
` the values from the maps extents to alter it to work properly
u# = fmod( (x#+abs(MG_map_xmin)) / MG_edge_length, MG_edge_length)
v# = fmod( (y#+abs(MG_map_ymin)) / MG_edge_length, MG_edge_length)
set vertexdata UV a, u#, v# ` vertex UV coorinates
next
for a = 0 to fsize
face = a * 3
set indexdata face , _FACES(a).v1 ` set correct face orientation
set indexdata face + 1, _FACES(a).v3
set indexdata face + 2, _FACES(a).v2
next
unlock vertexdata ` now close the object
set object MG_level_obj,1,1,1
endfunction
function MG_create_roof()
obj = find free object()
vsize = array count( _VERTS() )
fsize = array count( _FACES() )
make object new obj, vsize + 1, ( fsize + 1 ) * 3
lock vertexdata for limb obj, 0
for a = 0 to vsize
x# = _VERTS(a).x : y# = _VERTS(a).y
set vertexdata position a, x#, MG_edge_length, y#
u# = fmod( (x#+abs(MG_map_xmin)) / MG_edge_length, MG_edge_length)
v# = fmod( (y#+abs(MG_map_ymin)) / MG_edge_length, MG_edge_length)
set vertexdata UV a, u#, v#
next
for a = 0 to fsize
face = a * 3
set indexdata face , _FACES(a).v1
set indexdata face + 1, _FACES(a).v2
set indexdata face + 2, _FACES(a).v3
next
unlock vertexdata
set object obj,1,1,1
endfunction obj
function MG_create_walls()
obj = find free object()
esize = array count( _OUT() )
` sort the outer edges in order
dim walls() as _EDGE
array insert at bottom walls()
walls().v1 = _OUT(0).v1
walls().v2 = _OUT(0).v2
look = _OUT(0).v2
while array count(walls()) < esize
curr = 1
while ( _OUT(curr).v1 <> look )
inc curr
endwhile
vfound = _OUT(curr).v2
array insert at bottom walls()
walls().v1 = look : walls().v2 = vfound
look = vfound
endwhile
` now create the walls with correct UV's
vert2 = array count( _VERTS() )
make object new obj, (vert2+1)*2, (esize+1)*6
lock vertexdata for limb obj, 0
` store vertex data (no UV being set just yet)
for a = 0 to vert2
x# = _VERTS(a).x : y# = _VERTS(a).y
set vertexdata position a, x#,0.0,y#
set vertexdata position a+vert2+1, x#,MG_edge_length,y#
next
` set first vertex UV
set vertexdata UV walls(0).v1, 0.0, 0.0
set vertexdata UV walls(0).v1+vert2+1, 0.0, 1.0
` this next var will help wrap UV properly
currUV# = 0
start = walls(0).v1
x1# = _VERTS(start).x
y1# = _VERTS(start).y
` now loop through creating 2 triangle for each face
` and calculate UV based on the edge length
for a = 0 to vert2
vend = walls(a).v2
x2# = _VERTS(vend).x
y2# = _VERTS(vend).y
length# = abs( sqrt((x2#-x1#)*(x2#-x1#) + (y2#-y1#)*(y2#-y1#)) )
currUV# = currUV# + (length# / MG_edge_length)
`trace_write("Edge no, Length = "+str$(a)+","+str$(length# / MG_edge_length))
set vertexdata UV vend, currUV#, 0.0
set vertexdata UV vend+vert2+1, currUV#, 1.0
pos = a * 6
set indexdata pos, start
set indexdata pos+1, vend + vert2 + 1
set indexdata pos+2, start + vert2 + 1
set indexdata pos+3, start
set indexdata pos+4, vend
set indexdata pos+5, vend + vert2 + 1
` speeds up the loop a bit
x1# = x2# : y1# = y2# : start = vend
next
unlock vertexdata
undim walls() ` free the temp memory used
endfunction obj
` ====================
` generates a random texture and returns it's image ID
function MG_make_random_texture(s$)
bw=512 : bh=512
dim cols(3)
for a=0 to 3
cols(a) = rgb( rnd(3)*64, rnd(3)*64, rnd(3)*64 )
next
bm = find free bitmap()
create bitmap bm, bw, bh
lock pixels
for y=0 to bh-1
for x=0 to bw-1
dot x,y,cols(rnd(3))
next
next
unlock pixels
ink rnd(3),rnd(3)
for x = 0 to bw-1 step text width(s$)*2
for y = 0 to bh-1 step text height(s$)*2
text x,y,s$
next
next
for x = 0 to bw-1
ink cols(rnd(3)),cols(rnd(3))
if rnd(1)
line rnd(bw-1),rnd(bh-1),rnd(bw-1),rnd(bh-1)
else
circle rnd(bw-1),rnd(bh-1),rnd(bw/4)
endif
next
img = find free image()
get image img, 0,0,bw-1,bh-1
delete bitmap bm
undim cols()
array insert at bottom _IMG()
_IMG() = img
endfunction img
` ====================
` load a texture and add to array
function MG_load_texture(file$)
img = find free image()
load image file$, img
array insert at bottom _IMG()
_IMG() = img
endfunction img
` ====================
` free up all the resources used by the level
function MG_clear_level()
` main level object
sc_removeobject MG_level_obj
delete object MG_level_obj
` textures
for a=0 to array count( _IMG() )
delete image _IMG(a)
next
` main arrays
empty array _VERTS()
empty array _OUT()
empty array _IN()
empty array _FACES()
` objects
for a=0 to array count( _OBJ() )
if sc_collisionstatus( _OBJ(a) )
sc_removeobject _OBJ(a)
endif
delete object _OBJ(a)
next
` nothing else yet
endfunction
` let's make some floor obstacles
function MG_decorate_floor()
img1 = MG_make_random_texture(">--<")
img2 = MG_make_random_texture("#X#X#")
insize = array count( _IN() )
chance = rnd(80)+20
for a=3 to insize
if rnd(100)>chance
x# = MG_center_x_in(a)
y# = MG_center_y_in(a)
a# = MG_edge_angle_in(a)
sel = rnd(1)
select sel
case 0:
obj = find free object()
make object box obj, 70, 20, 15
yrotate object obj, a#
position object obj, x#, 10, y#
texture object obj, img1
sc_setupobject obj, 1, 2
array insert at bottom _OBJ()
_OBJ() = obj
endcase
case 1:
obj = find free object()
make object sphere obj, 20
yrotate object obj, a#
position object obj, x#, 10, y#
texture object obj, img2
sc_setupobject obj, 1, 1
array insert at bottom _OBJ()
_OBJ() = obj
endcase
endselect
endif
next
endfunction
Warning! May contain Nuts!