public override void SetTerrain (ITerrainChannel channel, short[] heightMap)
        {
            m_channel = channel;
            bool needToCreateHeightmapinODE = false;
            short[] _heightmap;
            if (!ODETerrainHeightFieldHeights.TryGetValue (RegionTerrain, out _heightmap))
            {
                needToCreateHeightmapinODE = true;//We don't have any terrain yet, we need to generate one
                _heightmap = new short[((m_region.RegionSizeX + 3) * (m_region.RegionSizeY + 3))];
            }

            int heightmapWidth = m_region.RegionSizeX + 2;
            int heightmapHeight = m_region.RegionSizeY + 2;

            int heightmapWidthSamples = m_region.RegionSizeX + 3; // + one to complete the 256m + 2 margins each side
            int heightmapHeightSamples = m_region.RegionSizeY + 3;

            float hfmin = 2000;
            float hfmax = -2000;

            for (int x = 0; x < heightmapWidthSamples; x++)
            {
                for (int y = 0; y < heightmapHeightSamples; y++)
                {
                    //Some notes on this part
                    //xx and yy are used for the original heightmap, as we are offsetting the new one by 1
                    // so we subtract one so that we can put the heightmap in correctly
                    int xx = x - 1;
                    if(xx < 0)
                        xx = 0;
                    if(xx > m_region.RegionSizeX - 1)
                        xx = m_region.RegionSizeX - 1;
                    int yy = y - 1;
                    if (yy < 0)
                        yy = 0;
                    if (yy > m_region.RegionSizeY - 1)
                        yy = m_region.RegionSizeY - 1;

                    short val = heightMap[yy * m_region.RegionSizeX + xx];
                    //ODE is evil... flip x and y
                    _heightmap[(x * heightmapHeightSamples) + y] = val;

                    hfmin = (val < hfmin) ? val : hfmin;
                    hfmax = (val > hfmax) ? val : hfmax;
                }
            }

            needToCreateHeightmapinODE = true;//ODE seems to have issues with not rebuilding :(
            TerrainHeightFieldHeights.Remove (RegionTerrain);
            TerrainHeightFieldlimits.Remove (RegionTerrain);
            ODETerrainHeightFieldHeights.Remove (RegionTerrain);
            if (RegionTerrain != IntPtr.Zero)
            {
                d.SpaceRemove (space, RegionTerrain);
                d.GeomDestroy (RegionTerrain);
            }
            if (!needToCreateHeightmapinODE)
            {
                TerrainHeightFieldHeights.Remove (RegionTerrain);
                TerrainHeightFieldlimits.Remove (RegionTerrain);
                ODETerrainHeightFieldHeights.Remove (RegionTerrain);
                float[] heighlimits = new float[2];
                heighlimits[0] = hfmin;
                heighlimits[1] = hfmax;
                TerrainHeightFieldHeights.Add (RegionTerrain, heightMap);
                TerrainHeightFieldlimits.Add (RegionTerrain, heighlimits);
                ODETerrainHeightFieldHeights.Add (RegionTerrain, _heightmap);
                return;//If we have already done this once, we don't need to do it again
            }
            lock (OdeLock)
            {
                const float scale = (1f / (float)Constants.TerrainCompression);
                const float offset = 0.0f;
                float thickness = (float)hfmin;
                const int wrap = 0;

                IntPtr HeightmapData = d.GeomHeightfieldDataCreate ();
                GC.AddMemoryPressure (_heightmap.Length);//Add the memory pressure properly (note: should we be doing this since we have it in managed memory?)
                //Do NOT copy it! Otherwise, it'll copy the terrain into unmanaged memory where we can't release it each time
                d.GeomHeightfieldDataBuildShort (HeightmapData, _heightmap, 0, heightmapHeight, heightmapWidth,
                                                 heightmapHeightSamples, heightmapWidthSamples, scale,
                                                 offset, thickness, wrap);

                d.GeomHeightfieldDataSetBounds (HeightmapData, hfmin, (float)hfmax + 1.0f);
                RegionTerrain = d.CreateHeightfield (space, HeightmapData, 1);

                if (RegionTerrain != IntPtr.Zero)
                {
                    d.GeomSetCategoryBits (RegionTerrain, (int)(CollisionCategories.Land));
                    d.GeomSetCollideBits (RegionTerrain, (int)(CollisionCategories.Space));
                }

                NullObjectPhysicsActor terrainActor = new NullObjectPhysicsActor ();

                actor_name_map[RegionTerrain] = terrainActor;

                d.Matrix3 R = new d.Matrix3 ();

                Quaternion q1 = Quaternion.CreateFromAxisAngle (new Vector3 (1, 0, 0), 1.5707f);
                Quaternion q2 = Quaternion.CreateFromAxisAngle (new Vector3 (0, 1, 0), 1.5707f);

                q1 = q1 * q2;

                Vector3 v3;
                float angle;
                q1.GetAxisAngle (out v3, out angle);

                d.RFromAxisAndAngle (out R, v3.X, v3.Y, v3.Z, angle);

                d.GeomSetRotation (RegionTerrain, ref R);
                d.GeomSetPosition (RegionTerrain, (m_region.RegionSizeX * 0.5f), (m_region.RegionSizeY * 0.5f), 0);

                float[] heighlimits = new float[2];
                heighlimits[0] = hfmin;
                heighlimits[1] = hfmax;

                TerrainHeightFieldHeights.Add (RegionTerrain, heightMap);
                ODETerrainHeightFieldHeights.Add (RegionTerrain, _heightmap);
                TerrainHeightFieldlimits.Add (RegionTerrain, heighlimits);
            }
        }
/* needs fixing if really needed

        public float[] ResizeTerrain512NearestNeighbor(float[] heightMap)
        {
            float[] returnarr = new float[262144];
            float[,] resultarr = new float[(int)WorldExtents.X, (int)WorldExtents.Y];

            // Filling out the array into its multi-dimensional components
            for (int y = 0; y < WorldExtents.Y; y++)
            {
                for (int x = 0; x < WorldExtents.X; x++)
                {
                    resultarr[y, x] = heightMap[y * (int)WorldExtents.Y + x];
                }
            }

            // Resize using Nearest Neighbor

            // This particular way is quick but it only works on a multiple of the original

            // The idea behind this method can be described with the following diagrams
            // second pass and third pass happen in the same loop really..  just separated
            // them to show what this does.

            // First Pass
            // ResultArr:
            // 1,1,1,1,1,1
            // 1,1,1,1,1,1
            // 1,1,1,1,1,1
            // 1,1,1,1,1,1
            // 1,1,1,1,1,1
            // 1,1,1,1,1,1

            // Second Pass
            // ResultArr2:
            // 1,,1,,1,,1,,1,,1,
            // ,,,,,,,,,,
            // 1,,1,,1,,1,,1,,1,
            // ,,,,,,,,,,
            // 1,,1,,1,,1,,1,,1,
            // ,,,,,,,,,,
            // 1,,1,,1,,1,,1,,1,
            // ,,,,,,,,,,
            // 1,,1,,1,,1,,1,,1,
            // ,,,,,,,,,,
            // 1,,1,,1,,1,,1,,1,

            // Third pass fills in the blanks
            // ResultArr2:
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1

            // X,Y = .
            // X+1,y = ^
            // X,Y+1 = *
            // X+1,Y+1 = #

            // Filling in like this;
            // .*
            // ^#
            // 1st .
            // 2nd *
            // 3rd ^
            // 4th #
            // on single loop.

            float[,] resultarr2 = new float[512, 512];
            for (int y = 0; y < WorldExtents.Y; y++)
            {
                for (int x = 0; x < WorldExtents.X; x++)
                {
                    resultarr2[y * 2, x * 2] = resultarr[y, x];

                    if (y < WorldExtents.Y)
                    {
                        resultarr2[(y * 2) + 1, x * 2] = resultarr[y, x];
                    }
                    if (x < WorldExtents.X)
                    {
                        resultarr2[y * 2, (x * 2) + 1] = resultarr[y, x];
                    }
                    if (x < WorldExtents.X && y < WorldExtents.Y)
                    {
                        resultarr2[(y * 2) + 1, (x * 2) + 1] = resultarr[y, x];
                    }
                }
            }

            //Flatten out the array
            int i = 0;
            for (int y = 0; y < 512; y++)
            {
                for (int x = 0; x < 512; x++)
                {
                    if (resultarr2[y, x] <= 0)
                        returnarr[i] = 0.0000001f;
                    else
                        returnarr[i] = resultarr2[y, x];

                    i++;
                }
            }

            return returnarr;
        }

        public float[] ResizeTerrain512Interpolation(float[] heightMap)
        {
            float[] returnarr = new float[262144];
            float[,] resultarr = new float[512, 512];

            // Filling out the array into its multi-dimensional components
            for (int y = 0; y < Constants.RegionSize; y++)
            {
                for (int x = 0; x < Constants.RegionSize; x++)
                {
                    resultarr[y, x] = heightMap[y * m_region.RegionSizeX + x];
                }
            }

            // Resize using interpolation

            // This particular way is quick but it only works on a multiple of the original

            // The idea behind this method can be described with the following diagrams
            // second pass and third pass happen in the same loop really..  just separated
            // them to show what this does.

            // First Pass
            // ResultArr:
            // 1,1,1,1,1,1
            // 1,1,1,1,1,1
            // 1,1,1,1,1,1
            // 1,1,1,1,1,1
            // 1,1,1,1,1,1
            // 1,1,1,1,1,1

            // Second Pass
            // ResultArr2:
            // 1,,1,,1,,1,,1,,1,
            // ,,,,,,,,,,
            // 1,,1,,1,,1,,1,,1,
            // ,,,,,,,,,,
            // 1,,1,,1,,1,,1,,1,
            // ,,,,,,,,,,
            // 1,,1,,1,,1,,1,,1,
            // ,,,,,,,,,,
            // 1,,1,,1,,1,,1,,1,
            // ,,,,,,,,,,
            // 1,,1,,1,,1,,1,,1,

            // Third pass fills in the blanks
            // ResultArr2:
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1
            // 1,1,1,1,1,1,1,1,1,1,1,1

            // X,Y = .
            // X+1,y = ^
            // X,Y+1 = *
            // X+1,Y+1 = #

            // Filling in like this;
            // .*
            // ^#
            // 1st .
            // 2nd *
            // 3rd ^
            // 4th #
            // on single loop.

            float[,] resultarr2 = new float[512, 512];
            for (int y = 0; y < m_region.RegionSizeY; y++)
            {
                for (int x = 0; x < m_region.RegionSizeX; x++)
                {
                    resultarr2[y * 2, x * 2] = resultarr[y, x];

                    if (y < m_region.RegionSizeY)
                    {
                        if (y + 1 < m_region.RegionSizeY)
                        {
                            if (x + 1 < m_region.RegionSizeX)
                            {
                                resultarr2[(y * 2) + 1, x * 2] = ((resultarr[y, x] + resultarr[y + 1, x] +
                                                               resultarr[y, x + 1] + resultarr[y + 1, x + 1]) / 4);
                            }
                            else
                            {
                                resultarr2[(y * 2) + 1, x * 2] = ((resultarr[y, x] + resultarr[y + 1, x]) / 2);
                            }
                        }
                        else
                        {
                            resultarr2[(y * 2) + 1, x * 2] = resultarr[y, x];
                        }
                    }
                    if (x < m_region.RegionSizeX)
                    {
                        if (x + 1 < m_region.RegionSizeX)
                        {
                            if (y + 1 < m_region.RegionSizeY)
                            {
                                resultarr2[y * 2, (x * 2) + 1] = ((resultarr[y, x] + resultarr[y + 1, x] +
                                                               resultarr[y, x + 1] + resultarr[y + 1, x + 1]) / 4);
                            }
                            else
                            {
                                resultarr2[y * 2, (x * 2) + 1] = ((resultarr[y, x] + resultarr[y, x + 1]) / 2);
                            }
                        }
                        else
                        {
                            resultarr2[y * 2, (x * 2) + 1] = resultarr[y, x];
                        }
                    }
                    if (x < m_region.RegionSizeX && y < m_region.RegionSizeY)
                    {
                        if ((x + 1 < m_region.RegionSizeX) && (y + 1 < m_region.RegionSizeY))
                        {
                            resultarr2[(y * 2) + 1, (x * 2) + 1] = ((resultarr[y, x] + resultarr[y + 1, x] +
                                                                 resultarr[y, x + 1] + resultarr[y + 1, x + 1]) / 4);
                        }
                        else
                        {
                            resultarr2[(y * 2) + 1, (x * 2) + 1] = resultarr[y, x];
                        }
                    }
                }
            }
            //Flatten out the array
            int i = 0;
            for (int y = 0; y < 512; y++)
            {
                for (int x = 0; x < 512; x++)
                {
                    if (Single.IsNaN(resultarr2[y, x]) || Single.IsInfinity(resultarr2[y, x]))
                    {
                        m_log.Warn("[PHYSICS]: Non finite heightfield element detected.  Setting it to 0");
                        resultarr2[y, x] = 0;
                    }
                    returnarr[i] = resultarr2[y, x];
                    i++;
                }
            }

            return returnarr;
        }
*/
        #endregion

        public override void SetTerrain (short[] heightMap)
        {
            short[] _heightmap;
            if(!ODETerrainHeightFieldHeights.TryGetValue(RegionTerrain, out _heightmap))
                _heightmap = new short[((m_region.RegionSizeX + 3) * (m_region.RegionSizeY + 3))];

            int heightmapWidth = m_region.RegionSizeX + 2;
            int heightmapHeight = m_region.RegionSizeY + 2;

            int heightmapWidthSamples = m_region.RegionSizeX + 3; // + one to complete the 256m + 2 margins each side
            int heightmapHeightSamples = m_region.RegionSizeY + 3;

            float hfmin = 2000;
            float hfmax = -2000;

            for (int x = 0; x < heightmapWidthSamples; x++)
            {
                for (int y = 0; y < heightmapHeightSamples; y++)
                {
                    //Some notes on this part
                    //xx and yy are used for the original heightmap, as we are offsetting the new one by 1
                    // so we subtract one so that we can put the heightmap in correctly
                    int xx = Util.Clip (x - 1, 0, m_region.RegionSizeX - 1);
                    int yy = Util.Clip (y - 1, 0, m_region.RegionSizeY - 1);

                    short val = heightMap[yy * m_region.RegionSizeX + xx];
                    //ODE is evil... flip x and y
                    _heightmap[(x * heightmapHeightSamples) + y] = val;

                    hfmin = (val < hfmin) ? val : hfmin;
                    hfmax = (val > hfmax) ? val : hfmax;
                }
            }

            lock (OdeLock)
            {
                if (RegionTerrain != IntPtr.Zero)
                {
                    d.SpaceRemove (space, RegionTerrain);
                    d.GeomDestroy (RegionTerrain);
                }
                TerrainHeightFieldHeights.Remove (RegionTerrain);
                ODETerrainHeightFieldHeights.Remove (RegionTerrain);
                TerrainHeightFieldlimits.Remove (RegionTerrain);

                actor_name_map.Remove (RegionTerrain);
                geom_name_map.Remove (RegionTerrain);

                const float scale = (1f / (float)Constants.TerrainCompression);
                const float offset = 0.0f;
                float thickness = (float)hfmin;
                const int wrap = 0;

                IntPtr HeightmapData = d.GeomHeightfieldDataCreate ();
                d.GeomHeightfieldDataBuildShort (HeightmapData, _heightmap, 0, heightmapHeight, heightmapWidth,
                                                 heightmapHeightSamples, heightmapWidthSamples, scale,
                                                 offset, thickness, wrap);

                d.GeomHeightfieldDataSetBounds (HeightmapData, hfmin, (float)hfmax + 1.0f);
                RegionTerrain = d.CreateHeightfield (space, HeightmapData, 1);

                if (RegionTerrain != IntPtr.Zero)
                {
                    d.GeomSetCategoryBits (RegionTerrain, (int)(CollisionCategories.Land));
                    d.GeomSetCollideBits (RegionTerrain, (int)(CollisionCategories.Space));
                }

                geom_name_map[RegionTerrain] = "Terrain";

                NullObjectPhysicsActor terrainActor = new NullObjectPhysicsActor()
                {
                    PhysicsActorType = (int)ActorTypes.Ground
                };

                actor_name_map[RegionTerrain] = terrainActor;

                d.Matrix3 R = new d.Matrix3 ();

                Quaternion q1 = Quaternion.CreateFromAxisAngle (new Vector3 (1, 0, 0), 1.5707f);
                Quaternion q2 = Quaternion.CreateFromAxisAngle (new Vector3 (0, 1, 0), 1.5707f);

                q1 = q1 * q2;

                Vector3 v3;
                float angle;
                q1.GetAxisAngle (out v3, out angle);

                d.RFromAxisAndAngle (out R, v3.X, v3.Y, v3.Z, angle);

                d.GeomSetRotation (RegionTerrain, ref R);
                d.GeomSetPosition (RegionTerrain, (m_region.RegionSizeX * 0.5f), (m_region.RegionSizeY * 0.5f), 0);

                float[] heighlimits = new float[2];
                heighlimits[0] = hfmin;
                heighlimits[1] = hfmax;

                TerrainHeightFieldHeights.Add (RegionTerrain, heightMap);
                ODETerrainHeightFieldHeights.Add (RegionTerrain, _heightmap);
                TerrainHeightFieldlimits.Add (RegionTerrain, heighlimits);
            }
        }