New function: Single Surface Drawing.
BlitzMax Forums/BlitzMax Programming/New function: Single Surface Drawing.
| ||
It was noted that the LoadAnimImage actually creates a new image for each frame (SHOCK). I developed a new Type that allows you to load and draw animated images without the system creating a shed load of new surfaces - Hence Single Surface Drawing. Stats :- DirectX - 40% Less Video Ram 28% speed increase OpenGL - 42% Less Video Ram 28% speed increase AND 57% faster than DirectX (nVidia Fx5200) [EDIT]This code no longer has any dependencies. The New Type: Type TAnimImage Field Image :TImage Field width :Int Field height :Int Field u0 :Float[] Field v0 :Float[] Field u1 :Float[] Field v1 :Float[] Function Load:TAnimImage(url:Object,cell_width:Float,cell_height:Float,start:Int,frames:Int,flags=-1) Local t:TAnimImage = New TAnimImage Local tx:Float Local ty:Float Local x_Cells:Int t.u0 = New Float[frames] t.v0 = New Float[frames] t.u1 = New Float[frames] t.v1 = New Float[frames] t.Image = LoadImage(url,flags) Local xDelta:Float = t.Image.Width / Pow2Size(t.image.width) Local yDelta:Float = t.Image.Height / Pow2Size(t.image.height) x_cells = t.Image.Width / cell_width For Local f = start To frames - 1 tx = (f Mod x_cells * cell_width) * xdelta ty = (f / x_cells * cell_Height) * ydelta t.u0[f] = Float(tx) / Float(t.Image.Width) t.v0[f] = Float(ty) / Float(t.Image.Height) t.u1[f] = Float(tx + cell_width * xdelta) / Float(t.Image.Width) t.v1[f] = Float(ty + cell_Height * ydelta) / Float(t.Image.Height) Next Return t End Function Function Pow2Size:Float( n ) Local t=1 While t<n t:*2 Wend Return t End Function Function Free(t:TAnimImage) t.Image = Null t = Null FlushMem() End Function Method Draw(x:Float,y:Float,width:Float,height:Float,frame:Int=0) Local DXFrame:TDX7ImageFrame = TDX7ImageFrame (image.frame(0)) If DXFrame DXFrame.setUV(u0[frame],v0[frame],u1[frame],v1[frame]) Else Local GLFrame:TGLImageFrame = TGLImageFrame(image.frame(0)) GLFrame.u0 = u0[frame] GLFrame.u1 = u1[frame] GLFrame.v0 = v0[frame] GLFrame.v1 = v1[frame] EndIf DrawImageRect(Self.Image,x,y,width,height) End Method End Type Example: Graphics 640,480,0,NOSYNC Print "Start Memory " + MemAlloced() Local now:Int = MilliSecs() Local blob:TAnimImage = TAnimImage.Load("gfx/stoneset0.png",32,64,0,32,FILTEREDIMAGE) 'Local blob:TImage = LoadAnimImage("gfx/stoneset0.png",32,64,0,32,FILTEREDIMAGE) For Local a = 1 To 100 Cls For Local b = 1 To 10000 blob.Draw(Rand(0,640),Rand(0,480),20,40,Rand(0,31)) 'DrawImageRect(blob,Rand(0,640),Rand(0,480),20,40,Rand(0,31)) Next Flip Next Print "End Memory " + MemAlloced() Local time:Int = MilliSecs()-now Print "Time Taken " + time WaitKey() To Set Image Handles Use: SetImageHandle(MyTAnimImage.Image,x,y) |
| ||
Wow nice on mate -thanks for sharing that. |
| ||
That's a great help, I think I'll be using it right away. Thanks a lot! |
| ||
Seems to draw some frames 'offset' from their intended start point. Image file 32*32*5 shows a problem but 32*32*4 does not. |
| ||
sorry tonyg, what do you mean? |
| ||
Should have added some code...Graphics 640,480,0,NOSYNC SeedRnd MilliSecs() Local blob:TAnimImage = TAnimImage.Load("animstrip5.png",32,32,0,5,FILTEREDIMAGE) Local blob1:TAnimImage = TAnimImage.Load("animstrip4.png",32,32,0,4,FILTEREDIMAGE) 'Local blob:TImage = LoadAnimImage("animstrip.png",32,64,0,10,FILTEREDIMAGE) While Not KeyHit(key_escape) Cls blob.Draw(0,0,32,32,Rand(0,4)) blob1.draw(0,50,32,32,Rand(0,3)) ' DrawImage blob,0,0,Rand(0,9) DrawText MouseX() + " " + MouseY(),0,100 Flip FlushMem Delay 500 Wend animstrip5.png is 32*32*5 animstrip4.png is 32*32*4 |
| ||
Probably my dodgy coding. I've only tested with texture sizes ^2 and not strips. |
| ||
I can't see anything wrong with the UV parms when I outpit them. May I've put SetUv in the wrong place. Which driver.bmx file should it be added to? I have it as a method of TImageFrame. |
| ||
If it compiles then you've got it in the right place > it's in the max2d.mod directory. Is this the Method you added to TImageFrame? Method SetUV(u0#,v0#,u1#,v1#) xyzuv[3]=u0 xyzuv[4]=v0 xyzuv[8]=u1 xyzuv[9]=v0 xyzuv[13]=u0 xyzuv[14]=v1 xyzuv[18]=u1 xyzuv[19]=v1 End Method |
| ||
Errr, no. I added... Method SetUV(u0#,v0#,u1#,v1#) Abstract I can't get that new method to build as 'Identifier xyzvu not found. <edit> although that's the method I have in d3d7max2d.bmx |
| ||
Ignore me..... So what I originally said to do. I got my wires crossed. |
| ||
This sounds fantastic! Thanks! I've wondered exactly how LoadAnimImage works - does it create 1 surface - or a surface for each frame? One thing preventing me using it though, it requires messing about with the official modules. I don't want to do this for fear of upsetting max and thus causing a debugging nightmare - having forgot a couple of lines in a particular module have been added. Plus the fact official updates would over-write any changes anyway. Could Mark please integrate this into the next update? |
| ||
Could Mark please integrate this into the next update? Ha HA ha ha ha HA ha ha haaaaa. There is a way around using my SetUV function : http://www.blitzbasic.com/codearcs/codearcs.php?code=1422 |
| ||
LoadAnimImage creates a single TImage but seperate pixmaps for each frame. I believe each pixmap is a seperate surface but I could be wrong. Huge numbers or animations and/or loading your tileset with loadanimimage (especially if the frames are odd size) might create lots of surfaces. There's also this... DrawImageBlock but it's OGL only. BTW ; Anybody got DrawImageArea or DrawImageBlock to work? If so, how? |
| ||
See this:-device.SetTexture 0,frame.surface That is the command that switches the texture according to the frame, I think it is safe to assume that seperate surfaces are used for each frame. The DrawImageBlock code works the same way as my code does, it sets the UV. |
| ||
This code now runs straight outta the box. Get it from the top thread. |
| ||
This code now runs straight outta the box SetUV? |
| ||
Hi Can i use the imagescollide functions with TAnimImage? Mfg Suco |
| ||
You still need the SetUV from the link you had previously. I added debug statements for the UV parms which all seem OK but it's still offset for anim frames > 4 |
| ||
Nope, that SetUV Function is the one already in Blitz, I just did not know how to access it directly. Now I know and so do you. |
| ||
Are we using different systems? Running the example *without* the changes you suggested to add SetUV produces... "Identifier setUV not found" |
| ||
Hmmn, Okay you win I changed the code again. BUT this is DX7 only now. |
| ||
This should work with GL too...Method Draw(x:Float,y:Float,width:Float,height:Float,frame:Int=0) Local DXFrame:TDX7ImageFrame = TDX7ImageFrame(image.frame(0)) If DXFrame DXFrame.setUV(u0[frame],v0[frame],u1[frame],v1[frame]) Else Local GLFrame:TGLImageFrame = TGLImageFrame(image.frame(0)) GLFrame.u0 = u0[frame] GLFrame.u1 = u1[frame] GLFrame.v0 = v0[frame] GLFrame.v1 = v1[frame] EndIf DrawImageRect(Self.Image,x,y,width,height) End Method |
| ||
Updated. |
| ||
They both 'work' fine now. However, does anybody else have a problem when the animation file has > 4 frames? <edit> it seems the problem occurs with odd numbers of frames. |
| ||
Tonyg, the files I am using with this at the moment consist of 32 frames. I don't have any issue at all (32x64x32). |
| ||
tonyg, does this work for you (you should see 16 coloured blocks)?...img$ = String(getenv_("BMXPath")) + "\samples\birdie\games\tiledrop\media\blocks.png" Graphics 640,480,0 Local blob:TAnimImage = TAnimImage.Load(img$, 32, 32, 0, 16, FILTEREDIMAGE) For Local b=0 To 15 blob.Draw(5 + (b * 40), 200, 30, 30, b) Next Flip WaitKey() End |
| ||
Yep that works but it's an even number of frames (might have missed my previous edit) Try this... Type TAnimImage Field Image :TImage Field width :Int Field height :Int Field u0 :Float[] Field v0 :Float[] Field u1 :Float[] Field v1 :Float[] Function Load:TAnimImage(url:Object,cell_width,cell_height,start:Int,frames:Int,flags=-1) Local t:TAnimImage = New TAnimImage Local tx:Int Local ty:Int Local x_Cells:Int t.u0 = New Float[frames] t.v0 = New Float[frames] t.u1 = New Float[frames] t.v1 = New Float[frames] t.Image = LoadImage(url,flags) x_cells = t.Image.Width / cell_width For Local f = start To frames - 1 tx = f Mod x_cells * cell_width ty = (f / x_cells * cell_Height) t.u0[f] = Float(tx) / Float(t.Image.Width) t.v0[f] = Float(ty) / Float(t.Image.Height) t.u1[f] = Float(tx + cell_width) / Float(t.Image.Width) t.v1[f] = Float(ty + cell_Height) / Float(t.Image.Height) Next Return t End Function Function Free(t:TAnimImage) t.Image = Null t = Null FlushMem() End Function Method Draw(x:Float,y:Float,width:Float,height:Float,frame:Int=0) Local DXFrame:TDX7ImageFrame = TDX7ImageFrame(image.frame(0)) If DXFrame DXFrame.setUV(u0[frame],v0[frame],u1[frame],v1[frame]) Else Local GLFrame:TGLImageFrame = TGLImageFrame(image.frame(0)) GLFrame.u0 = u0[frame] GLFrame.u1 = u1[frame] GLFrame.v0 = v0[frame] GLFrame.v1 = v1[frame] EndIf DrawImageRect(Self.Image,x,y,width,height) End Method End Type 'img$ = String(getenv_("BMXPath")) + "\samples\birdie\games\tiledrop\media\blocks.png" img$ = String(getenv_("BMXPath")) + "\samples\breakout\media\tiles.png" Graphics 640,480,0 Local blob:TAnimImage = TAnimImage.Load(img$, 32, 20, 0, 5, FILTEREDIMAGE) For Local b=0 To 4 blob.Draw(5 + (b * 40), 200, 30, 30, b) DrawText b,5+(b*40),200 Next Flip WaitKey() End |
| ||
ohh, thats not good. The problem seems to be with the code for calculating the x and y positions of each image in the strip. |
| ||
I thought so but debuglogging the UV coords used and they look 'ok' to my untrained eye. |
| ||
I'm about 50% of the way to sorting it.. Fingers crossed. |
| ||
The UV's are coming out correctly?!?!? So why is it not drawing correctly?!?! x-cells : 1 y-cells : 5 tx :0 ty :0 u0 :0.000000000 v0 :0.000000000 u1 :1.00000000 v1 :0.200000003 tx :0 ty :20 u0 :0.000000000 v0 :0.200000003 u1 :1.00000000 v1 :0.400000006 tx :0 ty :40 u0 :0.000000000 v0 :0.400000006 u1 :1.00000000 v1 :0.600000024 tx :0 ty :60 u0 :0.000000000 v0 :0.600000024 u1 :1.00000000 v1 :0.800000012 tx :0 ty :80 u0 :0.000000000 v0 :0.800000012 u1 :1.00000000 v1 :1.00000000 |
| ||
Yep... it's very weird. Here's the output for an 'even' number... 0.000000000 0.000000000 0.250000000 0.312500000 0.250000000 0.312500000 0.500000000 0.625000000 0.500000000 0.625000000 0.750000000 0.937500000 0.750000000 0.000000000 1.00000000 0.312500000 Is it at all possible the float inaccuracy has anything to do with it? (notice these are all n.n00000) If the uvs are limited to 4dec places what happens. I'll try it here but it might take a while. |
| ||
Is the texture that you're loading a power of two in each dimension? That can mess with texture coordinates if it isn't. |
| ||
Ah Warren hit the nail on the head - I think Bmax must be resizing the texture by padding it some way. If you make the image 32x128 and then change the dimensions of each frame accordingly it works. now to fix it. |
| ||
Should work now. Updated First Post. Someone please break it for me :( |
| ||
Looks good here.... |
| ||
I've got 189+ tiles here and the wrong image frames are being drawn. Looks to be one or two tiles off in all instances.'imgTiles:TImage = LoadAnimImage("gfx/tiles/" + tmpPath$, 16, 16, 0, 189 * tmpFrames, MASKEDIMAGE|FILTEREDIMAGE) imgTiles:TAnimImage = TAnimImage.Load("gfx/tiles/" + tmpPath$, 16, 16, 0, 189 * tmpFrames, MASKEDIMAGE|FILTEREDIMAGE) If map(x, y, 1) <> -1 Then imgTiles.Draw Int((a * 24) - scrollX), Int(b * 16), 16, 16, map(x, y, 1) 'DrawImage imgTiles, Int((a * 16) - scrollX), Int(b * 24), map(x, y, 1)My tile image is 144x336 Edit: You also left off the Type and field info in your first post. |
| ||
When you say wrong frames do you mean frames are being drawn in error or not at all? |
| ||
They're being drawn correctly, only it's showing a different frame than it should be. |
| ||
Can you see any obvious errors in my code? |
| ||
Hmmm. I think I found the problem...tx = (f Mod x_cells * cell_width) should be tx = (f Mod x_cells * cell_width) * xdelta ...and, famous last words, haven't broken it yet. |
| ||
yeah, i tested for height only... I need to employ some full time bug testers :D |
| ||
Very good work. Please consider adding to the next update Mark. |
| ||
Updated first post. |
| ||
A typo there Indiepath - should read... tx = (f Mod x_cells * cell_width) * xdelta One Eyed Jack, Indiepath laughed at me for suggesting that - you hold him and I'll hit him. ;-) |
| ||
And it can go faster : http://www.blitzbasic.com/Community/posts.php?topic=51795 |
| ||
And now, for the lazy coders version. Here, you just load an image specifying how many frames are contained within. Then draw the image using whatever frame you desire. No need to worry about cell sizes. The include: Test Program: The 'runner' animstrip is here Currently, the above only works with a strip of images layed out in a horizontal fashion. I'm not sure if the old limitation still exists where trying to handle images larger than the graphics display causes issues(?) When I tried loading the runner animation strip using an image width of 2280 the anim failed to show under DX and played at odd frame positions under GL. I'll see if I can track down any bugs in the LoadAnim method. |
| ||
I'm not sure if the old limitation still exists where trying to handle images larger than the graphics display causes issues(?) Follow the rules for textures and you can't go wrong. |
| ||
Works great now except that some tiles have a strange border around them. I'm loading the TAnimImage as MASKEDIMAGE|FILTEREDIMAGE, and also Drawing at Int() positions only.DrawImageRect(Self.Image, Int(x), Int(y), Int(width), Int(height))Example: Tiles are sopposed to be dark gray all around, with no light gray edges. ![]() I really want to use this since it gives my program a nice speed boost. Anyone know what the problem could be? |
| ||
The issue may be the small inaccurcies of using UV co-ordinates. If your image conforms to rules for textures ie ^ 2 then there should be no issues. |
| ||
Is anyone using this anymore? Is it stable? |
| ||
I tested it quite a bit when Tim posted it and it worked well after a few teething problems. It doesn't use anything non-standard so, effectively, you have all the source. |
| ||
I really ought to get round to using this. Now all I need is a way to make a "mesh" by drawing those textures joined properly so I can scroll a tilemap at non-integer coords without seeing the joins between the tiles (which you get if you draw all the tiles separately). |
| ||
Old B3D code but the priciples are there http://blitzbasic.com/codearcs/codearcs.php?code=1178 http://blitzbasic.com/codearcs/codearcs.php?code=1377 |
| ||
"...so I can scroll a tilemap at non-integer coords without seeing the joins between the tiles (which you get if you draw all the tiles separately)." Hi GA, do you have an .exe demo showing this? I'm not getting any lines and I'm just drawing tiles next to each other. |
| ||
Thanks Tim. Will study (when time) but I'm not an expert on DX regretably. MGE Dev: http://www.greyaliengames.com/framework.php scroll down and get "make me happy". Depending on the type of tile the issue is worse. It only happens at floating point coords of course because the edge is anti-aliased. If it was drawn at integer coords to a giant texture of many tiles and then the big texture was drawn at floating point coords, you wouldn't get the issue. |
| ||
GA - checked out the Make Me Happy demo (nice!), didn't see any lines between tiles. Was I supposed to? |
| ||
yeah you can see if you look carefully. Well you can on a CRT; if you have a TFT try it in windowed mode so there is no interpolation by your screen. Also try Speed Run then. there are slight sorta flickery lines between the titles as they slide from one int coord to another via floating point due to the anti-aliasing. btw make me happy took 8 hours (inc. crappy graphics) with my framework :-) Speed run took less time I think. |
| ||
hmm... here's a screen grab from windowed mode on my crt. Where are the lines???? The fact that you see lines on your end, makes me shrug again at the fact that already in 2 days I'm seeing more potential Max2d compatibility issues.![]() |
| ||
Guys, the lines you MAY be experiencing are most likely due to texture filtering (if you see discolored lines between tiles). Try disabling filtering, and there should be no lines in between tiles. Also try setting texture wrapping mode to CLAMP and see if that helps. The geometry will always jump from pixel to pixel, even at subpixel accuracy. (no subpixels on monitors) This is specially evident on LCD monitors. |
| ||
What lines? (Sorry for repeating...) Perhaps it's gpu related and not a stable 2DMax issue? |
| ||
The fact that you see lines on your end, makes me shrug again at the fact that already in 2 days I'm seeing more potential Max2d compatibility issues As suggested any issue is likely to be a video card 'oddity' rather than Max2D issue. Out of interest, what are the other potential Max2d compatbility issues you have seen? |
| ||
In my try to convert to Tanimimage I have to say that - I only converted some mainsprites. the memory usage increased from 70 to 80MB, the fps-count stays the same (in ogl and also in dx7, dx9 fuzzles the sprites...garbage like). bye MB |
| ||
MGE Developer: perhaps I should have been clearer. You can only see it as stuff is moving. Go into that game again and press D for debug mode then L for sLow motion. Now move across and watch the edges of the grey tiles, they all flicker in an unappealing way because each one is having it's edge filtered. You can't really notice it on the earth tiles. The sky is a static image so that doesn't count. If I do as HrdNutz suggests and turn off filtering when I load in the tiles, then they all get drawn at integer coords, which of course removes the problem, but then it doesn't look as smooth when it's scrolling. If you take a static screenshot, it looks fine. The problem is the edges are different from frame to frame. It should happen on all PCs because all that's happening is each tile is drawn individually so it's edges are anti-aliased (filtered) to "nothing" whereas if they were all drawn to a texture first and then displayed, the edges would anti-alias to the adjoining tiles and it would look better. That's uh my theory anyway. In fact I tested it by moving a larger tile made up of several smaller ones and the flickery joins were not present. Sometimes this sort of issue can be improved by adding a 1 pixel blank border round each sprite but it didn't help much with this tile problem. |
| ||
I'm actually very familiar with the line issue because my older engine suffered from it and I had to add a 1 pixel border around each tile in the texture. So, with that in mind, your demo has no line artifacting on my Intel 82845G driver. ;) It may happen on other GPU's, but it ain't happening on this one. lol.. |
| ||
hmm, weird. There should be *some* line weirdness esp. as I'm NOT adding the one pixel border in that demo. |
| ||
Grey is right about the rendering scene to texture and then to screen - that would appear much smoother. You can see this line flickering very clearly if you draw a filled rect without a texture - the edge will flicker from 1 pixel to the other instead of smoothly gliding in between a single pixel. Subpixels are achieved through texture filtering - monitors can't really display stuff in between pixels. There is another possible 'line issue' that could display a slight portion of the neighbouring texture cell due to texture filtering - this is a different issue than subpixel 'inaccuracy'. This is related to some driver/card implementations and texture clamping. |
| ||
I didn't know about the second line issue. |
| ||
As a fix for Greys stuff the following might helpFunction setwrap() D3D7GraphicsDriver().Direct3DDevice7().SetTextureStageState( 0, D3DTSS_ADDRESS, D3DTADDRESS_WRAP); End Function Function setnowrap() D3D7GraphicsDriver().Direct3DDevice7().SetTextureStageState( 0 , D3DTSS_ADDRESS , D3DTADDRESS_CLAMP) ; End Function |