Odd2D.mod - Please Test

BlitzMax Forums/BlitzMax Programming/Odd2D.mod - Please Test

Oddball(Posted 2010) [#1]
I am releasing my Max2D supplementary drivers(Odd2D) into the public domain.

Q: What is Odd2D.mod?
A: Odd2D.mod is an extension to the standard Max2D module which adds extra functionality.

Q: What extra functionality does it offer?
A: The main benefits are integrated aspect ratio correction for mismatched virtual resolutions, and full screen zoom and rotations. As of v1.03 the module contains two new PolyImage functions. There are also a few other minor additions.

Q: How much will it cost me?
A: Not a single penny. Further more the module code is Public Domain, so you can use it for whatever purpose you want.

Q: So you don't want anything for this?
A: Not exactly. I would like feedback, and lots of it please. If you try Odd2D I'd like to know how well it worked for your system. Speed, glitches, it's all helpful.

Q: But I make games for Windows, do I have to use OpenGL?
A: Not at all. There is a Direct3D7 and Direct3D9 driver as well as an OpenGl version.

Q: How is this possible? Did you write a complete Max2D clone?
A: The Odd2D drivers sit between the standard Max2D functions and the existing drivers. When functions are called the Odd2D driver intercepts them makes any necessary changes and passes them on to the standard drivers. This means that when you are not using any of the additional functionality the Odd2D drivers (should) work exactly the same as their Max2D equivalents.

Q: Okay, I get the idea. Where can I try it?
A: Here is the download - Odd2D.mod [20Kb] - The zip contains all three drivers.

Q: How do I set it up?
A: First you need to be able to build modules. There is a 'how to guide' in the module tweaks forum. Once you're set up for building modules then put the odd.mod folder in BlitzMax's mods folder and select 'Build Modules' from MaxIDE. Then select 'Rebuild Documentation' and you're ready to go.

Q: How do I add Odd2D to my projects?
A: In exactly the same way as the standard Max2D drivers. Just import the module, set the graphics driver to one of the Odd2D drivers and call Graphics as usual.
format_code('Import ODD.D3D9Odd2D

SetGraphicsDriver D3D9Odd2DDriver()
Graphics 800,600')
Q: I hear there are problems with SetViewport when using Direct3D. Does that affect Odd2D's aspect correction?
A: No. Odd2D does not use SetViewport for its aspect correction.

Q: What is SetScreenFocus and why is it useful?
A: Normally Max2D calculates all coordinates from the top left of the screen. You can alter this with SetOrigin but ultimately it's still calculated from the top left. SetScreenFocus alters this behaviour and allows you to set it to any point on the display. This is useful when doing full screen rotation and zooming, as the rotation and zoom will happen about the focus instead of the top left of the screen.

Q: Why do I need a SetZoom? Isn't that what virtual resolution is for?
A: SetZoom is only subtly different from virtual resolution. The zooming will be centred upon the screen focus instead of the top left of the screen. My advice would be if virtual resolution works for your needs then use that, but SetZoom is there for those that need it.

Q: Why do VirtualMouseX and VirtualMouseY now return incorrect results?
A: Unfortunately I cannot alter their behaviour as Max2D calculates them without accessing the driver. Use the new function OddMouse to get the correct draw position of the mouse pointer.

Q: This is all pretty cool, but it'd be better with XXX function implemented.
A: That's not exactly a question but I welcome all suggestions for additional features. I already have a few of my own ideas.

If you have anymore questions then please feel free to ask away.

Last edited show_time('2010-11-24 20:12:09')

Oddball(Posted 2010) [#2]
And a quick example:

Import ODD.GLOdd2D
Import ODD.D3D9Odd2D
Import ODD.D3D7Odd2D

SetGraphicsDriver GLOdd2DDriver()
'Uncomment a driver to try it (Windows only)
'SetGraphicsDriver D3D9Odd2DDriver()
'SetGraphicsDriver D3D7Odd2DDriver()

Graphics 800,600
SetVirtualResolution 400,300
SetClsColor 127,127,127

Local dw:Int=DesktopWidth()-50,dh:Int=DesktopHeight()-50
Local x:Float,y:Float,rot:Float,zoom:Float=1


SeedRnd MilliSecs()

If KeyDown(KEY_LEFT) Then rot:-5
If KeyDown(KEY_RIGHT) Then rot:+5
If KeyDown(KEY_UP)

If KeyHit(KEY_1) Then SetBorderMode BORDER_NONE
If KeyHit(KEY_4) Then SetBorderMode BORDER_BESTFIT_FILL
If KeyHit(KEY_5) Then SetBorderMode BORDER_BESTFIT_SOLID

If KeyHit(KEY_B) Then SetBorderColor Rand(0,255),Rand(0,255),Rand(0,255)

If KeyHit(KEY_G)
Graphics Rand(400,dw),Rand(300,dh)
SetVirtualResolution 400,300
SetClsColor 127,127,127

If KeyHit(KEY_EQUALS) Then zoom:+.1
If KeyHit(KEY_MINUS) Then zoom:-.1

SetScreenRotation rot+90
SetOrigin -x,-y
SetRotation 0
SetScreenFocus 200,250
SetZoom zoom

SeedRnd 0
For Local count:Int=0 To 10
SetColor Rand(0,255),Rand(0,255),Rand(0,255)

SetColor 255,0,0
SetRotation rot+90
DrawText "'-Focus",x,y

SetScreenRotation 0
SetScreenFocus 0,0
SetZoom 1
SetOrigin 0,0
SetRotation 0

DrawText "[1] Border mode NONE",5,5
DrawText "[2] Border mode LETTERBOX FILL",5,20
DrawText "[3] Border mode LETTERBOX SOLID",5,35
DrawText "[4] Border mode BESTFIT FILL",5,50
DrawText "[5] Border mode BESTFIT SOLID",5,65
DrawText "[B] Random border color",5,80
DrawText "[G] Random window size",5,95
DrawText "[+/-] Zoom in/out",5,110


Until KeyHit(KEY_ESCAPE) Or AppTerminate()



BlitzSupport(Posted 2010) [#3]
Sounds brilliant (and many thanks), but after building modules and running the demo, I get:

Compile Error

Duplicate identifier 'SetFocus' in modules 'pub.win32' and 'odd.odd2d'

(Temporary fix for the demo is to change SetFocus to ODD.odd2d.SetFocus.)

Thanks again!

Yan(Posted 2010) [#4]
Nice mod, David. :o)

I took the incredibly lazy and heavy handed route of adjusting the world matrix and DX/GL viewport to do this stuff.

Your approach does seem more elegant. :o)

Oddball(Posted 2010) [#5]
Thanks James. I haven't come across that error before with my testing, but this is why I've put it out there to catch these things. I've only got a Mac so all my Windows testing is done through a Parallels virtual machine. I think I'll just change the function name to SetScreenFocus that should fix it.

Cheers Ian. I actually thought I was taking the lazy route. I've tried to design the mod so it share as much code across both the Direct3D and OpenGL drivers as possible, keeping driver specific code to an absolute minimum.

I am interested to know what machines people have successfully tested this on and what kinds of performance you are getting too. As all the Max2D functions work as normal you can test the mod in any existing project by just setting the driver at the start.

EDIT: Okay, I've changed the name of SetFocus to SetScreenFocus. Link in first post or my sig.

Armitage 1982(Posted 2010) [#6]
Hi Oddball

I don't know how you did this but it's totally brilliant !

I did give a try to virtualResolution but my pixel perfect game with line of 1px width was buggy and this wasn't working !

Now with your module it's seem's everything nearly working as it should!

Indeed, I still have edges around my camera viewport (setviewport) that slips around 1 px but the biggest problem right now is that the DrawTexturedPoly isn't working anymore !

How would it be possible to do this with your driver :
format_code('Local OGLDriver:TGLOdd2DDriver= TGLOdd2DDriver(_max2dDriver)

EDIT - Ok found it
format_code('Local OGLDriver:TGLOdd2DDriver = TGLOdd2DDriver(_odd2dDriver)')
But for example I don't know how I can do this with DX since I'm not using it :
I also use glBindTexture and glTexParameteri in some part of my game. I can't tell is this still working.
I'm having white texture currently :


The problem may come from TGLImageFrame but I'm not sure...

EDIT Found it (too)
glBindTexture GL_TEXTURE_2D, Frame.glif.name
Need to pass through the glif Field ;-)

Armitage 1982(Posted 2010) [#7]
Also I'd like to have a VirtualGrabImage() code but this one is not compatible with the Aspect ratio :

bbdoc: Grab an image from the back buffer with Virtual support
Copies pixels from the back buffer to an image frame.

Only images created with the DYNAMICIMAGE flag can be grabbed.
End Rem
Function VirtualGrabImage(image:TImage, X:Int, Y:Int, Frame:Int = 0)

Local gcr:Int, gcg:Int, gcb:Int
GetMaskColor(gcr, gcg, gcb)

Local gcw:Float = VirtualResolutionWidth() / GraphicsWidth()
Local gch:Float = VirtualResolutionHeight() / GraphicsHeight()
Local pixmap:TPixmap = _odd2dDriver.GrabPixmap(X / gcw, Y / gch, image.width / gcw, image.Height / gch)

If image.flags&MASKEDIMAGE
pixmap = MaskPixmap(pixmap, gcr, gcg, gcb)

image.SetPixmap(Frame, pixmap)

End Function')

Oddball(Posted 2010) [#8]
Hi Armitage, I don't quite follow the first issue, the 1px edges. Do you have a screenshot showing it?

I'm not surprised that a function like DrawTexturedPoly doesn't work out of the box with this as it uses the Max2D mod in a way it wasn't intended to be used. I think I see the issue, but I haven't tested it. Try this:
DrawTexturedPolyOGL, TGLImageFrame(image.Frame(Frame)), xyuv, handle_x, handle_y, origin_x, origin_y, vertex * 4)

Function DrawTexturedPolyOGL (f:TOGLImageFrame, xy:Float[], handle_x:Float, handle_y:Float, origin_x:Float, origin_y:Float, vertex:Int)

Local Frame:TGLImageFrame=f.glif

Global TmpImage:TImage

If xy.length<6 Return

Local rot# = GetRotation()
Local tform_scale_x#, tform_scale_y#
GetScale tform_scale_x, tform_scale_y

Local s#=Sin(rot)
Local c#=Cos(rot)
Const Scale:Float = 1.0
Local ix#= c*tform_scale_x*scale
Local iy#= -s*tform_scale_y*scale
Local jx#= s*tform_scale_x*scale
Local jy#= c*tform_scale_y*scale

glBindTexture GL_TEXTURE_2D, Frame.name
glEnable GL_TEXTURE_2D

For Local i:Int = 0 Until Len xy Step 4
If vertex > -1 And i >= vertex Then Exit
Local x#=xy[i+0]+handle_x
Local y#=xy[i+1]+handle_y
Local u#=xy[i+2]
Local v#=xy[i+3]
glTexCoord2f u,v
glVertex2f x*ix+y*iy+origin_x,x*jx+y*jy+origin_y
If Not tmpImage Then tmpImage = CreateImage(1,1)
DrawImage TmpImage, -100, -100 ' Chtob zbit' flag texturi

End Function
I hope that works for you.

I am planning some textured poly functionality in Odd2D. In fact the reason I made Odd2D was so I could easily integrate stuff like this into the driver.

Oddball(Posted 2010) [#9]
Sorry. I almost missed that second post. You must have posted while I was typing. Again untested, I'm just typing straight into the codebox
bbdoc: Grab an image from the back buffer with Virtual support
Copies pixels from the back buffer to an image frame.

Only images created with the DYNAMICIMAGE flag can be grabbed.
End Rem
Function VirtualGrabImage(image:TImage, X:Int, Y:Int, Frame:Int = 0)

Local gcr:Int, gcg:Int, gcb:Int
GetMaskColor(gcr, gcg, gcb)

Local gcw:Float = _odd2dDriver.virt_width / GraphicsWidth()
Local gch:Float = _odd2dDriver.virt_height / GraphicsHeight()
Local pixmap:TPixmap = _odd2dDriver.GrabPixmap((X / gcw) + _odd2dDriver.Border_x*_odd2dDriver.ratio, (Y / gch) + _odd2dDriver.Border_y*_odd2dDriver.ratio, image.width / gcw, image.Height / gch)

If image.flags&MASKEDIMAGE
pixmap = MaskPixmap(pixmap, gcr, gcg, gcb)

image.SetPixmap(Frame, pixmap)

End Function
I'll have to test it out when I get some computer time.

Armitage 1982(Posted 2010) [#10]
Yes it's working !
I was editing my answer while you were probably sending yours ;)

The main problem I have is the grabimage (cf. answer before).
I would need some kind of virtualGrabImage (because I can't draw to texture).

Let's assume that my pixel problem is nothing.
I simply try to add a border inside any viewport (to use it like tiny camera)
It's probably like skidracer answer in this thread : http://blitzmax.com/Community/posts.php?topic=87903#997249

I will add little hack for the concerned resolution (640x480 with 1024x768 Virtual resolution)


Armitage 1982(Posted 2010) [#11]
About the grabimage it's totally related to the wait I do my "fake Draw To Texture".
I quickly switch from Virtual to default window size and it's ok now.

Does the VirtualMouseX() and VirtualMouseY() working correctly ?

I run into scaling issue with this. The mouse seems captured inside the whole virtual resolution.

Oddball(Posted 2010) [#12]
The virtual mouse functions will not return accurate results when using letterbox or bestfit border modes as I can't intercept those functions. I have included the function OddMouse which will return the draw coordinates of the mouse. That is if you were to draw an image at the x and y values returned by OddMouse then it would draw exactly where the pointer is. It adjusts for origin, screen rotation, focus, zoom, border mode, and virtual resolution.

CyBeRGoth(Posted 2010) [#13]
Excellent stuff as always Oddball, some very useful stuff here, thanks for sharing :)

So far no problems or glitches to report as I use it more I will let you know if any problems arise.

Armitage 1982(Posted 2010) [#14]

So I double the pixel border (since reducing from 1024 to 640 can lead to imprecise pixel perfect ratio, you can easily see this on objects while splitting your screen in 2 or 4 with SetViewPort for multiplayer, some of the pixels will "squeeze" or something) in order to avoid gap like the one you can see in the picture above (left camera border).

And as far as I know for the moment everything is running at the same speed with full features (even the complicated low level OGL operation).

At least thanks to your aspect ratio correction none of my objects are misplaced like they were with the native virtual resolution.

Thanks a lot !
I'm now able to switch between different resolution without hassles.
Must have, Should be official IMO!

Armitage 1982(Posted 2010) [#15]
Odd, I think if you play a bit with strange value for Setzoom() + setscale() you can crash the mod with a division by Zero.
Maybe secure this function would be better :)

Oddball(Posted 2010) [#16]
Have you got any example code showing this, as I can't get it to crash on my machine. I only use floats for dividing and they don't crash on divide by zero. Testing this did flag up a related bug with borders and SetScale though.

Armitage 1982(Posted 2010) [#17]
Probably due to my framework then.
I play a lot with setScale() it's a little bit hard to track this particular error.
If I encounter this again I will try to write a little example code :)

Oddball(Posted 2010) [#18]
Small update. I've added modulate 2x blend. Set it using MOD2XBLEND like so:
format_code('SetBlend MOD2XBLEND')Should work in OpenGL, Direct3d7 and Direct3D9. Again let me know if there are any issues.

Armitage 1982(Posted 2010) [#19]
Excellent !
Working Great.

By the way, since I'm using Odd2D.mod I get some VIOLATION_EXCEPTION_ERROR that immediately terminate my game.

I'm running a lot of software aside and I'm wondering if my free RAM is too low or non-existent (only have 1go Ram with Windows 7).

It happened very few times, maybe 3 or 4 times and always while my games was running more than a few minutes.

Did you ever encounter this particular case before Odd ?

I'm currently trying to catch this on debug but as always the bug only happened in release mode.

John G(Posted 2010) [#20]
Oddball. Wonderful, just wonderful. These are the missing extra functions which allow most of the power of OpenGL without requiring all those picky details. So far I've used some of your extensions on Mac PPC and WinXP -- flawless so far. SetFocus, SetZoom and, especially, SetScreenRotation all work wonderfully. Hope this functionality moves into BMX2 also. Great work!

Oddball(Posted 2010) [#21]
Update v1.03 - Download

I've added textured polygon functionality. This comes in two flavours; DrawPolyImage acts exactly like the DrawPoly command, but allows you to specify an image and uv coords to texture it; DrawImagePoly acts exactly like DrawImage except you specify a set of uv coords to only draw a polygon shaped portion of it. Hope you find them useful. Enjoy.

CyBeRGoth(Posted 2010) [#22]
Nice work adding the polygon stuff Odd this will come in handy :)

Richard Betson(Posted 2011) [#23]
This looks like it is working nicely. Good Job.

Now I have an omnidirectional scrolling, zooming and rotating screen. Thanks!


Armitage 1982(Posted 2011) [#24]
Hi David

Today I try this addon from Czar Flavius http://blitzbasic.com/Community/posts.php?topic=94692

And I found it would make a perfect addition for the odd.mod that I currently use as daily bases :)

As simple as adding :
to odd2d.bmx
format_code('Const DIRECTBLEND:Int = 7
Const MASKOFFBLEND:Int = 9')

And to glodd2d.bmx
Method SetBlend( blend:Int )
Super.SetBlend blend
Select m2d_blend
glEnable GL_BLEND
glEnable GL_BLEND
glBlendFunc GL_ONE,GL_ZERO
glEnable GL_BLEND
glEnable GL_BLEND
End Select
End Method

As stated it greatly help to create alpha mask image.
There is also a working DX version but I never use DX.

Oddball(Posted 2011) [#25]
I'll take a look at it, but from a glance I can't tell what it does. Do you have an example complete with images? The images in that link don't appear to be there anymore.

I have locked down Odd2D at the moment as I'm working on a commercial game that uses it, but as soon as that is done I have some exciting additions for Odd2D ready to be implemented.

Armitage 1982(Posted 2011) [#26]
Exciting additions
humm, I like that :D :D

This new set of blend allow this :

Tiny example with modified version of Odd2d.mod (OpenGL only, but DX is also possible, if you want me to remove the link after, just tell me ;) :


Import odd.odd2d
Import odd.glodd2d

Const folder:String = ""


Function MainF()

Graphics 512, 512

Local grass:TImage = LoadImage(folder + "grass.png", 0)
Local sand:TImage = LoadImage(folder + "sand.png", 0)
Local mask:TImage = LoadImage(folder + "mask.png", 0)

While Not (KeyDown(KEY_ESCAPE) Or AppTerminate())


DrawImage(mask, 0, 0)

DrawImage(grass, 0, 0)

DrawImage(sand, 0, 0)

Delay 1

End Function


Adding a GRAPHICS_ALPHABUFFER did not mess in any way with my current project.

Oddball(Posted 2011) [#27]
Interesting. The DIRECTBLEND is actually more of a NOBLEND as it works the same as the standard SOLIDBLEND except it also writes the alpha data to the screen buffer. The other two appear to be destination alpha blend modes, MASKONBLEND is a standard destination blend mode, and MASKOFF is the inverse of MASKONBLEND.

The method used above to achieve this affect seems counter intuitive to me. The way I would do this would be to draw the sand image to the screen, then combine the mask and grass image and then just ALPHABLEND them to the screen. Of course you'd need to be able to do some form of render to texture for that to be possible. ;)

I'll have a play around and add this in some form or another to the next Odd2D update.

Last edited show_time('2011-11-07 10:58:46')

Armitage 1982(Posted 2011) [#28]
Yeah of course, it was a simple example to get the idea.

Unfortunately I never found a simple render to texture that would run on lower machine, something easy that wouldn't mess too much with your driver like FBO shaders, etc.
For instance I'm using the grabImage function with all the known limitations (like max grabsize = max window size).

If you tell me you find a way to give to odd.mod a Render To Texture I would probably delay my monkey learning to revamp my whole engine with this crazy module ^^

AdamRedwoods(Posted 2011) [#29]
Unfortunately I never found a simple render to texture that would run on lower machine, something easy that wouldn't mess too much with your driver like FBO shaders

Curious, which chipsets specifically?

Armitage 1982(Posted 2011) [#30]
I think I remember having a few runtime crash on a old DMA while messing with render to texture. But I try this 2 years ago and I think it was rather a problem to find a correct solution that fit max2D or odd drivers nicely. Cannot switch the whole projet to a pure openGL project right now.

But if you have something fresh I always like having a look of course :)

Armitage 1982(Posted 2011) [#31]

The method used above to achieve this affect seems counter intuitive to me. The way I would do this would be to draw the sand image to the screen, then combine the mask and grass image and then just ALPHABLEND them to the screen. Of course you'd need to be able to do some form of render to texture for that to be possible. ;)

I would add something more: since you are rendering on the main backbuffer, the MASKONBLEND texture will corrupt your Alphabuffer. That mean any Timage using alpha (like PNG) rendered before at the same position will be affected by this mask. Be vigilant !

So I search again for a simple Render to Texture for OpenGL only and found this comment about the GMA crash related : http://blitzmax.com/Community/posts.php?topic=93479#1069582

So I try myself with a netbook on GMA graphic card (AspireOne D255) and it crash too !

Is it because of the GLbindTexture or framebuffer ?

I also read you were indeed trying to add render to texture in odd2d.mod, really cool !
But do you already know if this addon would use the same technics (and so fail on GMA card) ?

Since this problem is only present in poor graphic cards, it would be nice to exactly know what's causing this and find a way to at least avoid any crash if you can't use it.

Maybe it's due to mip-mapping ?
Read this on the Unity3D web site : Disabled mip-mapped Render Textures on Macs with Intel GMA 950 cards; occasional driver crashes.

Last edited show_time('2011-11-14 20:21:25')

Oddball(Posted 2011) [#32]
Well I don't have a GMA card to test, but there are only so many ways that an FBO can be implemented. I'd suggest that it's probably more of a driver issue if it's only occurring on GMA cards. We'll have to wait and see.

Armitage 1982(Posted 2011) [#33]
Anyway FBO cannot be achieved on those GMA card simply because GL_EXT_framebuffer_object is missing from the opengl extensions.

There is a nice code from Klepto to assert this : http://www.blitzbasic.com/Community/posts.php?topic=69804#788488
Warning - you have to add a few > -1 at the end of some test (like Extensions.Find("GL_EXT_framebuffer_object") > -1 )

The module beanage.fbo ( http://blitzmax.com/Community/posts.php?topic=93479#1069539 ) check this but after acquiring the frame-buffer, that's why it's crashing.

I will take a moment to think if FBO is really necessary in my project. There is probably others solutions to achieve the same effect.

Last edited show_time('2011-11-15 10:35:38')