Setting a low resolution to mimic older games.

BlitzMax Forums/OpenGL Module/Setting a low resolution to mimic older games.

Ryan Burnside(Posted 2008) [#1]
Hello,

Recently I have wanted to mimic lower resolution games using OpenGL. I need to set the OpenGL display window to run in a very low resolution.

I've been kindly helped but I'm not quite advanced enough in C++ to know what is going on in this code. I was told that making a low resolution screen in OpenGL involved writting the buffer into a texture then making a quad that takes the texture and blows it up to the screen resolution, be it 640x480 or whatever else.

I would really appreciate some help if possible. I'm on a very tight timeline to finish this small project. I can't use the native graphics commands in Bmax because vector drawing remains at the screen resoultion.


Here is some of the code in C




ImaginaryHuman(Posted 2008) [#2]
One way to do it is the way you mentioned, where you draw a smaller-than-screen-size version of your game either onto the backbuffer or onto a texture, and then either you draw that texture full-screen size or you grab that portion of the backbuffer into a texture and draw it full-screen.

However, that really is involving steps that you don't need to take. What you need to do is correctly define your viewing Frustum ie your projection matrix, with which in a single call it's possible to set up a scaled display. If you define the viewing area for a 800x600 screen as 0,0 through 400,300, for example, anything you draw in the top left 400x300 area of your screen will fill the whole screen with 2x2-sized `pixels`. Similarly if you said your projection covers coordinates 0,0 through 200,150 then you'll get 4x4-sized pixels. see the glFrustum, glPerspective or glOrtho2D commands. With this method you don't need to play with textures at all and you don't have the extra overhead of redrawing them.


Ryan Burnside(Posted 2008) [#3]
Yes but won't vector based drawing commands still appear as single pixel drawings? I don't think that the vector based drawing commands will scale will they? The raster based images on quads will probably size correctly but lines and polygons will still be a single pixel thick.


ImaginaryHuman(Posted 2008) [#4]
Yes they will. If you scale the projection matrix you are telling it to scale how it SEES the game world, and anything you `draw` you are drawing as geometry within the game world, so it will all be interpreted and scaled by the matrix.


Ryan Burnside(Posted 2008) [#5]
If possible, would you mind showing a quick bit of code?

It would help me immensely to see it done correctly first.


ImaginaryHuman(Posted 2008) [#6]
See the documentation for glOrtho or gluOrtho2D:

http://www.talisman.org/opengl-1.1/Reference/glOrtho.html

It's something like..

glMatrixMode GL_PROJECTION
glLoadIdentity()
glOrtho 0,400,0,300
glMatrixMode GL_MODELVIEW

which would give you a 2x2 pixel size on an 800x600 screen.

You might need to call glViewport or SetViewport (BlitzMax) afterwards e.g. SetViewport 0,0,400,300 - not sure.

Give it a try.


Ryan Burnside(Posted 2008) [#7]
I've done as you said, the lines and vector based drawing commands still draw in the original screen resolution.

SuperStrict
Framework brl.GLGraphics
GLGraphics 640, 480



glMatrixMode GL_PROJECTION
glLoadIdentity()
glOrtho 0, 160, 0, 140, 0, 1
glMatrixMode GL_MODELVIEW



glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT


glBegin(GL_LINE_LOOP)
glColor3f(.75, .25, .75)

glVertex3f(0, 0, 0)
glVertex3f(0, 32, 0)
glVertex3f(32, 32, 0)
glVertex3f(32, 0, 0)
glEnd()
glViewport(0, 0, 160, 120)
Flip
WaitKey()



ImaginaryHuman(Posted 2008) [#8]
Ahh. Oh well. I thought it would work but apparently not. It does scale the coordinate system correctly but the actual drawing operations are not scaled. You'd need to maybe try changing the line thickness. It would work fine with textures/images. Maybe write your own line routine which instead of drawing using GL_LINE_LOOP draws using GL_QUADS with image data - then the image of the line piece will scale.


Ryan Burnside(Posted 2008) [#9]
Yes, as you notice that is why everything was first rendered to a texture. Vector based drawing commands that have no mapping will maintain the window resolution. The initial code renders all vector objects and other drawing commands to a raster texture then expands the texture into a quad.

Now, is anyone willing to try to get this working from C code?


kfprimm(Posted 2008) [#10]
retrorender.bmx

Basically, import this into your program and call 'RenderRetro(width,height)', passing the resolution you wish. The rest is taken care of via hooks.


Ryan Burnside(Posted 2008) [#11]
OK, I see a flash of the screen in the correct resolution. When should I call the function? I think I have it in the wrong place perhaps. Thanks for all your help here. It is sort of an unique task that I ask for assistance with.


Strict
Framework brl.GLGraphics
GLGraphics 640, 480
Include "retro_render.bmx"
glMatrixMode (GL_PROJECTION)
glOrtho(0, 640, 480, 0, - 1000, 1000)
glMatrixMode(GL_MODELVIEW)


While Not KeyDown(KEY_ESCAPE)
	glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
	RenderRetro(160, 120)
	glBegin(GL_LINE_LOOP)
	For Local i:Float = 0 To 10
		glColor3f(i / 10, 0.0, 1.0)
		glVertex3f(80 + Cos(i * 36) * 40, 60 + Cos(i * 36) * 30, 0)
	Next
	glEnd()
	
	Flip
Wend



kfprimm(Posted 2008) [#12]
Yep, just call it once after GLGraphics. Sorry about that.
By the way, you may as well just Import it instead of including it. It'll speed up compile time.


ImaginaryHuman(Posted 2008) [#13]
I seriously suggest getting rid of the ReadPixels and reuploading of the texture, and instead use glCopyTexImage2D or glCopyTexSubImage2D directly from the backbuffer to an existing texture. It is MUCH faster than using pixmaps.


Ryan Burnside(Posted 2008) [#14]
ImaginaryHUman, could you provide an example of how this is done? I'm not trying to be a pain but I'm still struggeling a bit.


ImaginaryHuman(Posted 2008) [#15]
Look at the documentation for glCopyTexSubImage2D() in the standard OpenGL reference online. You create a dummy texture with glTexImage2D like you already do in your code, but you don't pass any data to it (set the pixels pointer to Null). Then you just call glCopyTexSubImage2D() with the appropriate rectangle to copy it into the currently bound texture.