/// <summary> /// Updates the internal quadtree to make it identical to the given terrain /// quadtree.Collects the tasks necessary to create the missing texture /// tiles, corresponding to newly created quads. /// </summary> protected virtual void GetTiles(QuadTree parent, ref QuadTree tree, TerrainQuad quad) { //if tree not created, create a new tree and check if its tile is needed if (tree == null) { tree = new QuadTree(parent); tree.NeedTile = NeedTile(quad); } //If this trees tile is needed get a tile and add its task to the schedular if the task is not already done if (tree.NeedTile && tree.Tile == null) { tree.Tile = Producer.GetTile(quad.Level, quad.Tx, quad.Ty); if (!tree.Tile.IsDone) { tree.Tile.CreateTile(); } } if (!quad.IsLeaf && Producer.HasChildren(quad.Level, quad.Tx, quad.Ty)) { for (int i = 0; i < 4; ++i) { GetTiles(tree, ref tree.Children[i], quad.GetChild(i)); } } }
protected override void Start() { base.Start(); SplitDist = 1.1f; HorizonCulling = true; DeformedFrustumPlanes = new Vector4d[6]; World.SkyNode.InitUniforms(m_terrainMaterial); FaceToLocal = CalculateFaceToLocal(m_face); LocalToWorld = /*Matrix4x4d.ToMatrix4x4d(transform.localToWorldMatrix) * */ FaceToLocal; float size = m_size; if (World.IsDeformed) { size = World.Radius; Deform = new SphericalDeformation(size); } else { Deform = new Deformation(); } Root = new TerrainQuad(this, null, 0, 0, -size, -size, 2.0 * size, m_zmin, m_zmax); }
/// <summary> /// Sets the shader uniforms that are necessary to project on screen the /// given TerrainQuad. This method can set the uniforms that are specific to /// the given quad. /// </summary> public virtual void SetUniforms(TerrainNode node, TerrainQuad quad, MaterialPropertyBlock matPropertyBlock) { if (matPropertyBlock == null || node == null || quad == null) { return; } double ox = quad.Ox; double oy = quad.Oy; double l = quad.Length; double distFactor = node.DistFactor; int level = quad.Level; matPropertyBlock.SetVector(m_uniforms.offset, new Vector4((float)ox, (float)oy, (float)l, (float)level)); Vector3d camera = node.LocalCameraPos; matPropertyBlock.SetVector(m_uniforms.camera, new Vector4((float)((camera.x - ox) / l), (float)((camera.y - oy) / l), (float)((camera.z - node.View.GroundHeight) / (l * distFactor)), (float)camera.z)); Vector3d c = node.LocalCameraPos; Matrix3x3d m = m_localToTangent * (new Matrix3x3d(l, 0.0, ox - c.x, 0.0, l, oy - c.y, 0.0, 0.0, 1.0)); matPropertyBlock.SetMatrix(m_uniforms.tileToTangent, MathConverter.ToMatrix4x4(m)); SetScreenUniforms(node, quad, matPropertyBlock); }
/// <summary> /// Updates the internal quadtree to make it identical to the given terrain /// quadtree.This method releases the texture tiles corresponding to /// deleted quads. /// </summary> protected virtual void PutTiles(QuadTree tree, TerrainQuad quad) { if (tree == null) { return; } //Check if this tile is needed, if not put tile. tree.NeedTile = NeedTile(quad); if (!tree.NeedTile && tree.Tile != null) { Producer.PutTile(tree.Tile); tree.Tile = null; } //If this qiad is a leaf then all children of the tree are not needed if (quad.IsLeaf) { if (!tree.IsLeaf) { tree.RecursiveDeleteChildren(this); } } else if (Producer.HasChildren(quad.Level, quad.Tx, quad.Ty)) { for (int i = 0; i < 4; ++i) { PutTiles(tree.Children[i], quad.GetChild(i)); } } }
/// <summary> /// /// </summary> protected virtual void SetScreenUniforms(TerrainNode node, TerrainQuad quad, MaterialPropertyBlock matPropertyBlock) { double ox = quad.Ox; double oy = quad.Oy; double l = quad.Length; Vector3d p0 = new Vector3d(ox, oy, 0.0); Vector3d p1 = new Vector3d(ox + l, oy, 0.0); Vector3d p2 = new Vector3d(ox, oy + l, 0.0); Vector3d p3 = new Vector3d(ox + l, oy + l, 0.0); Matrix4x4d corners = new Matrix4x4d(p0.x, p1.x, p2.x, p3.x, p0.y, p1.y, p2.y, p3.y, p0.z, p1.z, p2.z, p3.z, 1.0, 1.0, 1.0, 1.0); matPropertyBlock.SetMatrix(m_uniforms.screenQuadCorners, MathConverter.ToMatrix4x4(m_localToScreen * corners)); Matrix4x4d verticals = new Matrix4x4d(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0); matPropertyBlock.SetMatrix(m_uniforms.screenQuadVerticals, MathConverter.ToMatrix4x4(m_localToScreen * verticals)); }
/// <summary> /// Creates the four subquads of this quad. /// </summary> private void Subdivide() { float hl = (float)Length / 2.0f; m_children[0] = new TerrainQuad(Owner, this, 2 * Tx, 2 * Ty, Ox, Oy, hl, ZMin, ZMax); m_children[1] = new TerrainQuad(Owner, this, 2 * Tx + 1, 2 * Ty, Ox + hl, Oy, hl, ZMin, ZMax); m_children[2] = new TerrainQuad(Owner, this, 2 * Tx, 2 * Ty + 1, Ox, Oy + hl, hl, ZMin, ZMax); m_children[3] = new TerrainQuad(Owner, this, 2 * Tx + 1, 2 * Ty + 1, Ox + hl, Oy + hl, hl, ZMin, ZMax); }
/// <summary> /// Find all the quads in a terrain that need to be drawn. If a quad is a leaf and is visible it should /// be drawn. If that quads tile is not ready the first ready parent is drawn /// NOTE - because of the current set up all task are run on the frame they are generated so /// the leaf quads will always have tiles that are ready to be drawn /// </summary> private void FindDrawableQuads(TerrainQuad quad, List <TileSampler> samplers) { quad.Drawable = false; if (!quad.IsVisible) { quad.Drawable = true; return; } if (quad.IsLeaf) { for (int i = 0; i < samplers.Count; ++i) { TileProducer p = samplers[i].Producer; int l = quad.Level; int tx = quad.Tx; int ty = quad.Ty; if (p.HasTile(l, tx, ty) && p.FindTile(l, tx, ty, false, true) == null) { return; } } } else { int nDrawable = 0; for (int i = 0; i < 4; ++i) { FindDrawableQuads(quad.GetChild(i), samplers); if (quad.GetChild(i).Drawable) { ++nDrawable; } } if (nDrawable < 4) { for (int i = 0; i < samplers.Count; ++i) { TileProducer p = samplers[i].Producer; int l = quad.Level; int tx = quad.Tx; int ty = quad.Ty; if (p.HasTile(l, tx, ty) && p.FindTile(l, tx, ty, false, true) == null) { return; } } } } quad.Drawable = true; }
protected override void SetScreenUniforms(TerrainNode node, TerrainQuad quad, MaterialPropertyBlock matPropertyBlock) { double ox = quad.Ox; double oy = quad.Oy; double l = quad.Length; Vector3d p0 = new Vector3d(ox, oy, R); Vector3d p1 = new Vector3d(ox + l, oy, R); Vector3d p2 = new Vector3d(ox, oy + l, R); Vector3d p3 = new Vector3d(ox + l, oy + l, R); Vector3d pc = (p0 + p3) * 0.5; double l0 = p0.Magnitude; double l1 = p1.Magnitude; double l2 = p2.Magnitude; double l3 = p3.Magnitude; Vector3d v0 = p0.Normalized; Vector3d v1 = p1.Normalized; Vector3d v2 = p2.Normalized; Vector3d v3 = p3.Normalized; Matrix4x4d deformedCorners = new Matrix4x4d(v0.x * R, v1.x * R, v2.x * R, v3.x * R, v0.y * R, v1.y * R, v2.y * R, v3.y * R, v0.z * R, v1.z * R, v2.z * R, v3.z * R, 1.0, 1.0, 1.0, 1.0); matPropertyBlock.SetMatrix(m_uniforms.screenQuadCorners, MathConverter.ToMatrix4x4(m_localToScreen * deformedCorners)); Matrix4x4d deformedVerticals = new Matrix4x4d(v0.x, v1.x, v2.x, v3.x, v0.y, v1.y, v2.y, v3.y, v0.z, v1.z, v2.z, v3.z, 0.0, 0.0, 0.0, 0.0); matPropertyBlock.SetMatrix(m_uniforms.screenQuadVerticals, MathConverter.ToMatrix4x4(m_localToScreen * deformedVerticals)); matPropertyBlock.SetVector(m_uniforms.screenQuadCornerNorms, new Vector4((float)l0, (float)l1, (float)l2, (float)l3)); Vector3d uz = pc.Normalized; Vector3d ux = (new Vector3d(0, 1, 0)).Cross(uz).Normalized; Vector3d uy = uz.Cross(ux); Matrix4x4d ltow = node.LocalToWorld; Matrix3x3d tangentFrameToWorld = new Matrix3x3d(ltow[0, 0], ltow[0, 1], ltow[0, 2], ltow[1, 0], ltow[1, 1], ltow[1, 2], ltow[2, 0], ltow[2, 1], ltow[2, 2]); Matrix3x3d m = new Matrix3x3d(ux.x, uy.x, uz.x, ux.y, uy.y, uz.y, ux.z, uy.z, uz.z); matPropertyBlock.SetMatrix(m_uniforms.tangentFrameToWorld, MathConverter.ToMatrix4x4(tangentFrameToWorld * m)); }
/// <summary> /// Creates a new TerrainQuad. /// </summary> /// <param name="owner">the TerrainNode to which the terrain quadtree belongs</param> /// <param name="parent">the parent quad of this quad</param> /// <param name="tx">the logical x coordinate of this quad</param> /// <param name="ty">the logical y coordinate of this quad</param> /// <param name="ox">the physical x coordinate of the lower left corner of this quad</param> /// <param name="oy">the physical y coordinate of the lower left corner of this quad</param> /// <param name="length">the physical size of this quad</param> /// <param name="zmin">the minimum terrain elevation inside this quad</param> /// <param name="zmax">the maximum terrain elevation inside this quad</param> public TerrainQuad(TerrainNode owner, TerrainQuad parent, int tx, int ty, double ox, double oy, double length, float zmin, float zmax) { Owner = owner; m_parent = parent; Level = (m_parent == null) ? 0 : m_parent.Level + 1; Tx = tx; Ty = ty; Ox = ox; Oy = oy; ZMax = zmax; ZMin = zmin; Length = length; LocalBox = new Box3d(Ox, Ox + Length, Oy, Oy + Length, ZMin, ZMax); }
/// <summary> /// Returns true if a tile is needed for the given terrain quad. /// </summary> protected virtual bool NeedTile(TerrainQuad quad) { bool needTile = m_storeLeaf; //if the quad is not a leaf and producer has children //and if have been asked not to store parent then dont need tile if (!m_storeParent && !quad.IsLeaf && Producer.HasChildren(quad.Level, quad.Tx, quad.Ty)) { needTile = false; } //if this quad is not visilbe and have not been asked to store invisilbe quads dont need tile if (!quad.IsVisible) { needTile = false; } return(needTile); }
/// <summary> /// /// </summary> /// <param name="quad"></param> /// <param name="matPropertyBlock"></param> public void SetPerQuadUniforms(TerrainQuad quad, MaterialPropertyBlock matPropertyBlock) { Deform.SetUniforms(this, quad, matPropertyBlock); }
private void DrawQuad(TerrainNode node, TerrainQuad quad, List <TileSampler> samplers) { if (!quad.IsVisible) { return; } if (!quad.Drawable) { return; } if (quad.IsLeaf) { m_propertyBlock.Clear(); //Set the unifroms needed to draw the texture for this sampler for (int i = 0; i < samplers.Count; ++i) { samplers[i].SetTile(m_propertyBlock, quad.Level, quad.Tx, quad.Ty); } //Set the uniforms unique to each quad node.SetPerQuadUniforms(quad, m_propertyBlock); //Draw the mesh Graphics.DrawMesh(m_quadMesh, Matrix4x4.identity, node.Material, 0, Camera.main, 0, m_propertyBlock); } else { //draw quads in a order based on distance to camera int[] order = new int[4]; double ox = node.LocalCameraPos.x; double oy = node.LocalCameraPos.y; double cx = quad.Ox + quad.Length / 2.0; double cy = quad.Oy + quad.Length / 2.0; if (oy < cy) { if (ox < cx) { order[0] = 0; order[1] = 1; order[2] = 2; order[3] = 3; } else { order[0] = 1; order[1] = 0; order[2] = 3; order[3] = 2; } } else { if (ox < cx) { order[0] = 2; order[1] = 0; order[2] = 3; order[3] = 1; } else { order[0] = 3; order[1] = 1; order[2] = 2; order[3] = 0; } } int done = 0; for (int i = 0; i < 4; ++i) { if (quad.GetChild(order[i]).Visible == FRUSTUM_VISIBILTY.INVISIBLE) { done |= (1 << order[i]); } else if (quad.GetChild(order[i]).Drawable) { DrawQuad(node, quad.GetChild(order[i]), samplers); done |= (1 << order[i]); } } if (done < 15) { //If a leaf quad needs to be drawn but its tiles are not ready then this //will draw the next parent tile instead that is ready. //Because of the current set up all tiles always have there tasks run on the frame they are generated //so this section of code is never reached m_propertyBlock.Clear(); //Set the unifroms needed to draw the texture for this sampler for (int i = 0; i < samplers.Count; ++i) { samplers[i].SetTile(m_propertyBlock, quad.Level, quad.Tx, quad.Ty); } //Set the uniforms unique to each quad node.SetPerQuadUniforms(quad, m_propertyBlock); //Draw the mesh. Graphics.DrawMesh(m_quadMesh, Matrix4x4.identity, node.Material, 0, Camera.main, 0, m_propertyBlock); } } }