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!
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.
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?
Thanks!
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!
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:
[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.
@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 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.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
@Jordan - Yes, it was inspired by that. He gave me permission to use his idea.
Thanks Andrew, I’ll try it out.