/// <summary>
        /// Update the coordinates of hand skeleton points.
        /// </summary>
        private void UpdateHandSkeletonsData(float[] handSkeletons)
        {
            ShaderUtil.CheckGlError(TAG, "Update hand skeletons data start.");

            // Each point has a 3D coordinate. The total number of coordinates
            // is three times the number of skeleton points.
            int mPointsNum = handSkeletons.Length / 3;

            Log.Debug(TAG, "ARHand HandSkeletonNumber = " + mPointsNum);
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, mVbo);
            mNumPoints = mPointsNum;
            if (mVboSize < mNumPoints * BYTES_PER_POINT)
            {
                while (mVboSize < mNumPoints * BYTES_PER_POINT)
                {
                    // If the size of VBO is insufficient to accommodate the new point cloud, resize the VBO.
                    mVboSize *= 2;
                }
                GLES20.GlBufferData(GLES20.GlArrayBuffer, mVboSize, null, GLES20.GlDynamicDraw);
            }
            FloatBuffer mSkeletonPoints = FloatBuffer.Wrap(handSkeletons);

            GLES20.GlBufferSubData(GLES20.GlArrayBuffer, 0, mNumPoints * BYTES_PER_POINT,
                                   mSkeletonPoints);
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, 0);

            ShaderUtil.CheckGlError(TAG, "Update hand skeletons data end.");
        }
Exemplo n.º 2
0
        /**
         * Updates the OpenGL buffer contents to the provided point.  Repeated calls with the same
         * point cloud will be ignored.
         */
        public void Update(PointCloud cloud)
        {
            if (mLastPointCloud == cloud)
            {
                // Redundant call.
                return;
            }

            ShaderUtil.CheckGLError(TAG, "before update");

            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, mVbo);
            mLastPointCloud = cloud;

            // If the VBO is not large enough to fit the new point cloud, resize it.
            mNumPoints = mLastPointCloud.Points.Remaining() / FLOATS_PER_POINT;
            if (mNumPoints * BYTES_PER_POINT > mVboSize)
            {
                while (mNumPoints * BYTES_PER_POINT > mVboSize)
                {
                    mVboSize *= 2;
                }
                GLES20.GlBufferData(GLES20.GlArrayBuffer, mVboSize, null, GLES20.GlDynamicDraw);
            }
            GLES20.GlBufferSubData(GLES20.GlArrayBuffer, 0, mNumPoints * BYTES_PER_POINT,
                                   mLastPointCloud.Points);
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, 0);

            ShaderUtil.CheckGLError(TAG, "after update");
        }
        /// <summary>
        /// Update the coordinates of the hand bounding box.
        /// </summary>
        /// <param name="gesturePoints">Gesture hand box data.</param>
        private void UpdateHandBoxData(float[] gesturePoints)
        {
            ShaderUtil.CheckGlError(TAG, "Update hand box data start.");
            float[] glGesturePoints =
            {
                // Get the four coordinates of a rectangular box bounding the hand.
                gesturePoints[0], gesturePoints[1], gesturePoints[2],
                gesturePoints[3], gesturePoints[1], gesturePoints[2],
                gesturePoints[3], gesturePoints[4], gesturePoints[5],
                gesturePoints[0], gesturePoints[4], gesturePoints[5],
            };
            int gesturePointsNum = glGesturePoints.Length / COORDINATE_DIMENSION;

            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, mVbo);

            mNumPoints = gesturePointsNum;
            if (mVboSize < mNumPoints * BYTES_PER_POINT)
            {
                while (mVboSize < mNumPoints * BYTES_PER_POINT)
                {
                    // If the size of VBO is insufficient to accommodate the new point cloud, resize the VBO.
                    mVboSize *= 2;
                }
                GLES20.GlBufferData(GLES20.GlArrayBuffer, mVboSize, null, GLES20.GlDynamicDraw);
            }
            Log.Debug(TAG, "gesture.getGestureHandPointsNum()" + mNumPoints);
            FloatBuffer mVertices = FloatBuffer.Wrap(glGesturePoints);

            GLES20.GlBufferSubData(GLES20.GlArrayBuffer, 0, mNumPoints * BYTES_PER_POINT,
                                   mVertices);
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, 0);
            ShaderUtil.CheckGlError(TAG, "Update hand box data end.");
        }
        private void InitializeGlObjectData(Context context)
        {
            ObjectData objectData         = null;
            Optional   objectDataOptional = ReadObject(context);

            if (objectDataOptional.IsPresent)
            {
                objectData = objectDataOptional.Get().JavaCast <ObjectData>();
            }
            else
            {
                Log.Debug(TAG, "Read object error.");
                return;
            }

            mTexCoordsBaseAddress = FLOAT_BYTE_SIZE * objectData.objectIndices.Limit();
            mNormalsBaseAddress   = mTexCoordsBaseAddress + FLOAT_BYTE_SIZE * objectData.texCoords.Limit();
            int totalBytes = mNormalsBaseAddress + FLOAT_BYTE_SIZE * objectData.normals.Limit();

            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, mVertexBufferId);
            GLES20.GlBufferData(GLES20.GlArrayBuffer, totalBytes, null, GLES20.GlStaticDraw);
            GLES20.GlBufferSubData(
                GLES20.GlArrayBuffer, 0, FLOAT_BYTE_SIZE * objectData.objectVertices.Limit(), objectData.objectVertices);
            GLES20.GlBufferSubData(GLES20.GlArrayBuffer, mTexCoordsBaseAddress,
                                   FLOAT_BYTE_SIZE * objectData.texCoords.Limit(), objectData.texCoords);
            GLES20.GlBufferSubData(GLES20.GlArrayBuffer, mNormalsBaseAddress,
                                   FLOAT_BYTE_SIZE * objectData.normals.Limit(), objectData.normals);
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, 0);
            GLES20.GlBindBuffer(GLES20.GlElementArrayBuffer, mIndexBufferId);
            mIndexCount = objectData.indices.Limit();
            GLES20.GlBufferData(
                GLES20.GlElementArrayBuffer, INDEX_COUNT_RATIO * mIndexCount, objectData.indices, GLES20.GlStaticDraw);
            GLES20.GlBindBuffer(GLES20.GlElementArrayBuffer, 0);
            ShaderUtil.CheckGlError(TAG, "obj buffer load");
        }
        private void UpdateFaceGeometryData(ARFaceGeometry faceGeometry)
        {
            ShaderUtil.CheckGlError(TAG, "Before update data.");
            FloatBuffer faceVertices = faceGeometry.Vertices;

            // Obtain the number of geometric vertices of a face.
            mPointsNum = faceVertices.Limit() / 3;

            FloatBuffer textureCoordinates = faceGeometry.TextureCoordinates;

            // Obtain the number of geometric texture coordinates of the
            // face (the texture coordinates are two-dimensional).
            int texNum = textureCoordinates.Limit() / 2;

            Log.Debug(TAG, "Update face geometry data: texture coordinates size:" + texNum);

            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, mVerticeId);
            if (mVerticeBufferSize < (mPointsNum + texNum) * BYTES_PER_POINT)
            {
                while (mVerticeBufferSize < (mPointsNum + texNum) * BYTES_PER_POINT)
                {
                    // If the capacity of the vertex VBO buffer is insufficient, expand the capacity.
                    mVerticeBufferSize *= 2;
                }
                GLES20.GlBufferData(GLES20.GlArrayBuffer, mVerticeBufferSize, null, GLES20.GlDynamicDraw);
            }
            GLES20.GlBufferSubData(GLES20.GlArrayBuffer, 0, mPointsNum * BYTES_PER_POINT, faceVertices);

            GLES20.GlBufferSubData(GLES20.GlArrayBuffer, mPointsNum * BYTES_PER_POINT, texNum * BYTES_PER_COORD,
                                   textureCoordinates);
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, 0);

            mTrianglesNum = faceGeometry.TriangleCount;
            IntBuffer faceTriangleIndices = faceGeometry.TriangleIndices;

            Log.Debug(TAG, "update face geometry data: faceTriangleIndices.size: " + faceTriangleIndices.Limit());

            GLES20.GlBindBuffer(GLES20.GlElementArrayBuffer, mTriangleId);
            if (mTriangleBufferSize < mTrianglesNum * BYTES_PER_POINT)
            {
                while (mTriangleBufferSize < mTrianglesNum * BYTES_PER_POINT)
                {
                    // If the capacity of the vertex VBO buffer is insufficient, expand the capacity.
                    mTriangleBufferSize *= 2;
                }
                GLES20.GlBufferData(GLES20.GlElementArrayBuffer, mTriangleBufferSize, null, GLES20.GlDynamicDraw);
            }
            GLES20.GlBufferSubData(GLES20.GlElementArrayBuffer, 0, mTrianglesNum * BYTES_PER_POINT, faceTriangleIndices);
            GLES20.GlBindBuffer(GLES20.GlElementArrayBuffer, 0);
            ShaderUtil.CheckGlError(TAG, "After update data.");
        }
Exemplo n.º 6
0
        /// <summary>
        /// This method updates the connection data of skeleton points and is called when any frame is updated.
        /// </summary>
        /// <param name="handSkeletons">Bone point data of hand.</param>
        /// <param name="handSkeletonConnection">Data of connection between bone points of hand.</param>
        private void UpdateHandSkeletonLinesData(float[] handSkeletons, int[] handSkeletonConnection)
        {
            ShaderUtil.CheckGlError(TAG, "Update hand skeleton lines data start.");
            int pointsLineNum = 0;

            // Each point is a set of 3D coordinate. Each connection line consists of two points.
            float[] linePoint = new float[handSkeletonConnection.Length * 3 * 2];

            // The format of HandSkeletonConnection data is [p0,p1;p0,p3;p0,p5;p1,p2].
            // handSkeletonConnection saves the node indexes. Two indexes obtain a set
            // of connection point data. Therefore, j = j + 2. This loop obtains related
            // coordinates and saves them in linePoint.
            for (int j = 0; j < handSkeletonConnection.Length; j += 2)
            {
                linePoint[pointsLineNum * 3]     = handSkeletons[3 * handSkeletonConnection[j]];
                linePoint[pointsLineNum * 3 + 1] = handSkeletons[3 * handSkeletonConnection[j] + 1];
                linePoint[pointsLineNum * 3 + 2] = handSkeletons[3 * handSkeletonConnection[j] + 2];
                linePoint[pointsLineNum * 3 + 3] = handSkeletons[3 * handSkeletonConnection[j + 1]];
                linePoint[pointsLineNum * 3 + 4] = handSkeletons[3 * handSkeletonConnection[j + 1] + 1];
                linePoint[pointsLineNum * 3 + 5] = handSkeletons[3 * handSkeletonConnection[j + 1] + 2];
                pointsLineNum += 2;
            }
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, mVbo);
            mPointsNum = pointsLineNum;

            // If the storage space is insufficient, apply for twice the memory each time.
            if (mVboSize < mPointsNum * BYTES_PER_POINT)
            {
                while (mVboSize < mPointsNum * BYTES_PER_POINT)
                {
                    mVboSize *= 2;
                }
                GLES20.GlBufferData(GLES20.GlArrayBuffer, mVboSize, null, GLES20.GlDynamicDraw);
            }
            FloatBuffer linePoints = FloatBuffer.Wrap(linePoint);

            Log.Debug(TAG, "Skeleton skeleton line points num: " + mPointsNum);
            Log.Debug(TAG, "Skeleton line points: " + linePoints.ToString());
            GLES20.GlBufferSubData(GLES20.GlArrayBuffer, 0, mPointsNum * BYTES_PER_POINT,
                                   linePoints);
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, 0);
            ShaderUtil.CheckGlError(TAG, "Update hand skeleton lines data end.");
        }
 /// <summary>
 /// Update body connection data.
 /// </summary>
 private void UpdateBodySkeletonLineData(ARBody body)
 {
     FindValidConnectionSkeletonLines(body);
     ShaderUtil.CheckGlError(TAG, "Update body skeleton line data start.");
     GLES20.GlBindBuffer(GLES20.GlArrayBuffer, mVbo);
     mNumPoints = mPointsLineNum;
     if (mVboSize < mNumPoints * BYTES_PER_POINT)
     {
         while (mVboSize < mNumPoints * BYTES_PER_POINT)
         {
             // If the storage space is insufficient, allocate double the space.
             mVboSize *= 2;
         }
         GLES20.GlBufferData(GLES20.GlArrayBuffer, mVboSize, null, GLES20.GlDynamicDraw);
     }
     GLES20.GlBufferSubData(GLES20.GlArrayBuffer, 0, mNumPoints * BYTES_PER_POINT, mLinePoints);
     GLES20.GlBindBuffer(GLES20.GlArrayBuffer, 0);
     ShaderUtil.CheckGlError(TAG, "Update body skeleton line data end.");
 }
Exemplo n.º 8
0
        private void UpdateBodySkeleton()
        {
            ShaderUtil.CheckGlError(TAG, "Update Body Skeleton data start.");

            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, mVbo);
            mNumPoints = mPointsNum;

            if (mVboSize < mNumPoints * BYTES_PER_POINT)
            {
                while (mVboSize < mNumPoints * BYTES_PER_POINT)
                {
                    // If the size of VBO is insufficient to accommodate the new point cloud, resize the VBO.
                    mVboSize *= 2;
                }
                GLES20.GlBufferData(GLES20.GlArrayBuffer, mVboSize, null, GLES20.GlDynamicDraw);
            }
            GLES20.GlBufferSubData(GLES20.GlArrayBuffer, 0, mNumPoints * BYTES_PER_POINT, mSkeletonPoints);
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, 0);

            ShaderUtil.CheckGlError(TAG, "Update Body Skeleton data end.");
        }
        /// <summary>
        ///  Updates the OpenGL buffer contents to the provided point.  Repeated calls with the same
        ///  point cloud will be ignored.
        /// </summary>
        public void Update(PointCloud cloud)
        {
            if (mLastPointCloud == cloud)
            {
                return;
            }

            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, mVbo);
            mLastPointCloud = cloud;

            // If the VBO is not large enough to fit the new point cloud, resize it.
            mNumPoints = mLastPointCloud.Points.Remaining() / FLOATS_PER_POINT;
            if (mNumPoints * BYTES_PER_POINT > mVboSize)
            {
                while (mNumPoints * BYTES_PER_POINT > mVboSize)
                {
                    mVboSize *= 2;
                }
                GLES20.GlBufferData(GLES20.GlArrayBuffer, mVboSize, null, GLES20.GlDynamicDraw);
            }
            GLES20.GlBufferSubData(GLES20.GlArrayBuffer, 0, mNumPoints * BYTES_PER_POINT,
                                   mLastPointCloud.Points);
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, 0);
        }
Exemplo n.º 10
0
        /**
         * Creates and initializes OpenGL resources needed for rendering the model.
         *
         * @param context Context for loading the shader and below-named model and texture assets.
         * @param objAssetName  Name of the OBJ file containing the model geometry.
         * @param diffuseTextureAssetName  Name of the PNG file containing the diffuse texture map.
         */
        public void CreateOnGlThread(Context context, string objAssetName, string diffuseTextureAssetName)
        {
            // Read the texture.
            var textureBitmap = BitmapFactory.DecodeStream(context.Assets.Open(diffuseTextureAssetName));

            GLES20.GlActiveTexture(GLES20.GlTexture0);
            GLES20.GlGenTextures(mTextures.Length, mTextures, 0);
            GLES20.GlBindTexture(GLES20.GlTexture2d, mTextures[0]);

            GLES20.GlTexParameteri(GLES20.GlTexture2d,
                                   GLES20.GlTextureMinFilter, GLES20.GlLinearMipmapLinear);
            GLES20.GlTexParameteri(GLES20.GlTexture2d,
                                   GLES20.GlTextureMagFilter, GLES20.GlLinear);
            GLUtils.TexImage2D(GLES20.GlTexture2d, 0, textureBitmap, 0);
            GLES20.GlGenerateMipmap(GLES20.GlTexture2d);
            GLES20.GlBindTexture(GLES20.GlTexture2d, 0);

            textureBitmap.Recycle();

            ShaderUtil.CheckGLError(TAG, "Texture loading");

            // Read the obj file.
            var objInputStream = context.Assets.Open(objAssetName);
            var obj            = ObjReader.Read(objInputStream);

            // Prepare the Obj so that its structure is suitable for
            // rendering with OpenGL:
            // 1. Triangulate it
            // 2. Make sure that texture coordinates are not ambiguous
            // 3. Make sure that normals are not ambiguous
            // 4. Convert it to single-indexed data
            obj = ObjUtils.ConvertToRenderable(obj);

            // OpenGL does not use Java arrays. ByteBuffers are used instead to provide data in a format
            // that OpenGL understands.

            // Obtain the data from the OBJ, as direct buffers:
            IntBuffer   wideIndices = ObjData.GetFaceVertexIndices(obj, 3);
            FloatBuffer vertices    = ObjData.GetVertices(obj);
            FloatBuffer texCoords   = ObjData.GetTexCoords(obj, 2);
            FloatBuffer normals     = ObjData.GetNormals(obj);

            // Convert int indices to shorts for GL ES 2.0 compatibility
            ShortBuffer indices = ByteBuffer.AllocateDirect(2 * wideIndices.Limit())
                                  .Order(ByteOrder.NativeOrder()).AsShortBuffer();

            while (wideIndices.HasRemaining)
            {
                indices.Put((short)wideIndices.Get());
            }
            indices.Rewind();

            var buffers = new int[2];

            GLES20.GlGenBuffers(2, buffers, 0);
            mVertexBufferId = buffers[0];
            mIndexBufferId  = buffers[1];

            // Load vertex buffer
            mVerticesBaseAddress  = 0;
            mTexCoordsBaseAddress = mVerticesBaseAddress + 4 * vertices.Limit();
            mNormalsBaseAddress   = mTexCoordsBaseAddress + 4 * texCoords.Limit();
            int totalBytes = mNormalsBaseAddress + 4 * normals.Limit();

            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, mVertexBufferId);
            GLES20.GlBufferData(GLES20.GlArrayBuffer, totalBytes, null, GLES20.GlStaticDraw);
            GLES20.GlBufferSubData(
                GLES20.GlArrayBuffer, mVerticesBaseAddress, 4 * vertices.Limit(), vertices);
            GLES20.GlBufferSubData(
                GLES20.GlArrayBuffer, mTexCoordsBaseAddress, 4 * texCoords.Limit(), texCoords);
            GLES20.GlBufferSubData(
                GLES20.GlArrayBuffer, mNormalsBaseAddress, 4 * normals.Limit(), normals);
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, 0);

            // Load index buffer
            GLES20.GlBindBuffer(GLES20.GlElementArrayBuffer, mIndexBufferId);
            mIndexCount = indices.Limit();
            GLES20.GlBufferData(
                GLES20.GlElementArrayBuffer, 2 * mIndexCount, indices, GLES20.GlStaticDraw);
            GLES20.GlBindBuffer(GLES20.GlElementArrayBuffer, 0);

            ShaderUtil.CheckGLError(TAG, "OBJ buffer load");

            int vertexShader = ShaderUtil.LoadGLShader(TAG, context,
                                                       GLES20.GlVertexShader, Resource.Raw.object_vertex);
            int fragmentShader = ShaderUtil.LoadGLShader(TAG, context,
                                                         GLES20.GlFragmentShader, Resource.Raw.object_fragment);

            mProgram = GLES20.GlCreateProgram();
            GLES20.GlAttachShader(mProgram, vertexShader);
            GLES20.GlAttachShader(mProgram, fragmentShader);
            GLES20.GlLinkProgram(mProgram);
            GLES20.GlUseProgram(mProgram);

            ShaderUtil.CheckGLError(TAG, "Program creation");

            mModelViewUniform           = GLES20.GlGetUniformLocation(mProgram, "u_ModelView");
            mModelViewProjectionUniform =
                GLES20.GlGetUniformLocation(mProgram, "u_ModelViewProjection");

            mPositionAttribute = GLES20.GlGetAttribLocation(mProgram, "a_Position");
            mNormalAttribute   = GLES20.GlGetAttribLocation(mProgram, "a_Normal");
            mTexCoordAttribute = GLES20.GlGetAttribLocation(mProgram, "a_TexCoord");

            mTextureUniform = GLES20.GlGetUniformLocation(mProgram, "u_Texture");

            mLightingParametersUniform = GLES20.GlGetUniformLocation(mProgram, "u_LightingParameters");
            mMaterialParametersUniform = GLES20.GlGetUniformLocation(mProgram, "u_MaterialParameters");

            ShaderUtil.CheckGLError(TAG, "Program parameters");

            Android.Opengl.Matrix.SetIdentityM(mModelMatrix, 0);
        }
Exemplo n.º 11
0
        public void CreateOnGlThread(Context context, string objAssetName, string diffuseTextureAssetName)
        {
            // Read the texture.
            var textureBitmap = BitmapFactory.DecodeStream(context.Assets.Open(diffuseTextureAssetName));

            GLES20.GlActiveTexture(GLES20.GlTexture0);
            GLES20.GlGenTextures(mTextures.Length, mTextures, 0);
            GLES20.GlBindTexture(GLES20.GlTexture2d, mTextures[0]);

            GLES20.GlTexParameteri(GLES20.GlTexture2d,
                                   GLES20.GlTextureMinFilter, GLES20.GlLinearMipmapLinear);
            GLES20.GlTexParameteri(GLES20.GlTexture2d,
                                   GLES20.GlTextureMagFilter, GLES20.GlLinear);
            GLUtils.TexImage2D(GLES20.GlTexture2d, 0, textureBitmap, 0);
            GLES20.GlGenerateMipmap(GLES20.GlTexture2d);
            GLES20.GlBindTexture(GLES20.GlTexture2d, 0);

            textureBitmap.Recycle();

            ShaderUtil.CheckGLError(TAG, "Texture loading");

            // Read the obj file.
            var objInputStream = context.Assets.Open(objAssetName);
            var obj            = JavaGl.Obj.ObjReader.Read(objInputStream);

            obj = JavaGl.Obj.ObjUtils.ConvertToRenderable(obj);

            IntBuffer   wideIndices = JavaGl.Obj.ObjData.GetFaceVertexIndices(obj, 3);
            FloatBuffer vertices    = JavaGl.Obj.ObjData.GetVertices(obj);
            FloatBuffer texCoords   = JavaGl.Obj.ObjData.GetTexCoords(obj, 2);
            FloatBuffer normals     = JavaGl.Obj.ObjData.GetNormals(obj);

            ShortBuffer indices = ByteBuffer.AllocateDirect(2 * wideIndices.Limit())
                                  .Order(ByteOrder.NativeOrder()).AsShortBuffer();

            while (wideIndices.HasRemaining)
            {
                indices.Put((short)wideIndices.Get());
            }
            indices.Rewind();

            var buffers = new int[2];

            GLES20.GlGenBuffers(2, buffers, 0);
            mVertexBufferId = buffers[0];
            mIndexBufferId  = buffers[1];

            mVerticesBaseAddress  = 0;
            mTexCoordsBaseAddress = mVerticesBaseAddress + 4 * vertices.Limit();
            mNormalsBaseAddress   = mTexCoordsBaseAddress + 4 * texCoords.Limit();
            int totalBytes = mNormalsBaseAddress + 4 * normals.Limit();

            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, mVertexBufferId);
            GLES20.GlBufferData(GLES20.GlArrayBuffer, totalBytes, null, GLES20.GlStaticDraw);
            GLES20.GlBufferSubData(
                GLES20.GlArrayBuffer, mVerticesBaseAddress, 4 * vertices.Limit(), vertices);
            GLES20.GlBufferSubData(
                GLES20.GlArrayBuffer, mTexCoordsBaseAddress, 4 * texCoords.Limit(), texCoords);
            GLES20.GlBufferSubData(
                GLES20.GlArrayBuffer, mNormalsBaseAddress, 4 * normals.Limit(), normals);
            GLES20.GlBindBuffer(GLES20.GlArrayBuffer, 0);

            GLES20.GlBindBuffer(GLES20.GlElementArrayBuffer, mIndexBufferId);
            mIndexCount = indices.Limit();
            GLES20.GlBufferData(
                GLES20.GlElementArrayBuffer, 2 * mIndexCount, indices, GLES20.GlStaticDraw);
            GLES20.GlBindBuffer(GLES20.GlElementArrayBuffer, 0);

            ShaderUtil.CheckGLError(TAG, "OBJ buffer load");

            int vertexShader = ShaderUtil.LoadGLShader(TAG, context,
                                                       GLES20.GlVertexShader, Resource.Raw.object_vertex);
            int fragmentShader = ShaderUtil.LoadGLShader(TAG, context,
                                                         GLES20.GlFragmentShader, Resource.Raw.object_fragment);

            mProgram = GLES20.GlCreateProgram();
            GLES20.GlAttachShader(mProgram, vertexShader);
            GLES20.GlAttachShader(mProgram, fragmentShader);
            GLES20.GlLinkProgram(mProgram);
            GLES20.GlUseProgram(mProgram);

            ShaderUtil.CheckGLError(TAG, "Program creation");

            mModelViewUniform           = GLES20.GlGetUniformLocation(mProgram, "u_ModelView");
            mModelViewProjectionUniform =
                GLES20.GlGetUniformLocation(mProgram, "u_ModelViewProjection");

            mPositionAttribute = GLES20.GlGetAttribLocation(mProgram, "a_Position");
            mNormalAttribute   = GLES20.GlGetAttribLocation(mProgram, "a_Normal");
            mTexCoordAttribute = GLES20.GlGetAttribLocation(mProgram, "a_TexCoord");

            mTextureUniform = GLES20.GlGetUniformLocation(mProgram, "u_Texture");

            mLightingParametersUniform = GLES20.GlGetUniformLocation(mProgram, "u_LightingParameters");
            mMaterialParametersUniform = GLES20.GlGetUniformLocation(mProgram, "u_MaterialParameters");

            ShaderUtil.CheckGLError(TAG, "Program parameters");

            Android.Opengl.Matrix.SetIdentityM(mModelMatrix, 0);
        }