My simple 3D Z issue

BlitzMax Forums/BlitzMax Programming/My simple 3D Z issue

Ryan Burnside(Posted 2006) [#1]
Ok so I made a simple pseudo 3d plotting engine. This is not true human eye 3D but is based on the "one vanishing point" method of projection. If you draw a cube in this perspective the face is compleatly visable but the sides skew to point towards the vanashing point.

My engine calculates the x and y coordinate based on the x,y,z point. It then plots it. Any z point with a value greater than 0 is plotted. I thought I had it working 100% but then I noticed that points with a z value lower than the camera get plotted in reverse and the z value gets multiplied by -1.

This is a huge problem because nothing can be higher than the camera.

I'll include some quick shots and the source.

A cylindar with a positive z value( works GREAT!)





^The very same rings with a z of -2 (above the projection plane)

Now for the source. (Just copy and paste into the editor of your choice.)

 Strict
    graphics 640,480
    SeedRnd(MilliSecs()) 
    'now we make a some objects to demonsrtate the data
    Type spiral
    Global spiral_list:TList = New TList
    Field x#,y#,z#,direction#
    Function Create(x#,y#,z#)
    	Local temp:spiral= New spiral
    	temp.x =x
    	temp.y =y
    	temp.z = z
    	temp.direction = Rand(360)
    	
    	' make the first rotation
    
    	ListAddLast(spiral_list,temp)
    End Function 
    Method Update()
    
    ' draw each point
    	For Local j=0 To 35
    	'now we find the x and y of the point in question
    	
    	'first circle of points
    	Local x1#=plot_3D_x(x+Cos(j*10)*128,y+Sin(j*10)*128,z)
    	Local x2#=plot_3D_x(x+Cos(j*10+10)*128,y+Sin(j*10+10)*128,z)
    	Local y1#=plot_3D_Y(x+Cos(j*10)*128,y+Sin(j*10)*128,z)
    	Local y2#=plot_3D_Y(x+Cos(j*10+10)*128,y+Sin(j*10+10)*128,z)
    	SetColor 255,255,0
    	DrawLine(x1,y1,x2,y2)
    	' second circle
    	Local x3#=plot_3D_x(x+Cos(j*10)*128,y+Sin(j*10)*128,z+.5)
    	Local x4#=plot_3D_x(x+Cos(j*10+10)*128,y+Sin(j*10+10)*128,z+.5)
    	Local y3#=plot_3D_Y(x+Cos(j*10)*128,y+Sin(j*10)*128,z+.5)
    	Local y4#=plot_3D_Y(x+Cos(j*10+10)*128,y+Sin(j*10+10)*128,z+.5)
    	SetColor 255,0,0
    	DrawLine(x3,y3,x4,y4)
    	
    	' use the points of both circles together to make virtual sides
    	SetColor 255,128,0
    	DrawLine(x1,y1,x3,y3)
    	DrawLine(x2,y2,x3,y3)
    	SetBlend LIGHTBLEND
    	SetAlpha .5
    	
        
    	Local points:Float[]=[x1,y1,x2,y2,x4,y4,x3,y3]
    	DrawPoly(points)
    		SetBlend SOLIDBLEND
    	SetAlpha 1	
    	Next
    
   
    
    
     
    
    
   
    
    
    End Method
    Method Destroy()
    	ListRemove(spiral_list,Self)
    End Method
    
    EndType
    Type point
    Field x#,y#,z#
    EndType
    
    Function point_distance(x1#,y1#,x2#,y2#)

' get the x difference
Local x_distance#= x1-x2

' get the y difference
Local y_distance#= y1-y2

' find the difference using pythagroin therom
Local distance#=sqr((x_distance*x_distance)+(y_distance*y_distance))

Return distance
EndFunction
    Function point_direction#(x1#,y1#,x2#,y2#)
	    
	    Local Angle# = ATan2(y2-y1,x2-x1)
	    	If angle<0
	    		angle:+ 360
	    	End If
		
		Return angle
	EndFunction 
	' this plots one point 
	Function plot_3D(x3#,y3#,z3#) 
    Local direction#=point_direction(x3,y3,320,240)
    Local distance#= point_distance(x3,y3,320,240)
   	Local new_x#=320+Cos(direction+180)*((distance/z3))
    Local new_y#=240+Sin(direction+180)*((distance/z3))
    Plot new_x,new_y
    End Function
    'like the previous function except it finds the y coordinate
    Function plot_3D_Y#(x3#,y3#,z3#) 
    Local direction#=point_direction(x3,y3,320,240)
    Local distance#= point_distance(x3,y3,320,240)
   	
    Local new_y#=240+Sin(direction+180)*((distance/z3))
    Return new_y
    End Function 
    'like the previous function except it finds the x coordinate
    Function plot_3D_x#(x3#,y3#,z3#) 
    Local direction#=point_direction(x3,y3,320,240)
    Local distance#= point_distance(x3,y3,320,240)
   	Local new_x#=320+Cos(direction+180)*((distance/z3))
    
    Return new_x
    End Function
    
    Local angle=0 
    ' make a neat circle of varying z values
    For Local i=0 To 9
    	spiral.Create(320+Sin(i*36)*128,240+Cos(i*36)*128,Rand(3))
    Next
    While not KeyDown(KEY_ESCAPE)
    Cls 
    Plot 320,240
    
    
   
        ' update our spirals
    	For Local f:spiral =EachIn(spiral.spiral_list)
    		f.Update()
    	Next
    	
    
    	Flip
    Wend



neilo(Posted 2006) [#2]
You need to implement z clipping of your polygons.

Basically, when you start getting negative Z values, the maths you're using generates what's called a singularity right in the middle of the screen. As you move through your tubes, you would find a reflection of them converging to the center of the screen.

There's no simple fix with your code. An approach I tried some time ago was to not draw any polygon with a negative z ordinate, and this solved the problem. It introduced other visual problems as polygons simply winked out of existance.

My ultimate solution was to buy Andre LaMonth's Secrets of the 3D Game Programming Guru's and work through his polygon clipping code. Be warned: this is a massive book, and will take you some time to work through.

If you're looking for an answer right now, have a look at the MiniB3D things that's floating around. I haven't tried it myself, but there's plenty of good things being said.

Neil