Created
March 14, 2012 00:02
-
-
Save rojepp/2032834 to your computer and use it in GitHub Desktop.
F# Translation for Miguel. I have no idea if this works, and it is pretty much a verbatim translation, only some F# flare added. I didn't dare to do too much refactoring without having a way to test properly. Original: https://github.com/xamarin/monotouch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace GLCameraRipple | |
open System | |
open System.Drawing | |
open System.Runtime.InteropServices | |
//open MonoTouch.CoreFoundation | |
open Microsoft.FSharp.NativeInterop | |
type RippleModel(screenSize : Size, meshFactor: int, touchRadius: int, textureSize: Size) = | |
do Console.WriteLine ("New RippleModel"); | |
let poolWidth = screenSize.Width / meshFactor | |
let poolHeight = screenSize.Height / meshFactor | |
let texCoordFactorS, texCoordOffsetS, | |
texCoordFactorT, texCoordOffsetT = | |
if (float screenSize.Height) / (float screenSize.Width) < (float textureSize.Width) / (float textureSize.Height) then | |
let t = (textureSize.Height * screenSize.Height |> float) / (screenSize.Width * textureSize.Width |> float) | |
t , (1.0 - t) / 2.0, | |
1.0, 0.0 | |
else | |
let t = (screenSize.Width * textureSize.Width |> float) / (textureSize.Height * screenSize.Height |> float) | |
1.0, 0.0, | |
t , (1.0 - t) / 2.0 | |
let rippleCoeff = Array2D.init (touchRadius * 2 + 1) (touchRadius * 2 + 1) (fun x y -> | |
let distance = (x - touchRadius) * (x - touchRadius) + (y - touchRadius) * (y - touchRadius) |> float |> sqrt | |
if distance < (float touchRadius) then | |
let factor = distance / (float touchRadius) | |
((factor * Math.PI |> cos) + 1.0) * 256.0 | |
else 0.0 | |
) | |
// +2 for padding the border, mutable so we avoid array copy | |
let mutable rippleSource = Array2D.create (poolWidth + 2) (poolHeight + 2) 0.0 | |
let mutable rippleDest = Array2D.create (poolWidth + 2) (poolHeight + 2) 0.0 | |
let poolSize2 = poolWidth * poolHeight * 2 | |
let rippleVertices = Marshal.AllocHGlobal(poolSize2 * sizeof<float>); | |
let rippleTexCoords = Marshal.AllocHGlobal(poolSize2 * sizeof<float>); | |
let rippleIndicies = Marshal.AllocHGlobal((poolHeight - 1) * (poolWidth * 2 + 2) * sizeof<uint16>); | |
// Use nativeptr internally, expose nativeint | |
let verts = NativePtr.ofNativeInt<float> rippleVertices | |
let texcoords = NativePtr.ofNativeInt<float> rippleTexCoords | |
let indices = NativePtr.ofNativeInt<uint16> rippleIndicies | |
do // InitMesh() | |
for i in 0 .. poolHeight - 2 do | |
for j in 0 .. poolWidth - 1 do | |
// This is hideous, but can probably be improved with some inlined helper | |
NativePtr.set verts ((i * poolWidth + j) * 2 + 0) (-1.0 + (float j) * (2.0 / ((float poolWidth) - 1.0))) | |
NativePtr.set verts ((i * poolWidth + j) * 2 + 1) ( 1.0 - (float i) * (2.0 / ((float poolHeight) - 1.0))) | |
NativePtr.set texcoords ((i * poolWidth + j) * 2 + 0) ( (float i) / ((float poolHeight) - 1.0) * texCoordFactorS + texCoordOffsetS) | |
NativePtr.set texcoords ((i * poolWidth + j) * 2 + 1) ( (float i) / ((float poolHeight) - 1.0) * texCoordFactorS + texCoordOffsetS) | |
let index = ref 0 | |
let write (v: int) = NativePtr.set indices !index (uint16 v) | |
incr index | |
for i in 0 .. poolHeight - 1 do | |
for j in 0 .. poolWidth - 1 do | |
if i % 2 = 0 then | |
if j = 0 then | |
(i ) * poolWidth + j |> write | |
(i ) * poolWidth + j |> write | |
(i + 1) * poolWidth + j |> write | |
if j = (poolWidth - 1) then | |
(i + 1) * poolWidth + j |> write | |
else | |
if j = 0 then | |
(i + 1) * poolWidth + j |> write | |
(i + 1) * poolWidth + j |> write | |
(i ) * poolWidth + j |> write | |
if j = (poolWidth - 1) then | |
(i ) * poolWidth + j |> write | |
let simulate () = | |
for y in 0 .. poolHeight - 1 do | |
for x in 0 .. poolWidth - 1 do | |
// water update | |
let a, b, c, d = | |
rippleSource.[x + 1, y ], | |
rippleSource.[x + 1, y + 2], | |
rippleSource.[x , y + 1], | |
rippleSource.[x + 2, y + 1] | |
let result = (a + b + c + d) / 2.0 - rippleDest.[x + 1, y + 1] | |
let diminished = result - result / 32.0 | |
rippleDest.[x + 1, y + 1] <- diminished | |
// texcoords | |
for y in 0 .. poolHeight - 1 do | |
for x in 0 .. poolWidth - 1 do | |
let a, b, c, d = | |
rippleDest.[x + 1, y ], | |
rippleDest.[x + 1, y + 2], | |
rippleDest.[x , y + 1], | |
rippleDest.[x + 2, y + 1] | |
let inline clamp min max v = if v < min then min elif v > max then max else v | |
let s_offset = (b - a) / 2048.0 |> clamp -0.5 0.5 | |
let t_offset = (c - d) / 2048.0 |> clamp -0.5 0.5 | |
let s_tc = (float y) / (poolHeight - 1 |> float) * texCoordFactorS + texCoordOffsetS | |
let t_tc = (float x) / (poolWidth - 1 |> float) * texCoordFactorT + texCoordOffsetT | |
NativePtr.set texcoords ((y * poolWidth + x) * 2 + 0) (s_tc + s_offset) | |
NativePtr.set texcoords ((y * poolWidth + x) * 2 + 1) (t_tc + t_offset) | |
let temp = rippleSource | |
rippleSource <- rippleDest | |
rippleDest <- temp | |
let addRipple (l: PointF) = | |
let xIndex = (int (l.X / (float32 screenSize.Width ))) * poolWidth | |
let yIndex = (int (l.Y / (float32 screenSize.Height))) * poolHeight | |
for y in yIndex - touchRadius .. yIndex + touchRadius do | |
for x in xIndex - touchRadius .. xIndex + touchRadius do | |
if x >= 0 && x < poolWidth && y >= 0 && y < poolHeight then | |
rippleSource.[x + 1, y + 1] = rippleSource.[x + 1, y + 1] + rippleCoeff.[y - (yIndex - touchRadius), x - (xIndex - touchRadius)] |> ignore | |
member this.Vertices with get () = NativePtr.toNativeInt verts | |
member this.TexCoords with get () = NativePtr.toNativeInt texcoords | |
member this.Indices with get () = NativePtr.toNativeInt indices | |
member this.VertexSize with get () = poolWidth * poolHeight * 2 * sizeof<float> | |
member this.IndexSize with get () = (poolHeight - 1) * (poolWidth * 2 + 2) * sizeof<float> | |
member this.IndexCount with get () = this.IndexSize / sizeof<uint16> | |
member this.RunSimulation() = simulate () | |
member this.InitiateRippleAtLocation(location) = addRipple location |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Drawing; | |
using System.Runtime.InteropServices; | |
using MonoTouch.CoreFoundation; | |
namespace GLCameraRipple | |
{ | |
public class RippleModel | |
{ | |
Size screenSize; | |
int poolHeight, poolWidth; | |
int touchRadius, meshFactor; | |
float texCoordFactorS; | |
float texCoordOffsetS; | |
float texCoordFactorT; | |
float texCoordOffsetT; | |
// ripple coefficients | |
float[,] rippleCoeff; | |
// ripple simulation buffers | |
float[,] rippleSource; | |
float[,] rippleDest; | |
// data passed to GL | |
unsafe float* rippleVertices; | |
unsafe float* rippleTexCoords; | |
unsafe ushort* rippleIndicies; | |
public RippleModel(Size screenSize, int meshFactor, int touchRadius, Size textureSize) | |
{ | |
Console.WriteLine("New RippleModel"); | |
this.screenSize = screenSize; | |
this.meshFactor = meshFactor; | |
this.touchRadius = touchRadius; | |
poolWidth = screenSize.Width / meshFactor; | |
poolHeight = screenSize.Height / meshFactor; | |
if ((float)screenSize.Height / screenSize.Width < (float)textureSize.Width / textureSize.Height) | |
{ | |
texCoordFactorS = (float)(textureSize.Height * screenSize.Height) / (screenSize.Width * textureSize.Width); | |
texCoordOffsetS = (1 - texCoordFactorS) / 2f; | |
texCoordFactorT = 1; | |
texCoordOffsetT = 0; | |
} | |
else | |
{ | |
texCoordFactorS = 1; | |
texCoordOffsetS = 0; | |
texCoordFactorT = (float)(screenSize.Width * textureSize.Width) / (textureSize.Height * screenSize.Height); | |
texCoordOffsetT = (1 - texCoordFactorT) / 2f; | |
} | |
rippleCoeff = new float[touchRadius * 2 + 1, touchRadius * 2 + 1]; | |
// +2 for padding the border | |
rippleSource = new float[poolWidth + 2, poolHeight + 2]; | |
rippleDest = new float[poolWidth + 2, poolHeight + 2]; | |
unsafe | |
{ | |
int poolsize2 = poolWidth * poolHeight * 2; | |
rippleVertices = (float*)Marshal.AllocHGlobal(poolsize2 * sizeof(float)); | |
rippleTexCoords = (float*)Marshal.AllocHGlobal(poolsize2 * sizeof(float)); | |
rippleIndicies = (ushort*)Marshal.AllocHGlobal((poolHeight - 1) * (poolWidth * 2 + 2) * sizeof(ushort)); | |
} | |
InitRippleCoef(); | |
InitMesh(); | |
} | |
void InitRippleCoef() | |
{ | |
for (int y = 0; y <= 2 * touchRadius; y++) | |
{ | |
for (int x = 0; x <= 2 * touchRadius; x++) | |
{ | |
float distance = (float)Math.Sqrt((x - touchRadius) * (x - touchRadius) + (y - touchRadius) * (y - touchRadius)); | |
if (distance <= touchRadius) | |
{ | |
float factor = (distance / touchRadius); | |
// goes from -512 -> 0 | |
rippleCoeff[x, y] = -((float)Math.Cos(factor * Math.PI) + 1f) * 256f; | |
} | |
else | |
rippleCoeff[x, y] = 0; | |
} | |
} | |
} | |
unsafe void InitMesh() | |
{ | |
for (int i = 0; i < poolHeight; i++) | |
{ | |
for (int j = 0; j < poolWidth; j++) | |
{ | |
rippleVertices[(i * poolWidth + j) * 2 + 0] = -1f + j * (2f / (poolWidth - 1)); | |
rippleVertices[(i * poolWidth + j) * 2 + 1] = 1f - i * (2f / (poolHeight - 1)); | |
rippleTexCoords[(i * poolWidth + j) * 2 + 0] = (float)i / (poolHeight - 1) * texCoordFactorS + texCoordOffsetS; | |
rippleTexCoords[(i * poolWidth + j) * 2 + 1] = (1f - (float)j / (poolWidth - 1)) * texCoordFactorT + texCoordFactorT; | |
} | |
} | |
uint index = 0; | |
for (int i = 0; i < poolHeight - 1; i++) | |
{ | |
for (int j = 0; j < poolWidth; j++) | |
{ | |
if (i % 2 == 0) | |
{ | |
// emit extra index to create degenerate triangle | |
if (j == 0) | |
{ | |
rippleIndicies[index] = (ushort)(i * poolWidth + j); | |
index++; | |
} | |
rippleIndicies[index] = (ushort)(i * poolWidth + j); | |
index++; | |
rippleIndicies[index] = (ushort)((i + 1) * poolWidth + j); | |
index++; | |
// emit extra index to create degenerate triangle | |
if (j == (poolWidth - 1)) | |
{ | |
rippleIndicies[index] = (ushort)((i + 1) * poolWidth + j); | |
index++; | |
} | |
} | |
else | |
{ | |
// emit extra index to create degenerate triangle | |
if (j == 0) | |
{ | |
rippleIndicies[index] = (ushort)((i + 1) * poolWidth + j); | |
index++; | |
} | |
rippleIndicies[index] = (ushort)((i + 1) * poolWidth + j); | |
index++; | |
rippleIndicies[index] = (ushort)(i * poolWidth + j); | |
index++; | |
// emit extra index to create degenerate triangle | |
if (j == (poolWidth - 1)) | |
{ | |
rippleIndicies[index] = (ushort)(i * poolWidth + j); | |
index++; | |
} | |
} | |
} | |
} | |
} | |
public IntPtr Vertices | |
{ | |
get { unsafe { return (IntPtr)rippleVertices; } } | |
} | |
public IntPtr TexCoords | |
{ | |
get { unsafe { return (IntPtr)rippleTexCoords; } } | |
} | |
public IntPtr Indices | |
{ | |
get { unsafe { return (IntPtr)rippleIndicies; } } | |
} | |
public int VertexSize | |
{ | |
get { return poolWidth * poolHeight * 2 * sizeof(float); } | |
} | |
public int IndexSize | |
{ | |
get { return (poolHeight - 1) * (poolWidth * 2 + 2) * sizeof(ushort); } | |
} | |
public int IndexCount | |
{ | |
get { return IndexSize / sizeof(ushort); } | |
} | |
public unsafe void RunSimulation() | |
{ | |
for (int y = 0; y < poolHeight; y++) | |
{ | |
for (int x = 0; x < poolWidth; x++) | |
{ | |
// * - denotes current pixel | |
// | |
// a | |
// c * d | |
// b | |
// +1 to both x/y values because the border is padded | |
float a = rippleSource[x + 1, y]; | |
float b = rippleSource[x + 1, y + 2]; | |
float c = rippleSource[x, y + 1]; | |
float d = rippleSource[x + 2, y + 1]; | |
float result = (a + b + c + d) / 2f - rippleDest[x + 1, y + 1]; | |
result -= result / 32f; | |
rippleDest[x + 1, y + 1] = result; | |
} | |
} | |
for (int y = 0; y < poolHeight; y++) | |
{ | |
for (int x = 0; x < poolWidth; x++) | |
{ | |
// * - denotes current pixel | |
// | |
// a | |
// c * d | |
// b | |
// +1 to both x/y values because the border is padded | |
float a = rippleDest[x + 1, y]; | |
float b = rippleDest[x + 1, y + 2]; | |
float c = rippleDest[x, y + 1]; | |
float d = rippleDest[x + 2, y + 1]; | |
float s_offset = ((b - a) / 2048f); | |
float t_offset = ((c - d) / 2048f); | |
// clamp | |
s_offset = (s_offset < -0.5f) ? -0.5f : s_offset; | |
t_offset = (t_offset < -0.5f) ? -0.5f : t_offset; | |
s_offset = (s_offset > 0.5f) ? 0.5f : s_offset; | |
t_offset = (t_offset > 0.5f) ? 0.5f : t_offset; | |
float s_tc = (float)y / (poolHeight - 1) * texCoordFactorS + texCoordOffsetS; | |
float t_tc = (1f - (float)x / (poolWidth - 1)) * texCoordFactorT + texCoordOffsetT; | |
rippleTexCoords[(y * poolWidth + x) * 2 + 0] = s_tc + s_offset; | |
rippleTexCoords[(y * poolWidth + x) * 2 + 1] = t_tc + t_offset; | |
} | |
} | |
var tmp = rippleDest; | |
rippleSource = rippleDest; | |
rippleSource = tmp; | |
} | |
public void InitiateRippleAtLocation(PointF location) | |
{ | |
int xIndex = (int)((location.X / screenSize.Width) * poolWidth); | |
int yIndex = (int)((location.Y / screenSize.Height) * poolHeight); | |
for (int y = (int)yIndex - (int)touchRadius; y <= (int)yIndex + (int)touchRadius; y++) | |
for (int x = (int)xIndex - (int)touchRadius; x <= (int)xIndex + (int)touchRadius; x++) | |
{ | |
if (x >= 0 && x < poolWidth && y >= 0 && y < poolHeight) | |
// +1 to both x/y values because the border is padded | |
rippleSource[x + 1, y + 1] += rippleCoeff[(y - (yIndex - touchRadius)), x - (xIndex - touchRadius)]; | |
} | |
} | |
} | |
} |
Is it correct to remove the 2nd loop from the simulate() method? after the 1st one rippleDest is changed, so the values are different.
0 a
1 c * d
2 b
For each iteration we need to know the values for at least 3 rows, but the actual ones are there only at the 3nd iteration, not the 1st one
To remove the loop something like this is needed
76 for y in 0 .. poolHeight + 1 do
77 for x in 0 .. poolWidth - 1 do
78 if j < poolHeight then
79 // ...
87 if j >= 2 then
88 let a, b, c, d =
89 rippleDest.[x + 1, y - 2],
90 rippleDest.[x + 1, y],
91 rippleDest.[x , y - 1],
92 rippleDest.[x + 2, y - 1]```
It reduces the array read operations (-1 for each iteration), but adds 2 checks, looks like doesn't worse it
0 a1
1 c1 * d1
2 b1 (=a2)
3 c2 * d2
4 b2 ...
@luajalla No, it is an error to remove the second loop. I remember identifying that problem, but must have then forgotten about it.
It shows that I never actually ran this code. I just typed it out to see what this kind of code (gfx, sim) would look like in F#. :)
I'm slightly ashamed to admit I didn't bother with installing MonoTouch. Obviously I should have. :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@cocodrino - Yes, that is a nice feature of F#. I wasn't even trying to compress loc.