In the mood for Mode 7?

BlitzMax Forums/BlitzMax Programming/In the mood for Mode 7?

Casaber(Posted 2016) [#1]
I was looking around the web for sources related to that old Nintendo graphic kind of things. I noticed some ideas here on this forum about "mode 7" and the solution was to put pixels on the screen with some math, one by one.

So I was thinking.. would it not be possible to do this better using textured quads instead? Quads is what Im working with anyways so that would be perfect.





Simple quads? Maybe some kind of line-per-line technique? Using scale and rotation or just directly warping the 4 points?
Stitching several quads together maybe? I have so many ideas right now but not very much time to try things out.

I´m wondering how your take on the problem would be?


grable(Posted 2016) [#2]
If i had access to some 3D api, quads definitely ;)
Doing it in software is of course more fun.

I have done this in software before, many years ago. But cant for the life of me remember how anymore hehe.
I did find an old thread though, that has some code you can look at: http://www.blitzbasic.com/Community/posts.php?topic=81569


Yasha(Posted 2016) [#3]
Wikipedia's take on it seems fairly clear:

https://en.wikipedia.org/wiki/Super_Nintendo_Entertainment_System_technical_specifications#Video
https://en.wikipedia.org/wiki/Mode_7

You just apply one affine transformation to the coordinates. Do this line-by-line, changing the scale with each line, and you can create fake perspective.

You could use quads instead. Sounds like it would be missing the fun though. With quads, there is no "technique", you can just use real perspective.


Casaber(Posted 2016) [#4]
@Yasha Thanks. I'm no skipping the pixel version oh no, I´m interested in both. This was just an idea I wanted to explore. I guess I was told that affine transfoimation would be not Ortho but GLUsomething
I have to check that again, it was a different perspective in OpenGL.

@Grable You want a Quad? It just takes about 13 instructions to draw a quad and 2 additional ones to load and init them so that's no problem.

You just need to add some technique to handling the style of warping and projection etc aswell.
Right now I´m using simple vertex and no arrays, as It's just a Quad but I will change that.


' Init Graphics ------------------------------------------------------------------------------------
Global xres:Int = DesktopWidth() ; yres:Int = 1080 ; SetGraphicsDriver GLMax2DDriver(),0 ' Add the 0 when you want to use frontbuffer directly instead of backbuffer
' It Seem To give about 30% additional performance without tearing And making the difference from 30 fps And 60 fps at 
' times especially in windowed mode where opengl is weaker than, fullscreen are given a boost aswell.
xres=800 ; yres = 600 ; Graphics xres,yres,0,60,GRAPHICS_BACKBUFFER + GRAPHICS_DEPTHBUFFER ; glewinit ; AutoImageFlags MASKEDIMAGE 

' Load Images --------------------------------------------------------------------------------------
exampleimage:TImage = LoadImage(LoadBank("http::icons.iconarchive.com/icons/google/chrome/64/Google-Chrome-icon.png")) ; If Not exampleimage Then RuntimeError "Unable to download file" ; End

Cls 
DrawImage exampleimage,0,0
DrawQuad exampleimage,32,32,350,64,512,380,32,318
glflush ; Flip ; WaitKey
End

Function DrawQuad(tex:TImage,x0:Float,y0:Float,x1:Float,y1:Float,x2:Float,y2:Float,x3:Float,y3:Float)
id = TGLImageFrame(tex.frame(0)).name ; glEnable GL_TEXTURE_2D ; glBindTexture GL_TEXTURE_2D,id
glBegin GL_QUADS
glTexCoord2f 0.0,0.0 ; glVertex2f x0,y0
glTexCoord2f 1.0,0.0 ; glVertex2f x1,y1
glTexCoord2f 1.0,1.0 ; glVertex2f x2,y2
glTexCoord2f 0.0,1.0 ; glVertex2f x3,y3
glEnd
glDisable GL_TEXTURE_2D
End Function



dw817(Posted 2016) [#5]
That's a lovely QUAD you have there, Casaber. I asked about this earlier for myself.



Unfortunately it is warping incorrectly despite the code looking correct.


Casaber(Posted 2016) [#6]
That one is not working on Imac? Why do i get a gray window?

Heres's one that paints to canvas using opengl by the way. It might come handy.
Import maxgui.drivers
Global xres:Int = DesktopWidth() ; yres:Int = 1080 ; xres=800 ; yres = 600
AutoImageFlags MASKEDIMAGE

Global img:TImage = LoadImage(LoadBank("http::icons.iconarchive.com/icons/google/chrome/64/Google-Chrome-icon.png"))

Global MyWindow:TGadget=CreateWindow("OpenGL in MaxGUI Canvas Gadget",400,400,640,480,Null,WINDOW_TITLEBAR | WINDOW_CLIENTCOORDS | WINDOW_RESIZABLE | WINDOW_STATUS)
Global c:TGadget=CreateCanvas(0,0,GadgetWidth(MyWindow)-200,GadgetHeight(MyWindow),MyWindow)
Local GraphicContext:TGraphics=CanvasGraphics(c) ; SetGraphics GraphicContext

SetGraphicsDriver GLMax2DDriver(),0 ; glewinit ; glMatrixMode GL_PROJECTION ; glLoadIdentity ; gluOrtho2D 0,GadgetWidth(c),GadgetHeight(c),0
glMatrixMode GL_MODELVIEW ; glLoadIdentity ; glClearColor 0,0,0,1

Global x:Int=0 ; Global y:Int=0 , Vertices:Float[] = [-20.0,-20.0,0.0,-20.0,20.0,0.0,20.0,20.0,0.0,20.0,-20.0,0.0]
global Colors:Float[] = [1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0,0.0,0.0,1.0]
ActivateGadget(c) ; AddHook EmitEventHook,HookManager

Repeat
	WaitEvent
Forever
End

Function HookManager:Object(id:Int,data:Object,context:Object)
Local event:TEvent = TEvent(data) ; If event = Null Then Return Null
Select event.source
	Case c
		Select event.id
			Case EVENT_MOUSEENTER ; HideMouse
			Case EVENT_MOUSELEAVE ; ShowMouse
			Case EVENT_MOUSEMOVE ;	x=event.x ; y=event.y ; RedrawGadget c
			Case EVENT_GADGETPAINT ; drawcanvas
		End Select
	Case MyWindow
		Select event.id
			Case EVENT_WINDOWCLOSE ; End
			Case EVENT_WINDOWSIZE ; SetGadgetShape c,0,0,event.x-200,event.y
		End Select
End Select
End Function

Function drawcanvas()	
	SetGraphics CanvasGraphics (c) ; glLoadIdentity ; glClear GL_COLOR_BUFFER_BIT ; glTranslatef x,y,0 ; glEnableClientState GL_VERTEX_ARRAY 
        glEnableClientState GL_COLOR_ARRAY ; glColorPointer 3,GL_FLOAT,0,Colors ; glVertexPointer 3,GL_FLOAT,0,Vertices
	glDrawArrays GL_QUADS,0,4 ; glDisableClientState GL_VERTEX_ARRAY ; glDisableClientState GL_COLOR_ARRAY
 
       DrawImage img,0,0 ; DrawQuad img,32,32,350,64,512,380,32,318
        glflush ; Flip
End Function

Function DrawQuad(tex:TImage,x0:Float,y0:Float,x1:Float,y1:Float,x2:Float,y2:Float,x3:Float,y3:Float)
	id = TGLImageFrame(tex.frame(0)).name ; glEnable GL_TEXTURE_2D ; glBindTexture GL_TEXTURE_2D,id ; glBegin GL_QUADS
	glTexCoord2f 0.0,0.0 ; glVertex2f x0,y0 ; glTexCoord2f 1.0,0.0 ; glVertex2f x1,y1
	glTexCoord2f 1.0,1.0 ; glVertex2f x2,y2 ; glTexCoord2f 0.0,1.0 ; glVertex2f x3,y3 ; glEnd ; glDisable GL_TEXTURE_2D
End Function



Casaber(Posted 2016) [#7]
GUI is all new territory for me I wanted to try it out and see if it worked the same.
It seems so? I really hope it does because that would make for some interesting apps with user interfaces in the future.


dw817(Posted 2016) [#8]
Casaber, I can't get this last program to run because it exceeds my resolution and you have hardwired this resolution all across the code. Could you please use window size 800x600 or smaller to test. Thanks !


Casaber(Posted 2016) [#9]
I have 800 600 hardwired (overridden) in this one?

What happens? I get a 800x600 window and no screen whatsoever shoulod pop up. just a 800x600 window. That would fit your 800x600 screen no?


dw817(Posted 2016) [#10]
Here, I'll send photo.



You can see it's attempting to draw off the edge of the screen.

...

Found problem. I was running in DEBUG mode. Disabling debug and it runs.

Does it just draw the Google circle ? That is all I'm receiving here.

If it's all the same with you, let's work on the QUADS and find out how to get a proper 3D stretch effect, Mode 7, as you said.


Casaber(Posted 2016) [#11]
Odd that it gives debug error, something slipped by me for sure.

By the way, this is how it should look like;




dw817(Posted 2016) [#12]
It does in your first code, Casaber. But how to make a trapezoid for a good 3D effect ? That's the ticket.



Gotta run, BBL.


Casaber(Posted 2016) [#13]
Thinking alloud now..

I guess you could change the ortho perspective temporarily and use
"X-plane" and "Q coordinates" which uses extra coordinates that's not actually used and just thrown away. That's all 3d mumbo jumbo to me right now. But I have no problem to dive into that if I feel that's the ticket. I guess that's the GLUperspective way. But it does not seem to be a perfect solution it only looks very similiar it. There's probably a way to make it correct, or it might simply be "good enough". It seem to be a common solution.

Then you could use shader..
http://glslsandbox.com/e#26532.

..or you could use a tehcnique which uses affine and do it line by line. This is the one I´m leaning towards. Think ripping apart an image and stretch each line or chunk of an image into a new image
(which then becomes the map from an birds point of view, or a parallal looking floor)


dw817(Posted 2016) [#14]
I'm back, nice Sunday. trying your link. That's some amazingly small code. Not sure how that would get converted. But no, not a checkerboard. Just tilting a single image - that to start with.


Casaber(Posted 2016) [#15]
If you do it with shaders, the procedure would be to have shader that processes textures, and throwing them with correct perspective onto quads. But that's not interesting yet.

It's just one of many possible shaders later. That's not where I am now.

I wanted something more simple but ya, I got back to pixelling for a few secs to try to grasp this.
I found a simpler model (the one on the forum is nice, it is complex though) I found something simpler. I have to play with it to unsderstand it as it uses negative coordinates and my heads spins when I think about that. 0,0 is the centre of the screen.

The PIXELWRITE line in use, is a 1:1 pixel copy transformation with only offset. (press left mouse button)

The commented line is the one I need to get working to get more than offset (projection, scale, rotation)
but i cant get it to work just yet. It's supposedly a working algorithm and it is the same as the shader one. By the same author at least. And it is refreshingly simple I like it, I would love to get this working as soon as I can.

I thought I'll throw it out here as It's bedtime. It was a good few hours of head scratching I dislike that I didn't any immediete results. These things takes time to learn correctly.

You see where I´m in the middle of trying to keep the values inside the box (with ABS and MOD) but it still crashes.. I dont´know..

Graphics 800,600
tile:TPixmap = LoadPixmap(LoadBank("http::epicwallpapers.com/file/154/600x338/16:9/the-world-map-from-legend-of-zelda-links-awakening_8743972.png"))
buffer = CreatePixmap (800,600,PF_RGBA8888)
Local horizon:Float , fov:Float , px:Float , py:Float , pz:Float , scaling:Float, x:Float , y:Float
Local sx:Float , sy:Float , xres:Float , yres:Float = 512 , xoff:Float = 512, yoff:Float , angle:Float

Repeat
Cls
'For y = -yres/2 To y < yres/2 ; For x = -xres/2 To x < xres/2
For y = -256 To 256 ; For x = -256 To 256
      horizon = 20 ; fov = 200 ; px = x ; py = fov ; pz = y + horizon ; sx = px / pz ; sy = py / pz ; scaling = 100
      newsx = sx*Cos(angle)-sy*Sin(angle) ; newsy = sx*Sin(angle)+sy*Cos(angle) ' When rotation is wanted aswell

      WritePixel(buffer,256+x ,256+y,ReadPixel(tile,256+((x+xoff) Mod 256),256+y Mod 256)) 

    ' WritePixel(buffer,256+x, 256+y,ReadPixel(tile,256+   ((Abs(sx*scaling+xoff)) Mod 256) , 256+ ((Abs(sy*scaling+yoff)) Mod 256)  ))

Next ; Next
If MouseDown(1) Then xoff=xoff+1
DrawPixmap buffer,0,0
Flip
Until MouseDown(2)



dw817(Posted 2016) [#16]
I'm getting a crash here on that code, Casaber.

Unhandled Exception:Pixmap coordinates out of bounds

Here's a tilter I made sometime ago. It's not perfect but it does work. This uses your map image.




Casaber(Posted 2016) [#17]
Your using setviewport as a clipper to get lines out instead of Drawsubimage I LOVE THAT. Actually Viewport is really cheap nowadays so I might pick up on that for some occasion.
My attempt to convert the Bmax code is just a big crash at the moment ya, I know I posted it anyway becuase the algorithm is so damn visible and It might give some inspiration to others while I´m working on it.

I like yours. I wonder if that could be used somehow to animate it within that trapezoid doing a fly by or something.
I was thinking to convert your scale + setviewport into a DrawsubImagerect, which scales a source rectangle out of an image onto the screen within a ceretain rectangle, scaling it to fit.

DrawSubImageRect img, x,y,w,h, x,y,w,h
But my brain began to seriosly hurt. You have a gift far superior than anything I´ve never EVER seen in making graphical solutions.


Fly by ^^
' David's nifty little Tilter (01-18-16) - needs work
Strict
Global x,y,i
Global img:TImage=LoadImage(LoadBank("http::epicwallpapers.com/file/154/600x338/16:9/the-world-map-from-legend-of-zelda-links-awakening_8743972.png"))
Graphics 1024,768

Repeat
Cls
For i=0 To 383
  SetScale .5+i/1024.0,1
  SetViewport 0,192+i,1024,1
  DrawImage img,(384+x)-i/4.0,(192+y)
Next
x=x+0 ; y=y +1 
Flip
Until MouseDown(1)





dw817(Posted 2016) [#18]
Pretty cool fly by, Casaber. But yes, you can see watching the animation that merely tilting the horizontal axis solely does not in any way merit Mode 7.

Your using setviewport as a clipper to get lines out instead of Drawsubimage I LOVE THAT.

I started messing with SetViewport really about a year ago. Setting it smaller than an image size shows only the portion you reveal. I thought I could do something interesting with that.

Now, if REAL math could get involved in the vertical and start to close up the top of it, it just MIGHT become true 3D without the need for quads.

You have a gift far superior than anything I´ve never EVER seen in making graphical solutions.

Well thank you ! :) That's very kind of you. Sometimes the simplest solutions are the right ones.

I'll always be an old-school programmer I'm afraid though. Not really ready for TYPE or OBJECT ORIENTED programming, and yet - I still get manage to squeak by.

Slight variation to erase trail:




Casaber(Posted 2016) [#19]
Just got life in the code, still very "buggy" but it seem to work. Well starting to..
Graphics 800,600
tile:TPixmap = LoadPixmap(LoadBank("http::epicwallpapers.com/file/154/600x338/16:9/the-world-map-from-legend-of-zelda-links-awakening_8743972.png"))
buffer = CreatePixmap (800,600,PF_RGBA8888)
Local horizon:Float , fov:Float , px:Float , py:Float , pz:Float , scaling:Float, x:Float , y:Float
Local sx:Float , sy:Float , xres:Float , yres:Float = 512 , xoff:Float = 512, yoff:Float , angle:Float

Repeat
Cls
'For y = -yres/2 To y < yres/2 ; For x = -xres/2 To x < xres/2
For y = -256 To 256 ; For x = -256 To 256
      horizon = 20 ; fov = 200 ; px = x ; py = fov ; pz = y + horizon ; sx = px / pz ; sy = py / pz ; scaling = 100
      newsx = sx*Cos(angle)-sy*Sin(angle) ; newsy = sx*Sin(angle)+sy*Cos(angle)

   '  WritePixel(buffer,256+x ,256+y,ReadPixel(tile,256+((x+xoff) Mod 256),256+y Mod 256)) 

     WritePixel(buffer,256+x, 256+y,ReadPixel(tile,0+   ((Abs(newsx*scaling+xoff)) Mod 256) , 0+ ((Abs(newsy*scaling+yoff)) Mod 256)  ))

Next ; Next
angle = angle +1 
If MouseDown(1) Then xoff=xoff+1
DrawPixmap buffer,0,0
Flip
Until MouseDown(2)



EDIT did some work with the Quad now, it uses fixed coordinates right now, but it might be correct perspective
I will try get time later this week to open the Openg book and read up on texture correction and related guff.


' Init Graphics ------------------------------------------------------------------------------------
Global xres:Int = DesktopWidth() ; yres:Int = 1080 ; SetGraphicsDriver GLMax2DDriver(),0 ' Add the 0 when you want to use frontbuffer directly instead of backbuffer
' It Seem To give about 30% additional performance without tearing And making the difference from 30 fps And 60 fps at 
' times especially in windowed mode where opengl is weaker than,fullscreen are given a boost aswell.
xres=800 ; yres = 600 ; Graphics xres,yres,0,60,GRAPHICS_BACKBUFFER + GRAPHICS_DEPTHBUFFER ; glewinit ; AutoImageFlags MASKEDIMAGE 

' Load Images --------------------------------------------------------------------------------------
exampleimage:TImage = LoadImage(LoadBank("http::icons.iconarchive.com/icons/google/chrome/64/Google-Chrome-icon.png")) ; If Not exampleimage Then RuntimeError "Unable to download file" ; End

Cls 
DrawImage exampleimage,0,0
DrawQuad exampleimage,32,32,350,64,512,380,32,318
glflush ; Flip ; WaitKey
End

Function DrawQuad(tex:TImage,x0:Float,y0:Float,x1:Float,y1:Float,x2:Float,y2:Float,x3:Float,y3:Float)
id = TGLImageFrame(tex.frame(0)).name ; glEnable GL_TEXTURE_2D ; glBindTexture GL_TEXTURE_2D,id

glBegin GL_TRIANGLES
glTexCoord4f 0,0,0,600-500 ; glVertex2f 500,200				' Top Left (Triangle 1 starts here)
glTexCoord4f 0,1000-50,0,1000-50 ; glVertex2f 50,700		' Bottom Left
glTexCoord4f 1000-50,1000-50,0,1000-50 ; glVertex2f 1000,700	' Bottom Right
glTexCoord4f 1000-50,1000-50,0,1000-50 ; glVertex2f 1000,700	' Bottom Right (Triangle 2 starts here)
glTexCoord4f 600-500,0,0,600-500 ; glVertex2f 600,200		' Top Right
glTexCoord4f 0,0,0,600-500 ; glVertex2f 500,200				' Top Left
glEnd

Rem Old
glBegin GL_QUADS
glTexCoord2f 0.0,0.0 ; glVertex2f x0,y0
glTexCoord2f 1.0,0.0 ; glVertex2f x1,y1
glTexCoord2f 1.0,1.0 ; glVertex2f x2,y2
glTexCoord2f 0.0,1.0 ; glVertex2f x3,y3
glEnd
EndRem

glDisable GL_TEXTURE_2D
End Function


' Init Graphics ------------------------------------------------------------------------------------
Global xres:Int = DesktopWidth() ; yres:Int = 1080 ; SetGraphicsDriver GLMax2DDriver(),0 ' Add the 0 when you want to use frontbuffer directly instead of backbuffer
' It Seem To give about 30% additional performance without tearing And making the difference from 30 fps And 60 fps at 
' times especially in windowed mode where opengl is weaker than, fullscreen are given a boost aswell.
xres=800 ; yres = 600 ; Graphics xres,yres,0,60,GRAPHICS_BACKBUFFER + GRAPHICS_DEPTHBUFFER ; glewinit ; AutoImageFlags MASKEDIMAGE 

' Load Images --------------------------------------------------------------------------------------
exampleimage:TImage = LoadImage(LoadBank("http::icons.iconarchive.com/icons/google/chrome/64/Google-Chrome-icon.png")) ; If Not exampleimage Then RuntimeError "Unable to download file" ; End

Cls 
DrawImage exampleimage,0,0
DrawQuad exampleimage,32,32,350,64,512,380,32,318
glflush ; Flip ; WaitKey
End

Function DrawQuad(tex:TImage,x0:Float,y0:Float,x1:Float,y1:Float,x2:Float,y2:Float,x3:Float,y3:Float)
id = TGLImageFrame(tex.frame(0)).name ; glEnable GL_TEXTURE_2D ; glBindTexture GL_TEXTURE_2D,id
glBegin GL_QUADS

' probably this one is the correct one
glTexCoord3f 0,0,0 ; glVertex3f x0,y0,0
glTexCoord3f 0,1,0 ; glVertex3f x1,y1,0
glTexCoord3f 1,1,0 ; glVertex3f x2,y2,0
glTexCoord3f 1,0,0 ; glVertex3f x3,y3,0

' glTexCoord3f 0,0,0 ; glVertex3f x0,y0,0
' glTexCoord3f 0,1,0 ; glVertex3f x1,y1,0
' glTexCoord3f 1,1,0 ; glVertex3f x2,y2,0
' glTexCoord3f 0,1,0 ; glVertex3f x3,y3,0 

glEnd
glDisable GL_TEXTURE_2D
End Function



Casaber(Posted 2016) [#20]
No it´s still very much like a folding paper. They behave like 2 triangles.

I will declare this to be a problem of its own. I didn't anticipate warping to be a problem. I´ve done this 25+ years ago but I can´t remember one thing of it.


Casaber(Posted 2016) [#21]
I might do the Shader just to get an easy way out of it, but I really hate to leave this problem behind. I want to solve this.


Casaber(Posted 2016) [#22]
' Init Graphics ------------------------------------------------------------------------------------
Global xres:Int = DesktopWidth() ; yres:Int = 1080 ; SetGraphicsDriver GLMax2DDriver(),0 ' Add the 0 when you want to use frontbuffer directly instead of backbuffer
' It Seem To give about 30% additional performance without tearing And making the difference from 30 fps And 60 fps at 
' times especially in windowed mode where opengl is weaker than, fullscreen are given a boost aswell.
xres=800 ; yres = 600 ; Graphics xres,yres,0,60,GRAPHICS_BACKBUFFER + GRAPHICS_DEPTHBUFFER ; glewinit ; AutoImageFlags MASKEDIMAGE 

Global trap_wide:Float = 600 ; Global trap_narrow:Float = 300 ; Global Midd:Float = 320

' Load Images --------------------------------------------------------------------------------------
exampleimage:TImage = LoadImage(LoadBank("http::icons.iconarchive.com/icons/google/chrome/64/Google-Chrome-icon.png")) ; If Not exampleimage Then RuntimeError "Unable to download file" ; End
Local qx0:Float = 0.0,qy0:Float = 64.0 , qx1:Float =100.0,qy1:Float = 100.0 , qx2:Float = 640.0,qy2:Float = 512.0 , qx3:Float = 0.0,qy3:Float = 320.0

Repeat
Cls
' DrawImage exampleimage,0,0
DrawQuad exampleimage,qx0,qy0,qx1,qy1,qx2,qy2,qx3,qy3

trap_wide = MouseX()
glflush ; Flip
Until MouseDown(1)

Function DrawQuad(tex:TImage,x0:Float,y0:Float,x1:Float,y1:Float,x2:Float,y2:Float,x3:Float,y3:Float)
id = TGLImageFrame(tex.frame(0)).name ; glEnable GL_TEXTURE_2D ; glBindTexture GL_TEXTURE_2D,id
glMatrixMode GL_PROJECTION ; glLoadIdentity ; glOrtho 0,640,0,480,1,1000 ; glMatrixMode GL_MODELVIEW ; glLoadIdentity
glBegin GL_TRIANGLE_STRIP ' ; glColor3f 1,1,-1

' HARDCODED WORKING EXAMPLE THAT MIGHT HELP UNDERSTAND HOW TO DO IT
' COMMENTED LINES ARE 2d COORDINATES
' THE 2 LINES UNDERNETH EACH COMMENT ARE PROJECTIVE COORDINATES THAT NEEDS TO BE SOMEHOW CALCUALTED PROGRAMTICALLY

' Correct trapzoid (but 4 hardcoded coordinates) 

' glTexCoord4f 0,0,0,1   < 2d coordinate
glTexCoord4f 0,0,0,trap_wide ; glVertex3f Midd - trap_wide/2,10,-10 ' < magically calculated projection coordinate from that 2d coordinate..

' glTexCoord4f 1,0,0,1
glTexCoord4f trap_narrow,0,0,trap_narrow ; glVertex3f Midd - trap_narrow/2,470,-10

' glTexCoord4f 0,1,0,1
glTexCoord4f 0,trap_wide,0,trap_wide ; glVertex3f Midd + trap_wide/2,10,-10

' glTexCoord4f 1,1,0,1
glTexCoord4f trap_narrow,trap_narrow,0,trap_narrow ; glVertex3f Midd + trap_narrow/2,470,-10

glEnd
glDisable GL_TEXTURE_2D
End Function



Casaber(Posted 2016) [#23]
I´m thinking.. this now.

As 2 triangles are so inseparatble when warping perspectives. Let's not have two but one for a sprite. And cut the edges instead.

This will also demand some serious downtime with openg few weeks from nowl. But I like the idea.




Casaber(Posted 2016) [#24]
Or one could skip the idea of quads until all shaders are coming along nicely?

Maybe that's a good idea to NOT have quads for awhile. I would really like that.
One could have software quads, easy peasy, they are fast enough for Nintendo graphics and then some.

And we may have shader quads soon enough, but let's not have these triangular problems.
The solutions are ugly and old, soon non-existent, They seem to be shaders underneath the surface anyway so nothing is lost.


Casaber(Posted 2016) [#25]
I wanted to see If I could learn some Monkey while at it, and convert something. Almost there. Very nice.


' Simple Mode 7 (buggy conversion from Monkey)

Local XRes:Float = 640 , YRes:Float = 480 , YLines:Float = 240 , X:Float = 930 , Y:Float = 700 , Height:Float = 32 , r:Float = 90-45 , HFOV:Float = 60 , VFOV:Float = 45
Local vas:Float[Ylines] , vd:Float[Ylines] , VScale:Float[Ylines] , VDelta:Float = VFOV / YLines , va:Float = VDelta , hl:Float
Local camd:Float , camx:Float , camy:Float , cosine:Float , sine:Float , xhit:Float , yhit:Float , xpos:Float , ypos:Float

For a = 0 To YLines-1 ; vas[a] = va ; vd[a] = Height / Tan(vas[a]) ; hl = (vd[a]*Tan(HFOV)) * 2 ; VScale[a] = (1024/hl) * (1024/XRes) ; va = va + VDelta ; Next

SetGraphicsDriver GLMax2DDriver(),0 ; Graphics xres,yres,0,60,GRAPHICS_BACKBUFFER + GRAPHICS_DEPTHBUFFER ; glewinit ; AutoImageFlags MASKEDIMAGE 

Local map:TImage = LoadImage(LoadBank("http::epicwallpapers.com/file/154/600x338/16:9/the-world-map-from-legend-of-zelda-links-awakening_8743972.png"))
mapw = ImageWidth(map) ; maph = ImageHeight(map)

Repeat

If MouseDown(1) Then x = x + Cos(r) * 2 ; y = y + -Sin(r) * 2 ; r = r- ((MouseX() - (xres/2.0)) / xres)*2
x = x + (KeyDown(KEY_UP)-KeyDown(KEY_DOWN)) * Cos(r) * 4
y = y + (KeyDown(KEY_UP)-KeyDown(KEY_DOWN)) * -Sin(r) * 4
r = r - (KeyDown(KEY_RIGHT) - KeyDown(KEY_LEFT)) * 2
			
camd = Tan(22.5) * Height * 5 ; camX = X - (camd * Cos(r)) ; camY = Y - (camd * -Sin(r)) ; camDir = r ; cosine = Cos(camDir) ; sine = -Sin(camDir) ' Cam pos

For a = 8 To YLines-1 Step 1
	XHit = camX + (Cosine*vd[a]) ; yhit = camY + (Sine*vd[a]) ; xpos = XHit Mod mapw ; ypos = YHit Mod maph
	If XHit < 0 Then xpos = 1024-xpos
	If YHit < 0 Then ypos = 1024-ypos
	
    ' Clip, rotate, scale, translate before drawing
 	  SetViewport 0,yres/2+a,xres,1 ' CLIP
      SetHandle xpos,ypos '(MOVE) ''  SetImageHandle map,xpos,ypos '(MOVE)
    ' Translate xres/2,240+a ' TRANSLATE (another MOVE)
      SetRotation -(camDir-90) ' ROTATE
      SetScale VScale[a],VScale[a] ' SCALE 
    ' DrawImage map,xres/2,240+a ' TRANSLATE IS INBUILT HERE NOW
	  TileImage map,0,0,0
Next

SetViewport 0,0,xres,yres
Flip
Until MouseDown(2)

' Replacement for the inbuilt TILEIMAGE which otherwise ignores current SCALE and ROTATION settings.
Function TileImage(image:TImage, x:Float=0# ,y:Float=0#, frame:Int=0)
    Local scale_x#, scale_y#
    GetScale(scale_x#, scale_y#)
    Local viewport_x%, viewport_y%, viewport_w%, viewport_h%
    GetViewport(viewport_x, viewport_y, viewport_w, viewport_h)
    Local origin_x#, origin_y#
    GetOrigin(origin_x, origin_y)
    Local handle_X#, handle_y#
    GetHandle(handle_X#, handle_y#)
    Local image_h# = ImageHeight(image)
    Local image_w# = ImageWidth(image)
    Local w#=image_w * Abs(scale_x#)
    Local h#=image_h * Abs(scale_y#)
    Local ox#=viewport_x-w+1
    Local oy#=viewport_y-h+1
    origin_X = origin_X Mod w
    origin_Y = origin_Y Mod h
    Local px#=x+origin_x - handle_x
    Local py#=y+origin_y - handle_y
    Local fx#=px-Floor(px)
    Local fy#=py-Floor(py)
    Local tx#=Floor(px)-ox
    Local ty#=Floor(py)-oy
    If tx>=0 tx=tx Mod w + ox Else tx = w - -tx Mod w + ox
    If ty>=0 ty=ty Mod h + oy Else ty = h - -ty Mod h + oy
    Local vr#= viewport_x + viewport_w, vb# = viewport_y + viewport_h
    SetOrigin 0,0
    Local iy#=ty
    While iy<vb + h
        Local ix#=tx
        While ix<vr + w
            DrawImage(image, ix+fx,iy+fy, frame)
            ix=ix+w
        Wend
        iy=iy+h
    Wend
    SetOrigin origin_x, origin_y
End Function


Original .Monkey




Casaber(Posted 2016) [#26]
This is how the Nintendo did it in hardware. Changing offset, rotation and scaling per scanline, it also matches nicely
what is good practice with modern GPU's. It's the perfect match and, It's simple.

Not bugfree yet though, not that simple.


dw817(Posted 2016) [#27]
You definitely got something going on there, Casaber. I'll mention this in the hopes it helps. Not trying to promote another programming language. There is however one called B4GL or as some people call it BAGEL. It is geared very heavily for 3D but misses a lot of the good and powerful functions BlitzMAX has (otherwise I would be there instead of here).

I'll post one of its demos and source for you.

https://www.mediafire.com/?7a1dds7d8rjszup


Casaber(Posted 2016) [#28]
@dw817 what features do you miss from the other BASIC's you´ve used? is there some certain things that are important? What do you find BMax lacks?

About the Mode7 and graphics-wwise me tell you graphics is about covered since few last days. This is just for fun and learning Bmax and Monkey mainly, I thought Mode7 would be fun. It's one of those things I´ve always wanted to do myself from the ground up. I´ve seen numrous implementations. But this one is the best think, It's very close to yours actually as you might noticed. You did it very SNES like.


dw817(Posted 2016) [#29]
It's the other way around, Casaber. BlitzMAX has just about everything I need. Almost. There are a few things that are pretty advanced I could do in GFA that I don't think you can do in BlitzMAX. Not easily anyways.

No, I was saying that B4GL has some serious limitations that make it unusable for my code.

The first of which is the ability to have controls like you can do in MaxGUI. No, you can only make graphic screens. No ability to INCBIN any data, you must use a DLL just to have any audio, and it definitely has no ability to access the Internet or load any files from the Internet either.

It truly is a game programming language - and not much more.

BlitzMAX on the other hand has been shining through many interesting types of ways to express itself. No, it is with BlitzMAX I will make my home.

As for Mode 7, I think the best example you will see of this is Nintendo's shining and epic 3D racer, "F-Zero."

https://www.youtube.com/watch?v=BIe4AvKLSa4


Casaber(Posted 2016) [#30]
I tried to convert that Mode7 bc it looked good what I saw and it even had that simple Quad that I did at first And yet it was perspectiv correctly, I was half right. 3D is not my area, and this is the perfect entrance now. Just starting touching it abit.

I failed to convert everything so it won´t even run, it was close though. I think I know what's missing, the knowledge of matrixes. I should not even try to grasp that just yet.
It was interesting to see that old quad there though in all its glory.


Local camera:Float[3*3] , temp:Float[3] , x:Float , y:Float , xaxis:Float , yxais:Float , speed:Float = 0.02 
camera = MatrixTranslate 0,5,0

Repeat
    glClear GL_DEPTH_BUFFER_BIT + GL_COLOR_BUFFER_BIT
    glLoadMatrixf RTInvert(camera)

    For x = -10 To 10
        For y = -10 To 10
            glPushMatrix
            glTranslatef x*3,0,y*3
            glBegin GL_QUADS
                glColor3f 1,0,0) ; glVertex2f  1,3
                glColor3f 0,1,0) ; glVertex2f -1,3
                glColor3f 0,0,1) ; glVertex2f -1,0
                glColor3f 1,1,1) ; glVertex2f  1,0
            glEnd
            glPopMatrix
        Next
    Next

    Flip  
    
    While SyncTimer(10)
        camera(3) = camera(3) + -camera(2) * speed
        xaxis = -MouseX() * 30
        yaxis =  MouseY() * 30
        If KeyDown(KEY_LEFT)  Then xaxis =  1
        If KeyDown(KEY_RIGHT) Then xaxis = -1
        If KeyDown(KEY_UP)    Then yaxis = -1
        If KeyDown(KEY_DOWN)  Then yaxis =  1


        camera = camera * MatrixRotateZ (xaxis) * MatrixRotateX (yaxis)
        temp = camera(3)
        camera(3) = vec4 (0,0,0,1)
        camera = MatrixRotateY (camera# (0)(1)) * camera
        camera(3) = temp
        camera = Orthonormalize (camera)

    Wend
Forever



Casaber(Posted 2016) [#31]
Ya F-zero is really nice, also Zelda I intenesly remember seeing those for the first time, it was.. just magic. I adds something special to the games. I hope the upcoming bug fix will make a really nice implementation of MODE 7.

The next step would to be throw a CRT shader ontop of it. And you got that true retro look.


dw817(Posted 2016) [#32]
Ah now you're pushing it, Casaber. What I really like is a filter called 2xSal (Kreed). As it is there are only 2 filters BlitzMAX has for cleaning up graphics, dithered and not.

But with an emulator like ZSNES and other game emulators, you have quite a few selections available.



https://en.wikipedia.org/wiki/Image_scaling


Casaber(Posted 2016) [#33]
I must tell you in before hand, I got the most amazing speed out of those old pixel code, actually you could do probably it all using CPU (at least some of those). And still have time.

I got 25 320x240 writes, 25 reads of 320x240 screens so far, with few shaders ontop and offlinebuffers with static pixels, and pushing everything on opengl single (non double buffered) screen .

Double buffered works equally well but I seem to push 30% more power when single buffering.
Alpha rotation and scale seem to work when you want to throw your offline canvases on the screen (no cost essentially).

So you could stretch low resolutions. And you could draw using any image and any primtive and read it back as pixels (all gpu and with alpha rotation blending), do something with the cpu.
I only need to setup a few standard shaders now, and add stencil/depth support (I really want that I´m not sure for what yet but it will do much good).


But this time I want to give it time to mature and give out something with quality.

(I´m often to eager and throw out my code too fast)


Casaber(Posted 2016) [#34]
For example.. If you use just a single of all your offline buffer as a screen-sized (or bigger) then you have a backbuffer for doublebufferin on your very own, one that you may clear only when you like it.

And it won't be a misseehving backbuffer that you hope works on all machines, but a true buffer that does its job on each and every machine quick and easy.

About that CRT shader it will actually be a color mask where you could use those older style palettes (I will reuse that mask for a GLOW/BLOOM shader which uses that palette remapper to pick out lighter colors that you would want to glow). So it will actually be CRT/GLOW/Palette shader. Multipurpose :)

I forgot !! one of my favorite shaders I'm thinking is something like this (not the bending though, don´t like that so much , but the style otherwise, imagine having a crispness setting and the like you know? ya.. Really nice). It got big, you should view it from about a mile to appriciate it.


But I have big hopes for this, it makes BMax a fun language to use.

I really start to enjoy programming again, and I guess that's my little baby haha.
It´s a shame it takes abit of time to unwind and get into the mood. I need a few good hours each time to get something sensible done.

Anyways hope your weekend treated you good ! :) I'll be signing off


dw817(Posted 2016) [#35]
Casaber, I can do that, the old-style CRT look ? Notta problem.



Bedtime, c-ya on the morrow.


Casaber(Posted 2016) [#36]
Rememeber that font you liked? I do that with it already. I do a harsch scanline (black) I really enjoy that. Glow is better than anliasing I think So I rather have it harsch and/or glowing.

Wanna see? This is also a good example I think what you can to do without ANY hardware, just pixels all the way by hand.
EDIT About the inlined function, it's nothing, just move it if you want to, it won't mind. I was doing something. Also notice it's not optimised or anything (i even left some MOD's in there). Still runs smooth.

This is part of my little baby so be good to it ;)

Global buffer:Int[128*128]
Local img2:TPixmap=LoadPixmap(LoadBank("http::s14.postimg.org/xotmro9vh/128x1282.png"))
For y=0 To 127 ; For x=0 To 127 ; buffer(x+y*128) = ReadPixel(img2,x,y) ; Next ; Next

AddHook FlipHook,irq,Null

Global fps:Int , fpstotal:Int , msstart:Float , ms:Float = MilliSecs()
Global map[4096,4096] ; For y=0 To 316 ; For x=0 To 475; map[x,y]=Rnd(127) ; Next ; Next
For temp=0 To 255 ; map[temp,0]=temp ; Next
Global cmap[4096,4096] ; For y=0 To 316 ; For x=0 To 475; cmap[x,y]=Rnd(15) ; Next ; Next
Global wx:Int , wy:Int , cx:Int , c:Int = buffer[0] , cols:Int[16]
RestoreData colors ; For temp=0 To 15 ; ReadData cols[temp] ; Next
writetomap "                                        ",0,0
writetomap "    **** COMMODORE 64 BASIC V2 ****     ",0,1
writetomap "                                        ",0,2
writetomap " 64K RAM SYSTEM  38911 BASIC BYTES FREE ",0,3
writetomap "                                        ",0,4
writetomap "READY.                                  ",0,5
writetomap "                                        ",0,4
Global xres:Int = 1280 , yres:Int = 800 , pixels:Int[xres*yres] , s:Int = 2
GLGraphics xres,yres,0,60,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER ' Fullscreen, change 32 into 0 for windowed.
glViewport 0,0,xres,yres ; glMatrixMode GL_PROJECTION ; glLoadIdentity ; glOrtho 0,xres,yres,0,0,1 ; glPixelZoom 1,-1 ; glRasterPos2i 0,0

Repeat
For y = 0 To yres-1 Step 1 ; For x = 0 To xres-1 Step 1 ; pixels[y*xres+x] = 0 ; Next ; Next

Function readinputs()
wx=wx-s*KeyDown(KEY_LEFT)+s*KeyDown(KEY_RIGHT) ; wy=wy-s*KeyDown(KEY_UP)+s*KeyDown(KEY_DOWN) ; wx=Max(0,Min(10000,wx)) ; wy=Max(0,Min(10000,wy))
mx = MouseX() ; my = MouseY() ; xxx = (wx+mx) Sar 4 ; yyy = (wy+my) Sar 4 ; If MouseDown(1) Then map(xxx,yyy) = 42 ; cmap(xxx,yyy) = 7
End Function

readinputs ; wx=wx-s*KeyDown(KEY_LEFT)+s*KeyDown(KEY_RIGHT) ; wy=wy-s*KeyDown(KEY_UP)+s*KeyDown(KEY_DOWN) ; wx=Max(0,Min(10000,wx)) ; wy=Max(0,Min(10000,wy))
alive ' Animate tiles, colortiles, and atlas
display wx,wy,0,0,1280,800 ' Create buffer
glDrawPixels xres,yres,GL_BGRA,GL_UNSIGNED_BYTE,Pixels ' Render buffer

Delay 1 ; Flip 1
 
Until KeyHit(KEY_ESCAPE) Or AppTerminate()
End

Function alive()
For temp=1 To 16 ; buffer[Int(Rand(0,15))+Int((Rand(0,15))*128)]=$ffffffff * Int(Rand(0,1)) ; Next
cx=(cx+1) Mod 128 ; For tempy=0 To 15 ; For temp=0 To 15+32 ; buffer[16+temp+((0+tempy)*128)]=buffer[0+temp+cx+(16+tempy)*128] ; Next ; Next ; For temp=1 To 1600 ; cmap[Rnd(400),Rnd(250)]=Rnd(16) ; Next
End Function

Function display(wx,wy,ofx=0,ofy=0,sx=640,sy=400) 
scrx = wx & 15 ; scry = wy & 15 ; mapx = wx Shr 4 ; mapy = wy Shr 4
cnty = 0 ; For y = mapy To mapy+(sy Shr 4+1)
cntx = 0 ; For x = mapx To mapx+(sx Shr 4+1)
tilex = map(x,y) & 15 ; tiley = map(x,y) Shr 4
colour = cols(cmap(x,y)) ; xx = cntx - scrx + ofx ; yy=cnty - scry + ofy
For ty = 0 To 7 ; For tx = 0 To 7
If (xx+tx+tx >= ofx) And (yy+ty+ty >= ofy) And (xx+tx+tx < ofx+sx) And (yy+ty+ty < ofy+sy).. ' Mind the steps
 And buffer[tilex Shl 3 + tx + (tiley Shl 3 + ty)*128] <> c Then pixels[xx+tx+tx+(yy+ty+ty)*xres] = colour 
Next ; Next
cntx = cntx+16 ; Next
cnty = cnty+16 ; Next
End Function

Function writetomap(t$,x,y)
	For tempx = 0 To Len(t$)-1 ; temp = Asc(Mid$(t$,1+tempx,1)) ; map[tempx+x,y] = temp ; cmap[tempx+x,y] = 14 ; Next
End Function

#colors
DefData $000000,$FFFFFF,$68372B,$70A4B2,$6F3D86,$588D43,$352879,$B8C76F,$6F4F25,$433900,$9A6759,$444444,$6C6C6C,$9AD284,$6C5EB5,$959595

Function irq:Object(id,data:Object,context:Object)	
	Return data
End Function



Chalky(Posted 2016) [#37]
That is one cool piece of code! It would make a great screensaver.


dw817(Posted 2016) [#38]
Wow, that's a crazy looking thing, Casaber. Reminds me of some screens I've seen when the APPLE used to crash when I was working on zealous machine-code.


Casaber(Posted 2016) [#39]
Thanks Chalky and Dw817. You can scroll and paint in it too. It's a 16 Megabyte world. Perfect for making some platformer or shootem up :) or anything you want.


Casaber(Posted 2016) [#40]
My thumb hurts from coding today, I think it's time to rest. I´m glad that you enjoyed that piece of code.

The Mode 7 though is only halfdebugged, on the good side that means that only turns are wrong now.

' Simple Mode 7 (buggy conversion from Monkey)

Local XRes:Float = 640 , YRes:Float = 480 , YLines:Float = 240 , X:Float = 930 , Y:Float = 700 , Height:Float = 32 , r:Float = 90-45 , HFOV:Float = 60 , VFOV:Float = 45
Local vas:Float[Ylines] , vd:Float[Ylines] , VScale:Float[Ylines] , VDelta:Float = VFOV / YLines , va:Float = VDelta , hl:Float
Local camd:Float , camx:Float , camy:Float , cosine:Float , sine:Float , xhit:Float , yhit:Float , xpos:Float , ypos:Float

For a = 0 To YLines-1 ; vas[a] = va ; vd[a] = Height / Tan(vas[a]) ; hl = (vd[a]*Tan(HFOV)) * 2 ; VScale[a] = (1024/hl) * (1024/XRes) ; va = va + VDelta ; Next

SetGraphicsDriver GLMax2DDriver(),0 ; Graphics xres,yres,0,60,GRAPHICS_BACKBUFFER + GRAPHICS_DEPTHBUFFER ; glewinit ; AutoImageFlags MASKEDIMAGE 

Local map:TImage = LoadImage(LoadBank("http::epicwallpapers.com/file/154/600x338/16:9/the-world-map-from-legend-of-zelda-links-awakening_8743972.png"))
mapw = ImageWidth(map) ; maph = ImageHeight(map)

Repeat
If MouseDown(1) Then x = x + Cos(r) * 2 ; y = y + -Sin(r) * 2 ; r = r- ((MouseX() - (xres/2.0)) / xres)*2
x = x + (KeyDown(KEY_UP)-KeyDown(KEY_DOWN)) * Cos(r) * 4
y = y + (KeyDown(KEY_UP)-KeyDown(KEY_DOWN)) * -Sin(r) * 4
r = r - (KeyDown(KEY_RIGHT) - KeyDown(KEY_LEFT)) * 2
			
camd = Tan(22.5) * Height * 5 ; camX = X - (camd * Cos(r)) ; camY = Y - (camd * -Sin(r)) ; camDir = r ; cosine = Cos(camDir) ; sine = -Sin(camDir) ' Cam pos

For a = 8 To YLines-1 Step 1
	XHit = camX + (Cosine*vd[a]) ; yhit = camY + (Sine*vd[a]) ; xpos = XHit Mod mapw ; ypos = YHit Mod maph
	If XHit < 0 Then xpos = 1024-xpos
	If YHit < 0 Then ypos = 1024-ypos
	
    ' Clip, rotate, scale, translate before drawing
 	  SetViewport 0,yres/2+a,xres,1
      SetImageHandle map,xpos,ypos
      SetRotation -(camDir-90)
      SetScale VScale[a],VScale[a] 
	  TileImage map,xres/2,240+a
Next

SetViewport 0,0,xres,yres
Flip
Until MouseDown(2)

' Replacement for the inbuilt TILEIMAGE which otherwise ignores current SCALE and ROTATION settings.
Function TileImage(image:TImage, x:Float=0# ,y:Float=0#, frame:Int=0)
    Local scale_x#, scale_y#
    GetScale(scale_x#, scale_y#)
    Local viewport_x%, viewport_y%, viewport_w%, viewport_h%
    GetViewport(viewport_x, viewport_y, viewport_w, viewport_h)
    Local origin_x#, origin_y#
    GetOrigin(origin_x, origin_y)
    Local handle_X#, handle_y#
    GetHandle(handle_X#, handle_y#)
    Local image_h# = ImageHeight(image)
    Local image_w# = ImageWidth(image)
    Local w#=image_w * Abs(scale_x#)
    Local h#=image_h * Abs(scale_y#)
    Local ox#=viewport_x-w+1
    Local oy#=viewport_y-h+1
    origin_X = origin_X Mod w
    origin_Y = origin_Y Mod h
    Local px#=x+origin_x - handle_x
    Local py#=y+origin_y - handle_y
    Local fx#=px-Floor(px)
    Local fy#=py-Floor(py)
    Local tx#=Floor(px)-ox
    Local ty#=Floor(py)-oy
    If tx>=0 tx=tx Mod w + ox Else tx = w - -tx Mod w + ox
    If ty>=0 ty=ty Mod h + oy Else ty = h - -ty Mod h + oy
    Local vr#= viewport_x + viewport_w, vb# = viewport_y + viewport_h
    SetOrigin 0,0
    Local iy#=ty
    While iy<vb + h
        Local ix#=tx
        While ix<vr + w
            DrawImage(image, ix+fx,iy+fy, frame)
            ix=ix+w
        Wend
        iy=iy+h
    Wend
    SetOrigin origin_x, origin_y
End Function



Kryzon(Posted 2016) [#41]
Reminds me of this thread:
http://www.blitzbasic.com/Community/posts.php?topic=99346

Some of the images are down. There's a few additional images in the archived page:
http://web.archive.org/web/20130728075410/http://blitzbasic.com/Community/posts.php?topic=99346

But the images in the first post of that thread are definitely gone.


Casaber(Posted 2016) [#42]
@Kryzon that one looked really nice, hills and valleys where bloody sexy.

Back over inflatland I had a last second debug moment here before leaving, I tried away with the - in rotation and BAM, working. Who'd guess.
It needs some polishing and understanding now. But I feel really good about this.

' Simple Mode 7 (Conversion from Monkey)

Local XRes:Float = 640 , YRes:Float = 480 , YLines:Float = 240 , X:Float = 930 , Y:Float = 700 , Height:Float = 32 , r:Float = 90-45 , HFOV:Float = 60 , VFOV:Float = 45
Local vas:Float[Ylines] , vd:Float[Ylines] , VScale:Float[Ylines] , VDelta:Float = VFOV / YLines , va:Float = VDelta , hl:Float
Local camd:Float , camx:Float , camy:Float , cosine:Float , sine:Float , xhit:Float , yhit:Float , xpos:Float , ypos:Float

For a = 0 To YLines-1 ; vas[a] = va ; vd[a] = Height / Tan(vas[a]) ; hl = (vd[a]*Tan(HFOV)) * 2 ; VScale[a] = (1024/hl) * (1024/XRes) ; va = va + VDelta ; Next

SetGraphicsDriver GLMax2DDriver(),0 ; Graphics xres,yres,0,60,GRAPHICS_BACKBUFFER + GRAPHICS_DEPTHBUFFER ; glewinit ; AutoImageFlags MASKEDIMAGE 

Local map:TImage = LoadImage(LoadBank("http::epicwallpapers.com/file/154/600x338/16:9/the-world-map-from-legend-of-zelda-links-awakening_8743972.png"))
mapw = ImageWidth(map) ; maph = ImageHeight(map)

Repeat
Cls
If MouseDown(1) Then x = x + Cos(r) * 2 ; y = y + -Sin(r) * 2 ; r = r- ((MouseX() - (xres/2.0)) / xres)*2
x = x + (KeyDown(KEY_UP)-KeyDown(KEY_DOWN)) * Cos(r) * 4
y = y + (KeyDown(KEY_UP)-KeyDown(KEY_DOWN)) * -Sin(r) * 4
r = r - (KeyDown(KEY_RIGHT) - KeyDown(KEY_LEFT)) * 2
			
camd = Tan(22.5) * Height * 5 ; camX = X - (camd * Cos(r)) ; camY = Y - (camd * -Sin(r)) ; camDir = r ; cosine = Cos(camDir) ; sine = -Sin(camDir) ' Cam pos

For a = 8 To YLines-1 Step 1
	XHit = camX + (Cosine*vd[a]) ; yhit = camY + (Sine*vd[a]) ; xpos = XHit Mod mapw ; ypos = YHit Mod maph
	If XHit < 0 Then xpos = 1024-xpos
	If YHit < 0 Then ypos = 1024-ypos
	
     ' Clip, rotate, scale, translate before drawing.
      SetViewport 0,yres/2+a,xres,1
      SetImageHandle map,xpos,ypos
      SetRotation (camDir-90)
      SetScale VScale[a],VScale[a] 
      DrawImage map,xres/2,240+a 
      'TileImage map,xres/2,240+a
Next

SetViewport 0,0,xres,yres
Flip
Until MouseDown(2)

' Replacement for the inbuilt TILEIMAGE which otherwise ignores current SCALE and ROTATION settings.
Function TileImage(image:TImage, x:Float=0# ,y:Float=0#, frame:Int=0)
    Local scale_x#, scale_y#
    GetScale(scale_x#, scale_y#)
    Local viewport_x%, viewport_y%, viewport_w%, viewport_h%
    GetViewport(viewport_x, viewport_y, viewport_w, viewport_h)
    Local origin_x#, origin_y#
    GetOrigin(origin_x, origin_y)
    Local handle_X#, handle_y#
    GetHandle(handle_X#, handle_y#)
    Local image_h# = ImageHeight(image)
    Local image_w# = ImageWidth(image)
    Local w#=image_w * Abs(scale_x#)
    Local h#=image_h * Abs(scale_y#)
    Local ox#=viewport_x-w+1
    Local oy#=viewport_y-h+1
    origin_X = origin_X Mod w
    origin_Y = origin_Y Mod h
    Local px#=x+origin_x - handle_x
    Local py#=y+origin_y - handle_y
    Local fx#=px-Floor(px)
    Local fy#=py-Floor(py)
    Local tx#=Floor(px)-ox
    Local ty#=Floor(py)-oy
    If tx>=0 tx=tx Mod w + ox Else tx = w - -tx Mod w + ox
    If ty>=0 ty=ty Mod h + oy Else ty = h - -ty Mod h + oy
    Local vr#= viewport_x + viewport_w, vb# = viewport_y + viewport_h
    SetOrigin 0,0
    Local iy#=ty
    While iy<vb + h
        Local ix#=tx
        While ix<vr + w
            DrawImage(image, ix+fx,iy+fy, frame)
            ix=ix+w
        Wend
        iy=iy+h
    Wend
    SetOrigin origin_x, origin_y
End Function




dw817(Posted 2016) [#43]
That's a bit better, Casaber. Here are a few lines to change. As you can see, you will need to use REAL # numbers in order to slow down the movements to normal.
x = x + (KeyDown(KEY_UP)-KeyDown(KEY_DOWN)) * Cos(r/10.0)
y = y + (KeyDown(KEY_UP)-KeyDown(KEY_DOWN)) * -Sin(r/1000.0)
r = r - (KeyDown(KEY_RIGHT) - KeyDown(KEY_LEFT))/10.0


Looking forward to seeing your next installment soon !


Casaber(Posted 2016) [#44]
another debugged one
this one does not behave like the original, but to me looks more stable, the controls are off though.




Casaber(Posted 2016) [#45]
@DW817 those lines only adds slow hickups to my movement? I defined all as floats already? How do you mean real numbers they should be already?


Casaber(Posted 2016) [#46]
Hey in the new one, the hickups does not appear when I throw in those lines. I think that's a good sign. But this is not how the original behave, and I did a one on one comparison?

I must have dreamt it. I run the Monkey original code beside the Bmax, and i get the 1st version, not the 2nd one.

Could there have been a bug in the original Monkey code?
I'm abit lost 1st I thought that it was all about truth values being different in Monkey and BMax (1 vs -1).

But then this 2nd (which I got by trying negating the trigonometry directly) made me.. confused. I´m not sure what was the original bug now.


Casaber(Posted 2016) [#47]
hah add in an x=x+1 or y=y+1 and enjoy your ride :)

Seem to work alright, now it's the controls only left to do. Happy as can be.
It can endless tile fine aswell.

But not with that png it get black areas for some reason.

EDIT I think it wants square pictures, the original SNES had 128x128 tiles with 8x8 tiles, making a 1024x1024).
That's the hardcoded 1024 I'll bet changing those to width and height of the image would be half the fix,
and having square image would be the other half I have the feeling.


Casaber(Posted 2016) [#48]
r I think is still buggy, you cant do r=r+1, it circles instead of swirling the camera


Casaber(Posted 2016) [#49]
The original Monkey code Swirls with r=r+1 but.. it has a buggy horizon instead (it can´t tile forever it gives alot of graphic errors at the horizon),
which the latest post here fixes (not intended but it happened). Maybe the original had an bug, I´m not the one to ask, but it looks like it might.

But the camera rotation are circling in this one. That's the only thing left to fix now.


Casaber(Posted 2016) [#50]
this is just a retouched one, where I will contine with later
fixed the small things I´ve said above in it.




Casaber(Posted 2016) [#51]
Testing height (it was fixed in Monkey version but actually it's real nice to use it dynamically. Okay, time to leave I´m hooked on this.

It still has areas inbetween them if you look carefully you see it at those great heights.. okay the bug level might best be unsaid now. But it's something visually comprehensible at least now, I like that.



Also trying this, it might be correctly tiling, the dark areas i saw might have been just the gray shadings of the pic I´m currently using. I need more time to test.
But right now it looks okay. It has some kind of bendyness to it though. The world belongs to bugs..




dw817(Posted 2016) [#52]
GOOD JOB ON THAT LAST ONE !! You are definitely getting there. Get the rotation down and I think you'll have the whole thing, Casaber !


Casaber(Posted 2016) [#53]
Thanks, but this was HARD.

Any rotation expert in the house?
I´m exhausted here.

I would really like to get this specific implementation working, It's the one that's true to the SNES how it did it too.


Casaber(Posted 2016) [#54]
So I did this instead, which is based on a tutorial I found on the web for MODE 7. I think the previous MODE7 on this forum was based on the same tutorial. (It has a bug btw in it's rotation AND ffffff should be MOD 360 in it)

But here I went for another simple approach (the only thing I skimped on was the X scale and Y Scale, i mashed them together because I don´t need to scale x and y differently.
So there's just "scale", which is nice.

It's a simple (but complete) MODE 7 which uses pixelling, based on a great tutorial. I'm very pleased to have a software too. It's FAST.
I'm just intrigued by the concept of the previous version though so any help would be appreciated.



' Simple Mode 7 using software rendering (quick, 60 fps+)

Local a:Float = 0 , x:Float = 0 , y:Float = 0 , dx:Float = 0 , dy:Float = 0 , speed:Float = 0
Global space_z:Float = 50 , horizon:Int = 20, scale:Float = 1000 , obj_scale:Float = 50 ' Set view and scale here.
Global map:Int[1024*1024] ; Global img:TPixmap = LoadPixmap(LoadBank("http::static.monstermmorpg.com/images/maps/Meteor-Falls.png"))
For y=0 To 1023 ; For x=0 To 1024 ; map(x+y*1024) = ReadPixel(img,x,y) ; Next ; Next ' Load png 1024 x 1024

Global xres:Int = 1024 , yres:Int = 768 , pixels:Int[xres*yres]
GLGraphics xres,yres,0,60,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER ; glViewport 0,0,xres,yres
glMatrixMode GL_PROJECTION ; glLoadIdentity ; glOrtho 0,xres,yres,0,0,1 ; glPixelZoom 1,-1 ; glRasterPos2i 0,0

x=0 ; y = 0 ; a= 0
Repeat
	speed = speed + KeyDown(KEY_UP)*0.1-KeyDown(KEY_DOWN)*0.1 ; speed=Max(-5,Min(5,speed))
    a = a         - KeyDown(KEY_LEFT)+KeyDown(KEY_RIGHT) ; a = a Mod 360
	dx = speed * Cos (a) ; dy =  speed * Sin (a) ; x :+ dx ; y :+ dy
	drawplane a,x,y
	Delay 1 ; Flip 1
Until KeyHit(KEY_ESCAPE) Or AppTerminate()
End

Function drawplane(a:Float,cx:Float,cy:Float)
	Local sx:Int , sy:Int , d:Float , hs:Float , maskx:Int = 1023 , masky:Int = 1023 
	Local dx:Float , dy:Float , spx:Float,spy:Float 
	For sy = 0 Until yres-1
		d = space_z*scale / Float(sy + horizon) ; hs = d / scale ; dx = -Sin(a) * hs ; dy = Cos(a) * hs
		spx = cx + d*Cos(a) - Float(xres) / 2 * dx ; spy = cy + d*Sin(a) - Float(xres) / 2 * dy
		For sx = 0 Until xres-1 ; pixels[sx+sy*xres] = map[  (Int(spx) & maskx) + ( (Int(spy) & masky)*1024)]
		spx :+ dx ; spy :+ dy ; Next
	Next
	glDrawPixels xres,yres,GL_BGRA,GL_UNSIGNED_BYTE,pixels
End Function



Everything is presentation so if you want to see it in action fullscreen with some style I personally adore. Here you go.




Casaber(Posted 2016) [#55]
This is pushing it but (this COULD and SHOULD drop below 60fps on weak machines ..1st draft, heavy optimization is next step. It doesnot have to drop below 60 fps on weak machines , that's the important bit.

EDIT If you put this in singlebuffering mode, and fullscreen mode, it will probably push it well above 60 fps wo. optimzation, asis.

The point is, these are LIVE tiles, pixel level and tile levelled animated per frame, and colorised live aswell (1 color per tile), could be used for animating ocean tiles for instance.
Don´t mind about my retro text, I', using it for a few things, so ya.. it's there

Just place youreself somewhere on the map, and stand still for a moment, and look upon the horizon full of ..STUFF.. it looks like waves. Very soothing.


Global buffer:Int[128*128]
Local img2:TPixmap=LoadPixmap(LoadBank("http::s14.postimg.org/xotmro9vh/128x1282.png"))
For yy=0 To 127 ; For xx=0 To 127 ; buffer(xx+yy*128) = ReadPixel(img2,xx,yy) ; Next ; Next
AddHook FlipHook,irq,Null ; Global fps:Int , fpstotal:Int , msstart:Float , ms:Float = MilliSecs()

Global map[4096,4096] ; For yy=0 To 316 ; For xx=0 To 475; map[xx,yy]=Rnd(127) ; Next ; Next
For temp=0 To 255 ; map[temp,0]=temp ; Next
Global cmap[4096,4096] ; For yy=0 To 316 ; For xx=0 To 475; cmap[xx,yy]=Rnd(15) ; Next ; Next
Global wx:Int , wy:Int , cx:Int , c:Int = buffer[0] , cols:Int[16]
RestoreData colors ; For temp=0 To 15 ; ReadData cols[temp] ; Next
writetomap "                                        ",0,0
writetomap "    **** COMMODORE 64 BASIC V2 ****     ",0,1
writetomap "                                        ",0,2
writetomap " 64K RAM SYSTEM  38911 BASIC BYTES FREE ",0,3
writetomap "                                        ",0,4
writetomap "READY.                                  ",0,5
writetomap "                                        ",0,4

Global xres:Int = 1024 , yres:Int = 768 ' Set resolution here.

Local a:Float = 0 , x:Float = 0 , y:Float = 0 , dx:Float = 0 , dy:Float = 0 , speed:Float = 0
Global space_z:Float = 50 , horizon:Int = 20, scale:Float = 1000 , obj_scale:Float = 50 ' Set view and scale here.
Global pmap:Int[1024*1024] ; Global img:TPixmap = LoadPixmap(LoadBank("http::static.monstermmorpg.com/images/maps/Meteor-Falls.png"))
For y=0 To 1023 ; For x=0 To 1024 ; pmap(x+y*1024) = ReadPixel(img,x,y) ; Next ; Next ' Load png 1024 x 1024
Global pixels:Int[xres*yres] ' 1024 x 1024

GLGraphics xres,yres,0,60,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER ; glViewport 0,0,xres,yres ' Set 0 for Windowed, 32 for Fullscreen here.
glMatrixMode GL_PROJECTION ; glLoadIdentity ; glOrtho 0,xres,yres,0,0,1 ; glPixelZoom 1,-1 ; glRasterPos2i 0,0 ; HideMouse

Repeat

Function readinputs()
	wx=wx-s*KeyDown(KEY_LEFT)+s*KeyDown(KEY_RIGHT) ; wy=wy-s*KeyDown(KEY_UP)+s*KeyDown(KEY_DOWN) ; wx=Max(0,Min(10000,wx)) ; wy=Max(0,Min(10000,wy))
	mx = MouseX() ; my = MouseY() ; xxx = (wx+mx) Sar 4 ; yyy = (wy+my) Sar 4 ; If MouseDown(1) Then map(xxx,yyy) = 42 ; cmap(xxx,yyy) = 7
End Function

	alive
        For yy = 0 To 1023 ; For xx = 0 To 1023 ; pmap[yy*1024+xx] = 0 ; Next ; Next ' Clear
        display wx,wy,0,0,1024,1024 ' Render tiles
	
	speed = speed + KeyDown(KEY_UP)*0.1-KeyDown(KEY_DOWN)*0.1 ; speed=Max(-5,Min(5,speed))
        a = a               - KeyDown(KEY_LEFT)+KeyDown(KEY_RIGHT) ; a = a Mod 360
	dx = speed * Cos (a) ; dy =  speed * Sin (a) ; x :+ dx ; y :+ dy
	drawplane a,x,y
	glDrawPixels xres,yres,GL_BGRA,GL_UNSIGNED_BYTE,pixels

	 Delay 1 ; Flip 1
Until KeyHit(KEY_ESCAPE) Or AppTerminate()
End

Function drawplane(a:Float,cx:Float,cy:Float)
	Local sx:Int , sy:Int , d:Float , hs:Float , maskx:Int = 1023 , masky:Int = 1023 
	Local dx:Float , dy:Float , spx:Float,spy:Float 
	For sy = 0 Until yres-1 Step 2
		d = space_z*scale / Float(sy + horizon) ; hs = d / scale ; dx = -Sin(a) * hs ; dy = Cos(a) * hs
		spx = cx + d*Cos(a) - Float(xres) / 2 * dx ; spy = cy + d*Sin(a) - Float(xres) / 2 * dy
		For sx = 0 Until xres-1 ; pixels[sx+sy*xres] = pmap[(Int(spx) & maskx) + ( (Int(spy) & masky)*1024)]
		spx :+ dx ; spy :+ dy ; Next
	Next
End Function

Function alive()
	For temp=1 To 16 ; buffer[Int(Rand(0,15))+Int((Rand(0,15))*128)]=$ffffffff * Int(Rand(0,1)) ; Next
	cx=(cx+1) Mod 128 ; For tempy=0 To 15 ; For temp=0 To 15+32 ; buffer[16+temp+((0+tempy)*128)]=buffer[0+temp+cx+(16+tempy)*128] ; Next ; Next ; For temp=1 To 1600 ; cmap[Rnd(400),Rnd(250)]=Rnd(16) ; Next
End Function

Function display(wx,wy,ofx=0,ofy=0,sx=640,sy=400) 
	scrx = wx & 15 ; scry = wy & 15 ; mapx = wx Shr 4 ; mapy = wy Shr 4
	cnty = 0 ; For y = mapy To mapy+(sy Shr 4+1)
	cntx = 0 ; For x = mapx To mapx+(sx Shr 4+1)
	tilex = map(x,y) & 15 ; tiley = map(x,y) Shr 4
	colour = cols(cmap(x,y)) ; xx = cntx - scrx + ofx ; yy=cnty - scry + ofy
	For ty = 0 To 7 ; For tx = 0 To 7
	If (xx+tx+tx >= ofx) And (yy+ty+ty >= ofy) And (xx+tx+tx < ofx+sx) And (yy+ty+ty < ofy+sy).. ' Mind the steps
 	 And buffer[tilex Shl 3 + tx + (tiley Shl 3 + ty)*128] <> c Then pmap[xx+tx+tx+(yy+ty+ty)*1024] = colour 
	Next ; Next
	cntx = cntx+16 ; Next
	cnty = cnty+16 ; Next
End Function

Function writetomap(t$,x,y)
	For tempx = 0 To Len(t$)-1 ; temp = Asc(Mid$(t$,1+tempx,1)) ; map[tempx+x,y] = temp ; cmap[tempx+x,y] = 14 ; Next
End Function

Function irq:Object(id,data:Object,context:Object)	
	Return data
End Function

#colors
DefData $000000,$FFFFFF,$68372B,$70A4B2,$6F3D86,$588D43,$352879,$B8C76F,$6F4F25,$433900,$9A6759,$444444,$6C6C6C,$9AD284,$6C5EB5,$959595



Chalky(Posted 2016) [#56]
There's a bug in that code:
For y=0 To 1023 ; For x=0 To 1024 ; pmap(x+y*1024) = ReadPixel(img,x,y) ; Next ; Next ' Load png 1024 x 1024
should be:
For y=0 To 1023 ; For x=0 To 1023 ; pmap(x+y*1024) = ReadPixel(img,x,y) ; Next ; Next ' Load png 1024 x 1024
Very nice effect though.


Casaber(Posted 2016) [#57]
Ya I've been buzy with alot of versions, it slipped in., there was a few more, I fixed them all here.

Rem ------------------------------------------
 ___     ____  _____  _____   ______   ______  
|    \  /    |/     \|     \ |   ___| |___   | 
|     \/     ||     ||      \|   ___|   /   /  
|__/\__/|____|\_____/|______/|______|  |___|   

EndRem

Global buffer:Int[128*128]
Local img2:TPixmap=LoadPixmap(LoadBank("http::s14.postimg.org/xotmro9vh/128x1282.png"))
For yy=0 To 127 ; For xx=0 To 127 ; buffer(xx+yy*128) = ReadPixel(img2,xx,yy) ; Next ; Next
AddHook FlipHook,irq,Null ; Global fps:Int , fpstotal:Int , msstart:Float , ms:Float = MilliSecs()

Global map[4096,4096] ; For yy=0 To 316 ; For xx=0 To 475; map[xx,yy]=Rnd(127) ; Next ; Next
For temp=0 To 255 ; map[temp,0]=temp ; Next
Global cmap[4096,4096] ; For yy=0 To 316 ; For xx=0 To 475; cmap[xx,yy]=Rnd(15) ; Next ; Next
Global wx:Int , wy:Int , cx:Int , c:Int = buffer[0] , cols:Int[16] , s:Int = 2
RestoreData colors ; For temp=0 To 15 ; ReadData cols[temp] ; Next
writetomap "                                        ",0,0
writetomap "    **** COMMODORE 64 BASIC V2 ****     ",0,1
writetomap "                                        ",0,2
writetomap " 64K RAM SYSTEM  38911 BASIC BYTES FREE ",0,3
writetomap "                                        ",0,4
writetomap "READY.                                  ",0,5
writetomap "                                        ",0,4

Global xres:Int = 1024 , yres:Int = 768 , pixels:Int[xres*yres] ' Set resolution here.
Local a:Float = 0 , x:Float = 0 , y:Float = 0 , dx:Float = 0 , dy:Float = 0 , speed:Float = 0
Global space_z:Float = 50 , horizon:Int = 20, scale:Float = 1000 , obj_scale:Float = 50 ' Set view and scale here.
Global pmap:Int[1024*1024] ; Global img:TPixmap = LoadPixmap(LoadBank("http::static.monstermmorpg.com/images/maps/Meteor-Falls.png"))
For y=0 To 1023 ; For x=0 To 1023 ; pmap(x+y*1024) = ReadPixel(img,x,y) ; Next ; Next ' Load png 1024 x 1024

GLGraphics xres,yres,0,60,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER ; glViewport 0,0,xres,yres ' Set 0 for Windowed, 32 for Fullscreen here.
glMatrixMode GL_PROJECTION ; glLoadIdentity ; glOrtho 0,xres,yres,0,0,1 ; glPixelZoom 1,-1 ; glRasterPos2i 0,0 '; HideMouse

Repeat

Function readinputs()
	wx=wx-s*KeyDown(KEY_LEFT)+s*KeyDown(KEY_RIGHT) ; wy=wy-s*KeyDown(KEY_UP)+s*KeyDown(KEY_DOWN) ; wx=Max(0,Min(10000,wx)) ; wy=Max(0,Min(10000,wy))
	mx = MouseX() ; my = MouseY() ; xxx = (wx+mx) Sar 4 ; yyy = (wy+my) Sar 4 ; If MouseDown(1) Then map(xxx,yyy) = 42 ; cmap(xxx,yyy) = 7
End Function
   
	' Comment if you want static landscape, uncomment for live tile landscape
	For yy = 0 To 1023 ; For xx = 0 To 1023 ; pmap[yy*1024+xx] = 0 ; Next ; Next ; alive ; display wx,wy,0,0,1024,1024 ' Render tiles

	speed = speed + KeyDown(KEY_UP)*0.1-KeyDown(KEY_DOWN)*0.1 ; speed=Max(-5,Min(5,speed))
        a = a               - KeyDown(KEY_LEFT)+KeyDown(KEY_RIGHT) ; a = a Mod 360
	dx = speed * Cos (a) ; dy =  speed * Sin (a) ; x :+ dx ; y :+ dy
	drawplane a,x,y
       ' readinputs ; for yy = 0 To yres ; For xx = 0 To 1023 ; pixels[yy*xres+xx] = pmap[yy*1024+xx]  ; Next ; Next
	glDrawPixels xres,yres,GL_BGRA,GL_UNSIGNED_BYTE,pixels
	Delay 1 ; Flip 1
Until KeyHit(KEY_ESCAPE) Or AppTerminate()
End

Function drawplane(a:Float,cx:Float,cy:Float)
	Local sx:Int , sy:Int , d:Float , hs:Float , maskx:Int = 1023 , masky:Int = 1023 
	Local dx:Float , dy:Float , spx:Float,spy:Float 
	For sy = 0 Until yres-1 Step 2
		d = space_z*scale / Float(sy + horizon) ; hs = d / scale ; dx = -Sin(a) * hs ; dy = Cos(a) * hs
		spx = cx + d*Cos(a) - Float(xres) / 2 * dx ; spy = cy + d*Sin(a) - Float(xres) / 2 * dy
		For sx = 0 Until xres-1 ; pixels[sx+sy*xres] = pmap[(Int(spx) & maskx) + ( (Int(spy) & masky)*1024)] ; spx :+ dx ; spy :+ dy ; Next
	Next
End Function

Function alive()
	For temp=1 To 16 ; buffer[Int(Rand(0,15))+Int((Rand(0,15))*128)]=$ffffffff * Int(Rand(0,1)) ; Next
	cx=(cx+1) Mod 128 ; For tempy=0 To 15 ; For temp=0 To 15+32 ; buffer[16+temp+((0+tempy)*128)]=buffer[0+temp+cx+(16+tempy)*128] ; Next ; Next ; For temp=1 To 1600 ; cmap[Rnd(400),Rnd(250)]=Rnd(16) ; Next
End Function

Function display(wx,wy,ofx=0,ofy=0,sx=640,sy=400) 
	scrx = wx & 15 ; scry = wy & 15 ; mapx = wx Shr 4 ; mapy = wy Shr 4
	cnty = 0 ; For y = mapy To mapy+(sy Shr 4+1)
	cntx = 0 ; For x = mapx To mapx+(sx Shr 4+1)
	tilex = map(x,y) & 15 ; tiley = map(x,y) Shr 4
	colour = cols(cmap(x,y)) ; xx = cntx - scrx + ofx ; yy=cnty - scry + ofy
	For ty = 0 To 7 ; For tx = 0 To 7
	If (xx+tx+tx >= ofx) And (yy+ty+ty >= ofy) And (xx+tx+tx < ofx+sx) And (yy+ty+ty < ofy+sy).. ' Mind the steps
 	 And buffer[tilex Shl 3 + tx + (tiley Shl 3 + ty)*128] <> c Then pmap[xx+tx+tx+(yy+ty+ty)*1024] = colour 
	Next ; Next
	cntx = cntx+16 ; Next
	cnty = cnty+16 ; Next
End Function

Function writetomap(t$,x,y)
	For tempx = 0 To Len(t$)-1 ; temp = Asc(Mid$(t$,1+tempx,1)) ; map[tempx+x,y] = temp ; cmap[tempx+x,y] = 14 ; Next
End Function

Function irq:Object(id,data:Object,context:Object)	
	Return data
End Function

#colors
DefData $000000,$FFFFFF,$68372B,$70A4B2,$6F3D86,$588D43,$352879,$B8C76F,$6F4F25,$433900,$9A6759,$444444,$6C6C6C,$9AD284,$6C5EB5,$959595



Casaber(Posted 2016) [#58]
Ya I've been buzy with alot of versions, it slipped in., there was a few more, I fixed them all here.

Rem ------------------------------------------
 ___     ____  _____  _____   ______   ______  
|    \  /    |/     \|     \ |   ___| |___   | 
|     \/     ||     ||      \|   ___|   /   /  
|__/\__/|____|\_____/|______/|______|  |___|   

EndRem

Global buffer:Int[128*128]
Local img2:TPixmap=LoadPixmap(LoadBank("http::s14.postimg.org/xotmro9vh/128x1282.png"))
For yy=0 To 127 ; For xx=0 To 127 ; buffer(xx+yy*128) = ReadPixel(img2,xx,yy) ; Next ; Next
AddHook FlipHook,irq,Null ; Global fps:Int , fpstotal:Int , msstart:Float , ms:Float = MilliSecs()

Global map[4096,4096] ; For yy=0 To 316 ; For xx=0 To 475; map[xx,yy]=Rnd(127) ; Next ; Next
For temp=0 To 255 ; map[temp,0]=temp ; Next
Global cmap[4096,4096] ; For yy=0 To 316 ; For xx=0 To 475; cmap[xx,yy]=Rnd(15) ; Next ; Next
Global wx:Int , wy:Int , cx:Int , c:Int = buffer[0] , cols:Int[16] , s:Int = 2
RestoreData colors ; For temp=0 To 15 ; ReadData cols[temp] ; Next
writetomap "                                        ",0,0
writetomap "    **** COMMODORE 64 BASIC V2 ****     ",0,1
writetomap "                                        ",0,2
writetomap " 64K RAM SYSTEM  38911 BASIC BYTES FREE ",0,3
writetomap "                                        ",0,4
writetomap "READY.                                  ",0,5
writetomap "                                        ",0,4

Global xres:Int = 1024 , yres:Int = 768 , pixels:Int[xres*yres] ' Set resolution here.
Local a:Float = 0 , x:Float = 0 , y:Float = 0 , dx:Float = 0 , dy:Float = 0 , speed:Float = 0
Global space_z:Float = 50 , horizon:Int = 20, scale:Float = 1000 , obj_scale:Float = 50 ' Set view and scale here.
Global pmap:Int[1024*1024] ; Global img:TPixmap = LoadPixmap(LoadBank("http::static.monstermmorpg.com/images/maps/Meteor-Falls.png"))
For y=0 To 1023 ; For x=0 To 1023 ; pmap(x+y*1024) = ReadPixel(img,x,y) ; Next ; Next ' Load png 1024 x 1024

GLGraphics xres,yres,0,60,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER ; glViewport 0,0,xres,yres ' Set 0 for Windowed, 32 for Fullscreen here.
glMatrixMode GL_PROJECTION ; glLoadIdentity ; glOrtho 0,xres,yres,0,0,1 ; glPixelZoom 1,-1 ; glRasterPos2i 0,0 '; HideMouse

Repeat

Function readinputs()
	wx=wx-s*KeyDown(KEY_LEFT)+s*KeyDown(KEY_RIGHT) ; wy=wy-s*KeyDown(KEY_UP)+s*KeyDown(KEY_DOWN) ; wx=Max(0,Min(10000,wx)) ; wy=Max(0,Min(10000,wy))
	mx = MouseX() ; my = MouseY() ; xxx = (wx+mx) Sar 4 ; yyy = (wy+my) Sar 4 ; If MouseDown(1) Then map(xxx,yyy) = 42 ; cmap(xxx,yyy) = 7
End Function
   
	' Comment if you want static landscape, uncomment for live tile landscape
	For yy = 0 To 1023 ; For xx = 0 To 1023 ; pmap[yy*1024+xx] = 0 ; Next ; Next ; alive ; display wx,wy,0,0,1024,1024 ' Render tiles

	speed = speed + KeyDown(KEY_UP)*0.1-KeyDown(KEY_DOWN)*0.1 ; speed=Max(-5,Min(5,speed))
        a = a               - KeyDown(KEY_LEFT)+KeyDown(KEY_RIGHT) ; a = a Mod 360
	dx = speed * Cos (a) ; dy =  speed * Sin (a) ; x :+ dx ; y :+ dy
	drawplane a,x,y
       ' readinputs ; for yy = 0 To yres ; For xx = 0 To 1023 ; pixels[yy*xres+xx] = pmap[yy*1024+xx]  ; Next ; Next
	glDrawPixels xres,yres,GL_BGRA,GL_UNSIGNED_BYTE,pixels
	Delay 1 ; Flip 1
Until KeyHit(KEY_ESCAPE) Or AppTerminate()
End

Function drawplane(a:Float,cx:Float,cy:Float)
	Local sx:Int , sy:Int , d:Float , hs:Float , maskx:Int = 1023 , masky:Int = 1023 
	Local dx:Float , dy:Float , spx:Float,spy:Float 
	For sy = 0 Until yres-1 Step 2
		d = space_z*scale / Float(sy + horizon) ; hs = d / scale ; dx = -Sin(a) * hs ; dy = Cos(a) * hs
		spx = cx + d*Cos(a) - Float(xres) / 2 * dx ; spy = cy + d*Sin(a) - Float(xres) / 2 * dy
		For sx = 0 Until xres-1 ; pixels[sx+sy*xres] = pmap[(Int(spx) & maskx) + ( (Int(spy) & masky)*1024)] ; spx :+ dx ; spy :+ dy ; Next
	Next
End Function

Function alive()
	For temp=1 To 16 ; buffer[Int(Rand(0,15))+Int((Rand(0,15))*128)]=$ffffffff * Int(Rand(0,1)) ; Next
	cx=(cx+1) Mod 128 ; For tempy=0 To 15 ; For temp=0 To 15+32 ; buffer[16+temp+((0+tempy)*128)]=buffer[0+temp+cx+(16+tempy)*128] ; Next ; Next ; For temp=1 To 1600 ; cmap[Rnd(400),Rnd(250)]=Rnd(16) ; Next
End Function

Function display(wx,wy,ofx=0,ofy=0,sx=640,sy=400) 
	scrx = wx & 15 ; scry = wy & 15 ; mapx = wx Shr 4 ; mapy = wy Shr 4
	cnty = 0 ; For y = mapy To mapy+(sy Shr 4+1)
	cntx = 0 ; For x = mapx To mapx+(sx Shr 4+1)
	tilex = map(x,y) & 15 ; tiley = map(x,y) Shr 4
	colour = cols(cmap(x,y)) ; xx = cntx - scrx + ofx ; yy=cnty - scry + ofy
	For ty = 0 To 7 ; For tx = 0 To 7
	If (xx+tx+tx >= ofx) And (yy+ty+ty >= ofy) And (xx+tx+tx < ofx+sx) And (yy+ty+ty < ofy+sy).. ' Mind the steps
 	 And buffer[tilex Shl 3 + tx + (tiley Shl 3 + ty)*128] <> c Then pmap[xx+tx+tx+(yy+ty+ty)*1024] = colour 
	Next ; Next
	cntx = cntx+16 ; Next
	cnty = cnty+16 ; Next
End Function

Function writetomap(t$,x,y)
	For tempx = 0 To Len(t$)-1 ; temp = Asc(Mid$(t$,1+tempx,1)) ; map[tempx+x,y] = temp ; cmap[tempx+x,y] = 14 ; Next
End Function

Function irq:Object(id,data:Object,context:Object)	
	Return data
End Function

#colors
DefData $000000,$FFFFFF,$68372B,$70A4B2,$6F3D86,$588D43,$352879,$B8C76F,$6F4F25,$433900,$9A6759,$444444,$6C6C6C,$9AD284,$6C5EB5,$959595



Here's the Mode7, which is much simpler on its own, static only. It might be useful for people when learning. (This is normal MODE7 without any bells and whistles).




dw817(Posted 2016) [#59]
Congratulations, Casaber ! You do realize you are likely the only programmer that has solved mode 7 =AND= posted the source code to show how it's done !

When you're up to it, next challenge. Take a good look at F-Zero. See how they have a static background (not 3D) above the horizon (vertical 3rd of screen), and that everything below is true and lovely 3D.




Casaber(Posted 2016) [#60]
dw817 :) Well there was a previous post here, I learned alot from that. Or, at least I got the idea roughly, I didn't learn from it becuase it was very complex source.
But I found the blog behind it, and got the ideas clearly from there. I had to look around quiet a few nights before finding a good simple solution.

Still, I want that HW solution that reminds me of SNES concepts (and yours too actually). You use the same mindset as they did with setting scale (and they also use rotation) and set it once differently per scanline.

It's a real headscratcher to get it working (and the Monkey original has it's flaw so I cannot rely 100% on it either). It's a rare solution The SNES engineers knew what they did.
They kind of started the whole thing and made 3d more readily available.. MODE7..inspired into Raycasting DOOM I guess etc and before we know it we ended up have amazing
3d games in 2016, and get all nostagic about 1980's again. That's how it works ;)

If anyone wants to try check it out and help, I would be most grateful and sleep better at night.

PS the TileiImage is robust and perfect condition. So it's not much code to look through actually, I guess good matrix knowledge and a clear mind is what's sought after, I have neither ;)




dw817(Posted 2016) [#61]
So far the best code I've seen of the bunch you've done so far, Casaber, is this one. Perfect acceleration and rotation. Works great here !



What really astounds me about this code is you can choose any vector to rotate, start accelerating, and it is flawless and seamless tiling despite the angle chosen.


Casaber(Posted 2016) [#62]
Thanks, I like that too, havn't seen any simpler mode7 example so far so I am abit proud of that one. Terseness important for clearity.

There are parallals in this to the hardware of course, so I could fix the hw later when the math feels like home. Which would take a few years ;)


Casaber(Posted 2016) [#63]
I'll bet someone will take this and make an even shorter now ;)

That's how things work haha.

I only wanted to have a good demo for my little project.. you know, I made this into a a few sleepless nights instead :s

You're planning to use this kind of graphics in you RPG project?


dw817(Posted 2016) [#64]
Perhaps, Casaber. It depends. As I am ultimately working on a new RPG Maker, I would use it to tilt the screen for when you travel by Airship, just like Final Fantasy.

Yet ... your code could also yield a great racing game with a bit of work involved. Might be interesting to see. :)


Casaber(Posted 2016) [#65]
Nice !! if you need help later with something just shout :) keep in touch!!

Over here things are quickly getting more and more into being normal so I won´t have as much time to spend on coding. I learnt alot about Bmax this week I feel.

A very descent platform to do stuff on, and I hope Monkey2 will only improve on it !


Casaber(Posted 2016) [#66]
I've changed my public email my old died, it's CassandrawCastell@...
This is my new trash mail but I check it at times.


dw817(Posted 2016) [#67]
Urrf ... Notta good idea to post your Email in the forum,Casaber. I know you can publicly list it in your account but SPAM 'bots often do cull through the Internet for any stray Emails to add to the collective.

And is it Cassandra ? That's fine. :) Once again some very fine coding you've done up there. I spent about an hour last night looking it over - trying to understand it. It's all very advanced to me.


BlitzSupport(Posted 2016) [#68]
Awesome demos here!


Casaber(Posted 2016) [#69]
Want to show one last thing how I did (halfway I´m in the middle of things) optimize the slow IF..THEN and show the difference.


This is only true for emmense loops, "inner" loops, "long" loops, you know those times when extreme amounts of things just needs to get done within a timeframe.
It's easy too look at modern machine as gods of infinite speed. Or rather they *are* extremely fast but what we throw at them (invisibly sometimes in the code) makes them very sad, and
it's hard to know sometimes as the rules are complex and hidden , you just have to test/measure and be surprised. Some things got learned with exprience sure. But not all.

They use every kind of odd thing to try speed them up.. caches, predictions, aligments, parallal instructions (SSE/NEON) which very nice actually but have so
very mechanical rules in what order registers should be used to get best speed. Everything gets very unhumane and these things are left best to the compilers to handle, but when the compiler fails it really bites your tail.

This is the original code with not too much thought into (well actually there are much thought gone into details in this one and that hit it with the ugly bat. you can's see every intention in
the code. Which I hate, but it was needed to bring things up to speed. And it didn't quiet make even then. It can't keep up with 60 fps. If it does you have a strong machine. Exremely strong. I get almost 60fps with loads of dropouts on my 3Ghz Mac.



This though, which I leave unravelled to be read.. This is the horror that happened when I wanted to get ONE IF .. THEN out of a very very intense loop. In the above.
This is what slowed it down (the major cause at least, there lots left you could do, but as you see in other place you could even have trigonometry without problem).
We're talking scale of
about 300.000 x 8 x 8 times that an IF..THEN happens. that was the slowdown. This one below should run fine. Even if it's in the middle of the optimizing process.
Don't even try to grasp that code. I need to code this within few days or I will forget what I was doing forever.

So ya, just wanted to show, that optimizing is needed when it really counts.
This brought this code up to speed.

60 fps here we come.

Global buffer:Int[256*256] ; Local img2:TPixmap=LoadPixmap(LoadBank("http::www.foxhack.net/files/blog/2008/01-09-charadata.png"))  ' Load a 256x256 Atlas with 8x8 tiles
For yy=0 To 255 ; For xx=0 To 255 ; buffer(xx+yy*256) = ReadPixel(img2,xx,yy) ; Next ; Next
Global map[4096,4096] , wx:Int , wy:Int , cx:Int , c:Int = buffer[0] , s:Int = 2 , xres:Int = 1024 , yres:Int = 768 , pixels:Int[xres*yres]
Local a:Float = 0 , x:Float = 0 , y:Float = 0 , dx:Float = 0 , dy:Float = 0 , speed:Float = 0
Global space_z:Float = 50 , horizon:Int = 20, scale:Float = 1000 , obj_scale:Float = 50 , pmap:Int[1024*1024] 

' Fill world with atlas 1:1 side by side
For worldy=0 To 4095 Step 32 ; For worldx=0 To 4095 Step 32 ; For temp2=0 To 31 ; For temp=0 To 31 ;	map(temp+worldx,temp2+worldy)=temp+temp2*32 ; Next ; Next ; Next ; Next
GLGraphics xres,yres,0,60,GRAPHICS_BACKBUFFER | GRAPHICS_DEPTHBUFFER ; glViewport 0,0,xres,yres
glMatrixMode GL_PROJECTION ; glLoadIdentity ; glOrtho 0,xres,yres,0,0,1 ; glPixelZoom 1,-1 ; glRasterPos2i 0,0 

Repeat
	For yy = 0 To 1023 ; For xx = 0 To 1023 ; pmap[yy*1024+xx] = 0 ; Next ; Next
	display wx,wy,    0,0,1024,1024 ' DRAW INTO AN NON-TILE ALIGNED RECTANGLE TO VIEW BUG
	speed = speed + KeyDown(KEY_UP)*0.1-KeyDown(KEY_DOWN)*0.1 ; speed=Max(-5,Min(5,speed))
	a = a - KeyDown(KEY_LEFT)+KeyDown(KEY_RIGHT) ; a = a Mod 360
	dx = speed * Cos (a) ; dy =  speed * Sin (a) ; x :+ dx ; y :+ dy
	drawplane a,x,y
	alive
	' readinputs ; For yy = 0 To yres ; For xx = 0 To 1023 ; pixels[yy*xres+xx] = pmap[yy*1024+xx]  ; Next ; Next ' Show 2D
	glDrawPixels xres,yres,GL_BGRA,GL_UNSIGNED_BYTE,pixels
	Delay 1 ; Flip 1
Until KeyHit(KEY_ESCAPE) Or AppTerminate()
End

Function readinputs()
	wx=wx-s*KeyDown(KEY_LEFT)+s*KeyDown(KEY_RIGHT) ; wy=wy-s*KeyDown(KEY_UP)+s*KeyDown(KEY_DOWN) ; wx=Max(0,Min(10000,wx)) ; wy=Max(0,Min(10000,wy))
	mx = MouseX() ; my = MouseY() ; xxx = (wx+mx) Sar 3 ; yyy = (wy+my) Sar 3; If MouseDown(1) Then map(xxx,yyy) = 42
End Function

Function alive()
	For temp=1 To 16 ; buffer[Int(Rand(0,15))+Int((Rand(0,15))*256)]=$ffffffff * Int(Rand(0,1)) ; Next
	cx=cx+1 Mod 256 ; For tempy=0 To 15 ; For temp=0 To 15+32 ; buffer[16+temp+((0+tempy)*256)]=buffer[0+temp+cx+(16+tempy)*256] ; Next ; Next
	'For temp=1 To 100 ; map[Rnd(1023),Rnd(1023)]=Rnd(1023) ; Next ' Change few tiles each frame (this could be thousands no problem, mind the rnd when it gets large though.)
End Function

Function drawplane(a:Float,cx:Float,cy:Float) ' This might need optimzation too but it has not as intens loops so actually it does not need at all as much as display() does.
	Local sx:Int , sy:Int , d:Float , hs:Float , maskx:Int = 1023 , masky:Int = 1023 
	Local dx:Float , dy:Float , spx:Float,spy:Float 
	For sy = 0 Until yres-1 Step 2
		d = space_z*scale / Float(sy + horizon) ; hs = d / scale ; dx = -Sin(a) * hs ; dy = Cos(a) * hs
		spx = cx + d*Cos(a) - Float(xres) / 2 * dx ; spy = cy + d*Sin(a) - Float(xres) / 2 * dy
		For sx = 0 Until xres-1 ; pixels[sx+sy*xres] = pmap[(Int(spx) & maskx) + ( (Int(spy) & masky)*1024)] ; spx :+ dx ; spy :+ dy ; Next
	Next
End Function

Function display(wx,wy,ofx=0,ofy=0,sx=640,sy=400) ' Here's where most optimization is needed (as it is it can't keep up with steady 60 fps on average machines, 
' every teeny fraction of Bmax knowledge should be put in here. Unrolling helps, less conditions helps.
	scrx = wx & 7 ; scry = wy & 7 ; mapx = wx Shr 3 ; mapy = wy Shr 3
	
	' Draw 99% of the tiles, inside the frame so we don´t need any conditions
	cnty = 8 ; For y = mapy+1 To mapy+(sy Shr 3-1)
	cntx = 8 ; For x = mapx+1 To mapx+(sx Shr 3-1)
	tilex = map(x,y) & 31 ; tiley = map(x,y) Shr 5 ' 32 tiles per line in atlas
	xx = cntx - scrx + ofx ; yy=cnty - scry + ofy



' IN THE MIDDLE OF THE OPTIMIZATION PROCESS..

' Doing many tiles and the horror below is half-simplified and unrolled (partly improved) of what the CPU does.
' What was before an invisible mess that the CPU had To go through.
' What slowed everything down mostly was         300.000 x 8 x 8 IF THEN, nothing else mostly.

' 2 Inner most loops of code have been partly unrolled here, and here we see something in the middle of the optimizing process, and its' towards the end actually.
' Very messy and the need to do these kind of thought processes are intense, and might even hide the intention of code (not always though), but often. Sometimes they actually clear things up. But we're are in the middle of things now. So, very messy.

' 99 % of the tiiles need no clipping, meaning no expansive IF THEN.

For ty = 0 To 7
a = (tiley Shl 3 + ty) Shl 8 ; a = a + tilex Shl 3 ; b = xx+(yy+ty) Shl 10
pmap[b] = buffer[a] ; pmap[b+1] = buffer[a+1] ; pmap[b+2] = buffer[a+2] ; pmap[b+3] = buffer[a+3]
pmap[b+4] = buffer[a+4] ; pmap[b+5] = buffer[a+5] ; pmap[b+6] = buffer[a+6] ; pmap[b+7] = buffer[a+7]
Next
cntx = cntx+8 ; Next
cnty = cnty+8 ; Next

' Now most is draw, we still have the outer "frame" of tiles, th edges, the top, bottom, left and right and these needs clipping. 

' Let's get saucy.

' TOP FRAME OF TILES W. CLIPPING
cnty = 0 ; For y = mapy To mapy '  Nothing here to loop, but middle of work so KEEP loops while working to see symmetry of code. This is messy enough already.
cntx = 0 ; For x = mapx To mapx+ sx Shr 3 + 0
tilex = map(x,y) & 31 ; tiley = map(x,y) Shr 5 ; xx = cntx- scrx + ofx ; yy=cnty - scry + ofy
For ty = 0 To 7
a = (tiley Shl 3 + ty) Shl 8 ; a = a + tilex Shl 3 ; b = xx+(yy+ty) Shl 10
If   xx >= ofx And yy+ty >= ofy And   xx < ofx+sx And yy+ty < ofy+sy   Then pmap[b] =  buffer[a]
If xx+1 >= ofx And yy+ty >= ofy And xx+1 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+1] = buffer[a+1]
If xx+2 >= ofx And yy+ty >= ofy And xx+2 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+2] = buffer[a+2]
If xx+3 >= ofx And yy+ty >= ofy And xx+3 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+3] = buffer[a+3]
If xx+4 >= ofx And yy+ty >= ofy And xx+4 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+4] = buffer[a+4]
If xx+5 >= ofx And yy+ty >= ofy And xx+5 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+5] = buffer[a+5]
If xx+6 >= ofx And yy+ty >= ofy And xx+6 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+6] = buffer[a+6]
If xx+7 >= ofx And yy+ty >= ofy And xx+7 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+7] = buffer[a+7]
Next
cntx = cntx+8 ; Next
cnty = cnty+8 ; Next

' BOTTOM FRAME OF TILES W. CLIPPING
cnty = sy ; For y = mapy+sy Shr 3 + 0 To mapy + sy Shr 3 + 0
cntx = 0 ; For x = mapx To mapx+ sx Shr 3 + 0
tilex = map(x,y) & 31 ; tiley = map(x,y) Shr 5 ; xx = cntx- scrx + ofx ; yy=cnty - scry + ofy
For ty = 0 To 7
a = (tiley Shl 3 + ty) Shl 8 ; a = a + tilex Shl 3 ; b = xx+(yy+ty) Shl 10
If   xx >= ofx And yy+ty >= ofy And   xx < ofx+sx And yy+ty < ofy+sy   Then pmap[b] =  buffer[a]
If xx+1 >= ofx And yy+ty >= ofy And xx+1 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+1] = buffer[a+1]
If xx+2 >= ofx And yy+ty >= ofy And xx+2 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+2] = buffer[a+2]
If xx+3 >= ofx And yy+ty >= ofy And xx+3 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+3] = buffer[a+3]
If xx+4 >= ofx And yy+ty >= ofy And xx+4 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+4] = buffer[a+4]
If xx+5 >= ofx And yy+ty >= ofy And xx+5 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+5] = buffer[a+5]
If xx+6 >= ofx And yy+ty >= ofy And xx+6 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+6] = buffer[a+6]
If xx+7 >= ofx And yy+ty >= ofy And xx+7 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+7] = buffer[a+7]
Next
cntx = cntx+8 ; Next
cnty = cnty+8 ; Next

' LEFT FRAME OF TILES W. CLIPPING
cnty = 0 ; For y = mapy To mapy+sy Shr 3 + 0
cntx = 0 ; For x = mapx+0 To mapx+0
tilex = map(x,y) & 31 ; tiley = map(x,y) Shr 5 ; xx = cntx - scrx + ofx ; yy=cnty - scry + ofy
For ty = 0 To 7
a = (tiley Shl 3 + ty) Shl 8 ; a = a + tilex Shl 3 ; b = xx+(yy+ty) Shl 10
If   xx >= ofx And yy+ty >= ofy And   xx < ofx+sx And yy+ty < ofy+sy   Then pmap[b] =  buffer[a]
If xx+1 >= ofx And yy+ty >= ofy And xx+1 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+1] = buffer[a+1]
If xx+2 >= ofx And yy+ty >= ofy And xx+2 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+2] = buffer[a+2]
If xx+3 >= ofx And yy+ty >= ofy And xx+3 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+3] = buffer[a+3]
If xx+4 >= ofx And yy+ty >= ofy And xx+4 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+4] = buffer[a+4]
If xx+5 >= ofx And yy+ty >= ofy And xx+5 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+5] = buffer[a+5]
If xx+6 >= ofx And yy+ty >= ofy And xx+6 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+6] = buffer[a+6]
If xx+7 >= ofx And yy+ty >= ofy And xx+7 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+7] = buffer[a+7]
Next
cntx = cntx+8 ; Next
cnty = cnty+8 ; Next

' RIGHT FRAME OF TILES W. CLIPPING
cnty = 0 ; For y = mapy To mapy + sy Shr 3 + 0
cntx = sx ; For x = mapx+ sx Shr 3 + 0 To mapx + sx Shr 3 + 0 
tilex = map(x,y) & 31 ; tiley = map(x,y) Shr 5 ; xx = cntx - scrx + ofx ; yy=cnty- scry + ofy
For ty = 0 To 7
a = (tiley Shl 3 + ty) Shl 8 ; a = a + tilex Shl 3 ; b = xx+(yy+ty) Shl 10
If   xx >= ofx And yy+ty >= ofy And   xx < ofx+sx And yy+ty < ofy+sy   Then pmap[b] =  buffer[a]
If xx+1 >= ofx And yy+ty >= ofy And xx+1 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+1] = buffer[a+1]
If xx+2 >= ofx And yy+ty >= ofy And xx+2 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+2] = buffer[a+2]
If xx+3 >= ofx And yy+ty >= ofy And xx+3 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+3] = buffer[a+3]
If xx+4 >= ofx And yy+ty >= ofy And xx+4 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+4] = buffer[a+4]
If xx+5 >= ofx And yy+ty >= ofy And xx+5 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+5] = buffer[a+5]
If xx+6 >= ofx And yy+ty >= ofy And xx+6 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+6] = buffer[a+6]
If xx+7 >= ofx And yy+ty >= ofy And xx+7 < ofx+sx And yy+ty < ofy+sy	 Then pmap[b+7] = buffer[a+7]
Next 
cntx = cntx+8 ; Next
cnty = cnty+8 ; Next

End Function



Casaber(Posted 2016) [#70]
Just wanted to share that optimizing experience and thoughts, hope that's okay and interesting for some.


Casaber(Posted 2016) [#71]
I reliased (afterwards of course what else) that you could made another approach here, and that would been to have a larger array with an
extra unusued space around it, and just go wild on it, and only draw the middle. Becuase it's far cheaper to do more pixels and no conditions.

I'm not gonna try implement that now as Im all sick of optimizing for today. But that it might have been a better idea.
Or worse I don´t know. I wanted a perfect sized array solution, but it would have kept the code size down.

If you could have just used hw clipping then I would have died. actually I really needed to use arrays internally before actually throwing anything on the screen so neat handling was needed before the actual screen. Otherwise HW clipping is practically free today I guess. You could clip all day long.


Casaber(Posted 2016) [#72]
But this one single IF..THEN, killed the performance.


dw817(Posted 2016) [#73]
You're peaking out at 24%, Casaber, on that last entry. Turned off DEBUG to run it. My that thing really flies ! :D

I know you don't want to fight this right now, but at some point you might consider seeing how difficult it is to plot a sprite in the middle, like a sprite facing up (we see its back, not the front) and to circumnavigate the 3D terrain acknowledging obstacle tiles when touched.

Also I changed your STEP 2 back to STEP 1 to see how much slower it is to draw the entire screen without skipping every other line. It's not very slow at all, the CPU here only increased by 4% and maintained the same speed.


Casaber(Posted 2016) [#74]
without pseudo3d you could EASLY plot 128 128x128 sprites with pixelwrite ontop. no problem
with this added mode7.. Im not sure, i might woik the same, i would not like to try without optimizing further.
But you know.. still not in the optimizing mood today, haha.

If you want to know why, I have other plans to use other graphics (using my little project) so that would allow mixing HW and software.

Not this week though.. but it's planned.I even named the project now

Graphene (one of the strongest materials in the world, and lightest too)
I´m focus on readable simple power code.

I think you're one of the ppl who KNOWS the strength of short terse code.
When you know all the other rules by heart. This is the one most true rule of them all.

K.I.S.S.


Casaber(Posted 2016) [#75]
The way to KISS is often messy in the middle. And most ppl leave the stage before even trying.

It needs a pro to get through the mess haha.

Coding is the perfect life analogy ;)


dw817(Posted 2016) [#76]
I think you're one of the ppl who KNOWS the strength of short terse code.

Well thank you Casaber, I try. :) But yes, I am a bit of a lazy SOB and I REALLY hate to write long code, whether it's mine or a routine I am making use of. That's one reason I have such short solutions often.

I may be able to cull that code of yours down very succinctly, and you may lose rotation but not the scrolling up down left right - and that's fine for what I might try with it.


Casaber(Posted 2016) [#77]
There's a very nasty floating point bug in that mode7. It shows up only after awhile, so that's a clue in itself. Keep it going for some time and you'll see it. And oh you'll see it.. haha.
Things gets sawtooth shaped all over the place. That's one for later, I want to grasp the 3D fully first.

I didn't expect me wanting to learn 3D ever in life, but you know It's not that weird and strange to me anymore. It feels abit closer now, more within the range of possiblities so to speak.


dw817(Posted 2016) [#78]
Can you send a screenshot of it please, Casaber ? The Floating Point bug you are running into.


Casaber(Posted 2016) [#79]
sure , haha now I'm having hard time to lure it. I'm not sure if its pure time, or if you need to move around alot, or if it's about changing window forth and back.

One or a combination of those 3, and it comes in several flavors;
minor, which just has abit odd edges, almost "okay looking"
and a severe version which makes the whole screen look sawtoothed, like if you had broken glass shards ontop of the screen. And theres inbetween aswell.

Right now I couldn't get none of those. The hour before I had all of them.

No wait I think I just had one now, the minor one..





dw817(Posted 2016) [#80]
Looks good here, Casaber. BTW be aware that REAL and DOUBLE numbers in BlitzMAX are far from accurate. Try out this simple code:
Strict
Local i!
For i!=0! To 1! Step .01!
  Print i!
Next
Now if it was working correctly, you would have a simple display of:
0
0.01
0.02
0.03
etc.


But as you can see, you do not. There is no 0.03, 0.15, or even just "1" at all. This then is something to consider when using your REAL or DOUBLE math variables.


Casaber(Posted 2016) [#81]
Ya i Know, it's tricky sometimes.

Have you used fixedpoint ever? Those seemed to have vanished though, I guess they have uses and they're still there hidden behind types like INT and 64bit versions of INT so they are possible. But they are not actually used much. They're abit counter intuitive, I guess in how they are presented and used.
But same goes with floats/doubles with those "hidden" incorrectness lurking inside them despite all cleverness.

But this must be some ackumulating bug so float should suffice I'm thinking. Float would be great not even doubles would be needed, and throwing doubles on the mix would only make it better when it's bugfree.

I think it's some kind of ackumulation problem here.


BlitzSupport(Posted 2016) [#82]

BTW be aware that REAL and DOUBLE numbers [...] are far from accurate.



This is the same for any language using floating point numbers -- they are inaccurate, and that's how they work within only 32/64 bits.


Now if it was working correctly, you would have a simple display of:
0
0.01
0.02
0.03
etc.


It is working correctly: In computing, floating point is the formulaic representation that approximates a real number so as to support a trade-off between range and precision.

You would need a software-based 'bignum'-type library for this kind of accuracy, not hardware-based floating-point numbers.


Casaber(Posted 2016) [#83]
I shall look into that when I´m clear in the head, I had too many sleepless nights at the hospital recently. Th waiting kills me.
Coding really helps you from keeping worrying about things.

But i would nevver had the energy to focus and make things like this otherwise.

bignum is powerful, noone would ever trust floats for handling currencies and the like.
I don´t even hink there have been a better idea yet since 1986 how to store numbers efficiently.
But I do hate my floats. Oh yes. Did I tell you that I´m starting doing alot of DSP recently and need to become friends with them. *Shivering*

But floats has speeds nowadays, so I just put my hat on and pretend they work.


Casaber(Posted 2016) [#84]
But there's precision in them when you know what you do I've noticed. I was so suprised.

I´m not the one to know those rules, but I´ve seen it in action enough to believe it.
Those floats knew what they did. Nothing escaped them.

But that's not how they are used by most ppl and I would never expect ppl to learn them. That would be to ask for too much anyway.

I don´t even think there's much literature on that kind of aspect of floats. How to USE THEM. If those books exist they are proably using big academic words and a boring language, making sure that noone ever EVER actually learn from them.


Casaber(Posted 2016) [#85]
global cx, i think thats the bug.


Casaber(Posted 2016) [#86]
Stupid bug, so very human of me.


dw817(Posted 2016) [#87]
Well now Blitz, I have heard this in the past, and I might believe it if it weren't for the fact that GFA gives flawless results. And no, there was no way I could prove it - until now.

Watch out because I finally have GFA working in Windows 8 so I can prove its flawless execution and handing of real variables.



Here is the text file pasted:
0
0.01
0.02
0.03
0.04
0.05
0.06
0.07
0.08
0.09
0.1
0.11
0.12
0.13
0.14
0.15
0.16
0.17
0.18
0.19
0.2
0.21
0.22
0.23
0.24
0.25
0.26
0.27
0.28
0.29
0.3
0.31
0.32
0.33
0.34
0.35
0.36
0.37
0.38
0.39
0.4
0.41
0.42
0.43
0.44
0.45
0.46
0.47
0.48
0.49
0.5
0.51
0.52
0.53
0.54
0.55
0.56
0.57
0.58
0.59
0.6
0.61
0.62
0.63
0.64
0.65
0.66
0.67
0.68
0.69
0.7
0.71
0.72
0.73
0.74
0.75
0.76
0.77
0.78
0.79
0.8
0.81
0.82
0.83
0.84
0.85
0.86
0.87
0.88
0.89
0.9
0.91
0.92
0.93
0.94
0.95
0.96
0.97
0.98
0.99
1


This is the part where you tell me now that BlitzMAX still isn't flawed in real variables and that GFA is the one with the problem. :)


Casaber(Posted 2016) [#88]
dw817 Nice, actually that's the kind of precision you want as a stable base for doing math.

I looked around quickliy on the surface and it loosk like GFA solved it using some clever storage of their own?

If I understood this right.
http://forum.purebasic.com/english/viewtopic.php?p=236491

This will become a nice reading later. I put it here.
https://randomascii.wordpress.com/2012/03/08/float-precisionfrom-zero-to-100-digits-2/

If I got it right. Bmax uses the standard float/double nothing special, and GFA made an extra effort behind the scenes. I wonder if that slowed things down much if it where so.


dw817(Posted 2016) [#89]
No kidding, Casaber ! All this talk of 'it's my fault I don't like BlitzMAX's real numbers.'

Well I guess it got me off my duff to install GFA to show if any programming language is doing it correctly, THEY are. :D

And that's a good thing. I have available ATM a little over 700 programs I wrote in GFA and now I can finally examine, modify, and run them and in time transfer their code to BlitzMAX.

Also, as for GFA being slow. NO. It is not. It has (or had ?) really REALLY fast execution.


Casaber(Posted 2016) [#90]
Sounds fun :) 700 programs haha, take a deep dive.


grable(Posted 2016) [#91]
Instead of blaming BlitzMax (or any other langage for that matter) for "not having accurate floating point numbers" you should instead blame your CPU, as that is what BlitzMax uses to implement floats. IE It uses hardware floating point for speed. Just like C, C++ or any other systems langauge.

And about GFA, they are probably doing it all in software. IE its not very fast.
And if you really need accurate floats, YOU can do it in software too ;)


dw817(Posted 2016) [#92]
Aaand we come back to it again. Can ANYONE take up the challenge, take up the mantle and write a program in BlitzMAX (a page of length or less) that can reproduce the results GFA just gave ?

That is, count from 0 to 1 by decimal 01. Count from 0 (zero) percent to 100 percent, with no weird or stray numbers interfering the output.


Yasha(Posted 2016) [#93]
All this talk of 'it's my fault I don't like BlitzMAX's real numbers.'


so very tired of this

That's because it is. You are wilfully ignoring the facts of how real numbers work. BlitzMax's math works correctly. Your expectations of it are wrong. This has been explained to you at length in another thread, there's no excuse for spreading misinformation further. BlitzMax is giving the correct results for what you asked it to do.

If you want perfect decimal math, use a number format that supports decimal representation, rather than complaining that a number type neither designed nor intended for what you want doesn't do what you want! To specifically count from 0 to 100 (or any range in perfect fixed steps), use integers, that's exactly what they're for.

GFA BASIC is an interpreter. It will be hiding more involved, slower, operations underneath those operators. You can replicate the results in software, but because BlitzMax's math operators are mapped to hardware operations, it will look like math implemented in software. This is also what has already been pointed out.


Casaber(Posted 2016) [#94]
I might become slow dq817.. would speed be an issue?

GFA might have done a pure software solution or something like using the hw in some other fashion (which is hard to imagein bc its very hardwired how CPU's wants their math done but nonetheless)

And if it did things fast using harware it would have gone more famous bc that would have been amazing. SO I´m thinking. it was probably software and not using IEEE/HW much at all.

More like something like a bigint with decimalpoint?


Casaber(Posted 2016) [#95]
There have been som many amazing minds trying to bend IEEE to do its best, so I´m not that positive its possible to use HW and get perfect results.

They kinda of decided that years and years ago, that its' "okay".


Casaber(Posted 2016) [#96]
GFA being a learners tool (and a professional tool at once) I think they reliased that they needed somthing that showed numbers very truthfully and not infuse confusion.


dw817(Posted 2016) [#97]
GFABasic has a compiler I purchased, Yasha, so no, you cannot say that it is only an interpreter. I know this because I have compiled code in it that runs differently from interpreted. And the compiler wasn't cheap. It was American currency converted to Deutsche marks.

To do the total pixel demo, Grable ? Where you fill the screen completely w random colors, I got about 15 frames-per-second, minju, this was on a 500mhz computer so that is pretty good. That was with compilation though, not interpreted.

GFA also could run so fast it would tie up the entire CPU which is why you needed the command PEEKEVENT to give some CPU back to the rest of the OS.

I wrote S2 with it, so it's no slouch for programming.



Also understand that I've been using GFA for nearly 18-years and not once, not once did it give me 'correct' floating point numbers as BlitzMAX delivers them. And I am glad for that.

Now if SOMEONE will write a small bit of code to achieve the same results as BlitzMAX, then it can be written off, just as with BlitzMAX you have no native ability to go frameless - but a solution was found.

Yasha, can you post a code example that reproduces the same results as GFA ? Using 'decimal number format' if that so helps you ?

But =IF= this simple task of counting from zero to 1 by 1% is so immensely difficult for BlitzMAX to accomplish correctly - then you can spout all you want, there is a PROBLEM and I know I'm not alone in this belief.
a!=1
b!=3

If a!/b!<>1!/3! Then Print"flawed"



Casaber(Posted 2016) [#98]
Yes. It's a serious problem of IEEE, and it should be solved.

These kind of problems pop up everywhere, and libraries such as BIGINT and so on comes along. What does Bmax use for correct roundings?


Henri(Posted 2016) [#99]
Hmmm...It seems to work ?
Local a:Double = 1, b:Double = 3

If String(a/b) <> String(1.0!/3.0!) Then Print "Oh no!"


-Henri


Casaber(Posted 2016) [#100]
I tried the

for i=0 to 100
print i/100
next

It's the more interesting problem for me, to be able to show at least 2 decimals correctly.


dw817(Posted 2016) [#101]
You have something good there, Henri. I am trying this though:
Local i!

For i!=0 To 1 Step .01
  Print String(i!)
Next
And still getting the same results. Hmm ... Let's try this using your String method:
Local i!,a$

For i!=0.0! To 1.0! Step .01!
  a$=String(i!+.01!)
  Print Left$(a$,Instr(a$,".")+2)
Next
Almost perfect.


Casaber(Posted 2016) [#102]
dw817 I just got the same results indpendely searching the forum

Local i2:Float' = 10.3
For i2=0 To 100
Print Left(i2/100,Instr(i2,".")+2)
Next

WaitKey


Which is interresing I guess.. on a string conversion level


Casaber(Posted 2016) [#103]
I need to go now, but this was interesting actually. This thread has approved to discuss this matter, it's very MODE 7 related. The IEEE enigma continues.

But I'm SO fond of integers right now, you wouldn´t believe it.


dw817(Posted 2016) [#104]
Local i!,a$,c
For i!=0.0! To 1.0+.01! Step .01!
  a$=String(i!+.01)
  Print c+" "+Left$(a$,Instr(a$,".")+2)
  c:+1
Next
Awright, I got it. But yeah, whatta way to fudge the numbers. :/


dw817(Posted 2016) [#105]
Casaber, when I was working on the early TRS 80, there were no float values. I remember writing a program to emulate it. And yes, you are on the right track, I used INTEGER variables to emulate the float.

How did I do it ? It was all a matter of using a$ and b$ (the only two strings available in the language), and putting a visual decimal point between the two of them.

The same method of building a float might be done in BlitzMAX but perhaps not so easily.

Integers can act as FLOAT if treated properly. It is a blessing that BlitzMAX has integers that count past 999999999.

Have a good rest, I think I need one too. :)


Casaber(Posted 2016) [#106]
I hope we find an elegant solution, Personally I have'nt tried libraries yet on Bmax, I figure that will help but I hope there's also a more direct solution.

Btw the funny thing is.. the bug was not even floating point error, don't global CX and its' gone. Well.. I hope. haven't throughly tested it yet ;)

Have a good weekend everyone.


Casaber(Posted 2016) [#107]
Ya have a good rest dw817 :) We'll talk later about those 700 golden tresures of yours muHAHAAH.