OSDN Git Service

Merge branch 'master' of https://github.com/ole1986/radegast
[radegast/radegast.git] / Radegast / GUI / Rendering / RenderTerrain.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2014, Radegast Development Team
4 // All rights reserved.
5 // 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 
9 //     * Redistributions of source code must retain the above copyright notice,
10 //       this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //     * Neither the name of the application "Radegast", nor the names of its
15 //       contributors may be used to endorse or promote products derived from
16 //       this software without specific prior CreateReflectionTexture permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 // $Id$
30 //
31
32 using System;
33 using System.Collections.Generic;
34 using OpenTK.Graphics.OpenGL;
35 using System.Drawing;
36 using OpenMetaverse;
37 using OpenMetaverse.Rendering;
38
39 namespace Radegast.Rendering
40 {
41     public class RenderTerrain : SceneObject
42     {
43         RadegastInstance Instance;
44         GridClient Client { get { return Instance.Client; } }
45
46         public bool Modified = true;
47         float[,] heightTable = new float[256, 256];
48         Face terrainFace;
49         uint[] terrainIndices;
50         ColorVertex[] terrainVertices;
51         int terrainTexture = -1;
52         bool fetchingTerrainTexture = false;
53         Bitmap terrainImage = null;
54         int terrainVBO = -1;
55         int terrainIndexVBO = -1;
56         bool terrainVBOFailed = false;
57         bool terrainInProgress = false;
58         bool terrainTextureNeedsUpdate = false;
59         float terrainTimeSinceUpdate = RenderSettings.MinimumTimeBetweenTerrainUpdated + 1f; // Update terrain om first run
60         MeshmerizerR renderer;
61         Simulator sim { get { return Instance.Client.Network.CurrentSim; } }
62
63         public RenderTerrain(RadegastInstance instance)
64         {
65             this.Instance = instance;
66             renderer = new MeshmerizerR();
67         }
68
69         public void ResetTerrain()
70         {
71             ResetTerrain(true);
72         }
73
74         public void ResetTerrain(bool removeImage)
75         {
76             if (terrainImage != null)
77             {
78                 terrainImage.Dispose();
79                 terrainImage = null;
80             }
81
82             if (terrainVBO != -1)
83             {
84                 Compat.DeleteBuffer(terrainVBO);
85                 terrainVBO = -1;
86             }
87
88             if (terrainIndexVBO != -1)
89             {
90                 Compat.DeleteBuffer(terrainIndexVBO);
91                 terrainIndexVBO = -1;
92             }
93
94             if (removeImage)
95             {
96                 if (terrainTexture != -1)
97                 {
98                     GL.DeleteTexture(terrainTexture);
99                     terrainTexture = -1;
100                 }
101             }
102
103             fetchingTerrainTexture = false;
104             Modified = true;
105         }
106
107         private void UpdateTerrain()
108         {
109             if (sim == null || sim.Terrain == null) return;
110
111             WorkPool.QueueUserWorkItem(sync =>
112             {
113                 int step = 1;
114
115                 for (int x = 0; x < 256; x += step)
116                 {
117                     for (int y = 0; y < 256; y += step)
118                     {
119                         float z = 0;
120                         int patchNr = ((int)x / 16) * 16 + (int)y / 16;
121                         if (sim.Terrain[patchNr] != null
122                             && sim.Terrain[patchNr].Data != null)
123                         {
124                             float[] data = sim.Terrain[patchNr].Data;
125                             z = data[(int)x % 16 * 16 + (int)y % 16];
126                         }
127                         heightTable[x, y] = z;
128                     }
129                 }
130
131                 terrainFace = renderer.TerrainMesh(heightTable, 0f, 255f, 0f, 255f);
132                 terrainVertices = new ColorVertex[terrainFace.Vertices.Count];
133                 for (int i = 0; i < terrainFace.Vertices.Count; i++)
134                 {
135                     byte[] part = Utils.IntToBytes(i);
136                     terrainVertices[i] = new ColorVertex()
137                     {
138                         Vertex = terrainFace.Vertices[i],
139                         Color = new Color4b()
140                         {
141                             R = part[0],
142                             G = part[1],
143                             B = part[2],
144                             A = 253 // terrain picking
145                         }
146                     };
147                 }
148                 terrainIndices = new uint[terrainFace.Indices.Count];
149                 for (int i = 0; i < terrainIndices.Length; i++)
150                 {
151                     terrainIndices[i] = terrainFace.Indices[i];
152                 }
153                 terrainInProgress = false;
154                 Modified = false;
155                 terrainTextureNeedsUpdate = true;
156                 terrainTimeSinceUpdate = 0f;
157             });
158         }
159
160         void UpdateTerrainTexture()
161         {
162             if (!fetchingTerrainTexture)
163             {
164                 fetchingTerrainTexture = true;
165                 WorkPool.QueueUserWorkItem(sync =>
166                 {
167                     Simulator sim = Client.Network.CurrentSim;
168                     terrainImage = TerrainSplat.Splat(Instance, heightTable,
169                         new UUID[] { sim.TerrainDetail0, sim.TerrainDetail1, sim.TerrainDetail2, sim.TerrainDetail3 },
170                         new float[] { sim.TerrainStartHeight00, sim.TerrainStartHeight01, sim.TerrainStartHeight10, sim.TerrainStartHeight11 },
171                         new float[] { sim.TerrainHeightRange00, sim.TerrainHeightRange01, sim.TerrainHeightRange10, sim.TerrainHeightRange11 });
172
173                     fetchingTerrainTexture = false;
174                     terrainTextureNeedsUpdate = false;
175                 });
176             }
177         }
178
179         public bool TryGetVertex(int indeex, out ColorVertex picked)
180         {
181             if (indeex < terrainVertices.Length)
182             {
183                 picked = terrainVertices[indeex];
184                 return true;
185             }
186             picked = new ColorVertex();
187             return false;
188         }
189
190         public override void Render(RenderPass pass, int pickingID, SceneWindow scene, float time)
191         {
192             terrainTimeSinceUpdate += time;
193
194             if (Modified && terrainTimeSinceUpdate > RenderSettings.MinimumTimeBetweenTerrainUpdated)
195             {
196                 if (!terrainInProgress)
197                 {
198                     terrainInProgress = true;
199                     ResetTerrain(false);
200                     UpdateTerrain();
201                 }
202             }
203
204             if (terrainTextureNeedsUpdate)
205             {
206                 UpdateTerrainTexture();
207             }
208
209             if (terrainIndices == null || terrainVertices == null) return;
210
211             GL.Color3(1f, 1f, 1f);
212             GL.EnableClientState(ArrayCap.VertexArray);
213             GL.EnableClientState(ArrayCap.TextureCoordArray);
214             GL.EnableClientState(ArrayCap.NormalArray);
215             if (pass == RenderPass.Picking)
216             {
217                 GL.EnableClientState(ArrayCap.ColorArray);
218                 GL.ShadeModel(ShadingModel.Flat);
219             }
220
221             if (terrainImage != null)
222             {
223                 if (terrainTexture != -1)
224                 {
225                     GL.DeleteTexture(terrainTexture);
226                 }
227
228                 terrainTexture = RHelp.GLLoadImage(terrainImage, false);
229                 terrainImage.Dispose();
230                 terrainImage = null;
231             }
232
233             if (pass != RenderPass.Picking && terrainTexture != -1)
234             {
235                 GL.Enable(EnableCap.Texture2D);
236                 GL.BindTexture(TextureTarget.Texture2D, terrainTexture);
237             }
238
239             if (!RenderSettings.UseVBO || terrainVBOFailed)
240             {
241                 unsafe
242                 {
243                     fixed (float* normalPtr = &terrainVertices[0].Vertex.Normal.X)
244                     fixed (float* texPtr = &terrainVertices[0].Vertex.TexCoord.X)
245                     fixed (byte* colorPtr = &terrainVertices[0].Color.R)
246                     {
247                         GL.NormalPointer(NormalPointerType.Float, ColorVertex.Size, (IntPtr)normalPtr);
248                         GL.TexCoordPointer(2, TexCoordPointerType.Float, ColorVertex.Size, (IntPtr)texPtr);
249                         GL.VertexPointer(3, VertexPointerType.Float, ColorVertex.Size, terrainVertices);
250                         if (pass == RenderPass.Picking)
251                         {
252                             GL.ColorPointer(4, ColorPointerType.UnsignedByte, ColorVertex.Size, (IntPtr)colorPtr);
253                         }
254                         GL.DrawElements(BeginMode.Triangles, terrainIndices.Length, DrawElementsType.UnsignedInt, terrainIndices);
255                     }
256                 }
257             }
258             else
259             {
260                 if (terrainVBO == -1)
261                 {
262                     Compat.GenBuffers(out terrainVBO);
263                     Compat.BindBuffer(BufferTarget.ArrayBuffer, terrainVBO);
264                     Compat.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(terrainVertices.Length * ColorVertex.Size), terrainVertices, BufferUsageHint.StaticDraw);
265                     if (Compat.BufferSize(BufferTarget.ArrayBuffer) != terrainVertices.Length * ColorVertex.Size)
266                     {
267                         terrainVBOFailed = true;
268                         Compat.BindBuffer(BufferTarget.ArrayBuffer, 0);
269                         terrainVBO = -1;
270                     }
271                 }
272                 else
273                 {
274                     Compat.BindBuffer(BufferTarget.ArrayBuffer, terrainVBO);
275                 }
276
277                 if (terrainIndexVBO == -1)
278                 {
279                     Compat.GenBuffers(out terrainIndexVBO);
280                     Compat.BindBuffer(BufferTarget.ElementArrayBuffer, terrainIndexVBO);
281                     Compat.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(terrainIndices.Length * sizeof(uint)), terrainIndices, BufferUsageHint.StaticDraw);
282                     if (Compat.BufferSize(BufferTarget.ElementArrayBuffer) != terrainIndices.Length * sizeof(uint))
283                     {
284                         terrainVBOFailed = true;
285                         Compat.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
286                         terrainIndexVBO = -1;
287                     }
288                 }
289                 else
290                 {
291                     Compat.BindBuffer(BufferTarget.ElementArrayBuffer, terrainIndexVBO);
292                 }
293
294                 if (!terrainVBOFailed)
295                 {
296                     GL.NormalPointer(NormalPointerType.Float, ColorVertex.Size, (IntPtr)12);
297                     GL.TexCoordPointer(2, TexCoordPointerType.Float, ColorVertex.Size, (IntPtr)(24));
298                     if (pass == RenderPass.Picking)
299                     {
300                         GL.ColorPointer(4, ColorPointerType.UnsignedByte, ColorVertex.Size, (IntPtr)32);
301                     }
302                     GL.VertexPointer(3, VertexPointerType.Float, ColorVertex.Size, (IntPtr)(0));
303
304                     GL.DrawElements(BeginMode.Triangles, terrainIndices.Length, DrawElementsType.UnsignedInt, IntPtr.Zero);
305                 }
306
307                 Compat.BindBuffer(BufferTarget.ArrayBuffer, 0);
308                 Compat.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
309             }
310
311             if (pass == RenderPass.Picking)
312             {
313                 GL.DisableClientState(ArrayCap.ColorArray);
314                 GL.ShadeModel(ShadingModel.Smooth);
315             }
316             else
317             {
318                 GL.BindTexture(TextureTarget.Texture2D, 0);
319             }
320             GL.DisableClientState(ArrayCap.VertexArray);
321             GL.DisableClientState(ArrayCap.TextureCoordArray);
322             GL.DisableClientState(ArrayCap.NormalArray);
323         }
324     }
325 }