Rotating mesh's texture

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.

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.

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

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

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?

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?

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.

Yes, give us some code to experiment with.

@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:
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
    o.m.vertices = newPoints


"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.

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

@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.

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

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.action("Create Mesh",function() tm,pts = createMesh(n,convex) 


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})
    local tris 
    if convex then
        tris = decompose(pts) -- convex hull
        tris = decompose(pts,lines) -- non-convex version
    local texc = {}
    for _,v in ipairs(tris) do
        table.insert(texc,(v - o)/(2*r) + vec2(.5,.5))
    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
        for i=1,3 do
    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

function draw()
    background(0, 0, 0, 255)
    stroke(238, 153, 5, 255)
    for k=1,ntri,3 do
    tm.shader.time = ElapsedTime
    for k,v in ipairs(pts) do

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
    -- 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
    -- 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
        if okay then
    -- 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)
    -- 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
                if okay then
    return tris
-- 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
    -- 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
    return false

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;

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

@Andrew_Stacey - Thanks so much. It worked.