Could someone please give me a small example of how to stretch a large texture over a mesh divided into many polygons? For instance, putting a flag image over the cloth mesh in the cloth example. Or just an image stretched over a flat 2d mesh. Thanks.
If I understand correctly what you want, you might like to take a look at my “flexible words” example in http://www.twolivesleft.com/Codea/Talk/discussion/639/flexible-words/p1
(I can never remember how to embed youtube videos in this forum, the link is http://youtu.be/NDcBdeVnweY)
Thanks, @Andrew_Stacey. The only thing I care about is the UV mapping, and I think the answer is somewhere in the following code I ripped from your example:
method = REPELL
ode = TENSION
--showpoints = true
screen = mesh()
side = 50
spr = 1
fr = .1
ht = side*math.sqrt(3)/2
local n = math.floor(WIDTH/side)+1
local m = math.floor(HEIGHT/ht)+1
local ad,v,rpts,pt
local pts = {}
tri = {}
for i=1,m do
rpts = {}
ad = (1-i%2)*side/2
for j = 1,n do
v = vec2(ad + (j-1)*side,(i-1)*ht)
pt = {point = v, velocity = vec2(0,0), neighbours = {}}
if j ~= 1 then
table.insert(pt.neighbours,rpts[j-1])
table.insert(rpts[j-1].neighbours,pt)
end
if i ~= 1 then
table.insert(pt.neighbours,pts[i-1][j])
table.insert(pts[i-1][j].neighbours,pt)
if i%2 == 1 then
if j ~= 1 then
table.insert(pt.neighbours,pts[i-1][j-1])
table.insert(pts[i-1][j-1].neighbours,pt)
table.insert(tri,{pt,rpts[j-1],pts[i-1][j-1]})
table.insert(tri,{pt,pts[i-1][j-1],pts[i-1][j]})
end
else
if j ~= n then
table.insert(pt.neighbours,pts[i-1][j+1])
table.insert(pts[i-1][j+1].neighbours,pt)
table.insert(tri,{pt,pts[i-1][j],pts[i-1][j+1]})
end
if j ~= 1 then
table.insert(tri,{pt,rpts[j-1],pts[i-1][j]})
end
end
end
table.insert(rpts,pt)
end
table.insert(pts,rpts)
end
points = {}
for k,v in ipairs(pts) do
for l,u in ipairs(v) do
table.insert(points,u)
end
end
local n
for k,v in ipairs(points) do
n = 0
for l,u in ipairs(v.neighbours) do
n = n + 1
end
if n == 6 then
v.fixed = false
else
v.fixed = true
end
end
img = image(WIDTH,HEIGHT)
pushStyle()
setContext(img)
fill(0, 107, 255, 255)
noSmooth()
rect(0,0,WIDTH,HEIGHT)
font("Noteworthy-Bold")
fontSize(160)
fill(255, 255, 255, 255)
local fm = fontMetrics()
local h = 1.8*fm.xHeight
fill(0, 255, 21, 255)
text("Words",WIDTH/2,HEIGHT/2 + h)
text("are",WIDTH/2,HEIGHT/2)
text("flexible",WIDTH/2,HEIGHT/2 - h)
setContext()
popStyle()
screen.texture = img
texc = {}
for k,v in ipairs(tri) do
for l,u in ipairs(v) do
table.insert(texc,vec2(u.point.x/WIDTH,u.point.y/HEIGHT))
end
end
screen.texCoords = texc
Now I just have to spend a little time deciphering. Thanks for pointing me in the right direction.
I am having a little trouble getting this figured out, the code is a little complex. What I want to do is put a texture on a mesh like it is pictured left, NOT how it is pictured right. :
I think I need to create a list of texture coordinates and somehow tie those to vertices in my mesh, just having a hard time getting that done. If there is a simpler example I would love to see it, otherwise I will keep trying to figure it out.
Here’s another one which also renders a picture to a mesh. You’ll need to change the picture.
-- Use this function to perform your initial setup
--supportedOrientations(LANDSCAPE_ANY)
displayMode(FULLSCREEN)
ALLOW_ODD = false
SHOW_PICTURE = true
GAME = 1
GRID = 2
OPACITY = 3
SUCCESS = 4
drawfn = {}
touchedfn = {}
function setup()
bgcolour = color(40, 40, 50)
state = GRID
mrows = 6
mcolumns = 5
mwidth = WIDTH/mrows
mheight = HEIGHT/mcolumns
aduration = .8
rows = 4
columns = 4
opacity = 127
picture = "Documents:Snowman"
smash = mesh()
smash:addRect(0,0,WIDTH,HEIGHT)
smash.texture = picture
smash.texCoords = {
vec2(0,0),
vec2(0,1),
vec2(1,1),
vec2(0,0),
vec2(1,1),
vec2(1,0)
}
end
function initialise()
mash = mesh()
fold, unfold = foldem(rows,columns)
rwidth = WIDTH/rows
rheight = HEIGHT/columns
mash.texture = picture
local x,y
places = {}
for i = 1,rows do
x = (i-.5)*rwidth
for j = 1,columns do
y = (j-.5)*rheight
table.insert(places,{x,y})
end
end
-- kth piece is in p[k]th place, start with blank at top left
local pp = KnuthShuffle(rows*columns-1,ALLOW_ODD)
while is_identity(pp) do
pp = KnuthShuffle(rows*columns-1,ALLOW_ODD)
end
p = {1}
for k,v in ipairs(pp) do
p[k+1] = v+1
end
-- kth place contains pi[k]th piece
pi = {}
for k = 1,rows*columns do
pi[p[k]] = k -- inverse of p
mash:addRect(places[p[k]][1],places[p[k]][2],rwidth,rheight)
end
if SHOW_PICTURE then
mash:setRectColor(1,255,255,255,opacity)
else
mash:setRectColor(1,0,0,0,0)
end
local ij
for k = 2,rows*columns do
ij = unfold(k)
mash:setRectTex(k,
(ij[1]-1)/rows,
(ij[2]-1)/columns,
1/rows,
1/columns
)
end
end
function draw()
drawfn[state]()
end
drawfn[GAME] = function ()
background(bgcolour)
if animate then
local t = (ElapsedTime - atime)/aduration
local last
if t > 1 then
last = true
t = 1
end
-- smooth out the motion
t = (3 - 2*t)*t*t
for k,v in ipairs(animate) do
mash:setRect(v[1],
t*v[3][1] + (1-t)*v[2][1],
t*v[3][2] + (1-t)*v[2][2],
rwidth,
rheight)
end
if last then
animate = nil
end
else
if is_identity(p) then
state = SUCCESS
end
end
mash:draw()
end
drawfn[SUCCESS] = function ()
if not set then
backingMode(RETAINED)
mash:draw()
font("AcademyEngravedLetPlain")
fill(201, 180, 29, 255)
fontSize(100)
textWrapWidth(0)
text("Congratulations!",WIDTH/2,HEIGHT/2)
stime = ElapsedTime
set = true
end
local x,y,s,r,g,b
x = math.random(1,WIDTH)
y = math.random(1,HEIGHT)
s = math.random(25,75)
r = math.random(0,255)
g = math.random(0,255)
b = math.random(0,255)
fill(r,g,b,127)
ellipse(x,y,s)
if ElapsedTime - stime > 5 then
set = false
backingMode(STANDARD)
state = GRID
end
end
drawfn[GRID] = function ()
background(bgcolour)
local sw,sh = rows*mwidth,columns*mheight
smash:setRect(1,sw/2,sh/2,sw,sh)
smash:draw()
noFill()
strokeWidth(2)
stroke(166, 170, 23, 255)
noSmooth()
rectMode(CENTER)
for i = 1,mrows do
for j = 1,mcolumns do
rect((i - .5)*mwidth,(j - .5)*mheight,mwidth,mheight)
end
end
fontSize(50)
textWrapWidth(3*WIDTH/4)
textAlign(CENTER)
local msg = "Choose a grid size by dragging the corner of the picture. Double-tap to select."
local w,h = textSize(msg)
local x,y = WIDTH/2,HEIGHT-h/2
fill(0,0,0,127)
noStroke()
rect(x,y,w,h)
fill(25, 54, 156, 255)
text(msg,x,y)
end
drawfn[OPACITY] = function ()
background(bgcolour)
tint(255,255,255,opacity)
spriteMode(CORNER)
sprite(picture,0,0,WIDTH,HEIGHT)
fontSize(50)
textWrapWidth(3*WIDTH/4)
textAlign(CENTER)
local msg = "The picture is shown in the blank square. Drag a finger to choose how visible it is. Double-tap to select."
local w,h = textSize(msg)
local x,y = WIDTH/2,HEIGHT-h/2
fill(0,0,0,127)
noStroke()
rect(x,y,w,h)
fill(25, 54, 156, 255)
text(msg,x,y)
end
function touched(touch)
touchedfn[state](touch)
end
touchedfn[GRID] = function (touch)
if touch.state == MOVING then
rows = math.max(2,math.floor(touch.x/mwidth) + 1)
columns = math.max(2,math.floor(touch.y/mheight) + 1)
end
if touch.state == ENDED and touch.tapCount == 2 then
state = OPACITY
end
end
touchedfn[GAME] = function (touch)
if animate then
return
end
if touch.state == ENDED then
local tpiece = {
math.floor(touch.x/rwidth) + 1,
math.floor(touch.y/rheight) + 1
}
if lonedist(unfold(p[1]),tpiece) == 1 then
local l = fold(unpack(tpiece))
animate = {
{1,places[p[1]],places[l]},
{pi[l],places[l],places[p[1]]}
}
atime = ElapsedTime
-- piece 1 (blank) is now at place l,
-- what is now at l is the blank (piece 1),
-- what was in place l (pi[l]) is now
-- where the blank was (p[1]),
-- what is where the blank was (p[1]) is
-- what was in place l (pi[l])
p[1],pi[l],p[pi[l]],pi[p[1]] = l,1,p[1],pi[l]
end
end
end
touchedfn[OPACITY] = function (touch)
if touch.state == MOVING then
opacity = 255*math.min(1,math.max(0,(1.2*touch.x/WIDTH - .1)))
end
if touch.state == ENDED and touch.tapCount == 2 then
initialise()
state = GAME
end
end
function KnuthShuffle(n,odd)
local l
local o = 0
local p = {}
for k = 1,n do
p[k] = k
end
for k = 1,n-1 do
l = math.random(k,n)
if l ~= k then
p[k],p[l] = p[l],p[k]
o = 1 - o
end
end
if not odd and o == 1 then
p[1],p[2] = p[2],p[1]
end
return p
end
function lonedist(a,b)
return math.abs(a[1] - b[1]) + math.abs(a[2] - b[2])
end
function foldem(r,c)
return function (i,j)
return (i-1)*c + j
end,
function (k)
return {math.floor((k-1)/c)%r + 1,(k-1)%c + 1}
end
end
function is_identity(p)
for k,v in ipairs(p) do
if v ~= k then
return false
end
end
return true
end
The key, for me, is to remember that the picture covers the unit square [0,1] x [0,1]. So I imagine laying out my mesh on that square and then reading off the coordinates of each vertex. Those are the texture coordinates.
That is very informative, @Andrew_Stacey, thanks for this example. It seems like there is not enough documentation about how to do the texturing. After I fully grok it, I will add some info on this to the wiki.
that should do what you need, assuming all the quads with the same size:
function setup()
img = readImage("SpaceCute:Health Heart")
imgWidth = img.width
imgHeight = img.height
local numTileX = 4
local numTileY = 4
local tileWidth = imgWidth / numTileX
local tileHeight = imgHeight / numTileY
local tileU = 1 / numTileX
local tileV = 1 / numTileY
mesh = mesh()
mesh.texture = img
for i = 0, numTileX - 1 do
for j = 0, numTileY - 1 do
--mesh quads are centered on x,y, so a displace of 1/2 width, 1/2 height
--is required to set the image position in (0,0)
local r = mesh:addRect((i+.5)*tileWidth,(j+.5)*tileHeight,
tileWidth,tileHeight)
mesh:setRectTex(r,i*tileU,j*tileV,tileU,tileV)
end
end
end
function draw()
-- This sets a dark background color
background(40, 40, 50)
pushMatrix()
-- draw in the middle of the screen
translate((WIDTH - imgWidth)/2,(HEIGHT - imgHeight)/2)
mesh:draw()
popMatrix()
end
I was a little slow, but finally I grasp how to texturemap using either texCoords OR setTextRec. I think I will write a tutorial to try and help other newbs get it. Thanks for all the help, @shrike and @Andrew_Stacey.
Yes, please do Vega. I need to switch my app over to Meshes some time and I know for a fact another idea I have will need them.
Now my question is… is it possible to apply a mesh to a drawn line?