Mixing 3D and 2D in Blitz

BlitzMax Forums/OpenGL Module/Mixing 3D and 2D in Blitz

LeisureSuitLurie(Posted 2005) [#1]
Suppose I wanted to make a street fighter type game where most of the graphics are 2d sprites, but I wanted a real 3d floor/ceiling. Is there a way to mix an OpenGL perspective projection and then go back to the regular Blitz one?

Anyone done this yet?


ImaginaryHuman(Posted 2005) [#2]
I'm not exactly sure on the internals of Max2D but I always thought that the 2d stuff is always drawn at a fixed Z coordinate, like 1.0 or something, for `normal size`. Maybe there already is a perspective projection in place, you just don't notice it because you don't move things in Z or rotate around the X and Y axes?

If not it should be simple to set up - initialize the matrix with the projection matrix and draw the floor stuff then set it back and draw the `2D` stuff? I'm thinking that technically you wouldn't even need to switch back and forth between perspective and some other mode, given that 2d stuff is just quads that face the camera with a fixed Z position?


LeisureSuitLurie(Posted 2005) [#3]
Which is what I imagined to be the case, but I could not get it to work.

OTOH, I am a complete GLnewbie.


taxlerendiosk(Posted 2005) [#4]
Seems likely it's orthographic, not perspective, or there'd probably be some distortion towards the edges.


Kalakian(Posted 2005) [#5]
Hmmm, I tried replacing the bglCreateContext() with a call to Graphics(), and I got both my 3D and 2D showing. It ran really slowly, and the 2D wasn't positioned correctly, but its a start.

Its maybe worth playing about with this to find out exactly how and where the 2D is being drawn.


Kalakian(Posted 2005) [#6]
I've got it kind of working now, but hopefully it'll be enough for someone else to get it working properly :D

Before calling bglCreateContext(), call Graphics(). This will allow both the 2D and 3D to displayed, without the slowdown. You also need to make sure GL_CULL_FACE is not enabled. I haven't looked into how to switch this off yet, so I just haven't enabled it in the first place. However, a better method would be to enable it before drawing the 3D, then disable it before drawing the 2D.

Before drawing your 3d each frame, you will have to reset the camera as you will be changing it to get the 2D positioned correctly.
This should be something like:
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45.0, Float(800)/Float(600), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)


After drawing the 3D, set the camera and model translation as follows:
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, (Float(slx)/Float(sly))*1.045, 600.0, 700.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(-397,284,-686.5)
glRotatef(180,1.0,0.0,0.0)

The reason for rotating the object 190 round the x-axis is so that the origin is at top-left of screen instead of bottom-left.

I've modified the GLTutorial to add 2D to it as follows:
Strict

'Import "defines.bmx"
Global slx 	= 800	' Screen (or window) Width
Global sly 	= 600	' Screen (or window) Height
Global depth 	= 32	' 16-Bit, or 32-Bit

Local isDebug:Byte = False
?Debug
	isDebug = True
?
If isDebug
	Graphics(slx,sly,0)
	bglCreateContext(slx,sly,depth,0,BGL_BACKBUFFER|BGL_DEPTHBUFFER)
Else
	Graphics(slx,sly,depth,60)
	bglCreateContext(slx,sly,depth,0,BGL_BACKBUFFER|BGL_DEPTHBUFFER|BGL_FULLSCREEN)
EndIf

Global moveabout:float=-5.0
Global direction:float=0
Global anglex:Float=0
Global angley:Float=0
Global up:tImage

Type tCamera
	Field rotx:Float
	Field roty:Float
	Field rotz:Float
	Field tranx:Float
	Field trany:Float
	Field tranz:Float
End Type

Global cam:tCamera=New tCamera

Type tObject
	Field rotx:Float
	Field roty:Float
	Field rotz:Float
	Field tranx:Float
	Field trany:Float
	Field tranz:Float
	Field scalex:Float
	Field scaley:Float
	Field scalez:Float
	Field offsetx:Float
	Field offsety:Float
	Field offsetz:Float
End Type

Global obj:tObject[5]
For Local i = 0 To 4
	obj[i] = New tObject
Next

Init()

While Not KeyDown(KEY_ESCAPE)

	' Clear the buffers
	glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
	
	Tick()
	Render() ' Render to the backbuffer
	
	bglSwapBuffers ' Instead of Flip
	'Flip

Wend 

End

Function Init()
	cam.tranz=-12
	cam.rotx=20
	cam.trany=-1
	glClearColor(0.0, 0.0, 0.0, 0.0)
	glClearDepth(1.0)
	'glEnable(GL_CULL_FACE)
	glEnable(GL_DEPTH_TEST)
	glDepthFunc(GL_LEQUAL)
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	gluPerspective(45.0, Float(slx)/Float(sly), 0.1, 100.0)
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()
	
	up = LoadImage("../res/gfx/up.png")
EndFunction

Function Render()
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	gluPerspective(45.0, Float(slx)/Float(sly), 0.1, 100.0)
	glMatrixMode(GL_MODELVIEW)
	
	glLoadIdentity()

	glTranslatef(cam.tranx,cam.trany,cam.tranz)
	glRotatef(cam.rotx,1.0,0.0,0.0)
	glRotatef(cam.roty,0.0,1.0,0.0)
	glRotatef(cam.rotz,0.0,0.0,1.0)
	'glTranslatef(0,0,-15.0)
	'glRotatef(anglex,1.0,0.0,0.0)
	'glRotatef(angley,0.0,1.0,0.0)

	' ** Tank Body
	glPushMatrix()

	glTranslatef(obj[0].tranx+obj[0].offsetx, obj[0].trany+obj[0].offsety, obj[0].tranz+obj[0].offsetz)

	glRotatef(obj[0].rotx, 1.0, 0, 0)
	glRotatef(obj[0].roty, 0, 1.0, 0)
	glRotatef(obj[0].rotz, 0, 0, 1.0)

	glBegin(GL_QUADS)

		glColor3f(0.3,0.8,0.2)		' Back Face
		glVertex3f(4.0, 1.0, -2.0)
		glVertex3f(3.0, -1.0, -2.0)
		glVertex3f(-3.0, -1.0, -2.0)
		glVertex3f(-4.0, 1.0, -2.0)

		glColor3f(0.3,0.8,0.2)		' Front Face
		glVertex3f(-4.0, 1.0, 2.0)
		glVertex3f(-3.0, -1.0, 2.0)
		glVertex3f(3.0, -1.0, 2.0)
		glVertex3f(4.0, 1.0, 2.0)

		glColor3f(0.3, 0.9,0.3)		' Left Face
		glVertex3f(-4.0, 1.0, -2.0)
		glVertex3f(-3.0, -1.0, -2.0)
		glVertex3f(-3.0, -1.0, 2.0)
		glVertex3f(-4.0, 1.0, 2.0)

		glColor3f(0.3, 0.9,0.3)		' Right Face
		glVertex3f(4.0, 1.0, 2.0)
		glVertex3f(3.0, -1.0, 2.0)
		glVertex3f(3.0, -1.0, -2.0)
		glVertex3f(4.0, 1.0, -2.0)

		glColor3f(0.3, 1.0,0.2)		' Top Face
		glVertex3f(-4.0, 1.0, -2.0)
		glVertex3f(-4.0, 1.0, 2.0)
		glVertex3f(4.0, 1.0, 2.0)
		glVertex3f(4.0, 1.0, -2.0)

		glColor3f(0.3, 1.0,0.2)		' Bottom Face
		glVertex3f(-3.0, -1.0, 2.0)
		glVertex3f(-3.0, -1.0, -2.0)
		glVertex3f(3.0, -1.0, -2.0)
		glVertex3f(3.0, -1.0, 2.0)

	glEnd()
	glPopMatrix()
	
	' Turret Section
	glPushMatrix()
	glTranslatef(obj[1].tranx+obj[1].offsetx, obj[1].trany+obj[1].offsety, obj[1].tranz+obj[1].offsetz)

	glRotatef(obj[1].rotx, 1.0, 0, 0)
	glRotatef(obj[1].roty, 0, 1.0, 0)
	glRotatef(obj[1].rotz, 0, 0, 1.0)

	glBegin(GL_QUADS)

		glColor3f(0.3,0.8,0.2)		' Back Face
		glVertex3f(-4.0, 1.0, -2.0)
		glVertex3f(-2.5, 2.0, -1.5)
		glVertex3f(0.5, 2.0, -1.5)
		glVertex3f(2.5, 1.0, -2.0)

		glColor3f(0.3,0.8,0.2)		' Front Face
		glVertex3f(2.5, 1.0, 2.0)
		glVertex3f(0.5, 2.0, 1.5)
		glVertex3f(-2.5, 2.0, 1.5)
		glVertex3f(-4.0, 1.0, 2.0)

		glColor3f(0.3, 0.9,0.3)		' Left Face
		glVertex3f(-4.0, 1.0, 2.0)
		glVertex3f(-2.5, 2.0, 1.5)
		glVertex3f(-2.5, 2.0, -1.5)
		glVertex3f(-4.0, 1.0, -2.0)

		glColor3f(0.3, 0.9,0.3)		' right Face
		glVertex3f(0.5, 2.0, 1.5)
		glVertex3f(2.5, 1.0, 2.0)
		glVertex3f(2.5, 1.0, -2.0)
		glVertex3f(0.5, 2.0, -1.5)

		glColor3f(0.3, 1.0,0.1)		' top Face
		glVertex3f(0.5, 2.0, -1.5)
		glVertex3f(-2.5, 2.0, -1.5)
		glVertex3f(-2.5, 2.0, 1.5)
		glVertex3f(0.5, 2.0, 1.5)
	glEnd()
	glPopMatrix()
	
	' Gun
	glPushMatrix()
	glTranslatef(obj[2].tranx+obj[2].offsetx, obj[2].trany+obj[2].offsety, obj[2].tranz+obj[2].offsetz)

	glRotatef(obj[1].rotx, 1.0, 0, 0)
	glRotatef(obj[1].roty, 0, 1.0, 0)
	glRotatef(obj[1].rotz, 0, 0, 1.0)

	glBegin(GL_QUADS)

		glColor3f(0.3,0.8,0.2)		' Back Face
		glVertex3f(0.0, 1.2, -0.5)
		glVertex3f(0.0, 1.7, -0.5)
		glVertex3f(4.0, 1.7, -0.5)
		glVertex3f(4.5, 1.2, -0.5)

		glColor3f(0.3,0.8,0.2)		' Front Face
		glVertex3f(4.5, 1.2, 0.5)
		glVertex3f(4.0, 1.7, 0.5)
		glVertex3f(0.0, 1.7, 0.5)
		glVertex3f(0.0, 1.2, 0.5)

		glColor3f(0.3,0.7,0.3)		' Top Face
		glVertex3f(4.0, 1.7, 0.5)
		glVertex3f(4.0, 1.7, -0.5)
		glVertex3f(0.0, 1.7, -0.5)
		glVertex3f(0.0, 1.7, 0.5)

		glColor3f(0.3,0.7,0.2)		' Bottom Face
		glVertex3f(0.0, 1.2, 0.5)
		glVertex3f(0.0, 1.2, -0.5)
		glVertex3f(4.5, 1.2, -0.5)
		glVertex3f(4.5, 1.2, 0.5)

	glEnd()

	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	gluPerspective(45, (Float(slx)/Float(sly))*1.045, 600.0, 700.0)
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity()
	glTranslatef(-397,284,-686.5)
	glRotatef(180,1.0,0.0,0.0)

	SetColor(255,255,255)
	DrawRect(0,0,slx/2,sly/2)
EndFunction

Function Tick()
	
	obj[0].roty:+0.01
	If obj[0].roty>360
		obj[0].roty:-360.0
	EndIf
	obj[1].roty:+0.03
	If obj[1].roty>360
		obj[1].roty:-360.0
	EndIf
	
	anglex:+0.01
	If (anglex>360)
		anglex=0.0
	EndIf
	angley:+0.02
	If (angley>360)
		angley=0.0
	EndIf
	
EndFunction


As you can see, this method is completely messy, and there are several problems with it, but hopefully someone else can improve this until we find a better way.

NOTE: I've only tested this in windowed mode of 800*600, so it might be even worse in other modes :P


eizdealer(Posted 2005) [#7]
You will suffer 'Gimbal Lock' problems with that camera rotation code. Use Google for explanations of it.

A good way to avoid Gimbal Lock is using Quaternions. Tell me if you need the code for it, since I've got it working here.


Kalakian(Posted 2005) [#8]
The camera rotation code is part of PaulJG's OpenGL Tuorial http://www.blitzbasic.com/Community/posts.php?topic=42662 so blame him :P
Thanks for that tutorial PaulJG btw.

Yeah, I know to use quaternians for rotation, especially if rotating around all 3 axis and you want to interpolate between frames. This was the only 3D code I had done in max, so just used that to test with the 2D stuff. I don't really care about how good/bad the 3D code is atm - it's not even mine :D.

I just want a decent method of using 2D on 3D, and I know this isn't it, but thought it might help someone else come up with a decent method if they used this as a basis.