We would like to have some liquid simulation in Corona as shown in here:
http://www.patrickmatte.com/stuff/physicsLiquid/
But that will require that the Corona Team give us some API to load images from memory (like a table):
display.newImage(width, height, table)
where the table is {255,255,127,255,… } so R,G,B,A,…R,G,B,A
(red, green,blue, and alpha) or for gray images only {255,255,…} for Gray and Alpha.
Or another API to some way modify the image bytes in memory directly.
Right now we can not do this, but let’s have a workaround:
- We need a way to create an image (not even in RGB only gray scale and alpha).
- Then do a blur
- Then apply a threshold.
- And show it on screen.
What we have:
-
We can load images from a folder. But this images have to be PNG or JPEG.
-
So we need a way to save PNG images and for that I will use this code: Minimal PNG encoder from Christian Fröschlin (www.chrfr.de) ported to LUA by myself.
-
Bitwise operators in Lua (bitwise.lua) made by Carlos (you can find it in the share your code)
So here is the example app:
[lua]local physics = require(“physics”)
local png = require(“minPng”)
local img = png.newPNG()
local aquaImg = nil
local gray = {}
local i
local w1= math.floor(display.contentWidth/8)
local h1= math.floor(display.contentHeight/8)
local wh1= w1*h1
for i=1,wh1 do
gray[#gray+1]=0
end
local frame = 0
local refreshImage
– Start Physics
physics.start()
– Hide status bar
display.setStatusBar( display.HiddenStatusBar )
– Create the border to prevent the liquid to fall
borderCollisionFilter = { categoryBits = 1, maskBits = 6 } – collides with (4 & 2) only
borderBodyElement = { friction=0.4, bounce=0.8, filter=borderCollisionFilter }
local borderTop = display.newRect( 0, 0, 320, 1 )
borderTop:setFillColor( 0, 0, 0, 0) – make invisible
physics.addBody( borderTop, “static”, borderBodyElement )
local borderBottom = display.newRect( 0, 479, 320, 1 )
borderBottom:setFillColor( 0, 0, 0, 0) – make invisible
physics.addBody( borderBottom, “static”, borderBodyElement )
local borderLeft = display.newRect( 0, 1, 1, 480 )
borderLeft:setFillColor( 0, 0, 0, 0) – make invisible
physics.addBody( borderLeft, “static”, borderBodyElement )
local borderRight = display.newRect( 319, 1, 1, 480 )
borderRight:setFillColor( 0, 0, 0, 0) – make invisible
physics.addBody( borderRight, “static”, borderBodyElement )
local wall = display.newRect( 100, 350, 140, 20 )
wall:setFillColor( 255, 255, 0)
physics.addBody( wall, “static”, borderBodyElement )
– Create 100 balls that will serve as the liquid elements
local red = {}
local redCollisionFilter = { categoryBits = 2, maskBits = 3 } – collides with (2 & 1) only
local redBody = { density=0.2, friction=0, bounce=0, radius=10, filter=redCollisionFilter }
for i = 1,100 do
red[i] = display.newCircle(160, 200 + math.random(20),10)
– To see only the water add 0 to the alpha here
red[i]:setFillColor(0,127,255 ) – ,0) – To make the balls transparent
red[i].myName = “ball”
red[i].id = i
physics.addBody( red[i], redBody )
end
– Blur and threshold an image
function blurAndThreshold(pix,w,h,radius)
local y,x,ix,iy,y1,x1
local c
local img = {}
local delta, ss, se
ss = math.floor(radius/2) - 1
se = ss * 2
delta = math.floor(255/(radius*radius))
for y=0,h-1 do
for x=0,w-1 do
ix = x - ss
iy = y - ss
c = 1
for y1 = iy,iy+se do
for x1 = ix,ix+se do
if x1<0 or x1>=w then
c = c + 0
elseif y1<0 or y1>=h then
c = c + 0
else
if (pix[(y1*w)+x+1]>0) then
c = c + 1
end
end
end
end
img[(y*w)+x+1] = c * delta
end
end
– Threshold
for i=1,#img do
if img[i]>delta then
pix[i] = 255
else
pix[i] = 0
end
end
end
– Draw a circle inside a image buffer
function drawCircle(pix,w,h,ox,oy,radius)
local r2 = radius*radius
local ny,nx
for y=-radius,radius do
for x=-radius,radius do
ny = y+oy
nx = x+ox
if (nx>0 and nx<=w and ny>0 and ny<=h) then
if(x*x+y*y<=r2) then
pix[(ny*w)+nx+1] = 255
end
end
end
end
end
– Callback to frame
local onFrameUpdate = function( event )
if refreshImage == 1 then
– Clear the buffer image
for i=1,#gray do
gray[i]=0
end
– Draw in the buffer image some dots corresponding to the same position
– of the physical objects
for i = 1,#red do
drawCircle(gray,w1,h1,math.floor(red[i].x/8),math.floor(red[i].y/8),2)
end
– Blur and threshold the image with a 11 pixels square filter
blurAndThreshold(gray,w1,h1,11)
– Save the image as a uncompressed PNG
– Thanks to the Minimal PNG encoder to create PNG streams
– Copyright 2006-2009 Christian Fröschlin
– www.chrfr.de
img:toimage( “aguatest”…frame…".png", system.DocumentsDirectory, w1, h1, gray, gray)
– Remove previous image from screen
if aquaImg then
aquaImg:removeSelf()
aquaImg = nil
end
– Load the new created image and show it.
aquaImg = display.newImageRect(“aguatest”…frame…".png",
system.DocumentsDirectory,display.contentWidth,display.contentHeight)
aquaImg.x = display.contentWidth*0.5
aquaImg.y = display.contentHeight*0.5
aquaImg:toBack()
– This is a flag that will trigger
refreshImage = 0
– Uncomment this line if you want a bunch of images
–frame = frame + 1
– Uncomment this line to have a double buffer
frame = (frame + 1) % 2
end
end
local function onGlobalCollision( event )
if (event.object1.myName~=nil and event.object2.myName~=nil) then
– A collision ended let’s refresh the images
if ( event.phase == “ended”) then
– Update the image
refreshImage = 1
end
end
end
– Create event listeners
Runtime:addEventListener( “enterFrame”, onFrameUpdate )
Runtime:addEventListener( “collision”, onGlobalCollision )[/lua]
and here is the minPNG.lua (remeber this is a Copyright 2006-2009 Christian Fröschlin):
[lua]-- Minimal PNG encoder to create PNG streams (and MIDP images) from RGBA arrays.
– *
– * Copyright 2006-2009 Christian Fröschlin
– *
– * www.chrfr.de
– *
– *
– * Changelog:
– *
– * 10/14/11: Lua version convertion thanks to Emilio Aguirre.
– *
– * 09/22/08: Fixed Adler checksum calculation and byte order
– * for storing length of zlib deflate block. Thanks
– * to Miloslav Ruzicka for noting this.
– *
– * 05/12/09: Split PNG and ZLIB functionality into separate classes.
– * Added support for images > 64K by splitting the data into
– * multiple uncompressed deflate blocks.
– *
– * Terms of Use:
– *
– * You may use the PNG encoder free of charge for any purpose you desire, as long
– * as you do not claim credit for the original sources and agree not to hold me
– * responsible for any damage arising out of its use.
– *
– * If you have a suitable location in GUI or documentation for giving credit,
– * I’d appreciate a mention of
– *
– * PNG encoder © 2006-2009 by Christian Fröschlin, www.chrfr.de
– *
– * but that’s not mandatory.
– *
– * 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.
module(…, package.seeall)
local bit = require (“bitwise”)
function appendTable(t, t2)
for _,v in ipairs(t2) do
t[#t+1] = v
end
end
function insertIntTable(t, value)
table.insert(t,bit.bit_logic_rshift(bit.bit_and(value,4278190080),24)) – x>>24
table.insert(t,bit.bit_logic_rshift(bit.bit_and(value,16711680),16)) – x>>16
table.insert(t,bit.bit_logic_rshift(bit.bit_and(value,65280),8)) – x>>8
table.insert(t,bit.bit_and(value,255)) – x>>0
end
function newZLIB()
local BLOCK_SIZE = 32000
local obj = {}
function obj:writeUncompressedDeflateBlock(zlib, last, raw, off, length)
table.insert(zlib,last) – Final flag, Compression type 0
table.insert(zlib,bit.bit_and(length,255)) – Length LSB
table.insert(zlib,bit.bit_rshift(bit.bit_and(length,65280),8)) – Length MSB
table.insert(zlib,bit.bit_and(bit.bit_not(length),255)) – Length 1st complement LSB
table.insert(zlib,bit.bit_rshift(bit.bit_and(bit.bit_not(length),65280),8)) – Length 1st complement MSB
for i=off,(off+length-1) do
table.insert(zlib,raw[i]) – Data
end
end
function obj:toZLIB(raw)
local zlib = {}
local tmp = 8
table.insert(zlib,tmp) – CM = 8, CMINFO = 0
table.insert(zlib,(31 - (bit.bit_lshift(tmp,8) % 31)) % 31) – FCHECK (FDICT/FLEVEL=0)
local pos = 0
while (#raw - pos > BLOCK_SIZE) do
self:writeUncompressedDeflateBlock(zlib, 0, raw, pos+1, BLOCK_SIZE)
pos = pos + BLOCK_SIZE;
end
self:writeUncompressedDeflateBlock(zlib, 1, raw, pos+1, #raw - pos)
– zlib check sum of uncompressed data
adler = self:calcADLER32(raw)
– Insert integer starting from high bit
insertIntTable(zlib, adler)
return zlib
end
function obj:calcADLER32(raw)
local s1 = 1
local s2 = 0
local i
local r
for i=1,#raw do
local abs = raw[i]
if abs<0 then
abs = abs + 256
end
s1 = (s1 + abs) % 65521;
s2 = (s2 + s1) % 65521;
end
r = bit.bit_lshift(s2,16) + s1
return r
end
return obj
end
function newPNG()
local obj = {}
obj.crcTable = {0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,
249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,
498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,
325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,
997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,
901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,
651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,
671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,
1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,
2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,
1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,
1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,
1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,
1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,
1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,
1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,
3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,
3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,
4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,
4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,
3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,
3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,
3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,
3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,
2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,
2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,
2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,
2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,
2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,
2932959818,3654703836,1088359270,936918000,2847714899,3736837829,1202900863,817233897,
3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,
3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117}
obj.ZLIB = newZLIB()
function obj:toZLIB(raw)
return self.ZLIB:toZLIB(raw)
end
–function createCRCTable()
– If you want to create the crcTable on the fly
– on comment these:
– local crcTable = {};
– local poly = 3988292384
– for i=0,255 do
– local c = i
– for k = 0, 7 do
– if (bit.bit_and(c,1)) == 1 then
– c = bit.bit_xor(poly,bit.bit_logic_rshift(c,1))
– else
– c = bit.bit_logic_rshift(c,1)
– end
– end
– crcTable[#crcTable+1] = c
–end
–return crcTable
–end
function obj:updateCRC(crc, raw)
local crc = 0
for i=1,#raw do
local index = bit.bit_xor(crc,raw[i])
index = bit.bit_and(index,255) + 1
local c = bit.bit_logic_rshift(crc,8)
crc = bit.bit_xor(self.crcTable[index],c)
end
return crc
end
function obj:toChunk(id, raw)
local chunk = {}
local bid = {}
– Insert length
insertIntTable(chunk,#raw)
table.insert(bid,string.byte(id))
table.insert(bid,string.byte(id,2))
table.insert(bid,string.byte(id,3))
table.insert(bid,string.byte(id,4))
appendTable(chunk,bid)
appendTable(chunk,raw)
local crc = 4294967295;
crc = self:updateCRC(crc, bid);
crc = self:updateCRC(crc, raw);
insertIntTable(chunk,bit.bit_not(crc))
return chunk
end
function obj:createHeaderChunk(width,height,colortype)
– colortype 4 Alpha and Gray
– colortype 6 ARGB
local chunk = {}
insertIntTable(chunk,width)
insertIntTable(chunk,height)
table.insert(chunk,8) – Bitdepth
table.insert(chunk,colortype)
table.insert(chunk,0) – Compression
table.insert(chunk,0) – Filter
table.insert(chunk,0) – Interlace
return self:toChunk(“IHDR”, chunk)
end
function obj:createDataChunk(width,height,alpha,red,green,blue)
local source = 1
raw = {}
if (green ~=nil and blue~=nil) then
–ARGB
for y=1,height do
raw[#raw+1] = 0 – No filter
for x=1,width do
raw[#raw+1] = red[source]
raw[#raw+1] = green[source]
raw[#raw+1] = blue[source]
raw[#raw+1] = alpha[source]
source = source + 1
end
end
else
– Alpha and Gray
for y=1,height do
raw[#raw+1] = 0 – No filter
for x=1,width do
raw[#raw+1] = red[source]
raw[#raw+1] = alpha[source]
source = source + 1
end
end
end
return self:toChunk(“IDAT”, self:toZLIB(raw))
end
function obj:createTrailerChunk()
return self:toChunk(“IEND”, {})
end
function obj:toPNG(width,height,alpha,red,green,blue)
local signature = {137, 80, 78, 71, 13, 10, 26, 10};
local colortype = 4
if (green ~=nil and blue~=nil) then
colortype = 6
end
local header = self:createHeaderChunk(width, height,colortype);
local data = self:createDataChunk(width, height, alpha, red, green, blue);
local trailer = self:createTrailerChunk();
local png = {}
appendTable(png,signature)
appendTable(png,header)
appendTable(png,data)
appendTable(png,trailer)
return png
end
function obj:toimage(filename, path, width, height, alpha, red, green,blue)
– Add save png to file
local png = self:toPNG(width, height, alpha, red, green, blue)
local results = true – assume no errors
local wfilePath = system.pathForFile( filename, path )
– print (wfilePath)
local wfh, errStr = io.open( wfilePath, “wb” )
if not wfh then
print( “writeFileName open error!” )
results = false – error
else
for i=1,#png do
if not wfh:write ( string.char( png[i] )) then
print( “write error!” )
results = false – error
break
end
end
end
– Clean up our file handles
wfh:close()
return results
end
return obj
end
[/lua]
That’s it. This is a proof of concept that we can create liquid in corona, but so far the frame rate is slowwwwww. So for the test I create an image 1/8 of the size of the screen and then blow up to fit the entire screen.
Use this code in the emultor.
Hope this helps.
Cheers,
Emilio [import]uid: 9975 topic_id: 16651 reply_id: 316651[/import]
