3D Bezier Curve

Blitz3D Forums/Blitz3D Programming/3D Bezier Curve

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

edit: I've worked through the problems I originally posted about, and of course have some new ones :)

move the white points around (click and drag) and see if you can give me a hint why its doubling up the points when my code only creates 4


setcolor.bb:
Global SetColorTexture = LoadTexture("purewhite.png")
Global SetColorBrush = CreateBrush(255,255,255)
BrushAlpha SetColorBrush, 1
BrushShininess SetColorBrush, 0
BrushTexture SetColorBrush, SetColorTexture


Function SetColor(mesh,r,g,b)
	For k=1 To CountSurfaces(mesh)
		PaintSurface(GetSurface(mesh,k), SetColorBrush)
	Next
	For k=1 To CountChildren(mesh)
		SetColor(GetChild(mesh, k), r,g,b)
	Next
	EntityColor mesh, r,g,b
End Function


bez.bb:
Type bez
	Field x#, y#, z#	; point coords
	Field pa#, ya#		; pitch and yaw of control vector
	Field len#			; length of control vector
	Field sec%			; sections to next point
	Field pt			; point entity (sphere)
	Field cyl			; control line (cylinder)
	Field cp1, cp2		; fwd and back control points
	Field piv			; pivot containing curve segments
End Type

Function BezierNew.bez(x#,y#,z#,pa#,ya#,Len#)
	tmp.bez = New bez
	tmp\x = x
	tmp\y = y
	tmp\z = z
	tmp\pa = pa
	tmp\ya = ya
	tmp\Len = Len
	tmp\pt = 0
	tmp\cyl = 0
	tmp\cp1 = 0
	tmp\cp2 = 0
	tmp\piv = 0
	tmp\sec = 8
	Return tmp;	
End Function

Function BezierAdd.bez(blist.bez, b.bez)
	blist.bez = New bez
	blist\x = b\x
	blist\y = b\y
	blist\z = b\z
	blist\pa = b\pa
	blist\ya = b\ya
	blist\Len = b\Len
	blist\sec = b\sec
	Return blist
End Function

Function BezierInit()
	spline.bez = Last bez
	pt2.bez = spline.bez
	If pt2\pt = 0 
		BezierCreatePoint(pt2)
	EndIf
	For spline.bez = Each bez
		pt1.bez = pt2.bez
		pt2.bez = spline.bez
		If pt2\pt = 0 
			BezierCreatePoint(pt2)
		EndIf
		BezierUpdateSegment(pt1,pt2)
	Next
End Function

Function IsBezierPoint(ent)
	For spline.bez = Each bez
		If spline\pt = ent Return True
	Next
	Return False
End Function

Function BezierListIndex(ent)
	ndx = 0
	For spline.bez = Each bez
		If spline\pt = ent 
			Return ndx
		EndIf
		If spline\cp1 = ent 
			Return ndx
		EndIf
		If spline\cp2 = ent 
			Return ndx
		EndIf
		ndx = ndx + 1
	Next
	Return -1
End Function

Function BezierControlIndex(ent)
	For spline.bez = Each bez
		If spline\cp1 = ent 
			Return 1
		EndIf
		If spline\cp2 = ent 
			Return 2
		EndIf
	Next
	Return -1
End Function

Function BezierMovePoint(ndx, x#, y#, z#)
	cnt = 0
	spline.bez = Last bez
	pre.bez = spline.bez
	For spline.bez = Each bez
		If cnt = ndx
			; update point
			spline\x = spline\x + x
			spline\y = spline\y + y
			spline\z = spline\z + z
			BezierUpdatePoint(spline.bez)
			cur.bez = spline.bez
			spline = After spline
			If spline = Null
				spline.bez = First bez
			EndIf
			BezierUpdateSegment(pre, cur)
			BezierUpdateSegment(cur, spline.bez)
			Return
		EndIf
		pre.bez = spline.bez
		cnt = cnt + 1
	Next
End Function

Function BezierMoveControl(ndx, ctrl, x#, y#, z#)
	cnt = 0
	spline.bez = Last bez
	pre.bez = spline.bez
	For spline.bez = Each bez
		If cnt = ndx
			; update control
			If ctrl = 1
				; update cp1
				x = x + EntityX(spline\cp1)
				y = y + EntityY(spline\cp1)
				z = z + EntityZ(spline\cp1)
				MoveEntity(spline\cp1, x, y, z)
				nx# = EntityX(spline\cp1)
				ny# = EntityY(spline\cp1)
				nz# = EntityZ(spline\cp1)
				p# = VectorPitch(nx,ny,nz)
				y# = VectorYaw(nx,ny,nz)
				d# = Sqr(nx*nx + ny*ny + nz*nz)
				spline\pa = p
				spline\ya = y + 90.0
				spline\Len = d
			Else 
				; update cp2
				x = x + EntityX(spline\cp2)
				y = y + EntityY(spline\cp2)
				z = z + EntityZ(spline\cp2)
				MoveEntity(spline\cp2, x, y, z)
				nx# = -EntityX(spline\cp2)
				ny# = -EntityY(spline\cp2)
				nz# = -EntityZ(spline\cp2)
				p# = VectorPitch(nx,ny,nz)
				y# = VectorYaw(nx,ny,nz)
				d# = Sqr(nx*nx + ny*ny + nz*nz)
				spline\pa = p
				spline\ya = y + 90.0
				spline\Len = d
			EndIf
			BezierUpdatePoint(spline.bez)
			cur.bez = spline.bez
			spline = After spline
			If spline = Null
				spline.bez = First bez
			EndIf
			BezierUpdateSegment(pre, cur)
			BezierUpdateSegment(cur, spline.bez)
			Return
		EndIf
		pre.bez = spline.bez
		cnt = cnt + 1
	Next
End Function

Function BezierCreatePoint(pt.bez)
	pt\pt = CreateSphere(8)
	ScaleMesh(pt\pt,0.07,0.07,0.07)
	EntityPickMode pt\pt, 2
	
	pt\cyl = CreateCylinder(8,1,pt\pt)
	ScaleMesh(pt\cyl,0.02,1,0.02)

	pt\cp1 = CreateSphere(8,pt\pt)
	ScaleMesh(pt\cp1,0.05,0.05,0.05)
	EntityPickMode pt\cp1, 2
	pt\cp2 = CreateSphere(8,pt\pt)
	ScaleMesh(pt\cp2,0.05,0.05,0.05)
	EntityPickMode pt\cp2, 2

	SetColor(pt\pt, 255, 255, 255)
	SetColor(pt\cp1, 255, 255, 0)
	SetColor(pt\cp2, 255, 0, 255)
	SetColor(pt\cyl, 0, 255, 0)
	EntityFX pt\pt,5
	
	pt\piv = CreatePivot(pt\pt)
	For i=1 To pt\sec
		sec = CreateCylinder(8,1,pt\piv)
		SetColor(sec, 0, 0, 255)
		ScaleMesh(sec,0.02,1,0.02)
	Next

	BezierUpdatePoint(pt)
End Function

Function BezierUpdatePoint(pt.bez)
	RotateEntity(pt\cyl, pt\pa, pt\ya, 90)
	ScaleEntity(pt\cyl,1,pt\Len/2.0,1)
	cpz# = (pt\Len/2.0) * Sin(pt\ya) * Cos(pt\pa)
	cpy# = (pt\Len/2.0) * Sin(pt\pa)
	cpx# = (pt\Len/2.0) * Cos(pt\ya) * Cos(pt\pa)
	PositionEntity(pt\cp1, cpx#, cpy#, cpz#)
	PositionEntity(pt\cp2, -cpx#, -cpy#, -cpz#)
	PositionEntity(pt\pt, pt\x, pt\y, pt\z)
End Function

Function BezierUpdateSegment(pt1.bez, pt2.bez)
	
	Local lx#, ly#, lz#			; last point coords
	Local inc#, t#, sec%		; loop control
	Local nt#, nt2#, nt3#		; precomputed (1-t) factors
	Local t2#, t3#				; precomputed (t) factors
	Local p1px#, p1py#, p1pz#	; pt1 x/y/z
	Local p1cx#, p1cy#, p1cz#	; pt1 control x/y/z
	Local p2px#, p2py#, p2pz#	; pt2 x/y/z
	Local p2cx#, p2cy#, p2cz#	; pt2 control x/y/z
	Local x#, y#, z#			; bezier results
	Local dx#, dy#, dz#			; distance from last point to current
	Local dist#					; distance vector magnitude
	Local pit#, yaw#			; pitch and yaw of segment
	Local px#, py#, pz#			; centerpoint of current segment
	Local nx#, ny#, nz#			; center point relative to base point
	
	DebugLog ""
	DebugLog "starting " + pt1\pt + " " + pt2\pt
	
	lx = pt1\x
	ly = pt1\y
	lz = pt1\z
	
	inc = 1.0 / pt1\sec
	t = inc
	sec = 1
	While t<=1.0
		
		nt = 1.0-t
		nt2 = nt * nt
		nt3 = nt2 * nt
		t2 = t * t
		t3 = t2 * t
		
		p1px = pt1\x
		p1py = pt1\y
		p1pz = pt1\z
		p1cx = EntityX(pt1\cp1) + p1px
		p1cy = EntityY(pt1\cp1) + p1py
		p1cz = EntityZ(pt1\cp1) + p1pz
		;DebugLog "Pt1(x,y,z) Pc1(x,y,z) = " +p1px+" "+p1py+" "+p1pz+"      "+p1cx+" "+p1cy+" "+p1cz

		p2px = pt2\x
		p2py = pt2\y
		p2pz = pt2\z
		p2cx = EntityX(pt2\cp2) + p2px
		p2cy = EntityY(pt2\cp2) + p2py
		p2cz = EntityZ(pt2\cp2) + p2pz
		;DebugLog "Pt2(x,y,z) Pc2(x,y,z) = " +p2px+" "+p2py+" "+p2pz+"      "+p2cx+" "+p2cy+" "+p2cz

		x = p1px*nt3 + 3.0*p1cx*nt2*t + 3.0*p2cx*nt*t2 + p2px*t3
		y = p1py*nt3 + 3.0*p1cy*nt2*t + 3.0*p2cy*nt*t2 + p2py*t3
		z = p1pz*nt3 + 3.0*p1cz*nt2*t + 3.0*p2cz*nt*t2 + p2pz*t3
		;DebugLog "t x y z = " + t + "      " + x + " " + y + " " + z		
		
		dx = x - lx
		dy = y - ly
		dz = z - lz
		
		;DebugLog "dx dy dz = " + dx + " " + dy + " " + dz
		
		dist = Sqr(dx*dx + dy*dy + dz*dz)
		
		pit = VectorPitch(dx,dy,dz)
		yaw = VectorYaw(dx,dy,dz)
		
		;DebugLog "dist / pitch / yaw = " + dist + " / " + pit + " / " + yaw

		px = lx + dx/2.0
		py = ly + dy/2.0
		pz = lz + dz/2.0
		
		;DebugLog "px py pz = " + px + " " + py + " " + pz
		
		nx = px-p1px
		ny = py-p1py
		nz = pz-p1pz
		
		;DebugLog "sec / nx ny nz = " + sec + " / " + nx + " " + ny + " " + nz
		
		seg = GetChild(pt1\piv, sec)
		PositionEntity(seg, nx, ny, nz)
		ScaleEntity(seg, 1, dist/2.0, 1)
		RotateEntity(seg, pit+90, yaw, 0)
		
		lx=x
		ly=y
		lz=z
		
		t = t + inc
		sec = sec + 1
		
	Wend	

End Function



bezier.bb:
;**************************************************
;*     3D Bezier Spline by John Barrett 2004      *
;**************************************************
;*       Email: john@...         *
;**************************************************
;*    Based on Jeppe Nielson's 2D Bezier code     *
;*       Based on Wedoe´s bezier function         *
;**************************************************

Graphics3D 800,600,16,2
SetBuffer BackBuffer()

; Setup the lighting...
AmbientLight 255,255,255

; CAMERA PIVOT
Global camera_pivot= CreatePivot()
EntityRadius camera_pivot,0.1

; Setup the camera.
Global camera = CreateCamera(camera_pivot)

PositionEntity(camera_pivot, 0, 0, 0)
PositionEntity(camera, 0, 10, 0)
RotateEntity(camera, 90,0,0)

Color 255, 255, 255

Include "setcolor.bb"
Include "bez.bb"

Global spline.bez

pt1.bez = BezierNew(0,0,-1,0,0,1.5)
pt2.bez = BezierNew(1,0,0,0,90,1.5)
pt3.bez = BezierNew(0,0,1,0,180,1.5)
pt4.bez = BezierNew(-1,0,0,0,270,1.5)
BezierAdd(spline,pt1)
BezierAdd(spline,pt2)
BezierAdd(spline,pt3)
BezierAdd(spline,pt4)

BezierInit()

; mouse x to view translation matrix
		mxtx = 1: mxty = 0: mxtz = 0
; mouse y to view translation matrix
		mytx = 0: myty = 0: mytz = -1

curbpt = -1
curcpt = -1

Repeat
	If KeyHit(33) ; f=front
		PositionEntity(camera, 0, 0, -10)
		RotateEntity(camera, 0,0,0)
		mxtx = 1: mxty = 0: mxtz = 0
		mytx = 0: myty = -1: mytz = 0
	EndIf
	If KeyHit(38) ; l=left
		PositionEntity(camera, -10, 0, 0)
		RotateEntity(camera, 0,270,0)
		mxtx = 0: mxty = 0: mxtz = -1
		mytx = 0: myty = -1: mytz = 0
	EndIf
	If KeyHit(19) ; r=right 
		PositionEntity(camera, 10, 0, 0)
		RotateEntity(camera, 0,90,0)
		mxtx = 0: mxty = 0: mxtz = 1
		mytx = 0: myty = -1: mytz = 0
	EndIf
	If KeyHit(20) ; t=top
		PositionEntity(camera, 0, 10, 0)
		RotateEntity(camera, 90,0,0)
		mxtx = 1: mxty = 0: mxtz = 0
		mytx = 0: myty = 0: mytz = -1
	EndIf
	
	If MouseDown(1)
		If curbpt = -1 And curcpt = -1
			; find out what is being clicked
			mx = MouseX()
			my = MouseY()
			ent = CameraPick(camera, mx, my)
			ent = PickedEntity()
			If ent <> 0
				If IsBezierPoint(ent)
					curbpt = BezierListIndex(ent)
				Else
					curbpt = BezierListindex(ent)
					curcpt = BezierControlIndex(ent)
				EndIf
				; setup to track movement next frame
				MouseXSpeed()
				MouseYSpeed()
			EndIf
		Else
			; move currently clicked entity
			mx = MouseXSpeed()
			my = MouseYSpeed()
			tx = mxtx * mx + mytx * my
			ty = mxty * mx + myty * my
			tz = mxtz * mx + mytz * my
			If curcpt = -1
				; moving point
				BezierMovePoint(curbpt, tx*0.025, ty*0.025, tz*0.025)
			Else
				; moving control
				BezierMoveControl(curbpt, curcpt, tx*0.025, ty*0.025, tz*0.025)
			EndIf
		EndIf
	Else
		curbpt = -1
		curcpt = -1
	EndIf
		
	RenderWorld
	Flip
Until KeyDown(1)
End