示例#1
0
        /// <summary>
        /// Render the vertices using current implementation.
        /// </summary>
        public void Render(PrimitiveType type)
        {
            // Notes:
            // GL.GetError() cannot be called upon render the immediate mode implementation.
            // There is no way to check automatically whether our data is valid or not.
            // Just make sure the specified colors, texcoords and positions are valid and correct.

            // Check the error before begin(?)
            GLChecker.CheckError();

            // Specify primitive mode.
            GL.Begin((OpenTK.Graphics.OpenGL.PrimitiveType)((int)type));

            // Set the Color, Texture Coordinate and Positions.
            for (int i = 0; i < _vertices.Length; i++)
            {
                // Color.
                GL.Color4(_vertices[i].Color.R, _vertices[i].Color.G, _vertices[i].Color.B, _vertices[i].Color.A);

                // TexCoord.
                GL.TexCoord2(_vertices[i].TexCoords.X, _vertices[i].TexCoords.Y);

                // Position.
                GL.Vertex2(_vertices[i].Position.X, _vertices[i].TexCoords.Y);
            }

            // Finished.
            GL.End();

            // Check error again(?)
            GLChecker.CheckError();
        }
示例#2
0
        public static void Bind(Shader shader)
        {
            // Make sure that we can use shaders
            if (!IsAvailable)
            {
                throw new NotSupportedException("Failed to bind or unbind shader. The system doesn't support shaders.\n" +
                                                "Shader.IsAvailable must return true in order to use Shader class.");
            }

            if (shader != null && shader._program > 0)
            {
                // Enable the program
                GLChecker.Check(() => GL.UseProgram(shader._program));

                // Bind the textures
                shader.BindTextures();

                // Bind the current texture
                if (shader._currentTexture != -1)
                {
                    GLChecker.Check(() => GL.Uniform1(shader._currentTexture, 0));
                }
            }
            else
            {
                // Bind no shader
                GLChecker.Check(() => GL.UseProgram(0));
            }
        }
示例#3
0
 /// <summary>
 /// Releases all resources used by the <see cref="Texture"/>.
 /// </summary>
 public void Dispose()
 {
     if (_textureId > 0)
     {
         GLChecker.Check(() => GL.DeleteTexture(_textureId));
     }
 }
示例#4
0
        /// <summary>
        /// Upload the Vertices into Buffer Data.
        /// </summary>
        public void Update(Vertex[] vertices)
        {
            // Assign vertices information
            _vertices = vertices;
            _length   = vertices.Length;

            // Pin the vertices, prevent GC wipe this pointer
            using (var memory = new MemoryLock(_vertices))
            {
                var pointer = memory.Address;

                // Calculate the stride and upload the vertices
                var stride = Vertex.Stride;
                GL.VertexPointer(2, VertexPointerType.Float, stride,
                                 pointer.Increment(0));
                GLChecker.CheckError();

                GL.TexCoordPointer(2, TexCoordPointerType.Float, stride,
                                   pointer.Increment(8));
                GLChecker.CheckError();

                GL.ColorPointer(4, ColorPointerType.UnsignedByte, stride,
                                pointer.Increment(16));
                GLChecker.CheckError();
            }
        }
示例#5
0
 /// <summary>
 /// Render the vertices using current implementation.
 /// </summary>
 public void Render(PrimitiveType type)
 {
     // Draw the Vertices
     GLChecker.Check(() =>
                     GL.DrawArrays((OpenTK.Graphics.OpenGL.PrimitiveType)type, 0, _length)
                     );
 }
示例#6
0
        /// <summary>
        /// Upload the Vertices into Buffer Data.
        /// </summary>
        public void Update(Vertex[] vertices)
        {
            // Check whether the system support VBO
            if (!IsAvailable)
            {
                throw new NotSupportedException("The System doesn't support Vertex Buffer Object.");
            }

            // Check whether the handles are valid.
            if (_vertexBufferId <= 0)
            {
                Create();
            }

            // Setup vertices information
            _count = vertices.Length;

            // Bind and Upload Vertices
            GLChecker.Check(() => GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferId));
            GLChecker.Check(() =>
                            GL.BufferData(BufferTarget.ArrayBuffer,
                                          new IntPtr(vertices.Length * Vertex.Stride),
                                          vertices,
                                          BufferUsageHint.StaticDraw)
                            );

            // Set the Vertices Pointers
            GLChecker.Check(() => GL.VertexPointer(2, VertexPointerType.Float, Vertex.Stride, 0));
            GLChecker.Check(() => GL.TexCoordPointer(2, TexCoordPointerType.Float, Vertex.Stride, Vector2.Stride));
            GLChecker.Check(() => GL.ColorPointer(4, ColorPointerType.UnsignedByte, Vertex.Stride, Vector2.Stride + Vector2.Stride));
        }
示例#7
0
 /// <summary>
 /// Releases all resources used by the <see cref="VertexBufferRenderer"/>.
 /// </summary>
 public void Dispose()
 {
     if (_vertexBufferId > 0)
     {
         GLChecker.Check(() => GL.DeleteBuffer(_vertexBufferId));
     }
 }
示例#8
0
        /// <summary>
        /// Copy the <see cref="Texture"/> pixels to an <see cref="Image"/> instance.
        /// </summary>
        /// <returns><see cref="Image"/> containing the texture's pixels.</returns>
        public Image ToImage()
        {
            // Check for invalid / empty texture, just return empty bitmap
            if (_textureId <= 0 || (Size.Width == 0 && Size.Height == 0))
            {
                return(new Bitmap(Size.Width, Size.Height));
            }

            // Setup Bitmap data and use lockbit method to copy the pixels later
            Bitmap     bmp  = new Bitmap(Size.Width, Size.Height);
            BitmapData data = bmp.LockBits(
                new Rectangle(0, 0, bmp.Width, bmp.Height),
                ImageLockMode.WriteOnly,
                System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            // Copy the texture pixels into bitmap data
            GLChecker.Check(() => GL.BindTexture(TextureTarget.Texture2D, _textureId));
            GLChecker.Check(() => GL.GetTexImage(TextureTarget.Texture2D,
                                                 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0));

            // Pixels copied, Unlock it
            bmp.UnlockBits(data);

            if (_pixelsFlipped)
            {
                bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
            }

            return(bmp);
        }
示例#9
0
        /// <summary>
        /// Update the texture from an <see cref="Image"/>.
        /// </summary>
        /// <param name="image"><see cref="Image"/> to copy to the texture.</param>
        /// <param name="x">X offset in the texture where to copy the source image</param>
        /// <param name="y">Y offset in the texture where to copy the source image</param>
        /// <param name="width">Width of the pixel region contained in image.</param>
        /// <param name="height">Height of the pixel region contained in image.</param>
        public void Update(Image image, int x, int y, int width, int height)
        {
            // Check if texture is previously created
            if (_textureId <= 0)
            {
                throw new AccessViolationException("Invalid Texture Handle.");
            }

            // Copy bitmap to specified region
            using (Bitmap bmp = new Bitmap(image))
            {
                BitmapData data = bmp.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly,
                                               System.Drawing.Imaging.PixelFormat.Format32bppArgb);

                GLChecker.Check(() => GL.BindTexture(TextureTarget.Texture2D, _textureId));
                GLChecker.Check(() => GL.TexSubImage2D(TextureTarget.Texture2D, 0,
                                                       x, y, width, height,
                                                       OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0));

                GLChecker.Check(() => GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter,
                                                      (int)(_isSmooth ? TextureMinFilter.Linear : TextureMinFilter.Nearest)));

                bmp.UnlockBits(data);

                // Force an OpenGL flush, so that the texture data will appear updated
                // in all contexts immediately (solves problems in multi-threaded apps)
                GLChecker.Check(() => GL.Flush());
            }
        }
示例#10
0
        /// <summary>
        /// Specify values for matrix array uniform.
        /// </summary>
        /// <param name="name">The name of uniform variable in GLSL.</param>
        /// <param name="matrix">The array of matrix values.</param>
        public void SetUniformArray(string name, float[][] matrix)
        {
            int matrixSize = 0;

            if (matrix.Length == 3 * 3)
            {
                matrixSize = 3 * 3;
            }
            else if (matrix.Length == 4 * 4)
            {
                matrixSize = 4 * 4;
            }
            else
            {
                throw new ArgumentException("Matrix data must either 3x3 (9 elements) or 4x4 (16 elements).");
            }

            float[] contiguous = new float[matrixSize * matrix.Length];
            for (int i = 0; i < matrix.Length; ++i)
            {
                Array.Copy(matrix[i], 0, contiguous, matrixSize * i, matrixSize);
            }

            using (var binder = new UniformBinder(this, name))
            {
                if (binder.Location != -1)
                {
                    GLChecker.Check(() => GL.Uniform2(binder.Location, contiguous.Length, contiguous));
                }
            }
        }
示例#11
0
        /// <summary>
        /// Generate Implementation Handle.
        /// This function will do nothing if VertexBufferObject is NOT supported or not preferred.
        /// </summary>
        public void Create()
        {
            // Check whether the system support VBO
            if (!IsAvailable)
            {
                throw new NotSupportedException("The System doesn't support Vertex Buffer Object.");
            }

            // Engine preference
            bool overrideHandle = true;

            // Check whether the handle is exist, and delete it if asked to do so.
            if (_vertexBufferId > 0 && overrideHandle)
            {
                GLChecker.Check(() => GL.DeleteBuffer(_vertexBufferId));
            }
            // .. and throw the exception if its not asked to delete existing buffer.
            else if (_vertexBufferId > 0 && !overrideHandle)
            {
                throw new InvalidOperationException("Vertex Buffer Handle is already exist.");
            }

            // Generate the handle
            GLChecker.Check(() => GL.GenBuffers(1, out _vertexBufferId));
        }
示例#12
0
        internal void InvalidateMipMap()
        {
            GLChecker.Check(() => GL.BindTexture(TextureTarget.Texture2D, _textureId));
            GLChecker.Check(() => GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter,
                                                  (int)(_isSmooth ? TextureMinFilter.Linear : TextureMinFilter.Nearest)));

            _hasMipmap = false;
        }
示例#13
0
 public void Dispose()
 {
     // Disable program object
     if (CurrentProgram > 0 && (CurrentProgram != SavedProgram))
     {
         GLChecker.Check(() => GL.Arb.UseProgramObject(SavedProgram));
     }
 }
示例#14
0
 /// <summary>
 /// Specify value for four component uniform.
 /// </summary>
 /// <param name="name">The name of uniform variable in GLSL.</param>
 /// <param name="vector">Value of the scalar.</param>
 public void SetUniform(string name, Vector4 <double> vector)
 {
     using (var binder = new UniformBinder(this, name))
     {
         if (binder.Location != -1)
         {
             GLChecker.Check(() => GL.Uniform4(binder.Location, vector.X, vector.Y, vector.Z, vector.W));
         }
     }
 }
示例#15
0
 /// <summary>
 /// Specify value for one component uniform.
 /// </summary>
 /// <param name="name">The name of uniform variable in GLSL.</param>
 /// <param name="x">Value of the scalar.</param>
 public void SetUniform(string name, double x)
 {
     using (var binder = new UniformBinder(this, name))
     {
         if (binder.Location != -1)
         {
             GLChecker.Check(() => GL.Uniform1(binder.Location, x));
         }
     }
 }
示例#16
0
        /// <summary>
        /// Get the maximum texture size allowed.
        /// </summary>
        /// <returns>Maximum size allowed for textures, in pixels.</returns>
        internal static int GetMaximumSize()
        {
            if (!_maxSizeChecked)
            {
                _maxSizeChecked = true;
                GLChecker.Check(() => GL.GetInteger(GetPName.MaxTextureSize, out _maxSize));
            }

            return(_maxSize);
        }
示例#17
0
 /// <summary>
 /// Specify values for array uniform.
 /// </summary>
 /// <param name="name">The name of the uniform variable in GLSL.</param>
 /// <param name="scalarArray">The array of float values.</param>
 public void SetUniformArray(string name, float[] scalarArray)
 {
     using (var binder = new UniformBinder(this, name))
     {
         if (binder.Location != -1)
         {
             GLChecker.Check(() => GL.Uniform1(binder.Location, scalarArray.Length, scalarArray));
         }
     }
 }
示例#18
0
        /// <summary>
        /// Clear the entire target with a single color.
        /// </summary>
        /// <param name="color">Fill <see cref="Color"/> to use to clear the render target.</param>
        public void Clear(Color color)
        {
            if (Activate())
            {
                ApplyTexture(null);

                GLChecker.Check(() => GL.ClearColor(color));
                GLChecker.Check(() => GL.Clear(ClearBufferMask.ColorBufferBit));
            }
        }
示例#19
0
        internal static int GetMaxTextureUnits()
        {
            int maxUnits = 0;

            GLChecker.Check(() =>
                            GL.GetInteger(GetPName.MaxCombinedTextureImageUnits, out maxUnits)
                            );

            return(maxUnits);
        }
示例#20
0
 /// <summary>
 /// Specify values for array uniform.
 /// </summary>
 /// <param name="name">The name of uniform variable in GLSL.</param>
 /// <param name="vector">Value of the scalar.</param>
 public void SetUniformArray(string name, Vector4 <double>[] vector)
 {
     using (var binder = new UniformBinder(this, name))
     {
         if (binder.Location != -1)
         {
             double[] contiguous = vector.Flatten();
             GLChecker.Check(() => GL.Uniform2(binder.Location, contiguous.Length, contiguous));
         }
     }
 }
示例#21
0
        /// <summary>
        /// Generates a MipMap using the current <see cref="Texture"/> data.
        /// </summary>
        /// <returns></returns>
        public void GenerateMipMap()
        {
            if (!GLExtensions.IsAvailable("GL_EXT_framebuffer_object"))
            {
                return;
            }

            GLChecker.Check(() => GL.BindTexture(TextureTarget.Texture2D, _textureId));
            GLChecker.Check(() => GL.GenerateMipmap(GenerateMipmapTarget.Texture2D));
            GLChecker.Check(() => GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter,
                                                  (int)(_isSmooth ? TextureMinFilter.LinearMipmapLinear : TextureMinFilter.NearestMipmapLinear)));

            _hasMipmap = true;
            return;
        }
示例#22
0
        private void ApplyTransform(Transform transform)
        {
            // ModelView is always preferred in all cases of modules.
            // All operations that interact with the matrix will switch back the mode to ModelView.
            // So no need to change the matrix mode to ModelView.

            // Null transform should be replaced by identity transform
            if (transform == null)
            {
                transform = Transform.Identity;
            }

            // Apply Transformation Matrix
            GLChecker.Check(() => GL.LoadMatrix(transform.Matrix));
        }
示例#23
0
        private void ApplyBlendMode(BlendMode mode)
        {
            if (GLExtensions.IsAvailable("GL_EXT_blend_func_separate"))
            {
                GLChecker.Check(() =>
                                GL.BlendFuncSeparate((BlendingFactorSrc)FactorToGL(mode.ColorSrcFactor), (BlendingFactorDest)FactorToGL(mode.ColorDstFactor),
                                                     (BlendingFactorSrc)FactorToGL(mode.AlphaSrcFactor), (BlendingFactorDest)FactorToGL(mode.AlphaDstFactor))
                                );
            }
            else
            {
                var src = (BlendingFactorSrc)FactorToGL(mode.ColorSrcFactor);
                var dst = (BlendingFactorDest)FactorToGL(mode.ColorDstFactor);
                GLChecker.Check(() =>
                                GL.BlendFunc(src, dst)
                                );
            }

            if (GLExtensions.IsAvailable("GL_EXT_blend_minmax") && GLExtensions.IsAvailable("GL_EXT_blend_subtract"))
            {
                if (GLExtensions.IsAvailable("GL_EXT_blend_equation_separate"))
                {
                    GLChecker.Check(() =>
                                    GL.Ext.BlendEquationSeparate(
                                        (BlendEquationModeExt)EquationToGL(mode.ColorEquation),
                                        (BlendEquationModeExt)EquationToGL(mode.AlphaEquation))
                                    );
                }
                else
                {
                    GLChecker.Check(() =>
                                    GL.Ext.BlendEquation(EquationToGL(mode.ColorEquation))
                                    );
                }
            }
            else if ((mode.ColorEquation != BlendMode.Equation.Add) || (mode.AlphaEquation != BlendMode.Equation.Add))
            {
                if (!_isBlendingWarned)
                {
                    Logger.Warning("OpenGL extension EXT_blend_minmax and / or EXT_blend_subtract unavailable.\n" +
                                   "Selecting a blend equation is not possible\n" +
                                   "Ensure that hardware acceleration is enabled if available.");
                    _isBlendingWarned = true;
                }
            }

            Cache.LastBlendMode = mode;
        }
示例#24
0
        public void Dispose()
        {
            // Return to visible frame buffer
            GLChecker.Check(() => GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0));
            GLChecker.Check(() => GL.DrawBuffer(DrawBufferMode.Back));

            if (_frameBuffer > 0)
            {
                GLChecker.Check(() => GL.Ext.DeleteFramebuffer(_frameBuffer));
            }

            if (_depthBuffer > 0)
            {
                GLChecker.Check(() => GL.Ext.DeleteFramebuffer(_depthBuffer));
            }
        }
示例#25
0
        private void BindTextures()
        {
            int index = 1;

            foreach (KeyValuePair <int, Texture> texture in _textures)
            {
                GLChecker.Check(() => GL.Uniform1(texture.Key, index));
                GLChecker.Check(() => GL.ActiveTexture(TextureUnit.Texture0 + index));

                Texture.Bind(texture.Value);
                index++;
            }

            // Make sure that the texture unit which is left active is the number 0
            GLChecker.Check(() => GL.ActiveTexture(TextureUnit.Texture0));
        }
示例#26
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Texture"/> class
        /// from an existing <see cref="Image"/> instance with specified region.
        /// </summary>
        /// <param name="image">The existing <see cref="Image"/> instance.</param>
        /// <param name="region">The area of the <see cref="Image"/> to load.</param>
        public Texture(Image image, Rectangle region)
            : this()
        {
            // Retrieve the image size
            int areaWidth  = image.Width;
            int areaHeight = image.Height;

            // Load the entire image if the source area is either empty or contains the whole image
            if (region.Width == 0 || (region.Height == 0) ||
                ((region.Left <= 0) && (region.Top <= 0) && (region.Width >= areaWidth) && (region.Height >= areaHeight)))
            {
                // Load the entire image
                Create(image.Width, image.Height, false);
                Update(image);
            }
            else
            {
                // Clamp the region size to valid value
                int left   = (region.Left < 0) ? 0 : region.Left;
                int top    = (region.Top < 0) ? 0 : region.Top;
                int width  = (region.Left + region.Width > areaWidth)  ? areaWidth - region.Left : region.Width;
                int height = (region.Top + region.Height > areaHeight) ? areaHeight - region.Top : region.Height;

                Create(width, height, false);

                // Copy bitmap to specified region
                using (Bitmap bmp = new Bitmap(image))
                {
                    BitmapData data = bmp.LockBits(new Rectangle(left, top, width, height), ImageLockMode.ReadOnly,
                                                   System.Drawing.Imaging.PixelFormat.Format32bppArgb);

                    GLChecker.Check(() => GL.BindTexture(TextureTarget.Texture2D, _textureId));
                    GLChecker.Check(() => GL.TexImage2D(TextureTarget.Texture2D, 0,
                                                        0, 0, width, height,
                                                        OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0));

                    GLChecker.Check(() => GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter,
                                                          (int)(_isSmooth ? TextureMinFilter.Linear : TextureMinFilter.Nearest)));

                    bmp.UnlockBits(data);

                    // Force an OpenGL flush, so that the texture data will appear updated
                    // in all contexts immediately (solves problems in multi-threaded apps)
                    GLChecker.Check(() => GL.Flush());
                }
            }
        }
示例#27
0
        /// <summary>
        /// Bind the existing <see cref="Texture"/> to texturing target.
        /// If the <see cref="Texture"/> is null, it will unbind any <see cref="Texture"/> from texturing target.
        /// </summary>
        /// <param name="texture">Texture to bind.</param>
        public static void Bind(Texture texture, CoordinateType type = CoordinateType.Pixel)
        {
            if (texture != null && texture._textureId > 0)
            {
                Matrix4 matrix = new Matrix4
                                     (1f, 0f, 0f, 0f,
                                     0f, 1f, 0f, 0f,
                                     0f, 0f, 1f, 0f,
                                     0f, 0f, 0f, 1f);

                // Convert to normalized one ([0..1])
                if (type == CoordinateType.Pixel)
                {
                    matrix[0, 0] = 1f / texture.Size.Width;
                    matrix[1, 1] = 1f / texture.Size.Height;
                }

                if (texture._pixelsFlipped)
                {
                    matrix[1, 1] = -matrix[1, 1];
                    matrix[3, 1] = texture.Size.Height / texture._actualSize.Height;
                }

                // Bind the texture
                GLChecker.Check(() => GL.BindTexture(TextureTarget.Texture2D, texture._textureId));

                // Apply the texture matrix
                GLChecker.Check(() => GL.MatrixMode(MatrixMode.Texture));
                GLChecker.Check(() => GL.LoadMatrix(ref matrix));

                // Back to ModelView to prevent problems with the RenderTarget
                GLChecker.Check(() => GL.MatrixMode(MatrixMode.Modelview));
            }
            else
            {
                // Bind no texture (Unbind)
                GLChecker.Check(() => GL.BindTexture(TextureTarget.Texture2D, 0));

                // Reset Texture Matrix
                GLChecker.Check(() => GL.MatrixMode(MatrixMode.Texture));
                GLChecker.Check(() => GL.LoadIdentity());

                // RenderTarget always expect ModelView mode, so let's change it back
                GLChecker.Check(() => GL.MatrixMode(MatrixMode.Modelview));
            }
        }
示例#28
0
        /// <summary>
        /// Pop all previous OpenGL States.
        /// </summary>
        public void PopGLStates()
        {
#if DEBUG
            var error = GLChecker.GetError();
            if (error != ErrorCode.NoError)
            {
                Logger.Warning("OpenGL Error ({1}) has detected in user code.\n" +
                               "Make sure to check the error when using custom OpenGL codes.", error);
            }
#endif
            GL.MatrixMode(MatrixMode.Modelview);
            GL.PopMatrix();
            GL.MatrixMode(MatrixMode.Projection);
            GL.PopMatrix();
            GL.MatrixMode(MatrixMode.Texture);
            GL.PopMatrix();

            // Reset GL States
            Reset();
        }
示例#29
0
            public UniformBinder(Shader shader, string name)
            {
                SavedProgram   = 0;
                CurrentProgram = shader._program;
                Location       = -1;

                var uniform = this;

                if (CurrentProgram > 0)
                {
                    // Enable program object
                    GLChecker.Check(() => SavedProgram = GL.Arb.GetHandle(ArbShaderObjects.ProgramObjectArb));
                    if (CurrentProgram != SavedProgram)
                    {
                        GL.Arb.UseProgramObject(CurrentProgram);
                    }

                    // Store uniform location for further use outside constructor
                    Location = shader.GetUniformLocation(name);
                }
            }
示例#30
0
 /// <summary>
 /// Specify value for matrix uniform.
 /// </summary>
 /// <param name="name">The name of uniform variable in GLSL.</param>
 /// <param name="matrix">Value of the scalar.</param>
 public void SetUniform(string name, float[] matrix)
 {
     using (var binder = new UniformBinder(this, name))
     {
         if (binder.Location != -1)
         {
             if (matrix.Length == 9)
             {
                 GLChecker.Check(() => GL.UniformMatrix3(binder.Location, 1, false, matrix));
             }
             else if (matrix.Length == 16)
             {
                 GLChecker.Check(() => GL.UniformMatrix4(binder.Location, 1, true, matrix));
             }
             else
             {
                 throw new ArgumentException("Matrix data must either 3x3 (9 elements) or 4x4 (16 elements).");
             }
         }
     }
 }