Posted: 23rd Nov 2013 20:47
MakeSegmentedBox(
- object number
- width, height, depth
- width segments, height segments, depth segments
)



Create a box primitive with each face divided into a variable number of segments. Works like the 3ds Max box primitive.

Function
+ Code Snippet
function MakeSegmentedBox(objNum, w as float, h as float, d as float, wseg, hseg, dseg)
	// number of verts: 4*numPolys. numPolys = sum of polys needed for each of 6 regular sides of box. -ve and +ve facing sides on each axis are identical, hence 2x.
`	numPolysX = dseg * hseg
`	numPolysY = dseg * wseg
`	numPolysZ = wseg * hseg
`	numPolys = 2*numPolysX + 2*numPolysY + 2*numPolysZ
	numPolys = 2*dseg*hseg + 2*dseg*wseg + 2*wseg*hseg
	numVerts = 4 * numPolys
	
	// make the required number of polys.
	make object new objNum, numVerts, numPolys*6, FVF_XYZ || FVF_NORMAL || FVF_TEX1, 0
	`set object objNum, 1, 1, 0
	
	// Manipulate new object to make box, one side at a time (6 in all, facing: -ve x, +ve x, -ve y, +ve y, -ve z, and +ve z).
	lock vertexdata for limb objNum, 0
	vi = 0	// vertex index.
	ii = 0	// indice index.
	
	pw# = d / dseg		// poly width
	ph# = h / hseg		// poly height
	uvw#= 1.0/dseg		// poly UV width
	uvh#= 1.0/hseg		// poly UV height
	offy# = -h*0.5 + ph#*0.5	// offA# values indicate a corner of the side; verts are added as though along a 2d grid with this as origin.
	offz# = -d*0.5 + pw#*0.5	// +pD#*0.5 puts us in the centre of the polygon; then we just move half a tile left/right and up/down to place each of its verts.
	for polyZ = 0 to dseg-1
		for polyY = 0 to hseg-1
			// +ve x face.
			offx# = w*0.5
			py# = offy#+polyY*ph# : pz# = offz#+polyZ*pw#
			set vertexdata position vi,   offx#, py#-ph#*0.5, pz#-pw#*0.5 : set vertexdata uv vi,   uvw#*polyZ, uvh#*(hseg-polyY)		`uvh#*((hseg-1-polyY)+1), cancelled
			set vertexdata position vi+1, offx#, py#+ph#*0.5, pz#-pw#*0.5 : set vertexdata uv vi+1, uvw#*polyZ, uvh#*(hseg-1-polyY)
			set vertexdata position vi+2, offx#, py#-ph#*0.5, pz#+pw#*0.5 : set vertexdata uv vi+2, uvw#*(polyZ+1), uvh#*(hseg-polyY)
			set vertexdata position vi+3, offx#, py#+ph#*0.5, pz#+pw#*0.5 : set vertexdata uv vi+3, uvw#*(polyZ+1), uvh#*((hseg-1-polyY))
			
			set indexdata ii, vi : set indexdata ii+1, vi+1 : set indexdata ii+2, vi+2
			set indexdata ii+3, vi+2 : set indexdata ii+4, vi+1 : set indexdata ii+5, vi+3
			inc ii, 6
			// -ve x face.
			offx# = -w*0.5
			set vertexdata position vi+4, offx#, py#+ph#*0.5, pz#+pw#*0.5 : set vertexdata uv vi+4, uvw#*(dseg-1-polyZ), uvh#*(hseg-1-polyY)
			set vertexdata position vi+5, offx#, py#+ph#*0.5, pz#-pw#*0.5 : set vertexdata uv vi+5, uvw#*(dseg-polyZ), uvh#*(hseg-1-polyY)
			set vertexdata position vi+6, offx#, py#-ph#*0.5, pz#+pw#*0.5 : set vertexdata uv vi+6, uvw#*(dseg-1-polyZ), uvh#*(hseg-polyY)
			set vertexdata position vi+7, offx#, py#-ph#*0.5, pz#-pw#*0.5 : set vertexdata uv vi+7, uvw#*(dseg-polyZ), uvh#*(hseg-polyY)
			set indexdata ii, vi+4 : set indexdata ii+1, vi+5 : set indexdata ii+2, vi+6
			set indexdata ii+3, vi+6 : set indexdata ii+4, vi+5 : set indexdata ii+5, vi+7
			inc ii, 6
			inc vi, 8
		next polyY
	next polyZ
	
	pw# = w / wseg		// poly width
	ph# = h / hseg		// poly height
	uvw#= 1.0/wseg		// poly UV width
	uvh#= 1.0/hseg		// poly UV height
	offx# = -w*0.5 + pw#*0.5
	offy# = -h*0.5 + ph#*0.5
	for polyX = 0 to wseg-1
		for polyY = 0 to hseg-1
			// -ve z face.
			offz# = -d*0.5
			px# = offx#+polyX*pw# : py# = offy#+polyY*ph#
			set vertexdata position vi,   px#-pw#*0.5, py#-ph#*0.5, offz# : set vertexdata uv vi,   uvw#*polyX, uvh#*(hseg-polyY)
			set vertexdata position vi+1, px#-pw#*0.5, py#+ph#*0.5, offz# : set vertexdata uv vi+1, uvw#*polyX, uvh#*(hseg-1-polyY)
			set vertexdata position vi+2, px#+pw#*0.5, py#-ph#*0.5, offz# : set vertexdata uv vi+2, uvw#*(polyX+1), uvh#*(hseg-polyY)
			set vertexdata position vi+3, px#+pw#*0.5, py#+ph#*0.5, offz# : set vertexdata uv vi+3, uvw#*(polyX+1), uvh#*(hseg-1-polyY)
			set indexdata ii, vi : set indexdata ii+1, vi+1 : set indexdata ii+2, vi+2
			set indexdata ii+3, vi+2 : set indexdata ii+4, vi+1 : set indexdata ii+5, vi+3
			inc ii, 6
			// +ve z face.
			offz# = d*0.5
			set vertexdata position vi+4, px#+pw#*0.5, py#+ph#*0.5, offz# : set vertexdata uv vi+4, uvw#*(wseg-1-polyX), uvh#*(hseg-1-polyY)
			set vertexdata position vi+5, px#-pw#*0.5, py#+ph#*0.5, offz# : set vertexdata uv vi+5, uvw#*(wseg-polyX), uvh#*(hseg-1-polyY)
			set vertexdata position vi+6, px#+pw#*0.5, py#-ph#*0.5, offz# : set vertexdata uv vi+6, uvw#*(wseg-1-polyX), uvh#*(hseg-polyY)
			set vertexdata position vi+7, px#-pw#*0.5, py#-ph#*0.5, offz# : set vertexdata uv vi+7, uvw#*(wseg-polyX), uvh#*(hseg-polyY)
			set indexdata ii, vi+4 : set indexdata ii+1, vi+5 : set indexdata ii+2, vi+6
			set indexdata ii+3, vi+6 : set indexdata ii+4, vi+5 : set indexdata ii+5, vi+7
			inc ii, 6
			inc vi, 8
		next polyY
	next polyX
	
	pw# = w / wseg		// poly width
	ph# = d / dseg		// poly height
	uvw#= 1.0/wseg		// poly UV width
	uvh#= 1.0/dseg		// poly UV height
	offx# = -w*0.5 + pw#*0.5
	offz# = -d*0.5 + ph#*0.5
	for polyX = 0 to wseg-1
		for polyZ = 0 to dseg-1
			// +ve y face.
			offy# = h*0.5
			px# = offx#+polyX*pw# : pz# = offz#+polyZ*ph#
			set vertexdata position vi,   px#-pw#*0.5, offy#, pz#-ph#*0.5 : set vertexdata uv vi,   uvw#*polyX, uvh#*(dseg-polyZ)
			set vertexdata position vi+1, px#-pw#*0.5, offy#, pz#+ph#*0.5 : set vertexdata uv vi+1, uvw#*polyX, uvh#*(dseg-1-polyZ)
			set vertexdata position vi+2, px#+pw#*0.5, offy#, pz#-ph#*0.5 : set vertexdata uv vi+2, uvw#*(polyX+1), uvh#*(dseg-polyZ)
			set vertexdata position vi+3, px#+pw#*0.5, offy#, pz#+ph#*0.5 : set vertexdata uv vi+3, uvw#*(polyX+1), uvh#*(dseg-1-polyZ)
			set indexdata ii, vi : set indexdata ii+1, vi+1 : set indexdata ii+2, vi+2
			set indexdata ii+3, vi+2 : set indexdata ii+4, vi+1 : set indexdata ii+5, vi+3
			inc ii, 6
			// -ve y face.
			offy# = -h*0.5
			set vertexdata position vi+4, px#+pw#*0.5, offy#, pz#+ph#*0.5 : set vertexdata uv vi+4, uvw#*(wseg-1-polyX), uvh#*(dseg-1-polyZ)
			set vertexdata position vi+5, px#-pw#*0.5, offy#, pz#+ph#*0.5 : set vertexdata uv vi+5, uvw#*(wseg-polyX), uvh#*(dseg-1-polyZ)
			set vertexdata position vi+6, px#+pw#*0.5, offy#, pz#-ph#*0.5 : set vertexdata uv vi+6, uvw#*(wseg-1-polyX), uvh#*(dseg-polyZ)
			set vertexdata position vi+7, px#-pw#*0.5, offy#, pz#-ph#*0.5 : set vertexdata uv vi+7, uvw#*(wseg-polyX), uvh#*(dseg-polyZ)
			set indexdata ii, vi+4 : set indexdata ii+1, vi+5 : set indexdata ii+2, vi+6
			set indexdata ii+3, vi+6 : set indexdata ii+4, vi+5 : set indexdata ii+5, vi+7
			inc ii, 6
			inc vi, 8
		next polyZ
	next polyX
	unlock vertexdata
	
	set object normals objNum
endfunction



Example
instructions on-screen (edit: can move camera base with arrow keys now)
+ Code Snippet
set display mode desktop width(), desktop height(), 32, 1
sync on : sync rate 30 : sync : autocam off : color backdrop 0x506490


global mx, my, mmx, mmy, mmz as float, kLeft, kRight, kUp, kDown, camZoom as float, camBaseX as float, camBaseY as float, camBaseZ as float
SetupUIAxisIndicator()



// EXAMPLE 1: CUBE SERIES
// creates a line of boxes then distorts them through a fairly random method to produce a caramel-like wave. Also creates two wavy pillars either end.
for n = 1 to 10
	if 0 `n %% 2 = 0
		make object box n, 1, 1, 1
	else
		MakeSegmentedBox(n, 1, 1, 2, 12, 20, 3)
	endif
	set object cull n, 0
	position object n, n*1.0-6, 0, 0
	
	lock vertexdata for limb n, 0
		vc = get vertexdata vertex count() - 1
		for vi = 0 to vc
			vx# = get vertexdata position x(vi)
			vy# = get vertexdata position y(vi)
			vz# = get vertexdata position z(vi)
			if n = 1 or n = 10
				vx# = vx# + sin(vy#*720)*0.33
				vz# = vz# + sin(vy#*1440)*0.33
				vy# = vy# * 6.0
			else
				if vy# >= 0 then vy# = vy# + cos((object position x(n)+vx#)*48.0) * 0.5
				if vy# > 0 then vy# = vy# * (1.2 + cos(vx#*360.0))
				vz# = vz# * abs(1.8-vy#)
			endif
			set vertexdata position vi, vx#, vy#, vz#
		next vi
	unlock vertexdata
next n



// EXAMPLE 2: BOX GRID
// makes a grid of cubes of varying segmentation and applies a little random distortion, plus a texture.
// (the random factor is hashed in; applying a unique random value per-vertex seperates faces from their neighbours resulting in a (hole-filled) wall of post-it notes.
//  uncomment the three "RndSign() * rnd(2) * 0.01" bits and comment out the following four lines (s#=... to vz#=...) to see this.)
for n = 1 to 100 : box rnd(128), rnd(128), rnd(128), rnd(128), 0x0880ffff : next n
set text font "Arial" : set text size 100 : center text 64, 14, "2" : set text size 20
get image 1, 0, 0, 128, 128

o = 12 `to 31
for q = 0 to 3
	for n = 0 to 4
		if q = 0 then ws = 2^n : hs = 2^n : ds = 2^n
		if q = 1 then ws = 2^n : hs = 1   : ds = 1
		if q = 2 then ws = 1   : hs = 2^n : ds = 1
		if q = 3 then ws = 1   : hs = 1   : ds = 2^n
		MakeSegmentedBox(o, 2, 2, 2, ws, hs, ds)
		position object o, n*2.2-4, 0, -q*2.5-4
		lock vertexdata for limb o, 0
			vc = get vertexdata vertex count() - 1
			for vi = 0 to vc
				vx# = get vertexdata position x(vi)` + RndSign() * rnd(2) * 0.01
				vy# = get vertexdata position y(vi)` + RndSign() * rnd(2) * 0.01
				vz# = get vertexdata position z(vi)` + RndSign() * rnd(2) * 0.01
				s# = wrap( hex to decimal( left$(hash md5(str$(vx#*vy#*vz#)), 4) ), -1000, 1000.0) * 0.0001
				vx# = vx# * (1.0 + s#)
				vy# = vy# * (1.0 + s#)
				vz# = vz# * (1.0 + s#)
				set vertexdata position vi, vx#, vy#, vz#
			next vi
		unlock vertexdata
		set object normals o
		set object smoothing o, 50
		texture object o, 1
		inc o
	next n
next q



// EXAMPLE 3: CURVY SHAPE
// A final long wavy box with many width segments.
MakeSegmentedBox(11, 8, 1, 1, 80, 1, 1)
position object 11, 0, 0, 4
set object wireframe 11, 1

lock vertexdata for limb 11, 0
	vc = get vertexdata vertex count() - 1
	for vi = 0 to vc
		vx# = get vertexdata position x(vi)
		vy# = get vertexdata position y(vi) + sin(vx#*40) * 1.33
		vz# = get vertexdata position z(vi) + cos(vx#*60) * 1.0
		set vertexdata position vi, vx#, vy#, vz#
	next vi
unlock vertexdata



// setup camera.
position camera -12, 8, -20
point camera 0, 0, -8


// main loop.
do
	UpdateInput()
	
	// [ press w to wireframe one of the line of boxes   ]
	// [ move mouse over grid of boxes to wireframe each ]
	o = pick object(mx, my, 12, 31)
	for n = 12 to 31 : set object wireframe n, (o=n) : next n
	set object wireframe 5, (inkey$()="w")
	
	// [ hold right-mouse button to spin camera ]
	UpdateCamera((mouseclick()>1 && mouseclick()<3), 1, 10)
	UpdateUIAxisIndicator()
	
	set cursor 0,0
	print "fps: ", screen fps()
	print "try: mouse over grid of boxes"
	print "try: w key"
	print "hold right-mouse and drag to spin camera, arrow keys to move. Mousewheel zooms."
sync
nice wait 15
loop



function MakeSegmentedBox(objNum, w as float, h as float, d as float, wseg, hseg, dseg)
	// number of verts: 4*numPolys. numPolys = sum of polys needed for each of 6 regular sides of box. -ve and +ve facing sides on each axis are identical, hence 2x.
`	numPolysX = dseg * hseg
`	numPolysY = dseg * wseg
`	numPolysZ = wseg * hseg
`	numPolys = 2*numPolysX + 2*numPolysY + 2*numPolysZ
	numPolys = 2*dseg*hseg + 2*dseg*wseg + 2*wseg*hseg
	numVerts = 4 * numPolys
	
	// make the required number of polys.
	make object new objNum, numVerts, numPolys*6, FVF_XYZ || FVF_NORMAL || FVF_TEX1, 0
	`set object objNum, 1, 1, 0
	
	// Manipulate new object to make box, one side at a time (6 in all, facing: -ve x, +ve x, -ve y, +ve y, -ve z, and +ve z).
	lock vertexdata for limb objNum, 0
	vi = 0	// vertex index.
	ii = 0	// indice index.
	
	pw# = d / dseg		// poly width
	ph# = h / hseg		// poly height
	uvw#= 1.0/dseg		// poly UV width
	uvh#= 1.0/hseg		// poly UV height
	offy# = -h*0.5 + ph#*0.5	// offA# values indicate a corner of the side; verts are added as though along a 2d grid with this as origin.
	offz# = -d*0.5 + pw#*0.5	// +pD#*0.5 puts us in the centre of the polygon; then we just move half a tile left/right and up/down to place each of its verts.
	for polyZ = 0 to dseg-1
		for polyY = 0 to hseg-1
			// +ve x face.
			offx# = w*0.5
			py# = offy#+polyY*ph# : pz# = offz#+polyZ*pw#
			set vertexdata position vi,   offx#, py#-ph#*0.5, pz#-pw#*0.5 : set vertexdata uv vi,   uvw#*polyZ, uvh#*(hseg-polyY)		`uvh#*((hseg-1-polyY)+1), cancelled
			set vertexdata position vi+1, offx#, py#+ph#*0.5, pz#-pw#*0.5 : set vertexdata uv vi+1, uvw#*polyZ, uvh#*(hseg-1-polyY)
			set vertexdata position vi+2, offx#, py#-ph#*0.5, pz#+pw#*0.5 : set vertexdata uv vi+2, uvw#*(polyZ+1), uvh#*(hseg-polyY)
			set vertexdata position vi+3, offx#, py#+ph#*0.5, pz#+pw#*0.5 : set vertexdata uv vi+3, uvw#*(polyZ+1), uvh#*((hseg-1-polyY))
			
			set indexdata ii, vi : set indexdata ii+1, vi+1 : set indexdata ii+2, vi+2
			set indexdata ii+3, vi+2 : set indexdata ii+4, vi+1 : set indexdata ii+5, vi+3
			inc ii, 6
			// -ve x face.
			offx# = -w*0.5
			set vertexdata position vi+4, offx#, py#+ph#*0.5, pz#+pw#*0.5 : set vertexdata uv vi+4, uvw#*(dseg-1-polyZ), uvh#*(hseg-1-polyY)
			set vertexdata position vi+5, offx#, py#+ph#*0.5, pz#-pw#*0.5 : set vertexdata uv vi+5, uvw#*(dseg-polyZ), uvh#*(hseg-1-polyY)
			set vertexdata position vi+6, offx#, py#-ph#*0.5, pz#+pw#*0.5 : set vertexdata uv vi+6, uvw#*(dseg-1-polyZ), uvh#*(hseg-polyY)
			set vertexdata position vi+7, offx#, py#-ph#*0.5, pz#-pw#*0.5 : set vertexdata uv vi+7, uvw#*(dseg-polyZ), uvh#*(hseg-polyY)
			set indexdata ii, vi+4 : set indexdata ii+1, vi+5 : set indexdata ii+2, vi+6
			set indexdata ii+3, vi+6 : set indexdata ii+4, vi+5 : set indexdata ii+5, vi+7
			inc ii, 6
			inc vi, 8
		next polyY
	next polyZ
	
	pw# = w / wseg		// poly width
	ph# = h / hseg		// poly height
	uvw#= 1.0/wseg		// poly UV width
	uvh#= 1.0/hseg		// poly UV height
	offx# = -w*0.5 + pw#*0.5
	offy# = -h*0.5 + ph#*0.5
	for polyX = 0 to wseg-1
		for polyY = 0 to hseg-1
			// -ve z face.
			offz# = -d*0.5
			px# = offx#+polyX*pw# : py# = offy#+polyY*ph#
			set vertexdata position vi,   px#-pw#*0.5, py#-ph#*0.5, offz# : set vertexdata uv vi,   uvw#*polyX, uvh#*(hseg-polyY)
			set vertexdata position vi+1, px#-pw#*0.5, py#+ph#*0.5, offz# : set vertexdata uv vi+1, uvw#*polyX, uvh#*(hseg-1-polyY)
			set vertexdata position vi+2, px#+pw#*0.5, py#-ph#*0.5, offz# : set vertexdata uv vi+2, uvw#*(polyX+1), uvh#*(hseg-polyY)
			set vertexdata position vi+3, px#+pw#*0.5, py#+ph#*0.5, offz# : set vertexdata uv vi+3, uvw#*(polyX+1), uvh#*(hseg-1-polyY)
			set indexdata ii, vi : set indexdata ii+1, vi+1 : set indexdata ii+2, vi+2
			set indexdata ii+3, vi+2 : set indexdata ii+4, vi+1 : set indexdata ii+5, vi+3
			inc ii, 6
			// +ve z face.
			offz# = d*0.5
			set vertexdata position vi+4, px#+pw#*0.5, py#+ph#*0.5, offz# : set vertexdata uv vi+4, uvw#*(wseg-1-polyX), uvh#*(hseg-1-polyY)
			set vertexdata position vi+5, px#-pw#*0.5, py#+ph#*0.5, offz# : set vertexdata uv vi+5, uvw#*(wseg-polyX), uvh#*(hseg-1-polyY)
			set vertexdata position vi+6, px#+pw#*0.5, py#-ph#*0.5, offz# : set vertexdata uv vi+6, uvw#*(wseg-1-polyX), uvh#*(hseg-polyY)
			set vertexdata position vi+7, px#-pw#*0.5, py#-ph#*0.5, offz# : set vertexdata uv vi+7, uvw#*(wseg-polyX), uvh#*(hseg-polyY)
			set indexdata ii, vi+4 : set indexdata ii+1, vi+5 : set indexdata ii+2, vi+6
			set indexdata ii+3, vi+6 : set indexdata ii+4, vi+5 : set indexdata ii+5, vi+7
			inc ii, 6
			inc vi, 8
		next polyY
	next polyX
	
	pw# = w / wseg		// poly width
	ph# = d / dseg		// poly height
	uvw#= 1.0/wseg		// poly UV width
	uvh#= 1.0/dseg		// poly UV height
	offx# = -w*0.5 + pw#*0.5
	offz# = -d*0.5 + ph#*0.5
	for polyX = 0 to wseg-1
		for polyZ = 0 to dseg-1
			// +ve y face.
			offy# = h*0.5
			px# = offx#+polyX*pw# : pz# = offz#+polyZ*ph#
			set vertexdata position vi,   px#-pw#*0.5, offy#, pz#-ph#*0.5 : set vertexdata uv vi,   uvw#*polyX, uvh#*(dseg-polyZ)
			set vertexdata position vi+1, px#-pw#*0.5, offy#, pz#+ph#*0.5 : set vertexdata uv vi+1, uvw#*polyX, uvh#*(dseg-1-polyZ)
			set vertexdata position vi+2, px#+pw#*0.5, offy#, pz#-ph#*0.5 : set vertexdata uv vi+2, uvw#*(polyX+1), uvh#*(dseg-polyZ)
			set vertexdata position vi+3, px#+pw#*0.5, offy#, pz#+ph#*0.5 : set vertexdata uv vi+3, uvw#*(polyX+1), uvh#*(dseg-1-polyZ)
			set indexdata ii, vi : set indexdata ii+1, vi+1 : set indexdata ii+2, vi+2
			set indexdata ii+3, vi+2 : set indexdata ii+4, vi+1 : set indexdata ii+5, vi+3
			inc ii, 6
			// -ve y face.
			offy# = -h*0.5
			set vertexdata position vi+4, px#+pw#*0.5, offy#, pz#+ph#*0.5 : set vertexdata uv vi+4, uvw#*(wseg-1-polyX), uvh#*(dseg-1-polyZ)
			set vertexdata position vi+5, px#-pw#*0.5, offy#, pz#+ph#*0.5 : set vertexdata uv vi+5, uvw#*(wseg-polyX), uvh#*(dseg-1-polyZ)
			set vertexdata position vi+6, px#+pw#*0.5, offy#, pz#-ph#*0.5 : set vertexdata uv vi+6, uvw#*(wseg-1-polyX), uvh#*(dseg-polyZ)
			set vertexdata position vi+7, px#-pw#*0.5, offy#, pz#-ph#*0.5 : set vertexdata uv vi+7, uvw#*(wseg-polyX), uvh#*(dseg-polyZ)
			set indexdata ii, vi+4 : set indexdata ii+1, vi+5 : set indexdata ii+2, vi+6
			set indexdata ii+3, vi+6 : set indexdata ii+4, vi+5 : set indexdata ii+5, vi+7
			inc ii, 6
			inc vi, 8
		next polyZ
	next polyX
	unlock vertexdata
	
	set object normals objNum
endfunction




// -- UTILITY FUNCTIONS --
function UpdateInput()
	mx = mousex()
	my = mousey()
	mmx = mousemovex()
`	mmy = mousemovey()
	mmz = mousemovez()
	kLeft = leftkey()
	kRight = rightkey()
	kUp = upkey()
	kDown = downkey()
endfunction

global oldEnableYRot as boolean, mouseStaticX, mouseStaticY
function UpdateCamera(enableYRot as boolean, forceYRotMouseStatic as boolean, baseDist as float)
	camZoom = camZoom + mmz*0.0066
	position camera camBaseX, camBaseY, camBaseZ
	
	if enableYRot
		yrotate camera camera angle y()+mmx*0.2
		if forceYRotMouseStatic
			if oldEnableYRot = 0 then hide mouse : mouseStaticX = mx : mouseStaticY = my
			position mouse mouseStaticX, mouseStaticY
		endif
	else
		if forceYRotMouseStatic and oldEnableYRot = 1 then show mouse
	endif
	oldEnableYRot = enableYRot
        
	move camera right (kRight-kLeft)*0.15
	yrotate camera camera angle y()-90
	move camera right (kUp-kDown)*0.15
	yrotate camera camera angle y()+90
	
	camBaseX = camera position x()
	camBaseY = camera position y()
	camBaseZ = camera position z()
	
	move camera -baseDist + camZoom
endfunction

function SetupUIAxisIndicator()
	make object box 1000, 1, 0.1, 0.1 : make object cone 1003, 0.1 : color object 1003, 0xff0000 : make mesh from object 1, 1003 : add limb 1000, 1, 1 : offset limb 1000, 1, 0.7, 0, 0 : rotate limb 1000, 1, 0, 0, -90 : delete mesh 1 : delete object 1003 : color object 1000, 0xff0000 : set object emissive 1000, 0xff0000 : set object cull 1000, 0
	make object box 1001, 0.1, 1, 0.1 : make object cone 1004, 0.1 : color object 1004, 0x00ff00 : make mesh from object 1, 1004 : add limb 1001, 1, 1 : offset limb 1001, 1, 0, 0.7, 0 : rotate limb 1001, 1, 0, 0, 0 : delete mesh 1 : delete object 1004 : color object 1001, 0x00ff00 : set object emissive 1001, 0x00ff00 : set object cull 1001, 0
	make object box 1002, 0.1, 0.1, 1 : make object cone 1005, 0.1 : color object 1005, 0x0000ff : make mesh from object 1, 1005 : add limb 1002, 1, 1 : offset limb 1002, 1, 0, 0, 0.7 : rotate limb 1002, 1, 90, 0, 0 : delete mesh 1 : delete object 1005 : color object 1002, 0x0000ff : set object emissive 1002, 0x0000ff : set object cull 1002, 0
	for n = 1000 to 1002 : disable object zdepth n : next n
endfunction

function UpdateUIAxisIndicator()
	for n = 1000 to 1002
		position object n, camera position x(), camera position y(), camera position z()
		rotate object n, camera angle x(), camera angle y(), camera angle z()
		move object n, 12 : move object left n, 9 : move object down n, 5
		rotate object n, 0, 0, 0
	next n
endfunction

function RndSign()
	s = 1 - rnd(1)*2
endfunction s



` FVF constants
#constant FVF_XYZ = 0x002
#constant FVF_XYZRHW = 0x004
#constant FVF_NORMAL = 0x010
#constant FVF_PSIZE = 0x020
#constant FVF_DIFFUSE = 0x040
#constant FVF_SPECULAR = 0x080
#constant FVF_TEX0 = 0x000
#constant FVF_TEX1 = 0x100
#constant FVF_TEX2 = 0x200
#constant FVF_TEX3 = 0x300
#constant FVF_TEX4 = 0x400
#constant FVF_TEX5 = 0x500
#constant FVF_TEX6 = 0x600
#constant FVF_TEX7 = 0x700
#constant FVF_TEX8 = 0x800


Notes
- This can probably done more easily and quickly by sticking a few plane primitives together, but this was a learning experience.
- Some aspects (particularly UVs) involved a lot of trial and error, so there's probably a lot that could be optimised. The way polys are added is a little haphazard; certainly, I wouldn't make any assumptions as to what number vertices can be found where.
- As far as I can tell thus far though, it's bug free
Posted: 14th Jan 2014 11:29
Hi there
I hadn't see this code before . I'm astonished, this is brilliant...!! .Playing with vertexdata is what I like best. I will keep it
Posted: 20th Jan 2014 22:21
Thanks : )

Some development:
I think the reason the object has intra-box face tears when jittered with a naive per-vertex method is because I haven't properly shared vertices between polygons (ie. each box face is just a landscape of disconnected tiles).

Rather than fix that, I've written a new version of the function which I'm sure is better:

+ Code Snippet
function MakeSegmentedBox(objNum, w as float, h as float, d as float, wseg, hseg, dseg)
	local objNegX, objNegZ, objPosX, objPosZ, objNegY, objPosY, meshNegX, meshNegZ, meshPosX, meshPosZ, meshNegY, meshPosY
	
	// start by creating faces as a series of planes: -x, -z, +x, +z, -y, +y.
	null = reserve free object(objNum, 1)	// ensure final object's number not used.
		objNegX = find free object() : make object plane objNegX, d, h, dseg, hseg : rotate object objNegX, -90,  90, 0 : fix object pivot objNegX
		objNegZ = find free object() : make object plane objNegZ, w, h, wseg, hseg : rotate object objNegZ, -90,   0, 0 : fix object pivot objNegZ
		objPosX = find free object() : make object plane objPosX, d, h, dseg, hseg : rotate object objPosX, -90, -90, 0 : fix object pivot objPosX
		objPosZ = find free object() : make object plane objPosZ, w, h, wseg, hseg : rotate object objPosZ, -90, 180, 0 : fix object pivot objPosZ
		objNegY = find free object() : make object plane objNegY, w, d, wseg, dseg : rotate object objNegY, 180,   0, 0 : fix object pivot objNegY
		objPosY = find free object() : make object plane objPosY, w, d, wseg, dseg
	release reserved object objNum
	
	// prepare meshes for object limbs.
	meshNegX = find free mesh() : make mesh from object meshNegX, objNegX
	meshNegZ = find free mesh() : make mesh from object meshNegZ, objNegZ
	meshPosX = find free mesh() : make mesh from object meshPosX, objPosX
	meshPosZ = find free mesh() : make mesh from object meshPosZ, objPosZ
	meshNegY = find free mesh() : make mesh from object meshNegY, objNegY
	meshPosY = find free mesh() : make mesh from object meshPosY, objPosY
	
	// object has faces on limbs 1 thru 6.
	begin new object
		zerolimb = add new limb()
		add new limb from mesh meshNegX : set new limb parent zerolimb : set new limb offset -w/2.0,      0,      0 : set new limb transparent 0
		add new limb from mesh meshNegZ : set new limb parent zerolimb : set new limb offset      0,      0, -d/2.0 : set new limb transparent 0
		add new limb from mesh meshPosX : set new limb parent zerolimb : set new limb offset  w/2.0,      0,      0 : set new limb transparent 0
		add new limb from mesh meshPosZ : set new limb parent zerolimb : set new limb offset      0,      0,  d/2.0 : set new limb transparent 0
		add new limb from mesh meshNegY : set new limb parent zerolimb : set new limb offset      0, -h/2.0,      0 : set new limb transparent 0
		add new limb from mesh meshPosY : set new limb parent zerolimb : set new limb offset      0,  h/2.0,      0 : set new limb transparent 0
	finish new object objNum
	
	// cleanup.
	delete object objNegX : delete mesh meshNegX
	delete object objNegZ : delete mesh meshNegZ
	delete object objPosX : delete mesh meshPosX
	delete object objPosZ : delete mesh meshPosZ
	delete object objNegY : delete mesh meshNegY
	delete object objPosY : delete mesh meshPosY
endfunction




(the result is slightly different in another way, however, in that you can no longer deform it by changing limb zero's verts; you have to change 6 limbs, one for each box face. I tried to make an alternate version that uses the method above then packs all verts back into limb zero, but couldn't do it.)

In any case, you'd still get inter-box face tears with per-vertex jitter; I don't think there's any way around that - if all box faces shared their edge verts they'd share their normals too...


edit: Updated example with improved camera.



+ Code Snippet
set display mode desktop width(), desktop height(), 32, 1
sync on : sync rate 30 : sync : autocam off : color backdrop 0x485A82

// gen'd demonstration texture.
for n = 1 to 100 : box rnd(128), rnd(128), rnd(128), rnd(128), 0x0880ffff : next n
set text font "Arial" : set text size 100 : center text 64, 14, "2" : set text size 20
get image 1, 0, 0, 128, 128

global mx, my, mmx, mmy, mmz as float, kLeft, kRight, kUp, kDown, camZoom as float, camBaseX as float, camBaseY as float, camBaseZ as float
SetupUIAxisIndicator(1000)

// example spinning boxes.
for n = 1 to 32
	sz = 7 + n/4
	MakeSegmentedBox(n, sz, sz, sz, 6, 9, 12)
	position object n, -0, 9, 0
	set object wireframe n, 1
next n
MakeSegmentedBox(33, 8, 0.1, 8, 1, 1, 1)

// example boxes-in-boxes.
MakeSegmentedBox(50,    64,    64,    64, 8, 8, 8) : position object 50,     16 + 28,     4 + 28,     0 + 28 : set object wireframe 50, 1
MakeSegmentedBox(51,     8,     8,     8, 8, 8, 8) : position object 51,          16,          4,          0 : set object wireframe 51, 1
MakeSegmentedBox(52,     1,     1,     1, 8, 8, 8) : position object 52,    16 - 3.5,    4 - 3.5,    0 - 3.5 : set object wireframe 52, 1
MakeSegmentedBox(53, 0.125, 0.125, 0.125, 8, 8, 8) : position object 53, 16 - 3.9375, 4 - 3.9375, 0 - 3.9375 : set object wireframe 53, 1

// example wavy.
MakeSegmentedBox(60, 160, 8, 32, 40, 1, 32) : position object 60, -4, -3.95, -20 : convert object fvf 60, FVF_XYZ||FVF_NORMAL||FVF_DIFFUSE
lock vertexdata for limb 60, 6					// top
	vc = get vertexdata vertex count() - 1
	for vi = 0 to vc
		vx# = get vertexdata position x(vi)` : dx# = (80.0 - abs(vx#)) / 80.0
		vz# = get vertexdata position z(vi)` : dz# = (16.0 - abs(vz#)) / 16.0
		`set vertexdata position vi, vx#, dx#*dz# * sin(vx#^2 + vz#^2) * 2.5, vz#
		set vertexdata diffuse vi, rgb(192+rnd(63), 236+rnd(15), 248+rnd(7)) ` 0xff0000ff + rnd(0x080800) ` 0xfff8f8ff + rnd(0x070700)
	next vi
unlock vertexdata
lock vertexdata for limb 60, 5					// bottom
	for vi = 0 to vc
		vx# = get vertexdata position x(vi) : dx# = (80.0 - abs(vx#)) / 80.0
		vz# = get vertexdata position z(vi) : dz# = (16.0 - abs(vz#)) / 16.0
		set vertexdata position vi, vx#, (dx#*dz#)^2.0 * -48.0, vz#
		set vertexdata diffuse vi, 0xffc0c0c0 + rnd(0x3f3f3f)
	next vi
unlock vertexdata
set object normals 60

// rainbow.
for n = 300 to 400
	sz = (n+1-300) * 0.4
	MakeSegmentedBox(n, sz, sz, sz, 1+rnd(9), 1+rnd(9), 1+rnd(9))
	position object n, 0, 20, -70
	rotate object n, rndSign()*0.25, rndSign()*0.25, rndSign()*0.25
	`set object wireframe n, 1
	set object cull n, 0
	color object n, hsv to rgb((n-300)*3.6, 1, 1)
	
	// remove limbs 3,4,5 (as they show grey rather than coloured under normal dbp lighting condition).
	remove limb n, 3	// 1 2 _3_ 4 5 6 -> 1 2 4 5 6
	remove limb n, 3	// 1 2 _4_ 5 6   -> 1 2 5 6
	remove limb n, 3	// 1 2 _5_ 6     -> 1 2 6
next n

// textured.
MakeSegmentedBox(401, 8, 8, 8, 1, 3, 9)
texture object 401, 1
position object 401, 72, 4, 0


// setup camera (with basepos indicator).
MakeSegmentedBox(100, 0.25, 0.25, 0.25, 8, 8, 8)
convert object fvf 100, FVF_XYZ||FVF_NORMAL||FVF_DIFFUSE : hide object 100
for n = 0 to 0
	lock vertexdata for limb 100, n
		vc = get vertexdata vertex count() - 1
		for vi = 0 to vc
			c = rnd(1)*255 `255 * (vi %% 2)
			set vertexdata diffuse vi, rgb(c,c,c)
		next vi
	unlock vertexdata
next n
camBaseX = 0 : camBaseZ = -30


// main loop.
do
	UpdateInput()
	
	// update spinning boxes.
	for n = 1 to 32
		xrotate object n, object angle x(n)+0.0066 + n*0.0033
		yrotate object n, object angle y(n)+0.01 + n*0.01
		zrotate object n, object angle z(n)+0.003 + n*0.0015
	next n
	
	// [ hold right-mouse button to spin camera ]
	UpdateCamera((mouseclick()>1 && mouseclick()<3), (mouseclick()>3 && mouseclick()<5), 1, 10)
	UpdateUIAxisIndicator()
	
	// update waves.
	lock vertexdata for limb 60, 6					// top
		vc = get vertexdata vertex count() - 1
		for vi = 0 to vc
			vx# = get vertexdata position x(vi) : dx# = (80.0 - abs(vx#)) / 80.0
			vz# = get vertexdata position z(vi) : dz# = (16.0 - abs(vz#)) / 16.0
			set vertexdata position vi, vx#, dx#*dz# * sin(hitimer()*0.1 + vx#^2 + vz#^2) * 2.5, vz#
		next vi
	unlock vertexdata
	set object normals 60
	
	// update rainbow.
	for n = 300 to 400
		sc# = 100.0 + sin(hitimer()*0.1 + n*3.6) * 50.0
		scale object n, sc#, sc#, sc#
		if rnd(4) = 0
			for o = 1 to 3
				if rnd(4) = 0
					lock vertexdata for limb n, o
						vc = get vertexdata vertex count() - 1
						for vi = 0 to vc
							vx# = get vertexdata position x(vi) + rndSign()*0.1
							vy# = get vertexdata position y(vi) + rndSign()*0.1
							vz# = get vertexdata position z(vi) + rndSign()*0.1
							set vertexdata position vi, vx#, vy#, vz#
						next vi
					unlock vertexdata
				endif
			next o
		endif
		`set object normals n
	next n
	
	set cursor 0,0
	print "fps: ", screen fps()
	print "hold right-mouse and drag to spin camera. Pan with arrow keys, or mouse with mousewheel held. Mousewheel scroll zooms."
sync
nice wait 15
loop


function MakeSegmentedBox(objNum, w as float, h as float, d as float, wseg, hseg, dseg)
	local objNegX, objNegZ, objPosX, objPosZ, objNegY, objPosY, meshNegX, meshNegZ, meshPosX, meshPosZ, meshNegY, meshPosY
	
	// start by creating faces as a series of planes: -x, -z, +x, +z, -y, +y.
	null = reserve free object(objNum, 1)	// ensure final object's number not used.
		objNegX = find free object() : make object plane objNegX, d, h, dseg, hseg : rotate object objNegX, -90,  90, 0 : fix object pivot objNegX
		objNegZ = find free object() : make object plane objNegZ, w, h, wseg, hseg : rotate object objNegZ, -90,   0, 0 : fix object pivot objNegZ
		objPosX = find free object() : make object plane objPosX, d, h, dseg, hseg : rotate object objPosX, -90, -90, 0 : fix object pivot objPosX
		objPosZ = find free object() : make object plane objPosZ, w, h, wseg, hseg : rotate object objPosZ, -90, 180, 0 : fix object pivot objPosZ
		objNegY = find free object() : make object plane objNegY, w, d, wseg, dseg : rotate object objNegY, 180,   0, 0 : fix object pivot objNegY
		objPosY = find free object() : make object plane objPosY, w, d, wseg, dseg
	release reserved object objNum
	
	// prepare meshes for object limbs.
	meshNegX = find free mesh() : make mesh from object meshNegX, objNegX
	meshNegZ = find free mesh() : make mesh from object meshNegZ, objNegZ
	meshPosX = find free mesh() : make mesh from object meshPosX, objPosX
	meshPosZ = find free mesh() : make mesh from object meshPosZ, objPosZ
	meshNegY = find free mesh() : make mesh from object meshNegY, objNegY
	meshPosY = find free mesh() : make mesh from object meshPosY, objPosY
	
	// object has faces on limbs 1 thru 6.
	begin new object
		zerolimb = add new limb()
		add new limb from mesh meshNegX : set new limb parent zerolimb : set new limb offset -w/2.0,      0,      0 : set new limb transparent 0
		add new limb from mesh meshNegZ : set new limb parent zerolimb : set new limb offset      0,      0, -d/2.0 : set new limb transparent 0
		add new limb from mesh meshPosX : set new limb parent zerolimb : set new limb offset  w/2.0,      0,      0 : set new limb transparent 0
		add new limb from mesh meshPosZ : set new limb parent zerolimb : set new limb offset      0,      0,  d/2.0 : set new limb transparent 0
		add new limb from mesh meshNegY : set new limb parent zerolimb : set new limb offset      0, -h/2.0,      0 : set new limb transparent 0
		add new limb from mesh meshPosY : set new limb parent zerolimb : set new limb offset      0,  h/2.0,      0 : set new limb transparent 0
	finish new object objNum
	
	// cleanup.
	delete object objNegX : delete mesh meshNegX
	delete object objNegZ : delete mesh meshNegZ
	delete object objPosX : delete mesh meshPosX
	delete object objPosZ : delete mesh meshPosZ
	delete object objNegY : delete mesh meshNegY
	delete object objPosY : delete mesh meshPosY
endfunction



// -- UTILITY FUNCTIONS --
function UpdateInput()
	mx = mousex()
	my = mousey()
	mmx = mousemovex()
	mmy = mousemovey()
	mmz = mousemovez()
	kLeft = leftkey()
	kRight = rightkey()
	kUp = upkey()
	kDown = downkey()
endfunction

global oldEnableYRot as boolean, mouseStaticX, mouseStaticY
function UpdateCamera(enableYRot as boolean, enableXYPan as boolean, forceYRotMouseStatic as boolean, baseDist as float)
	camZoom = min(-1.0, camZoom + mmz*0.033) `0.0066
	position camera camBaseX, camBaseY, camBaseZ
	
	if enableYRot
		yrotate camera camera angle y()+mmx*0.2
		xrotate camera clamp(camera angle x()+mmy*0.2, 0, 90)
		if forceYRotMouseStatic
			if oldEnableYRot = 0 then hide mouse : mouseStaticX = mx : mouseStaticY = my
			position mouse mouseStaticX, mouseStaticY
		endif
	else
		if forceYRotMouseStatic and oldEnableYRot = 1 then show mouse
	endif
	oldEnableYRot = enableYRot

	if enableXYPan
		move camera right mmx*sqrt(-camZoom)*0.02
		yrotate camera camera angle y()-90
		move camera right -mmy*sqrt(-camZoom)*0.02
		yrotate camera camera angle y()+90
	endif
	
	move camera right (kRight-kLeft)*0.15
	yrotate camera camera angle y()-90
	move camera right (kUp-kDown)*0.15
	yrotate camera camera angle y()+90
	
	camBaseX = camera position x()
	camBaseY = camera position y()
	camBaseZ = camera position z()
	
	move camera -baseDist + camZoom
	
	if enableXYPan or enableYRot
		show object 100
		position object 100, camBaseX, camBaseY, camBaseZ
	else
		hide object 100
	endif
endfunction

// uiAxis requires three objects with continuous id's, the first of which is the id passed to the SetupUIAxisIndicator function. Ensure objects first .. first+2 are free.
global uiAxisIndicator_first, uiAxisIndicator_last
function SetupUIAxisIndicator(id)
	uiAxisIndicator_first = id
	uiAxisIndicator_last = id+2
	make object box id,   1, 0.1, 0.1 : make object cone id+3, 0.1 : color object id+3, 0xff0000 : make mesh from object 1, id+3 : add limb id,   1, 1 : offset limb id,   1, 0.7, 0, 0 : rotate limb id,   1, 0, 0, -90 : delete mesh 1 : delete object id+3 : color object id,   0xff0000 : set object emissive id,   0xff0000 : set object cull id,   0
	make object box id+1, 0.1, 1, 0.1 : make object cone id+4, 0.1 : color object id+4, 0x00ff00 : make mesh from object 1, id+4 : add limb id+1, 1, 1 : offset limb id+1, 1, 0, 0.7, 0 : rotate limb id+1, 1, 0, 0, 0   : delete mesh 1 : delete object id+4 : color object id+1, 0x00ff00 : set object emissive id+1, 0x00ff00 : set object cull id+1, 0
	make object box id+2, 0.1, 0.1, 1 : make object cone id+5, 0.1 : color object id+5, 0x0000ff : make mesh from object 1, id+5 : add limb id+2, 1, 1 : offset limb id+2, 1, 0, 0, 0.7 : rotate limb id+2, 1, 90, 0, 0  : delete mesh 1 : delete object id+5 : color object id+2, 0x0000ff : set object emissive id+2, 0x0000ff : set object cull id+2, 0
	for n = id to id+2 : disable object zdepth n : next n
endfunction

function UpdateUIAxisIndicator()
	for n = uiAxisIndicator_first to uiAxisIndicator_last
		position object n, camera position x(), camera position y(), camera position z()
		rotate object n, camera angle x(), camera angle y(), camera angle z()
		move object n, 12 : move object left n, 9 : move object down n, 5
		rotate object n, 0, 0, 0
	next n
endfunction

function RndSign()
	s = 1 - rnd(1)*2
endfunction s



` FVF constants
#constant FVF_XYZ = 0x002
#constant FVF_XYZRHW = 0x004
#constant FVF_NORMAL = 0x010
#constant FVF_PSIZE = 0x020
#constant FVF_DIFFUSE = 0x040
#constant FVF_SPECULAR = 0x080
#constant FVF_TEX0 = 0x000
#constant FVF_TEX1 = 0x100
#constant FVF_TEX2 = 0x200
#constant FVF_TEX3 = 0x300
#constant FVF_TEX4 = 0x400
#constant FVF_TEX5 = 0x500
#constant FVF_TEX6 = 0x600
#constant FVF_TEX7 = 0x700
#constant FVF_TEX8 = 0x800



.
Posted: 23rd Jan 2014 1:37


Just for fun, I've improved the world space tripod from the example. It should be unproblematic moving it to other projects without modification (provided you need just the one).

Also made the camera base-position indicator easier to read (haven't properly formalised it though, so will need modification).

+ Code Snippet
set display mode desktop width(), desktop height(), 32, 1
sync on : sync rate 30 : sync : autocam off : color backdrop 0x485A82

global scrW, scrH
scrW = screen width() : scrH = screen height()

global mx, my, mmx, mmy, mmz as float, kLeft, kRight, kUp, kDown, camZoom as float, camBaseX as float, camBaseY as float, camBaseZ as float

SetupUiWST(1000, find free camera(), 24, scrH*0.88 - 24, 24 + scrH*0.12, scrH - 24)

// gen'd demonstration texture.
for n = 1 to 100 : box rnd(128), rnd(128), rnd(128), rnd(128), 0x0880ffff : next n
set text font "Arial" : set text size 100 : center text 64, 14, "2" : set text size 20
get image 1, 0, 0, 128, 128, 1

// example spinning boxes.
for n = 1 to 32
	sz = 7 + n/4
	MakeSegmentedBox(n, sz, sz, sz, 6, 9, 12)
	position object n, -0, 9, 0
	set object wireframe n, 1
next n
MakeSegmentedBox(33, 8, 0.1, 8, 1, 1, 1)

// example boxes-in-boxes.
MakeSegmentedBox(50,    64,    64,    64, 8, 8, 8) : position object 50,     16 + 28,     4 + 28,     0 + 28 : set object wireframe 50, 1
MakeSegmentedBox(51,     8,     8,     8, 8, 8, 8) : position object 51,          16,          4,          0 : set object wireframe 51, 1
MakeSegmentedBox(52,     1,     1,     1, 8, 8, 8) : position object 52,    16 - 3.5,    4 - 3.5,    0 - 3.5 : set object wireframe 52, 1
MakeSegmentedBox(53, 0.125, 0.125, 0.125, 8, 8, 8) : position object 53, 16 - 3.9375, 4 - 3.9375, 0 - 3.9375 : set object wireframe 53, 1

// example wavy.
MakeSegmentedBox(60, 160, 8, 32, 40, 1, 32) : position object 60, -4, -3.95, -20 : convert object fvf 60, FVF_XYZ||FVF_NORMAL||FVF_DIFFUSE
lock vertexdata for limb 60, 6					// top
	vc = get vertexdata vertex count() - 1
	for vi = 0 to vc
		vx# = get vertexdata position x(vi)` : dx# = (80.0 - abs(vx#)) / 80.0
		vz# = get vertexdata position z(vi)` : dz# = (16.0 - abs(vz#)) / 16.0
		`set vertexdata position vi, vx#, dx#*dz# * sin(vx#^2 + vz#^2) * 2.5, vz#
		set vertexdata diffuse vi, rgb(192+rnd(63), 236+rnd(15), 248+rnd(7)) ` 0xff0000ff + rnd(0x080800) ` 0xfff8f8ff + rnd(0x070700)
	next vi
unlock vertexdata
lock vertexdata for limb 60, 5					// bottom
	for vi = 0 to vc
		vx# = get vertexdata position x(vi) : dx# = (80.0 - abs(vx#)) / 80.0
		vz# = get vertexdata position z(vi) : dz# = (16.0 - abs(vz#)) / 16.0
		set vertexdata position vi, vx#, (dx#*dz#)^2.0 * -48.0, vz#
		set vertexdata diffuse vi, 0xffc0c0c0 + rnd(0x3f3f3f)
	next vi
unlock vertexdata
set object normals 60

// rainbow.
for n = 300 to 400
	sz = (n+1-300) * 0.4
	MakeSegmentedBox(n, sz, sz, sz, 1+rnd(9), 1+rnd(9), 1+rnd(9))
	position object n, 0, 20, -70
	rotate object n, rndSign()*0.25, rndSign()*0.25, rndSign()*0.25
	`set object wireframe n, 1
	set object cull n, 0
	color object n, hsv to rgb((n-300)*3.6, 1, 1)
	
	// remove limbs 3,4,5 (as they show grey rather than coloured under normal dbp lighting condition).
	remove limb n, 3	// 1 2 _3_ 4 5 6 -> 1 2 4 5 6
	remove limb n, 3	// 1 2 _4_ 5 6   -> 1 2 5 6
	remove limb n, 3	// 1 2 _5_ 6     -> 1 2 6
next n

// textured.
MakeSegmentedBox(401, 8, 8, 8, 1, 3, 9)
texture object 401, 1
position object 401, 72, 4, 0


// setup camera (with basepos indicator).
make object cone 100, 0.4 : xrotate object 100, 180 : fix object pivot 100 : offset limb 100, 0, 0, -0.24, 0 : set object cull 100, 0
make object sphere 101, 1   : color object 101, 0x808080 : set alpha mapping on 101, 20 : disable object zdepth 101
make object sphere 102, 0.9 : color object 102, 0x808080 : set alpha mapping on 102, 20
camBaseX = 0 : camBaseZ = -30


// main loop.
do
	UpdateInput()
	
	if controlkey() then box outline uiWST.viewx1, uiWST.viewy1, uiWST.viewx2, uiWST.viewy2, 0xff00ff00
	
	// update spinning boxes.
	for n = 1 to 32
		xrotate object n, object angle x(n)+0.0066 + n*0.0033
		yrotate object n, object angle y(n)+0.01 + n*0.01
		zrotate object n, object angle z(n)+0.003 + n*0.0015
	next n
	
	// [ hold right-mouse button to spin camera ]
	UpdateCamera(0, (mouseclick()>1 && mouseclick()<3), (mouseclick()>3 && mouseclick()<5), 1, 10)
	UpdateUiWST(camera angle x(0), camera angle y(0), camera angle z(0))
	
	
	// update waves.
	lock vertexdata for limb 60, 6
		vc = get vertexdata vertex count() - 1
		for vi = 0 to vc
			vx# = get vertexdata position x(vi) : dx# = (80.0 - abs(vx#)) / 80.0
			vz# = get vertexdata position z(vi) : dz# = (16.0 - abs(vz#)) / 16.0
			set vertexdata position vi, vx#, dx#*dz# * sin(hitimer()*0.1 + vx#^2 + vz#^2) * 2.5, vz#
		next vi
	unlock vertexdata
	q = q - 1 : if q =< 0 then q = 4 : set object normals 60	// [very slow, less frequently called]
	
	// update rainbow.
	for n = 300 to 400
		sc# = 100.0 + sin(hitimer()*0.1 + n*3.6) * 50.0
		scale object n, sc#, sc#, sc#
		if rnd(4) = 0
			for o = 1 to 3
				if rnd(4) = 0
					lock vertexdata for limb n, o
						vc = get vertexdata vertex count() - 1
						for vi = 0 to vc
							vx# = get vertexdata position x(vi) + rndSign()*0.1
							vy# = get vertexdata position y(vi) + rndSign()*0.1
							vz# = get vertexdata position z(vi) + rndSign()*0.1
							set vertexdata position vi, vx#, vy#, vz#
						next vi
					unlock vertexdata
				endif
			next o
		endif
		`set object normals n
	next n
	
	set cursor 0,0
	print "fps: ", screen fps()
	print "hold right-mouse and drag to spin camera. Pan with arrow keys, or mouse with mousewheel held. Mousewheel scroll zooms."
	print "controlkey: outline tripod viewport"
sync
nice wait 15
loop


function MakeSegmentedBox(objNum, w as float, h as float, d as float, wseg, hseg, dseg)
	local objNegX, objNegZ, objPosX, objPosZ, objNegY, objPosY, meshNegX, meshNegZ, meshPosX, meshPosZ, meshNegY, meshPosY
	
	// start by creating faces as a series of planes: -x, -z, +x, +z, -y, +y.
	null = reserve free object(objNum, 1)	// ensure final object's number not used.
		objNegX = find free object() : make object plane objNegX, d, h, dseg, hseg : rotate object objNegX, -90,  90, 0 : fix object pivot objNegX
		objNegZ = find free object() : make object plane objNegZ, w, h, wseg, hseg : rotate object objNegZ, -90,   0, 0 : fix object pivot objNegZ
		objPosX = find free object() : make object plane objPosX, d, h, dseg, hseg : rotate object objPosX, -90, -90, 0 : fix object pivot objPosX
		objPosZ = find free object() : make object plane objPosZ, w, h, wseg, hseg : rotate object objPosZ, -90, 180, 0 : fix object pivot objPosZ
		objNegY = find free object() : make object plane objNegY, w, d, wseg, dseg : rotate object objNegY, 180,   0, 0 : fix object pivot objNegY
		objPosY = find free object() : make object plane objPosY, w, d, wseg, dseg
	release reserved object objNum
	
	// prepare meshes for object limbs.
	meshNegX = find free mesh() : make mesh from object meshNegX, objNegX
	meshNegZ = find free mesh() : make mesh from object meshNegZ, objNegZ
	meshPosX = find free mesh() : make mesh from object meshPosX, objPosX
	meshPosZ = find free mesh() : make mesh from object meshPosZ, objPosZ
	meshNegY = find free mesh() : make mesh from object meshNegY, objNegY
	meshPosY = find free mesh() : make mesh from object meshPosY, objPosY
	
	// object has faces on limbs 1 thru 6.
	begin new object
		zerolimb = add new limb()
		add new limb from mesh meshNegX : set new limb parent zerolimb : set new limb offset -w/2.0,      0,      0 : set new limb transparent 0
		add new limb from mesh meshNegZ : set new limb parent zerolimb : set new limb offset      0,      0, -d/2.0 : set new limb transparent 0
		add new limb from mesh meshPosX : set new limb parent zerolimb : set new limb offset  w/2.0,      0,      0 : set new limb transparent 0
		add new limb from mesh meshPosZ : set new limb parent zerolimb : set new limb offset      0,      0,  d/2.0 : set new limb transparent 0
		add new limb from mesh meshNegY : set new limb parent zerolimb : set new limb offset      0, -h/2.0,      0 : set new limb transparent 0
		add new limb from mesh meshPosY : set new limb parent zerolimb : set new limb offset      0,  h/2.0,      0 : set new limb transparent 0
	finish new object objNum
	
	// cleanup.
	delete object objNegX : delete mesh meshNegX
	delete object objNegZ : delete mesh meshNegZ
	delete object objPosX : delete mesh meshPosX
	delete object objPosZ : delete mesh meshPosZ
	delete object objNegY : delete mesh meshNegY
	delete object objPosY : delete mesh meshPosY
endfunction



// -- UTILITY FUNCTIONS --
function UpdateInput()
	mx = mousex()
	my = mousey()
	mmx = mousemovex()
	mmy = mousemovey()
	mmz = mousemovez()
	kLeft = leftkey()
	kRight = rightkey()
	kUp = upkey()
	kDown = downkey()
endfunction

global oldEnableYRot as boolean, mouseStaticX, mouseStaticY
function UpdateCamera(camId, enableYRot as boolean, enableXYPan as boolean, forceYRotMouseStatic as boolean, baseDist as float)
	camZoom = min(-1.0, camZoom + mmz*0.033) `0.0066
	position camera camId, camBaseX, camBaseY, camBaseZ
	
	if enableYRot
		yrotate camera camId, camera angle y(camId)+mmx*0.2
		xrotate camera camId, clamp(camera angle x(camId)+mmy*0.2, 0, 90)
		if forceYRotMouseStatic
			if oldEnableYRot = 0 then hide mouse : mouseStaticX = mx : mouseStaticY = my
			position mouse mouseStaticX, mouseStaticY
		endif
	else
		if forceYRotMouseStatic and oldEnableYRot = 1 then show mouse
	endif
	oldEnableYRot = enableYRot

	if enableXYPan
		move camera right camId, mmx*sqrt(-camZoom)*0.02
		yrotate camera camId, camera angle y(camId)-90
		move camera right camId, -mmy*sqrt(-camZoom)*0.02
		yrotate camera camId, camera angle y(camId)+90
	endif
	
	move camera right camId, (kRight-kLeft)*0.15
	yrotate camera camId, camera angle y(camId)-90
	move camera right camId, (kUp-kDown)*0.15
	yrotate camera camId, camera angle y(camId)+90
	
	camBaseX = camera position x(camId)
	camBaseY = camera position y(camId)
	camBaseZ = camera position z(camId)
	
	move camera camId, -baseDist + camZoom
	
	// update camera base marker.
	if enableXYPan or enableYRot
		show object 100
		position object 100, camBaseX, camBaseY, camBaseZ
		scale object 100, 150 - sin(hitimer()*0.3) * 50.0, 300 + sin(hitimer()*0.3) * 200.0, 150 - sin(hitimer()*0.3) * 50.0
		show object 101
		position object 101, camBaseX, camBaseY, camBaseZ
		s# = (-baseDist+camZoom)*10.0 : scale object 101, s#, s#, s#
		show object 102
		position object 102, camBaseX, camBaseY, camBaseZ
		s# = (-baseDist+camZoom)*10.0 : scale object 102, s#, s#, s#
	else
		hide object 100
		hide object 101
		hide object 102
	endif
endfunction

// uiWST (World Space Tripod)
// uiWST requires four objects with continuous id's, the first of which is the id passed to the SetupUIAxisIndicator function. Ensure objects first .. first+3 are free.
// also requires a camera.
type t_uiWST  objFirst, objLast, camId, viewx1, viewy1, viewx2, viewy2, posx as float, posy as float, posz as float  endtype
global uiWST as t_uiWST
function SetupUiWST(id, camId, viewx1, viewy1, viewx2, viewy2)
	// IDs of meshes for bits of axis model; mesh 1 then recycled for complete axis model.
	local mesh1, mesh2, mesh3, mesh4
	
	// WST will be located out in a safe (presumably, empty) spot in space. [note: large numbers cause visible instability (float precision thing, probably)]
	uiWST.posx = -2048.0
	uiWST.posy = -2048.0
	uiWST.posz = -2048.0
	
	// [note: not really used...]
	uiWST.objFirst = id
	uiWST.objLast  = id+3
	
	// model an axis (will be x-facing).
	make object box id,    0.25, 0.025, 0.025 : mesh1 = find free mesh() : make mesh from object mesh1, id   : delete object id		// -ve facing length.
	make object box id+1,  0.4, 0.04, 0.04    : mesh2 = find free mesh() : make mesh from object mesh2, id+1 : delete object id+1		// +ve facing length.
	make object cone id+2, 0.03*0             : mesh3 = find free mesh() : make mesh from object mesh3, id+2 : delete object id+2		// +ve facing arrowhead [note: hidden].
	begin new object
		add new limb from mesh mesh1 : set new limb offset -0.25, 0, 0
		add new limb from mesh mesh2 : set new limb offset 0.25, 0, 0
		add new limb from mesh mesh3 : set new limb offset 0.7, 0, 0   : set new limb rotation 0, 0, -90
	finish new object id
	delete mesh mesh1 : delete mesh mesh2 : delete mesh mesh3
	make mesh from object mesh1, id
	delete object id
	
	// make 3 axes from the axis mesh.
	make object id,   mesh1 : color object id,   0xff0000 : set object emissive id,   0xff0000 : set object cull id,   0									// x.
	make object id+1, mesh1 : color object id+1, 0x00ff00 : set object emissive id+1, 0x00ff00 : set object cull id+1, 0 : rotate object id+1, 0, 0, 90		// y.
	make object id+2, mesh1 : color object id+2, 0x0000ff : set object emissive id+2, 0x0000ff : set object cull id+2, 0 : rotate object id+2, 0, -90, 0	// z.
	
	// make box at centre, each face axis-coloured.
	MakeSegmentedBox(id+3, 0.11, 0.11, 0.11, 1, 1, 1)
	convert object fvf id+3, FVF_XYZ||FVF_NORMAL||FVF_DIFFUSE
	set object light id+3, 0
	lock vertexdata for limb id+3, 1 : for n = 0 to 3 : set vertexdata diffuse n, 0xff0000 : next n : unlock vertexdata
	lock vertexdata for limb id+3, 2 : for n = 0 to 3 : set vertexdata diffuse n, 0x0000ff : next n : unlock vertexdata
	lock vertexdata for limb id+3, 3 : for n = 0 to 3 : set vertexdata diffuse n, 0xff0000 : next n : unlock vertexdata
	lock vertexdata for limb id+3, 4 : for n = 0 to 3 : set vertexdata diffuse n, 0x0000ff : next n : unlock vertexdata
	lock vertexdata for limb id+3, 5 : for n = 0 to 3 : set vertexdata diffuse n, 0x00ff00 : next n : unlock vertexdata
	lock vertexdata for limb id+3, 6 : for n = 0 to 3 : set vertexdata diffuse n, 0x00ff00 : next n : unlock vertexdata
	
	for n = id to id+3
		disable object zdepth n
		position object n, uiWST.posx, uiWST.posy, uiWST.posz
		set object mask n, 1 << camId
		set object radius n, -1		// [fixes flickering when posx/y/z are large]
	next n
	
	// setup camera for showing tripod.
	uiWST.camId  = camId
	uiWST.viewx1 = viewx1
	uiWST.viewy1 = viewy1
	uiWST.viewx2 = viewx2
	uiWST.viewy2 = viewy2
	
	make camera camId
	set camera aspect camId, (viewy2-viewy1)*1.0 / (viewx2-viewx1)	` square: 1.0
	set camera fov camId, 45										` 45, 61.9621391296
	set camera view camId, viewx1, viewy1, viewx2, viewy2
	set camera range camId, 0.001, 3								// close range, tripod is small.
	position camera camId, uiWST.posx, uiWST.posy, uiWST.posz
	rotate camera camId, 0, 0, 0
	backdrop off camId												// allow scene viewport to show behind rendered tripod.

	// cleanup.
	delete mesh mesh1
endfunction

function UpdateUiWST(ax#, ay#, az#)
	// update view angle.
	rotate camera uiWST.camId, ax#, ay#, az#
	position camera uiWST.camId, uiWST.posx, uiWST.posy, uiWST.posz : move camera 1, -1.7 `-2.0
	
	// label axes (3d-to-2d facilitated by shuffling object objFirst as a proxy).
	id = uiWST.objFirst
	tmp = current camera()
	set current camera uiWST.camId
	position object id, uiWST.posx+0.62, uiWST.posy, uiWST.posz : x_x = object screen x(id) : x_y = object screen y(id)
	position object id, uiWST.posx, uiWST.posy+0.62, uiWST.posz : y_x = object screen x(id) : y_y = object screen y(id)
	position object id, uiWST.posx, uiWST.posy, uiWST.posz+0.62 : z_x = object screen x(id) : z_y = object screen y(id)
	position object id, uiWST.posx, uiWST.posy, uiWST.posz
	set current camera tmp
	
	halfh = text height("gM") / 2
	fill circle x_x, x_y, halfh*1.2, 0x24000000
	fill circle y_x, y_y, halfh*1.2, 0x24000000
	fill circle z_x, z_y, halfh*1.2, 0x24000000
	center text x_x, x_y - halfh, "x"
	center text y_x, y_y - halfh, "y"
	center text z_x, z_y - halfh, "z"
endfunction

function RndSign()
	s = 1 - rnd(1)*2
endfunction s



` FVF constants
#constant FVF_XYZ = 0x002
#constant FVF_XYZRHW = 0x004
#constant FVF_NORMAL = 0x010
#constant FVF_PSIZE = 0x020
#constant FVF_DIFFUSE = 0x040
#constant FVF_SPECULAR = 0x080
#constant FVF_TEX0 = 0x000
#constant FVF_TEX1 = 0x100
#constant FVF_TEX2 = 0x200
#constant FVF_TEX3 = 0x300
#constant FVF_TEX4 = 0x400
#constant FVF_TEX5 = 0x500
#constant FVF_TEX6 = 0x600
#constant FVF_TEX7 = 0x700
#constant FVF_TEX8 = 0x800


Not entirely a tangent - the centre-cube of the tripod is made as a segmented cube so I can colour each limb/face seperately.

The tripod alone:

+ Code Snippet
// uiWST (World Space Tripod)
// uiWST requires four objects with continuous id's, the first of which is the id passed to the SetupUIAxisIndicator function. Ensure objects first .. first+3 are free.
// also requires a camera.
type t_uiWST  objFirst, objLast, camId, viewx1, viewy1, viewx2, viewy2, posx as float, posy as float, posz as float  endtype
global uiWST as t_uiWST
function SetupUiWST(id, camId, viewx1, viewy1, viewx2, viewy2)
	// IDs of meshes for bits of axis model; mesh 1 then recycled for complete axis model.
	local mesh1, mesh2, mesh3, mesh4
	
	// WST will be located out in a safe (presumably, empty) spot in space. [note: large numbers cause visible instability (float precision thing, probably)]
	uiWST.posx = -2048.0
	uiWST.posy = -2048.0
	uiWST.posz = -2048.0
	
	// [note: not really used...]
	uiWST.objFirst = id
	uiWST.objLast  = id+3
	
	// model an axis (will be x-facing).
	make object box id,    0.25, 0.025, 0.025 : mesh1 = find free mesh() : make mesh from object mesh1, id   : delete object id		// -ve facing length.
	make object box id+1,  0.4, 0.04, 0.04    : mesh2 = find free mesh() : make mesh from object mesh2, id+1 : delete object id+1		// +ve facing length.
	make object cone id+2, 0.03*0             : mesh3 = find free mesh() : make mesh from object mesh3, id+2 : delete object id+2		// +ve facing arrowhead [note: hidden].
	begin new object
		add new limb from mesh mesh1 : set new limb offset -0.25, 0, 0
		add new limb from mesh mesh2 : set new limb offset 0.25, 0, 0
		add new limb from mesh mesh3 : set new limb offset 0.7, 0, 0   : set new limb rotation 0, 0, -90
	finish new object id
	delete mesh mesh1 : delete mesh mesh2 : delete mesh mesh3
	make mesh from object mesh1, id
	delete object id
	
	// make 3 axes from the axis mesh.
	make object id,   mesh1 : color object id,   0xff0000 : set object emissive id,   0xff0000 : set object cull id,   0									// x.
	make object id+1, mesh1 : color object id+1, 0x00ff00 : set object emissive id+1, 0x00ff00 : set object cull id+1, 0 : rotate object id+1, 0, 0, 90		// y.
	make object id+2, mesh1 : color object id+2, 0x0000ff : set object emissive id+2, 0x0000ff : set object cull id+2, 0 : rotate object id+2, 0, -90, 0	// z.
	
	// make box at centre, each face axis-coloured.
	MakeSegmentedBox(id+3, 0.11, 0.11, 0.11, 1, 1, 1)
	convert object fvf id+3, FVF_XYZ||FVF_NORMAL||FVF_DIFFUSE
	set object light id+3, 0
	lock vertexdata for limb id+3, 1 : for n = 0 to 3 : set vertexdata diffuse n, 0xff0000 : next n : unlock vertexdata
	lock vertexdata for limb id+3, 2 : for n = 0 to 3 : set vertexdata diffuse n, 0x0000ff : next n : unlock vertexdata
	lock vertexdata for limb id+3, 3 : for n = 0 to 3 : set vertexdata diffuse n, 0xff0000 : next n : unlock vertexdata
	lock vertexdata for limb id+3, 4 : for n = 0 to 3 : set vertexdata diffuse n, 0x0000ff : next n : unlock vertexdata
	lock vertexdata for limb id+3, 5 : for n = 0 to 3 : set vertexdata diffuse n, 0x00ff00 : next n : unlock vertexdata
	lock vertexdata for limb id+3, 6 : for n = 0 to 3 : set vertexdata diffuse n, 0x00ff00 : next n : unlock vertexdata
	
	for n = id to id+3
		disable object zdepth n
		position object n, uiWST.posx, uiWST.posy, uiWST.posz
		set object mask n, 1 << camId
		set object radius n, -1		// [fixes flickering when posx/y/z are large]
	next n
	
	// setup camera for showing tripod.
	uiWST.camId  = camId
	uiWST.viewx1 = viewx1
	uiWST.viewy1 = viewy1
	uiWST.viewx2 = viewx2
	uiWST.viewy2 = viewy2
	
	make camera camId
	set camera aspect camId, (viewy2-viewy1)*1.0 / (viewx2-viewx1)	` square: 1.0
	set camera fov camId, 45										` 45, 61.9621391296
	set camera view camId, viewx1, viewy1, viewx2, viewy2
	set camera range camId, 0.001, 3								// close range, tripod is small.
	position camera camId, uiWST.posx, uiWST.posy, uiWST.posz
	rotate camera camId, 0, 0, 0
	backdrop off camId												// allow scene viewport to show behind rendered tripod.

	// cleanup.
	delete mesh mesh1
endfunction

function UpdateUiWST(ax#, ay#, az#)
	// update view angle.
	rotate camera uiWST.camId, ax#, ay#, az#
	position camera uiWST.camId, uiWST.posx, uiWST.posy, uiWST.posz : move camera 1, -1.7 `-2.0
	
	// label axes (3d-to-2d facilitated by shuffling object objFirst as a proxy).
	id = uiWST.objFirst
	tmp = current camera()
	set current camera uiWST.camId
	position object id, uiWST.posx+0.62, uiWST.posy, uiWST.posz : x_x = object screen x(id) : x_y = object screen y(id)
	position object id, uiWST.posx, uiWST.posy+0.62, uiWST.posz : y_x = object screen x(id) : y_y = object screen y(id)
	position object id, uiWST.posx, uiWST.posy, uiWST.posz+0.62 : z_x = object screen x(id) : z_y = object screen y(id)
	position object id, uiWST.posx, uiWST.posy, uiWST.posz
	set current camera tmp
	
	halfh = text height("gM") / 2
	fill circle x_x, x_y, halfh*1.2, 0x24000000
	fill circle y_x, y_y, halfh*1.2, 0x24000000
	fill circle z_x, z_y, halfh*1.2, 0x24000000
	center text x_x, x_y - halfh, "x"
	center text y_x, y_y - halfh, "y"
	center text z_x, z_y - halfh, "z"
endfunction

.