Howdy, Stranger!

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

Rotating mesh's texture

edited June 2013 in General Posts: 2,820

Hello,
I'm wondering if there is an effecient way to rotate a mesh's texture. In other words, I want to make the image on my mesh rotate while the mesh stays still.
Thanks!

Comments

  • IgnatzIgnatz Mod
    Posts: 5,396

    it depends a bit on the shape and size of the mesh and texture. If the texture is big, you can simply choose the texture coordinates so they rotate the piece of the texture that is being mapped, in other words, you do the rotation math for the vertices of the mesh.

    A kludge is to use setContext to create a rotated texture on each draw, and use that. But that may affect speed.

  • Posts: 2,820

    @Ignatz - Alright. I'll look into it. Thanks.

  • Posts: 2,161

    You could do this in a shader. Just rotate the texCoords.

  • Posts: 2,820

    So I rotating the meshes vertices works, but really is not efficient in speed. I've atempted using @Andrew_Stacey's method, without much luck. I don't know much about OpenGL Shaders other than how to change colors and translate meshes. Is there any chance I could have a bit more help?
    Thanks!

  • Posts: 2,820

    So I rotating the meshes vertices works, but really is not efficient in speed. I've atempted using @Andrew_Stacey's method, without much luck. I don't know much about OpenGL Shaders other than how to change colors and translate meshes. Is there any chance I could have a bit more help?
    Thanks!

  • IgnatzIgnatz Mod
    edited July 2013 Posts: 5,396

    Can you give an idea of the size and shape of your mesh and texture? I presume it's not circular, or you'd just rotate the mesh. But if it's rectangular, your texture will need to be bigger than the mesh to cover it as it rotates.

    I would have expected that rotating the vertices should be fast unless you have a lot of meshes.

  • Posts: 2,161

    Yes, give us some code to experiment with.

  • edited July 2013 Posts: 2,820

    @Ignatz, @Andrew_Stacey - My shapes come in every shape and size. Every shape has up to 20 points. Here is a little video of what my app is dealing with:
    [VIDEO REMOVED]
    Here's the code I use to rotate the mesh's texture:

    for i,o in ipairs(self.oList) do
        local newPoints = {}
        for i,v in ipairs(o.mPreset) do
            table.insert(newPoints,v:rotate(math.rad(o.body.angle)))
        end
        o.m.vertices = newPoints
        mapMeshCoords(o.m)
    end

    "o.mPreset" is the pre-triangulated points of my mesh.
    "o.m.vertices" is the vertices of my mesh being displayed.
    The "mapMeshCoords" function is Simeon's code for masking a texture.
    Thanks a lot for the help.

  • Posts: 371

    @Zoyt, sorry to go off topic... But how did you record such high quality video??

  • edited July 2013 Posts: 2,820

    @Jordan - I used the built in recorder. It may be a lower resolution if you have an older iPad. I have an iPad 3. If you're looking for a high resolution recording, you might try the app Reflecor for Mac which lets you AirPlay your screen to it, then record it.

  • Posts: 371

    Oh, maybe thats it :) Makes sense, sorry to bug you! But nice game! Is it inspired by (I think it was) John's Stack3r?

  • Posts: 2,161

    Here's a shader that rotates the texture. I've reused the triangulation code since the code snippet you gave isn't enough to build a new project around.

    function setup()
        img = readImage("Cargo Bot:Codea Icon")
        tm,pts = createMesh(20)
        parameter.integer("n",3,200,20)
        parameter.boolean("convex")
        parameter.action("Create Mesh",function() tm,pts = createMesh(n,convex) 
            end)
        parameter.watch("math.floor(ElapsedTime)")
    
    end
    
    function createMesh(n,convex)
        local a = 2*math.pi/n
        local r = 200
        local o = vec2(WIDTH,HEIGHT)/2
        local pts = {}
        local lines = {}
        local b
        for k=1,n do
            b = r*math.random()*vec2(1,0):rotate(k*a + .1*math.random()) + o
            table.insert(pts, b)
            table.insert(lines,{b, r*(b-o):normalize() + o})
        end
        local tris 
        if convex then
            tris = decompose(pts) -- convex hull
        else
            tris = decompose(pts,lines) -- non-convex version
        end
        local texc = {}
        for _,v in ipairs(tris) do
            table.insert(texc,(v - o)/(2*r) + vec2(.5,.5))
        end
        local cols = {}
        local r,g,b = 0,0,0
        local tc = #tris/3
        local cs = 255/math.ceil(math.pow(tc,1/3))
        for k = 1,tc do
            r = r + cs
            if r > 255 then
                r = 0
                g = g + cs
                if g > 255 then
                    g = 0
                    b = b + cs
                end
            end
            for i=1,3 do
                table.insert(cols,color(r,g,b,255))
                --table.insert(cols,color(255,255,255,125))
            end
        end
        local tm = mesh()
        tm.vertices = tris
        tm.texCoords = texc
        tm.texture = img
        tm.colors = cols
        tm.shader = shader(RotationShader())
        tm.shader.centre = vec2(.5,.5)
        return tm,pts
    end
    
    function draw()
        background(0, 0, 0, 255)
        stroke(238, 153, 5, 255)
        strokeWidth(5)
        --[[
        for k=1,ntri,3 do
            line(tris[k].x,tris[k].y,tris[k+1].x,tris[k+1].y)
            line(tris[k].x,tris[k].y,tris[k+2].x,tris[k+2].y)
            line(tris[k+2].x,tris[k+2].y,tris[k+1].x,tris[k+1].y)
        end
        --]]
        tm.shader.time = ElapsedTime
        tm:draw()
        for k,v in ipairs(pts) do
            ellipse(v.x,v.y,5)
        end
    end
    
    function decompose(pts,lines)
        local npts = {}
        -- create a copy of the points table where each point is
        -- augmented to a table consisting of:
        -- 1. the original point
        -- 2. an empty table which will hold a list of the points
        --    joined to this one
        -- 3. the index of the point
        for k,v in ipairs(pts) do
            table.insert(npts,{v,{},k})
        end
        -- the lens table is a list of all the pairs of points and
        -- the distances between them
        local lens = {}
        for l=1,#npts-1 do
            for m=l+1,#npts do
                table.insert(lens,{npts[l][1]:distSqr(npts[m][1]),npts[l],npts[m]})
            end
        end
        -- we sort this table so that the points with shortest
        -- separations are first
        table.sort(lens,function(a,b) return a[1] < b[1] end)
        -- now we go through the list of pairs and select the lines
        -- from these pairs of points.  We add a line if it does not
        -- cross a line already in the table.  As well as adding the
        -- line to the table of lines, we add each vertex to the
        -- table of the other vertex.  This means that each vertex
        -- ends up with a list of those vertices it is joined to.
        -- By passing in an initial set of lines we can declare some
        -- no-go regions of the shape as new lines will not be added
        -- if they cross the pre-defined lines, but the pre-defined
        -- lines are not used when working out which vertices are
        -- joined, so they become exclusion zones.
        local lines = lines or {}
        local okay
        for _,v in ipairs(lens) do
            okay = true
            for _,u in ipairs(lines) do
                if crossing(v[2][1],v[3][1],u[1],u[2]) then
                    okay = false
                    break
                end
            end
            if okay then
                table.insert(lines,{v[2][1],v[3][1]})
                table.insert(v[2][2],v[3])
                table.insert(v[3][2],v[2])
            end
        end
        -- For each vertex, we sort the list of adjoined vertices
        -- so that they are listed in cyclic order.  We also adjoin
        -- the first point to the end of the list for simplicity
        for _,v in ipairs(npts) do
            table.sort(v[2],function(a,b) return vec2(1,0):angleBetween(a[1] - v[1]) < vec2(1,0):angleBetween(b[1] - v[1]) end)
            table.insert(v[2],v[2][1])
        end
        -- Now we construct the table of triangles.  Each triangle is
        -- formed by starting with a vertex and taking two of its
        -- adjoining vertices, so long as these two are themselves
        -- adjoined.  To ensure that we only count each triangle
        -- once, we only consider a triangle if our starting vertex
        -- has the lowest index of the three under consideration.
        local tris = {}
        for _,v in ipairs(npts) do
            for k=1,#v[2]-1 do
                if v[3] < v[2][k][3] and v[3] < v[2][k+1][3] then
                    okay = false
                    for _,u in ipairs(v[2][k][2]) do
                        if u == v[2][k+1] then
                            okay = true
                        end
                    end
                    if okay then
                        table.insert(tris,v[1])
                        table.insert(tris,v[2][k][1])
                        table.insert(tris,v[2][k+1][1])
                    end
                end
            end
        end
        return tris
    end
    -- this is the tolerance at the end points when checking crossings
    local epsilon = 0.01
    function crossing(a,b,c,d)
        -- rebase at a
        b = b - a
        c = c - a
        d = d - a
        if b:cross(c) * b:cross(d) > 0 then
            -- both c and d lie on the same side of b so no intersection
            return false
        end
        -- if there is an intersection point, this will be it
        a = (b:cross(d) * c - b:cross(c) * d)/(b:cross(d) - b:cross(c))
        -- does the potential intersection point lie on the line
        -- segment?
        local l = a:dot(b)
        if l > epsilon and l < b:dot(b) - epsilon then
            return true
        end
        return false
    end
    
    function RotationShader()
        return [[
    //
    // A basic vertex shader
    //
    
    //This is the current model * view * projection matrix
    // Codea sets it automatically
    uniform mat4 modelViewProjection;
    
    //This is the current mesh vertex position, color and tex coord
    // Set automatically
    attribute vec4 position;
    attribute vec4 color;
    attribute vec2 texCoord;
    
    //This is an output variable that will be passed to the fragment shader
    varying lowp vec4 vColor;
    varying highp vec2 vTexCoord;
    
    uniform vec2 centre;
    uniform float time;
    
    void main()
    {
        //Pass the mesh color to the fragment shader
        vColor = color;
        vec2 tpos = texCoord - centre;
        float ct = cos(time);
        float st = sin(time);
        vTexCoord = vec2(ct*tpos.x - st*tpos.y,st*tpos.x + ct*tpos.y) + centre;
    
        //Multiply the vertex position by our combined transform
        gl_Position = modelViewProjection * position;
    }
    ]],[[
    //
    // A basic fragment shader
    //
    
    //Default precision qualifier
    precision highp float;
    
    //This represents the current texture on the mesh
    uniform lowp sampler2D texture;
    
    //The interpolated vertex color for this fragment
    varying lowp vec4 vColor;
    
    //The interpolated texture coordinate for this fragment
    varying highp vec2 vTexCoord;
    
    void main()
    {
        //Sample the texture at the interpolated coordinate
        lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;
    
        //Set the output color to the texture color
        gl_FragColor = col;
    }
    ]]
    end
    
  • Posts: 2,820

    @Jordan - Yes, it was inspired by that. He gave me permission to use his idea.
    Thanks Andrew, I'll try it out.

  • Posts: 2,820

    @Andrew_Stacey - Thanks so much. It worked.

Sign In or Register to comment.