0

I have been trying to get the GLCameraRipple sample from Apple running in a swift project. Unfortunately this relies heavily on using C style, thread-safe arrays only available in Objective-C.

I have been trying to use a bridging header so that the simulation can run in Objective-C code, and the drawing can run in swift code. That way the thread-safe thing would not be an issue.

I have taken the Objective-C code and translated it almost completely into swift with a few exceptions. I did cull some of the extra math since the texture is the same size as the screen for my needs. If you want to check my translation I have put them below.

Anyway I have made a class that will run in any xcode project with an opengl environment.

import Foundation
import GLKit
import OpenGLES

class WaterDrawer
{
    static var sim = RippleModel()
    static var shade = Shader("Shader2")

    static func pt(pt: CGPoint)
    {
        sim.initiateRippleAtLocation(pt)
    }
    static func firstStart(width: Int, height: Int)
    {
        sim.initWithScreenWidth(width / 4, iheight: height / 4, accWidth: width, accHeight: height)
        shade.begin()
        buildMatrix(width, height: height)
        bufferSetup()
    }

    static func draw()
    {
        glUseProgram(shade.progId)


        let posLoc = GLuint(glGetAttribLocation(shade.progId, "pos"))
        let texLoc = GLuint(glGetAttribLocation(shade.progId, "tc"))


        glBindBuffer(GLenum(GL_ARRAY_BUFFER), texVBO);
        glBufferData(GLenum(GL_ARRAY_BUFFER), GLsizeiptr(sim.getVertexSize()), sim.getTexCoords(), GLenum(GL_DYNAMIC_DRAW));
        glVertexAttribPointer(texLoc, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, BUFFER_OFFSET(0))
        glEnableVertexAttribArray(texLoc)

        glBindBuffer(GLenum(GL_ARRAY_BUFFER), posVBO)
        glVertexAttribPointer(posLoc, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, BUFFER_OFFSET(0))
        glEnableVertexAttribArray(posLoc)


        let uniOrtho = glGetUniformLocation(shade.progId, "matrix")
        glUniformMatrix4fv(uniOrtho, 1, GLboolean(GL_FALSE), &orthographicMatrix)


        glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indVBO)


        glDrawElements(GLenum(GL_TRIANGLE_STRIP), GLsizei(sim.getIndexCount()), GLenum(GL_UNSIGNED_SHORT), nil)

        glBindBuffer(GLenum(GL_ARRAY_BUFFER), 0)
        glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), 0)
        glDisableVertexAttribArray(posLoc)
        glDisableVertexAttribArray(texLoc)
    }

    static func update()
    {
        sim.runSimulation()
    }


    static var posVBO:GLuint = 0
    static var texVBO:GLuint = 0
    static var indVBO:GLuint = 0
    static func bufferSetup()
    {
        Whirl.crashLog("Started passing in buffer data")

        glGenBuffers(1, &indVBO);
        glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indVBO);
        glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), GLsizeiptr(sim.getIndexSize()), sim.getIndices(), GLenum(GL_STATIC_DRAW));

        glGenBuffers(1, &posVBO);
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), posVBO);
        glBufferData(GLenum(GL_ARRAY_BUFFER), GLsizeiptr(sim.getVertexSize()), sim.getVertices(), GLenum(GL_STATIC_DRAW));


        glGenBuffers(1, &texVBO);
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), texVBO);
        glBufferData(GLenum(GL_ARRAY_BUFFER), GLsizeiptr(sim.getVertexSize()), sim.getTexCoords(), GLenum(GL_DYNAMIC_DRAW));

        Whirl.crashLog("Finished passing in buffer Data")
    }


    static var orthographicMatrix:[GLfloat] = []
    static func buildMatrix(width: Int, height: Int)
    {
        orthographicMatrix = glkitmatrixtoarray(GLKMatrix4MakeOrtho(0, GLfloat(width), 0, GLfloat(height), -100, 100))
        //Storage.upScaleFactor
    }
    static func glkitmatrixtoarray(mat: GLKMatrix4) -> [GLfloat]
    {
        var buildme:[GLfloat] = []
        buildme.append(mat.m.0)
        buildme.append(mat.m.1)
        buildme.append(mat.m.2)
        buildme.append(mat.m.3)
        buildme.append(mat.m.4)
        buildme.append(mat.m.5)
        buildme.append(mat.m.6)
        buildme.append(mat.m.7)
        buildme.append(mat.m.8)
        buildme.append(mat.m.9)
        buildme.append(mat.m.10)
        buildme.append(mat.m.11)
        buildme.append(mat.m.12)
        buildme.append(mat.m.13)
        buildme.append(mat.m.14)
        buildme.append(mat.m.15)
        return buildme

    }

}

So theoretically this code can use either the swift implementation or the Objective-C implementation, I just need to switch the way the mesh is initiated.

Trouble is when I use the Objective-C one the screen is blank, I have checked, and the buffer data looks really weird in the frame capture.

Are you allowed to pass in data from an Objective-C code to a glBuffer?

Simulation.swift

import Foundation
import GLKit
import OpenGLES


class RippleModel
{
    var screenWidth:UInt32 = 0
    var screenHeight:UInt32 = 0
    var poolWidth:UInt32 = 0
    var poolHeight:UInt32 = 0
    var screenWidthi:Int = 0
    var screenHeighti:Int = 0
    var poolWidthi:Int = 0
    var poolHeighti:Int = 0

    let touchRadius:Int = 5 //5 i think

    var rippleVertices:[GLfloat] = []
    var rippleTexCoords:[GLfloat] = []
    var rippleIndices:[GLushort] = []//NOTE IF CHANGE THIS TO INTO SO MUCH DRAW CALL

    var rippleSource:[GLfloat] = []
    var rippleDest:[GLfloat] = []

    var rippleCoeff:[GLfloat] = []


    var VertexSize:GLsizeiptr = 0
    var IndicieSize:GLsizeiptr = 0
    var IndicieCount:Int = 0

    func calculateSizes()
    {
        VertexSize = rippleVertices.count * sizeof(GLfloat)
        IndicieSize = rippleIndices.count * sizeof(GLushort)
        IndicieCount = rippleIndices.count

        Whirl.crashLog("Data sizes Vertex size \(VertexSize)\tIndicie Size \(IndicieSize) \tIndicie Count \(IndicieCount)")
    }
    func figureOutCoefficent()
    {
        for y in 0...(2 * touchRadius)
        {
            for x in 0...(2 * touchRadius)
            {
                let dx = x - touchRadius
                let dy = y - touchRadius
                let distance = sqrt(GLfloat(dx * dx + dy * dy))

                let me = y * (touchRadius*2 + 1) + x
                if (distance <= GLfloat(touchRadius))
                {
                    let factor = distance / GLfloat(touchRadius)
                    rippleCoeff[me] = -(cos(factor*GLfloat(M_PI))+1.0) * 256.0;
                }
                else
                {
                    rippleCoeff[me] = 0.0
                }
            }
        }

    }
    init()
    {

    }
    func initWithScreenWidth( iwidth: Int,  iheight: Int, accWidth: Int, accHeight: Int)
    {
        screenWidth = UInt32(accWidth);screenWidthi = Int(screenWidth)
        screenHeight = UInt32(accHeight);screenHeighti = Int(screenHeight)

        poolWidth = UInt32(iwidth);poolWidthi = Int(poolWidth)
        poolHeight = UInt32(iheight);poolHeighti = Int(poolHeight)


        //WE DONT NEED TEX COORD MUMBO JUMBO IT IS FULL SCREEN ALREADY
        Whirl.crashLog("Started allocation")

        rippleCoeff = [GLfloat](count: Int( (touchRadius * 2 + 1) * (touchRadius*2 + 1) ), repeatedValue: 0)
        figureOutCoefficent()

        let simCount:Int = Int(poolWidth + 2) * Int(poolHeight + 2)
        rippleSource = [GLfloat](count: simCount, repeatedValue: 0)
        rippleDest = [GLfloat](count: simCount, repeatedValue: 0)

        let locb:Int = Int(poolWidth * poolHeight * 2)
        rippleVertices = [GLfloat](count: locb, repeatedValue: 0)
        rippleTexCoords = [GLfloat](count: locb, repeatedValue: 0)

        rippleIndices = [GLushort](count: Int(poolHeight - 1) * Int((poolWidth * 2) + 2), repeatedValue: 0)

        Whirl.crashLog("Finished allocation")

        initMesh()

        calculateSizes()
    }
    func initMesh()
    {
        Whirl.crashLog("Started initting pos coords")

        for i in 0..<poolHeight
        {let ii = GLfloat(i)
            for j in 0..<poolWidth
            {let jj = GLfloat(j)
                let cordb:Int = Int(i*poolWidth+j)*2


                rippleVertices[cordb + 0] = (jj / GLfloat(poolWidth - 1)) * GLfloat(screenWidth)
                rippleVertices[cordb + 1] = (ii / GLfloat(poolHeight - 1)) * GLfloat(screenHeight)


                rippleTexCoords[cordb + 0] = ii / GLfloat(poolHeight - 1)
                rippleTexCoords[cordb + 1] = (jj/GLfloat(poolWidth - 1))

            }
        }

        Whirl.crashLog("Finished initting pos coords")

        Whirl.crashLog("Started initting index coords")

        var index = 0
        for i in 0 ..< poolHeighti-1
        {
            for j in 0 ..< poolWidthi
            {
                if (i%2 == 0)
                {
                    // emit extra index to create degenerate triangle
                    if (j == 0)
                    {
                        rippleIndices[index] = GLushort(i*poolWidthi+j);
                        index += 1;
                    }

                    rippleIndices[index] = GLushort(i*poolWidthi+j);
                    index += 1;
                    rippleIndices[index] = GLushort((i+1)*poolWidthi+j);
                    index += 1;

                    // emit extra index to create degenerate triangle
                    if (j == (poolWidthi-1))
                    {
                        rippleIndices[index] = GLushort((i+1)*poolWidthi+j);
                        index += 1;
                    }
                }
                else
                {
                    // emit extra index to create degenerate triangle
                    if (j == 0)
                    {
                        rippleIndices[index] = GLushort((i+1)*poolWidthi+j);
                        index += 1;
                    }

                    rippleIndices[index] = GLushort((i+1)*poolWidthi+j);
                    index += 1;
                    rippleIndices[index] = GLushort(i * poolWidthi + j);
                    index += 1;

                    // emit extra index to create degenerate triangle
                    if (j == (poolWidthi-1))
                    {
                        rippleIndices[index] = GLushort(i * poolWidthi + j);
                        index += 1;
                    }
                }
            }
        }


        Whirl.crashLog("Finished initting coords")
    }

    var firstUpdate = true
    func runSimulation()
    {
        if (firstUpdate)
        {firstUpdate = false; Whirl.crashLog("First update")}

        let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

        //dispatch_apply(Int(poolHeight), queue, {(y: size_t) -> Void in
        for y in 0..<poolHeighti {
            let pw = self.poolWidthi
            for x in 1..<(pw - 1)
            {
                let ai:Int = (y    ) * (pw + 2) + x + 1
                let bi:Int = (y + 2) * (pw + 2) + x + 1
                let ci:Int = (y + 1) * (pw + 2) + x
                let di:Int = (y + 1) * (pw + 2) + x + 2
                let me:Int = (y + 1) * (pw + 2) + x + 1

                let a = self.rippleSource[ai]
                let b = self.rippleSource[bi]
                let c = self.rippleSource[ci]
                let d = self.rippleSource[di]

                var result = (a + b + c + d) / 2.0 - self.rippleDest[me]
                result -= result / 32.0

                self.rippleDest[me] = result
            }
        }
        //)


        let hm1 = GLfloat(poolHeight - 1)
        let wm1 = GLfloat(poolWidth - 1)
        //dispatch_apply(poolHeighti, queue, {(y: size_t) -> Void in
        for y in 0..<poolHeighti{
            let yy = GLfloat(y)
            let pw = self.poolWidthi
            for x in 1..<(pw - 1)
            {let xx = GLfloat(x)
                let ai:Int = (y    ) * (pw + 2) + x + 1
                let bi:Int = (y + 2) * (pw + 2) + x + 1
                let ci:Int = (y + 1) * (pw + 2) + x
                let di:Int = (y + 1) * (pw + 2) + x + 2

                let a = self.rippleDest[ai]
                let b = self.rippleDest[bi]
                let c = self.rippleDest[ci]
                let d = self.rippleDest[di]

                var s_offset = ((b - a) / 2048)
                var t_offset = ((c - d) / 2048)

                s_offset = (s_offset < -0.5) ? -0.5 : s_offset;
                t_offset = (t_offset < -0.5) ? -0.5 : t_offset;
                s_offset = (s_offset >  0.5) ?  0.5 : s_offset;
                t_offset = (t_offset >  0.5) ?  0.5 : t_offset;

                let s_tc = yy / hm1
                let t_tc = xx / wm1

                let me = (y * pw + x) * 2
                self.rippleTexCoords[me + 0] = s_tc + s_offset
                self.rippleTexCoords[me + 1] = t_tc + t_offset

            }
        }
        //)

        let pTmp = rippleDest
        rippleDest = rippleSource
        rippleSource = pTmp
    }

    var firstRipple:Bool = true
    func initiateRippleAtLocation(location: CGPoint)
    {
        if (firstRipple)
        {firstRipple = false; Whirl.crashLog("First ripple placement")}

        let xIndex = Int((GLfloat(location.x) / GLfloat(screenWidth)) * GLfloat(poolWidthi))
        let yIndex = Int((1.0 - (GLfloat(location.y) / GLfloat(screenHeighti))) * GLfloat(poolHeight))

        let lowy = yIndex - touchRadius
        let highy = yIndex + touchRadius
        let lowx = xIndex - touchRadius
        let highx = xIndex + touchRadius


        //Whirl.crashLog("Ripple at (\(xIndex) , \(yIndex))\tX:(\(lowx) - \(highx))\tY:(\(lowy) - \(highy))")

        for y in lowy...highy
        {
            for x in lowx...highx
            {
                if (x > 0 && x < (poolWidthi - 1) && y > 0 && y < poolHeighti)
                {
                    let ind = (poolWidthi + 2) * (y + 1) + x + 1
                    let coef = (y-(yIndex-touchRadius))*(touchRadius*2+1)+x-(xIndex-touchRadius)
                    let past = rippleSource[ind]
                    let coe = rippleCoeff[coef]
                    if (coe < past)
                    {
                        rippleSource[ind] = coe
                    }

                }
            }
        }
    }

    func rippleLine(location1: CGPoint, location2: CGPoint)
    {
        if (firstRipple)
        {firstRipple = false; Whirl.crashLog("First ripple placement")}

        let xIndex1 = Int((GLfloat(location1.x) / GLfloat(screenWidth)) * GLfloat(poolWidthi))
        let yIndex1 = Int((1.0 - (GLfloat(location1.y) / GLfloat(screenHeighti))) * GLfloat(poolHeight))
        let xIndex2 = Int((GLfloat(location2.x) / GLfloat(screenWidth)) * GLfloat(poolWidthi))
        let yIndex2 = Int((1.0 - (GLfloat(location2.y) / GLfloat(screenHeighti))) * GLfloat(poolHeight))

        let lowy1 = yIndex1 - touchRadius
        let highy1 = yIndex1 + touchRadius
        let lowx1 = xIndex1 - touchRadius
        let highx1 = xIndex1 + touchRadius

        let lowy2 = yIndex2 - touchRadius
        let highy2 = yIndex2 + touchRadius
        let lowx2 = xIndex2 - touchRadius
        let highx2 = xIndex2 + touchRadius

        let lowx = min(lowx1, lowx2)
        let highx = max(highx1, highx2)
        let lowy = min(lowy1, lowy2)
        let highy = max(highy1, highy2)

        for y in lowy...highy
        {
            for x in lowx...highx
            {
                if (x > 0 && x < (poolWidthi - 1) && y > 0 && y < poolHeighti)
                {
                    let ind = (poolWidthi + 2) * (y + 1) + x + 1

                    let tar = ldist(CGPoint(x: xIndex1, y: yIndex1), p2: CGPoint(x: xIndex2, y: yIndex2), me: CGPoint(x: x, y: y))

                    let dx = x - Int(tar.x)
                    let dy = y - Int(tar.y)
                    let distq = (dx * dx + dy * dy)

                    if (distq < touchRadius * touchRadius)
                    {
                        let factor = sqrt(GLfloat(distq)) / GLfloat(touchRadius)

                        rippleSource[ind] = -(cos(factor*GLfloat(M_PI))+1.0) * 256.0;
                    }
                    //rippleSource[ind] = 1000
                }
            }
        }
    }

    func ldist(p1: CGPoint, p2: CGPoint, me: CGPoint) -> CGPoint
    {
        let diffX = p2.x - p1.x
        let diffY = p2.y - p1.y

        var target = CGPoint()
        if ((diffX == 0) && (diffY == 0))
        {
            target = p1
        }

        let t = ((me.x - p1.x) * diffX + (me.y - p1.y) * diffY) / (diffX * diffX + diffY * diffY)

        if (t < 0)
        {
            target = p1
        }
        else if (t > 1)
        {
            target = p2
        }
        else
        {
            target = CGPoint(x: (p1.x + t * diffX), y: (p1.y + t * diffY))
        }

        let int = CGPoint(x: round(target.x), y: round(target.y))
        return int
    }

    func getVertices() -> [GLfloat]
    {
        //Return the mesh positions
        return rippleVertices
    }
    func getTexCoords() -> [GLfloat]
    {
        //Return the array of texture coords
        return rippleTexCoords
    }
    func getIndices() -> [GLushort]
    {
        //Return the array of indices
        return rippleIndices
    }
    func getVertexSize() -> GLsizeiptr
    {
        //Return the size of the mesh position array
        return VertexSize
    }
    func getIndexSize() -> GLsizeiptr
    {
        //Return the byte size of the incicie array
        return IndicieSize
    }
    func getIndexCount() -> GLsizeiptr
    {
        //This goes in the draw call, count of indices
        return IndicieCount
    }
}

RippleModel.m (from apple)

1
  • I don't believe in passing swift datatype to gl function. I think you need to convert the datatype to byte array or c-style format. Commented Sep 8, 2016 at 7:08

2 Answers 2

1
+100

Are you allowed to pass in data from an objective-c code to a glBuffer?

Why wouldn't you be allowed? Swift has a pointer API (UnsafePointer<T>, UnsafeMutablePointer<T>, etc.) exactly for this purpose. Obviously this is "unsafe" in the sense that the underlying memory the [Objective-]C pointer points to could change at anytime without the Swift pointer knowing. It also has no information about the size of the memory block that it points to.

Any C pointers or arrays can be bridged to Swift (probably as UnsafeMutablePointer<Void> which you will need to cast to your OpenGL type).

You can avoid any risk of referencing invalid memory by dereferencing the pointer (if it is non-nil) and copying the value stored at the pointer to a variable in your Swift application.

Sign up to request clarification or add additional context in comments.

Comments

1

With OpenGL ES being a pure set of C functions, I don't think passing a pointer of swift datatypes will work easily.

The following code will give you a hint of how to pass the index buffer.

var Indices: [GLubyte] = [
    0, 1, 2,
    2, 3, 0
]

var indexBuffer: GLuint = GLuint()
glGenBuffers(1, &indexBuffer)
glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indexBuffer)
glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), Indices.size(), Indices, GLenum(GL_STATIC_DRAW))

Reference: Here is a link with a working code. https://github.com/bradley/iOSSwiftOpenGL/blob/master/iOSSwiftOpenGL/OpenGLView.swift

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.