Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Seamless texture to a complex polygon

edited November 2012 in Questions Posts: 25

For a while I'm playing around with sprites running up and down a series of hills (2D game). I plan to fill the hills, represented by a physics.body chain with a seamless texture. What I try to do is, to complete the chain to a polygon, triangular it (build-in function) and use this for a mesh. Is that a way to do it and how to set the appropriate TexCoords? Probably I could use Rects too in my mesh but to be honest, I never understand the usage of the TexCoords.
Any hint is welcome!
Thanks in advance!

Comments

  • JohnJohn Admin Mod
    edited November 2012 Posts: 572

    The best way to do it is to set each texCoord element to the it's corresponding vertex divided by the width/height of the texture being used. The texture itself has to have power of 2 dimensions for wrapping to be enabled (i.e. 64x64, 128x128, 256x256)

    Here's an example that I modified to use a repeating texture:

    -- distPointToLineSeg(): shortest distance of a point to a line segment.
    function distPointToLineSeg(p , s1, s2)
        local v = s2 - s1
        local w = p - s1
    
        c1 = w:dot(v)
        if c1 <= 0 then
            return p:dist(s1)
        end
    
        c2 = v:dot(v)
        if c2 <= c1 then
            return p:dist(s2)
        end
    
        b = c1 / c2;
        pb = s1 + b * v;
        return p:dist(pb)
    end
    --===================================================================
    
    -- Use this function to perform your initial setup
    function setup()
        debugDraw = PhysicsDebugDraw()
    
        print("Hello Polygon!")
        print("1. Tap in clockwise order to create a polygon.")
        print("2. Drag existing points to move them.")
        print("3. Drag on lines to add new points.")
    
        -- the mesh to draw the polygon with
        polyMesh = mesh()
        -- the current set of vertices for the polygon
        verts = {}
        -- the polygon fill color
        col = color(101, 228, 107, 255)
    
        index = -1
        touchID = -1
    
        -- rigid body for the polygon
        polyBody = nil
    
        timer = 0
    
        local size = 16
        tex = image(size,size)
        setContext(tex)
        stroke(0, 255, 80, 255)
        strokeWidth(5)
        noFill()
        rectMode(CENTER)
        rect(size/2,size/2,size,size)
        setContext()
    
        polyMesh.texture = tex
    end
    
    -- This function gets called once every frame
    function draw()
    
        timer = timer + DeltaTime
        -- create a circle every 2 seconds
        if timer > 2 then
            local body = physics.body(CIRCLE, 25)
            body.restitution = 0.5
            body.x = WIDTH/2
            body.y = HEIGHT
            debugDraw:addBody(body)
            timer = 0
        end
    
        -- This sets the background color to black
        background(0, 0, 0)
    
        -- draw physics objects
        debugDraw:draw()
    
        -- draw the polygon interia
        fill(col)
        polyMesh:draw()
    
        pushStyle()
        lineCapMode(PROJECT)
        fill(255, 255, 255, 255)
    
        -- draw the polygon outline
        local pv = verts[1]
        for k,v in ipairs(verts) do
            noStroke()
            ellipse(v.x, v.y, 10, 10)
            stroke(col)
            strokeWidth(5)
            line(pv.x, pv.y, v.x, v.y)
            pv = v
        end
        if pv then
            line(pv.x, pv.y, verts[1].x, verts[1].y)
        end
        popStyle()
    
    end
    
    function touched(touch)
        local tv = vec2(touch.x, touch.y)
    
        if touch.state == BEGAN and index == -1 then        
            -- find the closest vertex within 50 px of thr touch
            touchID = touch.id
            local minDist = math.huge
            for k,v in ipairs(verts) do
                local dist = v:dist(tv)
                if dist < minDist and dist < 50 then
                    minDist = dist
                    index = k
                end
            end
    
            -- if no point is found near the touch, insert a new one           
            if index == -1 then
                index = #verts
                if index == 0 then
                    index = index + 1
                end
    
                -- if touch is within 50px to a line, insert point on line
                if #verts > 2 then
                    local minDist = math.huge
                    local pv = verts[index]
                    for k,v in ipairs(verts) do
                        local dist = distPointToLineSeg(tv, pv, v)
                        if dist < minDist and dist < 50 then
                            minDist = dist
                            index = k
                        end
                        pv = v
                    end
                end
    
                table.insert(verts, index, tv)
            else
                verts[index] = tv
            end
    
        elseif touch.state == MOVING and touch.id == touchID then
            verts[index] = tv 
        elseif touch.state == ENDED and touch.id == touchID then
            index = -1
        end
    
        -- use triangulate to generate triangles from the polygon outline for the mesh
        local tris = triangulate(verts)
            if tris then
            local uvs = {}
            for i = 1,#tris do
                local v = tris[i]
                table.insert(uvs, vec2(v.x/tex.width, v.y / tex.height))
            end
            polyMesh.vertices = tris
            polyMesh.texCoords = uvs
        end
    
    
        if polyBody then
            polyBody:destroy()
        end
        if #verts > 2 then
            polyBody = physics.body(POLYGON, unpack(verts))
            polyBody.type = STATIC
        end
    end
    
    PhysicsDebugDraw = class()
    
    function PhysicsDebugDraw:init()
        self.bodies = {}
        self.joints = {}
        self.touchMap = {}
        self.contacts = {}
    end
    
    function PhysicsDebugDraw:addBody(body)
        table.insert(self.bodies,body)
    end
    
    function PhysicsDebugDraw:addJoint(joint)
        table.insert(self.joints,joint)
    end
    
    function PhysicsDebugDraw:clear()
        -- deactivate all bodies
    
        for i,body in ipairs(self.bodies) do
            body:destroy()
        end
    
        for i,joint in ipairs(self.joints) do
            joint:destroy()
        end      
    
        self.bodies = {}
        self.joints = {}
        self.contacts = {}
        self.touchMap = {}
    end
    
    function PhysicsDebugDraw:draw()
    
        pushStyle()
        smooth()
        strokeWidth(5)
        stroke(128,0,128)
    
        local gain = 2.0
        local damp = 0.5
        for k,v in pairs(self.touchMap) do
            local worldAnchor = v.body:getWorldPoint(v.anchor)
            local touchPoint = v.tp
            local diff = touchPoint - worldAnchor
            local vel = v.body:getLinearVelocityFromWorldPoint(worldAnchor)
            v.body:applyForce( (1/1) * diff * gain - vel * damp, worldAnchor)
    
            line(touchPoint.x, touchPoint.y, worldAnchor.x, worldAnchor.y)
        end
    
        stroke(0,255,0,255)
        strokeWidth(5)
        for k,joint in pairs(self.joints) do
            local a = joint.anchorA
            local b = joint.anchorB
            line(a.x,a.y,b.x,b.y)
        end
    
        stroke(255,255,255,255)
        noFill()
    
    
        for i,body in ipairs(self.bodies) do
            pushMatrix()
            translate(body.x, body.y)
            rotate(body.angle)
    
            if body.type == STATIC then
                stroke(255,255,255,255)
            elseif body.type == DYNAMIC then
                stroke(150,255,150,255)
            elseif body.type == KINEMATIC then
                stroke(150,150,255,255)
            end
    
            if body.shapeType == POLYGON then
                strokeWidth(5.0)
                local points = body.points
                for j = 1,#points do
                    a = points[j]
                    b = points[(j % #points)+1]
                    line(a.x, a.y, b.x, b.y)
                end
            elseif body.shapeType == CHAIN or body.shapeType == EDGE then
                strokeWidth(5.0)
                local points = body.points
                for j = 1,#points-1 do
                    a = points[j]
                    b = points[j+1]
                    line(a.x, a.y, b.x, b.y)
                end      
            elseif body.shapeType == CIRCLE then
                strokeWidth(5.0)
                line(0,0,body.radius-3,0)
                strokeWidth(2.5)
                ellipse(0,0,body.radius*2)
            end
    
            popMatrix()
        end 
    
        stroke(255, 0, 0, 255)
        fill(255, 0, 0, 255)
    
        for k,v in pairs(self.contacts) do
            for m,n in ipairs(v.points) do
                ellipse(n.x, n.y, 10, 10)
            end
        end
    
        popStyle()
    end
    
    function PhysicsDebugDraw:touched(touch)
        local touchPoint = vec2(touch.x, touch.y)
        if touch.state == BEGAN then
            for i,body in ipairs(self.bodies) do
                if body.type == DYNAMIC and body:testPoint(touchPoint) then
                    self.touchMap[touch.id] = {tp = touchPoint, body = body, anchor = body:getLocalPoint(touchPoint)} 
                    return true
                end
            end
        elseif touch.state == MOVING and self.touchMap[touch.id] then
            self.touchMap[touch.id].tp = touchPoint
            return true
        elseif touch.state == ENDED and self.touchMap[touch.id] then
            self.touchMap[touch.id] = nil
            return true;
        end
        return false
    end
    
    function PhysicsDebugDraw:collide(contact)
        if contact.state == BEGAN then
            self.contacts[contact.id] = contact
            --sound(SOUND_HIT, 2643)
        elseif contact.state == MOVING then
            self.contacts[contact.id] = contact
        elseif contact.state == ENDED then
            self.contacts[contact.id] = nil
        end
    end
    
  • Posts: 25

    John, Wow - what a comprehensive comments. i'll Need some time to work it out. I think I already have the backbone for the bodies. The xy size dependent texCoords are probably the golden hint.
    Thanks a Lot! :)
    Albrecht

  • Posts: 666

    looks like anything with a hashmark expands to an tag.

  • Posts: 25

    John, I now tried your example and it works well after I removed the few html a tags. Impressive real time rendering and physics example.
    Also it worked well in my environment, although I have a more complex polygon which extends over some screen sizes and I triangulated the polygon by myself (btw. I've made screenshots for illustration, but honestly I don't know, where Codea have stored them). My only problem is now, that it works just with your image example (with setcontext() ...) but not the way it should be when using e.g.

     ...
     self.m = mesh()
     ...
     self.w, self.h = spriteSize("Cargo Bot:Crate Blue 2")
     self.m.texture = "Cargo Bot:Crate Blue 2" -- <- not working properly when drawing mesh
     ...
     for i,v in ipairs( self.verts) do
       table.insert(self.texcoords, vec2(v.x/self.w, v.y/ self.h)
     end
     self.m.vertices = self.verts
     self.m.texCoords = self.texcoords
    ...
    

    What can I do to make it work with externally created png Files?
    Thanks in advance!
    Albrecht

  • JohnJohn Admin Mod
    Posts: 572

    .@aciolino I changed back to the simple code formatting. Kind of annoying consider # is pretty common in lua.

  • Posts: 25

    Neither

    tex = image("Cargo Bot: Crate Blue 2")
    

    nor

    tex = readImage("Cargo Bot: Crate Blue 2")
    

    is working in my nor in John's Example with assigning it later on to the mesh.texture
    What a shame, so close, so easy, but not working for me. :-(

  • JohnJohn Admin Mod
    Posts: 572

    Images only repeat if they have power of 2 dimensions (i.e. 2, 4, 8, 16, 32, 64). I'm assuming "Cargo Bot: Crate Blue 2" doesn't have power of 2 dimensions. What you can do is re-render it to a new image using setContext() at the closest power of 2 dimensions and then it should work.

  • Posts: 25

    John, the Crate 2 Image I just used for demonstration. But I didn't read carefully. I thought square dimensions are sufficient and didn't understand the power of 2 rule. Yes, of course I can create e.g. 64x64 tiles and use them for the hill pattern. Thanks a lot again!
    Albrecht

  • Posts: 384

    i am using @john's mesh polygon fill routine to simulate a lake.
    I define a polygon and then tile it with a water image (64*64). This all works fine.
    I then thought i should be able to use the ripple shader from the codea examples to give a better 'watery' feel to the lake. This does not work, i just get a black filled polygon!
    I am not very expert in these things, so maybe this is stupid thing to try-if anyone has any ideas?

  • IgnatzIgnatz Mod
    Posts: 5,396

    If your lake is greater than 2048 pixels, you have exceeded limits and will get black.

    I've done a series of tutorials on 3D tiling, see here.

    http://coolcodea.wordpress.com/2013/05/25/64-3d-tiling-images-across-an-object/
    http://coolcodea.wordpress.com/2013/05/27/665-3d-terrain-hills/

    And many more

  • Posts: 384

    @Ignatz let me thank you for your wonderful tutorials-i really learnt a lot from them. The tilershader is a very elegant solution.
    I choose the @John approach as i thought i could just add on top of it the ripple shader.
    I have tried a variety of different size images and different size meshes, but don't find any combination that works? It seems that as soon as the shader is active the polygon filling becomes black. Even the falling physics circles are no longer visible?

    So does anyone know how to tile an arbitrary polygon and then add a shader effect, like ripple or swirl, to the full area of the polygon (or simultaneously to each unit of the tiling)?

Sign In or Register to comment.