Author Topic: Tree traversal problem:  (Read 5195 times)

0 Members and 1 Guest are viewing this topic.

Offline relsoft

  • DBF Aficionado
  • ******
  • Posts: 3303
  • Karma: 47
    • View Profile
Tree traversal problem:
« on: May 28, 2010 »
I'm trying to code a texture packer and found this nifty tutorial:

http://www.blackpawn.com/texts/lightmaps/default.html


Here's my implementation:
Code: [Select]
'' binary tree
''http://www.blackpawn.com/texts/lightmaps/default.html
#define DEBUG

enum
E_ROOT = 0
E_LEAF
end enum

type Trect
x1 as integer
y1 as integer
x2 as integer
y2 as integer
end type

type Timage
x as integer
y as integer
wid as integer
hei as integer
col as integer
end type

type Tnode

public:
declare constructor()
declare destructor()
declare function insert(byref img as Timage) as Tnode ptr

child(0 to 1) as Tnode ptr
rect as Trect
imageID as integer '' sprite index (0 = subdivided else terminator)

nodeID as integer

private:

end type



'' tests if b could fit inside a
'' 0 = smaller
'' 1 = fits
'' 2 = one axis larger
function rec_fits(byval A as Trect, byval B as Trect) as integer

'' a.x1 = b.x1
'' a.y1 = b.y1

dim as integer a_wid = (a.x2 - a.x1) + 1
dim as integer a_hei = (a.y2 - a.y1) + 1

dim as integer b_wid = (b.x2 - b.x1) + 1
dim as integer b_hei = (b.y2 - b.y1) + 1

if (b_wid > a_wid) or (b_hei > a_hei) then
return 2
endif

if (b_wid < a_wid) or (b_hei < a_hei) then
return 0
endif



return 1

end function


constructor Tnode()

child(0) = 0
child(1) = 0

imageID = 0
nodeID = E_LEAF

end constructor

destructor Tnode()

if child(0) then delete child(0)
if child(1) then delete child(1)

end destructor

function Tnode.insert(byref img as Timage) as Tnode ptr


    if (nodeID = E_ROOT) then 'we're not a leaf then
       
        ''print "root"
        ''sleep
       
        '''(try inserting into first child)
        dim as Tnode ptr newNode = child(0)->insert( img )
        if (newNode <> 0) then
        #ifdef DEBUG
        print "inserted at child(0) of root"
        #endif
        return newNode
        endif
       
        ''(no room, insert into second)
        #ifdef DEBUG
        print  "inserted at child(1) off root"
        #endif
        return child(1)->insert( img )
       
       
    else
   
    ''print "leaf"
    ''sleep
   
   
        ''(if there's already a lightmap here, return)
        if (imageID <> 0) then
        #ifdef DEBUG
        print "has image"
        #endif
        return 0
        endif

dim as Trect b
    b.x1 = rect.x1
    b.y1 = rect.y1
    b.x2 = (rect.x1 + img.wid)-1
    b.y2 = (rect.y1 + img.hei)-1
   
'' check fit value
dim as integer result = rec_fits(rect, b)
       
        #ifdef DEBUG
        print result
        #endif
       
        ''(if we're too small, return)
        '' one of the image axis is bigger than rect
        if (result = 2) then ''img doesn't fit in pnode->rect
            #ifdef DEBUG
            print "image too big"
            #endif
            return 0
        end if

        ''(if we're just right, accept)
        if (result = 1) then ''img fits perfectly in pnode->rect
            #ifdef DEBUG
            print "fits"
            #endif
            return @this
        end if
       
        ''(otherwise, gotta split this node and create some kids)
        '' ie: we are smaller so split the node
        child(0) = new Tnode
        child(1) = new Tnode
       
        ''(decide which way to split)
        dim as integer rw = (rect.x2 - rect.x1)+1
        dim as integer rh = (rect.y2 - rect.y1)+1
       
        dim as integer dw = rw - img.wid
        dim as integer dh = rh - img.hei
       
       
        '' child 0 rect = image rect dimensions
        if (dw > dh) then
           
            child(0)->rect.x1 = rect.x1
            child(0)->rect.y1 = rect.y1
            child(0)->rect.x2 = rect.x1 + (img.wid - 1)
            child(0)->rect.y2 = rect.y2
           
            child(1)->rect.x1 = rect.x1 + (img.wid)
            child(1)->rect.y1 = rect.y1
            child(1)->rect.x2 = rect.x2
            child(1)->rect.y2 = rect.y2
           
        else
            child(0)->rect.x1 = rect.x1
            child(0)->rect.y1 = rect.y1
            child(0)->rect.x2 = rect.x2
            child(0)->rect.y2 = rect.y1 + (img.hei - 1)                               
                 
        child(1)->rect.x1 = rect.x1
            child(1)->rect.y1 = rect.y1 + (img.hei)
            child(1)->rect.x2 = rect.x2
            child(1)->rect.y2 = rect.y2
           
        end if
       
       
       
        #ifdef DEBUG
            print "child(0)dimensions:"; child(0)->rect.x1,child(0)->rect.y1,child(0)->rect.x2,child(0)->rect.y2
print "child(1)dimensions:"; child(1)->rect.x1,child(1)->rect.y1,child(1)->rect.x2,child(1)->rect.y2
#endif
           
        '' return first child since it would fit perfectly
        '' ie. we have just calculated the bounding box that
        ''     can hold the image perfectly
       
        return child(0)->insert(img)
       
   
    end if
   
    return 0
   
end function



function insert_images(byval m_root as Tnode ptr, byref img as Timage) as integer
   
    static as integer id = 1
   
    dim as Tnode ptr pnode = m_root->insert(img)
   
    if (pnode <> 0) then
        ''copy pixels over from img into pnode->rc part of texture
        line(pnode->rect.x1,pnode->rect.y1)-(pnode->rect.x2,pnode->rect.y2), img.col, bf
        pnode->imageID = id
       
        #ifdef DEBUG
        print "yay!! ImageID = "; id
        print "dimensions:"; pnode->rect.x1,pnode->rect.y1,pnode->rect.x2,pnode->rect.y2     
        #endif
         id += 1
        return 0
    else
       return 1
endif

end function


randomize timer

'' 640 * 480, 8 bit

#ifndef DEBUG
screen 18,8,1
#endif


dim as Timage image(0 to 15)

dim as Tnode tree

tree.child(0) = new Tnode
tree.child(1) = new Tnode


tree.nodeID = E_ROOT
tree.rect.x1 = 0
tree.rect.y1 = 0
tree.rect.x2 = 255
tree.rect.y2 = 255

tree.child(0)->rect.x1 = 0
tree.child(0)->rect.y1 = 0
tree.child(0)->rect.x2 = 255
tree.child(0)->rect.y2 = 255

'tree.child(1)->rect.x1 = 0
'tree.child(1)->rect.y1 = 0
'tree.child(1)->rect.x2 = 255
'tree.child(1)->rect.y2 = 255


for i as integer = 0 to 15
image(i).x = 0
image(i).y = 0
image(i).wid = 16 '+ int(rnd*11)
image(i).hei = 16 '+ int(rnd*11)
image(i).col = 1 + int(rnd*15)
next

for i as integer = 0 to 15

insert_images(@tree, image(i))
   
next i


for i as integer = 0 to 5
dim as integer x1=image(i).x
next i
sleep


Problem is the coords stays at zero and cannot  somehow check if a node has an imageID in it.

Thanks in advance!
Challenge Trophies Won:

Offline relsoft

  • DBF Aficionado
  • ******
  • Posts: 3303
  • Karma: 47
    • View Profile
Re: Tree traversal problem:
« Reply #1 on: May 28, 2010 »
Nevermind I got it to work:

1 cup of coffee is all it took. I was drowsy while I coded this(I'm taking meds for allergies that makes me drowsy) and missed the fact that non-leaf <> root.

*my wife is gonna get pissed I drank coffee. :*(

Code: [Select]
'' binary tree
''http://www.blackpawn.com/texts/lightmaps/default.html
'#define DEBUG

enum
E_ROOT = 0
E_LEAF
E_NON_LEAF
end enum

type Trect
x1 as integer
y1 as integer
x2 as integer
y2 as integer
end type

type Timage
x as integer
y as integer
wid as integer
hei as integer
col as integer
end type

type Tnode

public:
declare constructor()
declare destructor()
declare function insert(byref img as Timage) as Tnode ptr
declare function is_leaf() as integer

child(0 to 1) as Tnode ptr
rect as Trect
imageID as integer '' sprite index (0 = subdivided else terminator)

nodeID as integer

private:

end type



'' tests if b could fit inside a
'' 0 = smaller
'' 1 = fits
'' 2 = one axis larger
function rec_fits(byval A as Trect, byval B as Trect) as integer

'' a.x1 = b.x1
'' a.y1 = b.y1

dim as integer a_wid = (a.x2 - a.x1) + 1
dim as integer a_hei = (a.y2 - a.y1) + 1

dim as integer b_wid = (b.x2 - b.x1) + 1
dim as integer b_hei = (b.y2 - b.y1) + 1

if (b_wid > a_wid) or (b_hei > a_hei) then
return 2
endif

if (b_wid < a_wid) or (b_hei < a_hei) then
return 0
endif



return 1

end function


constructor Tnode()

child(0) = 0
child(1) = 0

imageID = 0
nodeID = E_LEAF

end constructor

destructor Tnode()

if child(0) then delete child(0)
if child(1) then delete child(1)

end destructor


function Tnode.is_leaf() as integer

return ((child(0)=0) and (child(1)=0))

end function

function Tnode.insert(byref img as Timage) as Tnode ptr


'' non leaf
    if  (not is_leaf()) then 'we're not a leaf then
       
        ''print "root"
        ''sleep
       
        '''(try inserting into first child)
        dim as Tnode ptr newNode = child(0)->insert( img )
        if (newNode <> 0) then
        #ifdef DEBUG
        print "inserted at child(0) of root"
        #endif
        return newNode
        endif
       
        ''(no room, insert into second)
        #ifdef DEBUG
        print  "inserted at child(1) off root"
        #endif
        return child(1)->insert( img )
       
       
    else  '' leaf
   
    ''print "leaf"
    ''sleep
   
   
        ''(if there's already a lightmap here, return)
        if (imageID <> 0) then
        #ifdef DEBUG
        print "has image"
        #endif
        return 0
        endif

dim as Trect b
    b.x1 = rect.x1
    b.y1 = rect.y1
    b.x2 = (rect.x1 + img.wid)-1
    b.y2 = (rect.y1 + img.hei)-1
   
'' check fit value
dim as integer result = rec_fits(rect, b)
       
        #ifdef DEBUG
        print result
        #endif
       
        ''(if we're too small, return)
        '' one of the image axis is bigger than rect
        if (result = 2) then ''img doesn't fit in pnode->rect
            #ifdef DEBUG
            print "image too big"
            #endif
            return 0
        end if

        ''(if we're just right, accept)
        if (result = 1) then ''img fits perfectly in pnode->rect
            #ifdef DEBUG
            print "fits"
            #endif
            return @this
        end if
       
        ''(otherwise, gotta split this node and create some kids)
        '' ie: we are smaller so split the node
        child(0) = new Tnode
        child(1) = new Tnode
       
        ''(decide which way to split)
        dim as integer rw = (rect.x2 - rect.x1)+1
        dim as integer rh = (rect.y2 - rect.y1)+1
       
        dim as integer dw = rw - img.wid
        dim as integer dh = rh - img.hei
       
       
        '' child 0 rect = image rect dimensions
        if (dw > dh) then
           
            child(0)->rect.x1 = rect.x1
            child(0)->rect.y1 = rect.y1
            child(0)->rect.x2 = rect.x1 + (img.wid - 1)
            child(0)->rect.y2 = rect.y2
           
            child(1)->rect.x1 = rect.x1 + (img.wid)
            child(1)->rect.y1 = rect.y1
            child(1)->rect.x2 = rect.x2
            child(1)->rect.y2 = rect.y2
           
        else
            child(0)->rect.x1 = rect.x1
            child(0)->rect.y1 = rect.y1
            child(0)->rect.x2 = rect.x2
            child(0)->rect.y2 = rect.y1 + (img.hei - 1)                               
                 
        child(1)->rect.x1 = rect.x1
            child(1)->rect.y1 = rect.y1 + (img.hei)
            child(1)->rect.x2 = rect.x2
            child(1)->rect.y2 = rect.y2
           
        end if
       
       
       
       
        #ifdef DEBUG
            print "child(0)dimensions:"; child(0)->rect.x1,child(0)->rect.y1,child(0)->rect.x2,child(0)->rect.y2
print "child(1)dimensions:"; child(1)->rect.x1,child(1)->rect.y1,child(1)->rect.x2,child(1)->rect.y2
#endif
           
        '' return first child since it would fit perfectly
        '' ie. we have just calculated the bounding box that
        ''     can hold the image perfectly
       
        ''child(0)->nodeID = E_NON_LEAF
       
        return child(0)->insert(img)
       
   
    end if
   
    return 0
   
end function



function insert_images(byval m_root as Tnode ptr, byref img as Timage) as integer
   
    static as integer id = 1
   
    dim as Tnode ptr pnode = m_root->insert(img)
   
    if (pnode <> 0) then
        ''copy pixels over from img into pnode->rc part of texture
        line(pnode->rect.x1,pnode->rect.y1)-(pnode->rect.x2,pnode->rect.y2), img.col, bf
        pnode->imageID = id
       
        #ifdef DEBUG
        print "yay!! ImageID = "; id
        print "dimensions:"; pnode->rect.x1,pnode->rect.y1,pnode->rect.x2,pnode->rect.y2     
        #endif
         id += 1
        return 0
    else
       return 1
endif

end function


randomize timer

'' 640 * 480, 8 bit

#ifndef DEBUG
screen 18,8,1
#endif


dim as Timage image(0 to 15)

dim as Tnode tree

tree.child(0) = new Tnode
tree.child(1) = new Tnode


tree.nodeID = E_ROOT
tree.rect.x1 = 0
tree.rect.y1 = 0
tree.rect.x2 = 255
tree.rect.y2 = 255

tree.child(0)->rect.x1 = 0
tree.child(0)->rect.y1 = 0
tree.child(0)->rect.x2 = 255
tree.child(0)->rect.y2 = 255

'tree.child(1)->rect.x1 = 0
'tree.child(1)->rect.y1 = 0
'tree.child(1)->rect.x2 = 255
'tree.child(1)->rect.y2 = 255


for i as integer = 0 to 15
image(i).x = 0
image(i).y = 0
image(i).wid = 16 + int(rnd*11)
image(i).hei = 16 + int(rnd*11)
image(i).col = 1 + int(rnd*15)
next

for i as integer = 0 to 15

insert_images(@tree, image(i))
   
next i


for i as integer = 0 to 5
dim as integer x1=image(i).x
next i
sleep





« Last Edit: May 28, 2010 by relsoft »
Challenge Trophies Won:

Offline rdc

  • Pentium
  • *****
  • Posts: 1495
  • Karma: 140
  • Yes, it is me.
    • View Profile
    • Clark Productions
Re: Tree traversal problem:
« Reply #2 on: May 28, 2010 »
*my wife is gonna get pissed I drank coffee. :*(

I won't tell. :)

Neat code, as always.

Offline relsoft

  • DBF Aficionado
  • ******
  • Posts: 3303
  • Karma: 47
    • View Profile
Re: Tree traversal problem:
« Reply #3 on: May 28, 2010 »
Oh she will know. My daughter saw me drinking. :*(

BTW, that code could be used to pack textures into a single texture atlas.

Using a texture atlas would speed up the rendering of HW apps since you only bind a texture once for multiple sprites.

It would also make your memory usage smaller.
Challenge Trophies Won:

Offline rdc

  • Pentium
  • *****
  • Posts: 1495
  • Karma: 140
  • Yes, it is me.
    • View Profile
    • Clark Productions
Re: Tree traversal problem:
« Reply #4 on: May 28, 2010 »
BTW, that code could be used to pack textures into a single texture atlas.

Yeah, that was my thought about the technique. It seems it could be very useful for this kind of thing.

Offline relsoft

  • DBF Aficionado
  • ******
  • Posts: 3303
  • Karma: 47
    • View Profile
Re: Tree traversal problem:
« Reply #5 on: May 30, 2010 »
Challenge Trophies Won:

Offline rdc

  • Pentium
  • *****
  • Posts: 1495
  • Karma: 140
  • Yes, it is me.
    • View Profile
    • Clark Productions
Re: Tree traversal problem:
« Reply #6 on: May 30, 2010 »
Awesome.

Offline relsoft

  • DBF Aficionado
  • ******
  • Posts: 3303
  • Karma: 47
    • View Profile
Re: Tree traversal problem:
« Reply #7 on: May 30, 2010 »
Texture packer updated:

http://rel.betterwebber.com/junk.php?id=106

Changes:

Multiple images support
Instructions on how to use (readme.txt)
Challenge Trophies Won: