Thread Rating:
  • 1 Vote(s) - 4 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Rotated and scaled image drawing
#1
I wrote a quick prototype of drawing images with arbitrary rotation and scaling in n7. The code may look a bit too complicated for just drawing a transformed image. But when I add this stuff to the core (implement it in C and add new commands to n7), I will also let you draw textured polygons with any number of points. Here I just draw a textured rectangle, four points.

It's slow, but it's gonna get a lot faster, of course.

I'm already thinking about adding support for z coordinates and perspective correct interpolation (for polygons). Would be fun to allow some 3d rendering as in naalaa 5.

Code:
include "list.n7"

'#win32
#dbg

visible vPoints = fill([x: 0, y: 0, u: 0, v: 0], 4)
visible vXval = List()
vXval.Add([0, 0, 0])
vXval.Add([0, 0, 0])

set window "test", 640, 480
set redraw off

img = loadimage("logo.png")
pln width(img) + ", " + height(img)

a = 0
while not keydown(KEY_ESCAPE, true)
    a = (a + 1)%360
    set color 0, 0, 0
    cls
    set color 255, 255, 255, 0
    ' DrawImageTransformed(image, x, y, scale_x, scaleY, angle, pivot_x, pivot_y)
    ' draw scaled and centered (= pivot at center of image).
    DrawImageTransformed(img, 120, 240, 2, 2, 0, width(img)/2, height(img)/2)
    ' draw rotated and centered
    DrawImageTransformed(img, 320, 240, 1, 1, rad(a), width(img)/2, height(img)/2)
    ' draw rotated and scaled but put pivot at top left corner of image.
    DrawImageTransformed(img, 520, 240, 4, 4, rad(a), 0, 0)
    redraw
    fwait 60   
wend

function DrawImageTransformed(img, drawX, drawY, scaleX, scaleY, angle, pivotX, pivotY)
    srcX = 0
    srcY = 0
    srcW = width(img)
    srcH = height(img)
   
    xLeft = -pivotX*scaleX
    xRight = (srcW - pivotX)*scaleX
    yTop = -pivotY*scaleY
    yBottom = (srcH - pivotY)*scaleY
   
    vPoints[0].x = xLeft; vPoints[0].y = yTop; vPoints[0].u = 0; vPoints[0].v = 0
    vPoints[1].x = xRight; vPoints[1].y = yTop; vPoints[1].u = 1; vPoints[1].v = 0
    vPoints[2].x = xRight; vPoints[2].y = yBottom; vPoints[2].u = 1; vPoints[2].v = 1
    vPoints[3].x = xLeft; vPoints[3].y = yBottom; vPoints[3].u = 0; vPoints[3].v = 1
    for i = 0 to sizeof(vPoints) - 1
        x = vPoints[i].x*cos(angle) - vPoints[i].y*sin(angle)
        y = vPoints[i].y*cos(angle) + vPoints[i].x*sin(angle)
        vPoints[i].x = round(drawX + x)
        vPoints[i].y = round(drawY + y)
        if i = 0
            minX = vPoints[i].x
            maxX = vPoints[i].x
            minY = vPoints[i].y
            maxY = vPoints[i].y
        else
            minX = min(vPoints[i].x, minX)
            maxX = max(vPoints[i].x, maxX)
            minY = min(vPoints[i].y, minY)
            maxY = max(vPoints[i].y, maxY)
        endif
    next   
    if maxX < 0 or minX >= width(primary) or maxY < 0 or minY >= height(primary)  return
    set color 255, 255, 255, 0
    for y = minY to maxY
        numx = 0
        for i = 0 to sizeof(vPoints) - 1
            p0 = vPoints[i]
            p1 = vPoints[(i + 1)%sizeof(vPoints)]
            if p0.y <= y and p1.y > y or
                    p0.y > y and p1.y <= y
                dy = p1.y - p0.y
                if dy = 0
                    vXval[numx][0] = p0.x; vXval[numx][1] = p0.u; vXval[numx][2] = p0.v
                    numx = numx + 1
                    vXval[numx][0] = p1.x; vXval[numx][1] = p1.u; vXval[numx][2] = p1.v
                    numx = numx + 1
                else
                    f = (y - p0.y)/(p1.y - p0.y)
                    vXval[numx][0] = p0.x + f*(p1.x - p0.x)
                    vXval[numx][1] = p0.u + f*(p1.u - p0.u)
                    vXval[numx][2] = p0.v + f*(p1.v - p0.v)
                    numx = numx + 1
                endif
            endif
        next
        if numx
            vXval.size = numx
            vXval.SortByField(0, vXval.ASCENDING)
            for i = 0 to vXval.size - 2 step 2
                draw hraster img, y, vXval[i][0], vXval[i + 1][0],
                        vXval[i][1], vXval[i][2], vXval[i + 1][1], vXval[i + 1][2]
            next
        endif
    next   
   
    draw poly vPoints, false
endfunc


Attached Files
.zip   image_transform.zip (Size: 637.72 KB / Downloads: 12)
Reply
#2
Very cool... Not slow at all... Nicely done!
Logic is the beginning of wisdom.
Reply
#3
(03-03-2024, 08:01 PM)johnno56 Wrote: Very cool... Not slow at all... Nicely done!

It would be slow if you drew like 100 rotated and scaled images. But I'm working on the real impementation in C now, and it's a lot speedier. Hopefully I can release the real thing next week Smile
Reply
#4
ah yes... 100 images. Goes without saying... looking forward to the changes...
Logic is the beginning of wisdom.
Reply
#5
(03-03-2024, 10:49 AM)Marcus Wrote: I wrote a quick prototype of drawing images with arbitrary rotation and scaling in n7.
...

TEST DRIVE
Using your transform (scaling) function and polyline, here it is a car racing game.
It is not perfect yet and I don't know how to curve the road

...but it's playable  Big Grin

   
zoom in to enlarge the image


Code:
'=========================================
'            TEST DRIVE
'
'Controls : LEFT, RIGHT
'
'Limitation :
'- I don't know how to make a curved road
'
'Written in :
'N7 24.02.16 release
'
'Sources :
'TitleScreen https://www.imgflip.com
'TitleScreen Background music
'   This World Has Gone Crazy
'   by lemonmusicstudio
'   https://pixabay.com
'Sport Cars https://www.pngfind.com
'=========================================

'----------------
' INITIALIZATION
'----------------
#win32
include "assets/transform.n7"
include "assets/polyline.n7"; visible path= [] ; visible distance =[]; visible move_pixel = []
include "sfx.n7"            ; visible sfx = SFX() 'create an sfx instance
visible font1 = createfont("Arial",20,1,0,0,0)'name,size,bold,italic,underlined,smoothed
randomize clock()
set window "TEST DRIVE", 360, 271,false,2
set redraw off

'color definition
sky   = [51,204,204]
black = [0,0,0]
gray  = [100,100,100]
white = [255,255,255]
green = [51,153,102]

'Create PolyLine for each path
path[0] = PolyLine([[width(primary)/2,height(primary)/2-80],[width(primary)/2    ,height(primary)]])
path[1] = PolyLine([[width(primary)/2,height(primary)/2-80],[width(primary)/2-80,height(primary)]])
path[2] = PolyLine([[width(primary)/2,height(primary)/2-80],[width(primary)/2+80,height(primary)]])
move_pixel[0] = rnd(2,5);move_pixel[1] = rnd(2,5); move_pixel[2] = rnd(2,5);

'Sprite class definition
Sprite =
[
    '--- Properties ---
    img:0,x:0,y:0,dx:20,s:0,a:0,px:0,py:0,distance:1,
   
    '--- Methods ------
    Draw:function(x,y,s,a)
        ' DrawImageTransformed(image, x, y, scale_x, scaleY, angle, pivot_x, pivot_y)
        ' Parameter :
        '   image               = .png format
        '   (x,y)               = location of the image in the screen coordinates
        '   scale_x, scale_y    = scale the image's size
        '   angle               = in radian.
        '   (pivot_x, pivot_y)  = center point of the image. E.g:(0,0) = top left.
        this.x = x; this.y = y ; this.s = s; this.a = a
        this.px = width(this.img)/2; this.py = height(this.img)/2
        DrawImageTransformed(this.img, this.x,this.y, this.s, this.s, this.a, this.px, this.py)
        endfunc,
     XY:function(i)
           this.distance = (this.distance + move_pixel[i])%path[i].GetLength()
           pos = path[i].GetPoint(this.distance,true)     
           this.x = pos[0]
           this.y = pos[1]
         endfunc
]

'Initial Value
Player = copy(Sprite)
Player.img = loadimage("assets/player.png")
Player.x = width(primary)/2
Player.y = height(primary)-15
Player.a = rad(0)'convert degree to radian
Player.s = 1

Car = fill(Sprite,3)
Car[0].img = loadimage("assets/blue.png")
Car[1].img = loadimage("assets/red.png")
Car[2].img = loadimage("assets/yellow.png")

score = 0; lifes = 5; quit =false; collide = false


'---------------
' MAIN LOOP
'---------------
TitleScreen()
PlayTune(1)

do
    'Player is sent to out of screen if collide = true
    if Player.y = -100 then
        while keydown(KEY_RETURN,true)
           Player.y = height(primary)-15 
        wend
    endif

    'Prepare screen : green grass, blue sky, gray road, white lines, score and lifes
    set color green;cls
    set color sky
      draw rect 0,0,width(primary),height(primary)/2-80,1
    set color gray
      road = [width(primary)/2-8,height(primary)/2-80,width(primary)/2+8,height(primary)/2-80
             ,width(primary)/2+120,height(primary),width(primary)/2-120,height(primary)]
      draw poly road,1
    set color white
      draw line 0                 ,height(primary)/2-80 ,width(primary)         ,height(primary)/2-80
      draw line width(primary)/2  ,height(primary)/2-80 ,width(primary)/2       ,height(primary)
      draw line width(primary)/2-8,height(primary)/2-80 ,width(primary)/2-120   ,height(primary)
      draw line width(primary)/2+8,height(primary)/2-80 ,width(primary)/2+120   ,height(primary)
    set font font1
      set caret 10,5;wln "Score : "+score
      if Player.y = -100 then; set caret 120,100;wln "ENTER TO PLAY";endif
      set caret width(primary)-75,5;wln "Lifes : "+lifes 
      score = score + 1   
   
    'Check left and right Player's boundary
    if Player.x > 120 and Player.x < 240 then
       boundary = true
    elseif Player.x<= 120 then
       boundary = false
       Player.x = Player.x + 5
       PlayTune(2)
    else
       boundrary = false
       Player.x = Player.x - 5
       PlayTune(2)
    endif
   
    'Player's control
    if keydown(KEY_LEFT,true) and boundary then Player.x = Player.x - Player.dx
    if keydown(KEY_RIGHT,true) and boundary then Player.x = Player.x + Player.dx
    if keydown(KEY_ESCAPE,true) quit = true

    'Draw objects
    Player.Draw(Player.x,Player.y,Player.s,Player.a)
    Car[0].XY(0);Car[1].XY(1);Car[2].XY(2)
    foreach i in Car
        if i.y > height(primary)+20 then i.y = 0
        i.s = i.y/(height(primary))
        i.Draw(i.x,i.y,i.s,i.a)
       
        'Collision Detection
        if (Player.x>=i.x and Player.x<=i.x+51) and
           (Player.y+20>=i.y and Player.y+20<=i.y+34) then collide=true
        if (Player.x+45>=i.x and Player.x+45<=i.x+51) and
           (Player.y+20>=i.y and Player.y+20<=i.y+34) then collide=true
        if collide then
            lifes = lifes-1
            collide = false
            Player.y = -100
            set caret width(primary)-75,5;wln "Lifes : "+lifes
            PlayTune(1)
            if lifes <= 0 then Ending(white)
        endif       
    next   
   
    PlayTune(3)'car's sound effect       
   
    redraw
    fwait 10
until quit=true

'-----------
' FUNCTIONS
'-----------
function TitleScreen()
    car = loadimage("assets/titlescreen.png",5,3)
    track = loadmusic("assets/rock.wav")
    set music volume track,0.5
    play music track,1 '1 = looping

    i= 0
    while not keydown(KEY_RETURN,true)
        if i<=11 then; i = i+1; else; i=0; endif; draw image car,0,0,i
        set caret width(primary)/2,20
        set font font1; center "TEST DRIVE"
        set font 0 'default font
        center "Face the challenge"
        center "Conquer the street of..."
        center "The Fast and The Furious"
        center "Go Now...ENTER"
        redraw
        fwait 60
    wend
   
    'remove from memory
    free music track
    free image car 
endfunc

function Sound(b,x)
    mytune = sfx.SquareWave(b,x,0.5)'(duration,frequency, volume)
    play sound mytune; wait 50
    free sound mytune 'release sound from memory
endfunc

function PlayTune(i)
    select i
        case 1 'starting the game's scene
          Sound(0.5,261.63);Sound(0.5,293.66);Sound(0.5,329.63)
        case 2 'road's boundary
          Sound(0.5,329.63*3);Sound(0.5,261.63*3)
        case 3 'car's sound effect
          Sound(0.1,261.63/2) 
    endsel
endfunc

function Ending(c)
    set color c
    set font font1
    set caret width(primary)/2,5;center "THE END"
    redraw
    while not keydown(KEY_ESCAPE,true)
        wait 1
    wend
    TitleScreen()
    end
endfunc


Attached Files
.zip   Test Drive.zip (Size: 4.44 MB / Downloads: 6)
Reply
#6
Nice one Big Grin The awesome sound effect while driving sent me back to the atari 2600 days Smile
Reply
#7
Hehe, some quick code, but I'm not sure it will help much, it's just a visual thing:

Code:
#win32

set window "nah ...", 320, 240, false, 2
set redraw off

road = []
for z = 0 to 200  road[z] = sin(z*0.05)*6

playerz = 0.0
playerx = 0
p = dim(8)
while not keydown(KEY_ESCAPE, true)
    playerz = playerz + 0.075
    if keydown(KEY_LEFT)  playerx = playerx - 0.1
    if keydown(KEY_RIGHT) playerx = playerx + 0.1

    set color 64, 64, 128
    cls
    set color 200, 128, 64
    draw rect 0, 125, 320, 120, true
    
    set color 255, 255, 255
    for i = int(playerz) + 20 to int(playerz)
        if i < sizeof(road) - 2
            z = i + 1 - playerz + 1.1
            x0l = 160 + 200*(road[i + 1] - 1 - playerx)/z
            x0r = 160 + 200*(road[i + 1] + 1 - playerx)/z
            y0 = 120 + 130/z
            z = i - playerz + 1.1
            x1l = 160 + 200*(road[i] - 1 - playerx)/z
            x1r = 160 + 200*(road[i] + 1 - playerx)/z
            y1 = 120 + 130/z
            if i%2 = 0  set color 128, 128, 128
            else  set color 96, 96, 96
            p[0] = x0l; p[1] = y0; p[2] = x0r; p[3] = y0
            p[4] = x1r; p[5] = y1; p[6] = x1l; p[7] = y1
            draw poly p, true
        endif
    next
    
    redraw
    fwait 60
wend
Reply
#8
And here we go with the texture mapping using 'draw poly image' Smile

Code:
#win32

set window "nah ...", 640, 480, false
set redraw off

' create two road textures.
roadImg = []
roadImg[0] = createimage(160, 16)
set image roadImg[0]
for y = 0 to 15  for x = 0 to 159
    if rnd(3) = 0  set color 128, 128, 128
    else  set color 112, 112, 112
    set pixel x, y
next
set color 255, 255, 255
draw rect 0, 0, 8, 16, true
draw rect 152, 0, 8, 16, true
set image primary
roadImg[1] = createimage(160, 16)
set image roadImg[1]
for y = 0 to 15  for x = 0 to 159
    if rnd(3) = 0  set color 96, 96, 96
    else  set color 80, 80, 80
    set pixel x, y
next
set color 255, 255, 255
draw rect 76, 0, 8, 16, true
set image primary



road = []
for z = 0 to 200  road[z] = sin(z*0.05)*6

playerz = 0.0
playerx = 0
p = dim(16)
p[2] = 0; p[3] = 0
p[6] = 160; p[7] = 0
p[10] = 160; p[11] = 16
p[14] = 0; p[15] = 16
while not keydown(KEY_ESCAPE, true)
    playerz = playerz + 0.075
    if keydown(KEY_LEFT)  playerx = playerx - 0.1
    if keydown(KEY_RIGHT) playerx = playerx + 0.1

    set color 64, 64, 128
    cls
    set color 200, 128, 64
    draw rect 0, 125*2, 640, 240, true
    
    set color 255, 255, 255
    for i = int(playerz) + 20 to int(playerz)
        if i < sizeof(road) - 2
            z = i + 1 - playerz + 1.1
            x0l = 320 + 400*(road[i + 1] - 1 - playerx)/z
            x0r = 320 + 400*(road[i + 1] + 1 - playerx)/z
            y0 = 240 + 260/z
            z = i - playerz + 1.1
            x1l = 320 + 400*(road[i] - 1 - playerx)/z
            x1r = 320 + 400*(road[i] + 1 - playerx)/z
            y1 = 240 + 260/z
'            if i%2 = 0  set color 128, 128, 128
'            else  set color 96, 96, 96
            p[0] = x0l; p[1] = y0;
            p[4] = x0r; p[5] = y0
            p[8] = x1r; p[9] = y1;
            p[12] = x1l; p[13] = y1
            draw poly image roadImg[i%2], p
        endif
    next
    
    redraw
    fwait 60
wend

Edit Increased the resolution
Reply
#9
(03-06-2024, 06:20 PM)Marcus Wrote: And here we go with the texture mapping using 'draw poly image' Smile
...

Super awesome excellent...
Now, I need to find a way to put this texture mapping into TEST DRIVE the game a.k.a The Fast and The Furious 

Big Grin Big Grin Big Grin
Reply
#10
This graphics set might be of use for a 3d racing game, and I might give it a try: https://opengameart.org/content/25d-racing-resources
Reply


Forum Jump:


Users browsing this thread: 9 Guest(s)