#### 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 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!

Tagged:

• edited November 2012 Posts: 635

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
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
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.x, verts.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

table.insert(self.bodies,body)
end

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)
strokeWidth(2.5)
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?
Albrecht

• Posts: 635

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

• Posts: 635

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: 561

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?

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

And many more

• Posts: 561

@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)?