Skip to content

Instantly share code, notes, and snippets.

@ggcrunchy
Last active March 11, 2018 13:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ggcrunchy/42c6528c0a4b9d3e144b34e738d667a3 to your computer and use it in GitHub Desktop.
Save ggcrunchy/42c6528c0a4b9d3e144b34e738d667a3 to your computer and use it in GitHub Desktop.
Mesh-based cloth
--- Test for cloth.
-- Permission is hereby granted, free of charge, to any person obtaining
-- a copy of this software and associated documentation files (the
-- "Software"), to deal in the Software without restriction, including
-- without limitation the rights to use, copy, modify, merge, publish,
-- distribute, sublicense, and/or sell copies of the Software, and to
-- permit persons to whom the Software is furnished to do so, subject to
-- the following conditions:
--
-- The above copyright notice and this permission notice shall be
-- included in all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--
-- [ MIT license: http://www.opensource.org/licenses/mit-license.php ]
-- Standard library imports --
local ipairs = ipairs
local random = math.random
-- Modules --
local physics = require("physics")
-- Corona globals --
local display = display
local Runtime = Runtime
local timer = timer
-- Kick off physics.
physics.start()
-- Cell counts --
local NCols, NRows = 20, 30
-- Shorthand to hide point / cell confusion --
local VertsInRow = NCols + 1
-- Add indices in quads (straddling rows) and track edges containing them.
local edges, indices, ni = {}, {}, 0
for row = 1, NRows do
local roff1 = (row - 1) * VertsInRow
local roff2 = roff1 + VertsInRow
for col = 1, NCols do
-- Triangle 1
indices[ni + 1] = roff1 + col -- will be ni - 5
indices[ni + 2] = roff1 + col + 1 -- aka ni + 2, will be ni - 4
indices[ni + 3] = roff2 + col -- aka ni + 3, will be ni - 3
-- Triangle 2
indices[ni + 4] = indices[ni + 2]
indices[ni + 5] = indices[ni + 3]
indices[ni + 6] = roff2 + col + 1 -- will be ni
ni = ni + 6
if row > 1 then
-- Forward edge.
edges[#edges + 1] = indices[ni - 5]
edges[#edges + 1] = indices[ni - 4]
end
-- Downward edge.
edges[#edges + 1] = indices[ni - 5]
edges[#edges + 1] = indices[ni - 3]
end
-- Final downward edge.
edges[#edges + 1] = indices[ni - 4]
edges[#edges + 1] = indices[ni]
end
-- Last row: forward edges.
local roffn = ni - VertsInRow
for col = 1, NCols do
edges[#edges + 1] = indices[roffn + col]
edges[#edges + 1] = indices[roffn + col + 1]
end
-- Shorthand for content center --
local CX, CY = display.contentCenterX, display.contentCenterY
-- Mesh dimensions --
local W, H = math.floor(.6 * display.contentWidth), math.floor(.6 * display.contentHeight)
-- Hang the cloth from something.
local back = display.newRoundedRect(CX, CY - H / 2, W + 40, 50, 25)
back:setFillColor(.2, .3)
back:setStrokeColor(.1)
back.strokeWidth = 2
-- Add physics objects to an invisible group.
local ClothGroup = display.newGroup()
ClothGroup.isVisible = false
-- Cell dimensions --
local ColW, RowH = W / NCols, H / NRows
-- Upper-left corner of cloth --
local X0, Y0 = CX - W / 2, CY - H / 2
-- Calculate positions of cloth vertices / physics bodies.
local vertices = {}
for row = 0, NRows do
local y, top = row * RowH, row == 0
for col = 0, NCols do
local x = col * ColW
vertices[#vertices + 1] = x
vertices[#vertices + 1] = y
local body = display.newCircle(ClothGroup, X0 + x, Y0 + y, 5)
physics.addBody(body, top and "static" or "dynamic")
end
end
-- Add spring-like joints between all edges.
for i = 1, #edges, 2 do
local b1, b2 = ClothGroup[edges[i]], ClothGroup[edges[i + 1]]
local joint = physics.newJoint("distance", b1, b2, b1.x, b1.y, b2.x, b2.y)
joint.dampingRatio, joint.frequency = .12 + random() * .14, random(15, 21) -- numbers from code.google.com/p/box2c
end
-- Show the cloth mesh.
local mesh = display.newMesh{ x = CX, y = CY, mode = "indexed", indices = indices, vertices = vertices }
mesh.fill = { filename = "Image1.jpg", type = "image" }
-- Update the vertices each frame to reflect the physics state.
Runtime:addEventListener("enterFrame", function()
local mpath = mesh.path
for i = 1, ClothGroup.numChildren do
local body = ClothGroup[i]
mpath:setVertex(i, body.x - X0, body.y - Y0)
end
end)
do
local Shuffle, N = {}, 0
-- Populate an array (to be shuffled) with the indices of a few rows of points.
local NWindRows = 10
local From = math.floor(ClothGroup.numChildren / 2) - (NWindRows / 2) * VertsInRow
local Total = NWindRows * VertsInRow
assert(From >= Total, "Not enough rows to accommodate wind rows") -- catch easy-to-make tuning errors
for col = 0, Total - 1 do
Shuffle[#Shuffle + 1] = From - col
end
-- Every so often, randomly apply a force to a few points. To keep forces from
-- accumulating, shuffle these around a bit.
timer.performWithDelay(150, function()
for _ = 1, 20 do
-- Refill the array if empty. Otherwise, grab a random element and shrink it.
if N == 0 then
N = Total
end
local index = random(N)
local pos = Shuffle[index]
Shuffle[index], Shuffle[N], N = Shuffle[N], Shuffle[index], N - 1
-- Apply some "wind" to one of the points.
ClothGroup[pos]:applyForce(-.355 * random(), 0)
end
end, 0)
end
-- display.setDrawMode("wireframe")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment