/* * Updates the ground height below camera. */ void UpdateGroundHeight() { Vector3d2 localCamPos = GetTerrainNode().GetLocalCameraPos(); //If camera has moved update ground height if ((localCamPos - m_oldLocalCamera).Magnitude() > 1.0 && m_cameraQuad != null && m_cameraQuad.tile != null) { GPUTileStorage.GPUSlot slot = m_cameraQuad.tile.GetSlot()[0] as GPUTileStorage.GPUSlot; if (slot != null) { int border = GetProducer().GetBorder(); int tileSize = GetProducer().GetTileSizeMinBorder(0); float dx = m_cameraQuadCoords.x * tileSize; float dy = m_cameraQuadCoords.y * tileSize; //x,y are the non-normalized position in the elevations texture where the //ground height below the camera is. float x = dx + (float)border; float y = dy + (float)border; //Read the single value from the render texture CBUtility.ReadSingleFromRenderTexture(slot.GetTexture(), x, y, 0, m_groundBuffer, m_manager.GetReadData(), true); //Get single height value from buffer float[] height = new float[1]; m_groundBuffer.GetData(height); //Update the ground height. Stored as a static value in the TerrainNode script GetView().SetGroundHeight(Math.Max(0.0, height[0])); m_oldLocalCamera.x = localCamPos.x; m_oldLocalCamera.y = localCamPos.y; m_oldLocalCamera.z = localCamPos.z; } } m_cameraQuad = null; }
/* * Sets the uniforms necessary to access the texture tile for * the given quad. The samplers producer must be using a GPUTileStorage at the first slot * for this function to work */ void SetTile(ref RenderTexture tex, ref Vector3 coord, ref Vector3 size, int level, int tx, int ty) { if (!m_producer.IsGPUProducer()) { return; } Tile t = null; int b = m_producer.GetBorder(); int s = m_producer.GetCache().GetStorage(0).GetTileSize(); float dx = 0; float dy = 0; float dd = 1; float ds0 = (s / 2) * 2.0f - 2.0f * b; float ds = ds0; while (!m_producer.HasTile(level, tx, ty)) { dx += (tx % 2) * dd; dy += (ty % 2) * dd; dd *= 2; ds /= 2; level -= 1; tx /= 2; ty /= 2; if (level < 0) { Debug.Log("Proland::TileSampler::SetTile - invalid level"); return; } } QuadTree tt = m_root; QuadTree tc; int tl = 0; while (tl != level && (tc = tt.children[((tx >> (level - tl - 1)) & 1) | ((ty >> (level - tl - 1)) & 1) << 1]) != null) { tl += 1; tt = tc; } while (level > tl) { dx += (tx % 2) * dd; dy += (ty % 2) * dd; dd *= 2; ds /= 2; level -= 1; tx /= 2; ty /= 2; } t = tt.tile; while (t == null) { dx += (tx % 2) * dd; dy += (ty % 2) * dd; dd *= 2; ds /= 2; level -= 1; tx /= 2; ty /= 2; tt = tt.parent; if (tt == null) { Debug.Log("Proland::TileSampler::SetTile - null tile"); return; } t = tt.tile; } dx = dx * ((s / 2) * 2 - 2 * b) / dd; dy = dy * ((s / 2) * 2 - 2 * b) / dd; if (t == null) { Debug.Log("Proland::TileSampler::SetTile - tile is null"); return; } GPUTileStorage.GPUSlot gpuSlot = t.GetSlot(0) as GPUTileStorage.GPUSlot; if (gpuSlot == null) { Debug.Log("Proland::TileSampler::SetTile - gpuSlot is null"); return; } float w = gpuSlot.GetTexture().width; float h = gpuSlot.GetTexture().height; Vector4 coords; if (s % 2 == 0) { coords = new Vector4((dx + b) / w, (dy + b) / h, 0.0f, ds / w); } else { coords = new Vector4((dx + b + 0.5f) / w, (dy + b + 0.5f) / h, 0.0f, ds / w); } tex = gpuSlot.GetTexture(); coord = new Vector3(coords.x, coords.y, coords.z); size = new Vector3(coords.w, coords.w, (s / 2) * 2.0f - 2.0f * b); }
/* * Updates the terrainQuads min and max values. Used to create a better fitting bounding box. * Is not essental and can be disabled if retriving the heights data from the GPU is causing * performance issues. */ void UpdateMinMax() { //if no quads need read back or if disabled return if (m_needReadBack.Count == 0 || !m_enableMinMaxReadBack) { return; } //Make a copy of all the keys of the tiles that need to be read back Tile.Id[] ids = new Tile.Id[m_needReadBack.Count]; m_needReadBack.Keys.CopyTo(ids, 0); //Sort the keys by there level, lowest -> highest System.Array.Sort(ids, new Tile.ComparerID()); int count = 0; //foreach key read back the tiles data until the maxReadBacksPerFrame limit is reached foreach (Tile.Id id in ids) { QuadTreeZ t = m_needReadBack[id]; //If elevations container already contains key then data has been //read back before so just reapply the min/max values to TerranQuad if (m_elevations.ContainsKey(id)) { ElevationInfo info = m_elevations.Get(id); t.quad.SetZMin(info.min); t.quad.SetZMax(info.max); m_needReadBack.Remove(id); } else { //if for some reason the tile is null remove from container and continue if (t.tile == null) { m_needReadBack.Remove(id); continue; } GPUTileStorage.GPUSlot slot = t.tile.GetSlot()[0] as GPUTileStorage.GPUSlot; //If for some reason this is not a GPUSlot remove and continue if (slot == null) { m_needReadBack.Remove(id); continue; } RenderTexture tex = slot.GetTexture(); int size = tex.width * tex.height; ElevationInfo info = new ElevationInfo(); info.elevations = new float[size]; //Read back heights data from texture CBUtility.ReadFromRenderTexture(tex, 1, m_elevationsBuffer, m_manager.GetReadData()); //Copy into elevations info m_elevationsBuffer.GetData(info.elevations); //Find the min/max values for (int i = 0; i < size; i++) { if (info.elevations[i] < info.min) { info.min = info.elevations[i]; } if (info.elevations[i] > info.max) { info.max = info.elevations[i]; } } //Update quad t.quad.SetZMin(info.min); t.quad.SetZMax(info.max); //Store elevations to prevent having to read back again soon //Add to end of container m_elevations.AddLast(id, info); m_needReadBack.Remove(id); count++; //If the number of rad back to do per frame has hit the limit stop loop. if (count >= m_maxReadBacksPerFrame) { break; } } } //If the number of elevation info to store has exceded limit remove from start of container while (m_elevations.Count() > m_maxStoredElevations) { m_elevations.RemoveFirst(); } }