Exemple #1
0
    public void GenerateTreePrototypeData()
    {
        List <string>     prefabNames = new List <string>();
        List <GameObject> prefabs     = new List <GameObject>();

        System.Array.ForEach(m_ItemsToExtract, (x) => { prefabNames.Add(x.m_ItemPrefab.name); prefabs.Add(x.m_ItemPrefab); });

        if (TerrainUtils.TreeHashCheck(prefabNames.ToArray()))
        {
            Debug.LogError("Tree name hash collision, fix!");
            return;
        }

        GameObject[] proto = prefabs.ToArray();
        List <TreeSystemPrototypeData> managed = new List <TreeSystemPrototypeData>();

        for (int i = 0; i < proto.Length; i++)
        {
            GameObject prefab = proto[i];

            if (PrefabUtility.GetPrefabType(prefab) != PrefabType.ModelPrefab ||
                prefab.GetComponent <LODGroup>() == null ||
                prefab.GetComponentInChildren <BillboardRenderer>() == null)
            {
                Debug.LogError("Invalid prefab: " + prefab.name + ". Make sure that it is a SpeedTree, that it contains a 'LODGroup' and that it has a 'BillboardRenderer' component.");
                continue;
            }

            TreeSystemPrototypeData data = new TreeSystemPrototypeData();
            data.m_TreePrototype = prefab;
            // Use hash here instead of the old index
            data.m_TreePrototypeHash = TUtils.GetStableHashCode(prefab.name);

            // Instantiate LOD data that is going to be populated at runtime
            LOD[] lods = prefab.GetComponent <LODGroup>().GetLODs();
            TreeSystemLODData[] lodData = new TreeSystemLODData[lods.Length];
            // Generate some partial LOD data that doesn't have to be calculated at runtime
            data.m_LODData = lodData;

            for (int lod = 0; lod < lodData.Length; lod++)
            {
                TreeSystemLODData d = new TreeSystemLODData();
                lodData[lod] = d;
            }

            data.m_MaxLod3DIndex = lodData.Length - 2;

            managed.Add(data);
        }

        m_ManagedPrototypes = managed.ToArray();

        // Try and set the prototypes to our tree system
        TreeSystem t = FindObjectOfType <TreeSystem>();

        if (t)
        {
            t.m_ManagedPrototypes = m_ManagedPrototypes;
        }
    }
Exemple #2
0
    private void UpdateLODDataDistances(TreeSystemPrototypeData data)
    {
        LOD[] lods = data.m_TreePrototype.GetComponent <LODGroup>().GetLODs();
        TreeSystemLODData[] lodData = data.m_LODData;

        for (int i = 0; i < lodData.Length; i++)
        {
            // If it's the billboard move on
            if (i == lods.Length - 1)
            {
                continue;
            }

            TreeSystemLODData d = lodData[i];

            if (i == 0)
            {
                // If it's first 3D LOD
                d.m_StartDistance = 0;
                d.m_EndDistance   = ((1.0f - lods[i].screenRelativeTransitionHeight) * m_Settings.m_MaxTreeDistance);
            }
            else if (i == lodData.Length - 2)
            {
                // If it's last 3D LOD
                d.m_StartDistance = ((1.0f - lods[i - 1].screenRelativeTransitionHeight) * m_Settings.m_MaxTreeDistance);
                d.m_EndDistance   = m_Settings.m_MaxTreeDistance;
            }
            else
            {
                // If it's a LOD in between
                d.m_StartDistance = ((1.0f - lods[i - 1].screenRelativeTransitionHeight) * m_Settings.m_MaxTreeDistance);
                d.m_EndDistance   = ((1.0f - lods[i].screenRelativeTransitionHeight) * m_Settings.m_MaxTreeDistance);
            }
        }
    }
Exemple #3
0
    private GameObject GetColliderForPrototype(int hash)
    {
        TreeSystemPrototypeData data = m_OwnerSystem.m_ManagedPrototypesIndexed[hash];

        if (m_Cache.ContainsKey(hash) == false)
        {
            // If we don't contain the key create and add it

            // If we don't have a tree with a collider, like a bush or something, just add a null mapping
            if (data.m_TreePrototype.GetComponentInChildren <Collider>() == null)
            {
                m_Cache.Add(hash, null);
                return(null);
            }
            else
            {
                // Create the collider prototype and remove all it's mesh renderers and stuff
                GameObject colliderPrototype = Instantiate(data.m_TreePrototype, m_ColliderHolder.transform);
                colliderPrototype.name = "ColliderPrototype_" + data.m_TreePrototype.name;

                // Clear the lod group
                LODGroup lod = colliderPrototype.GetComponent <LODGroup>();
                if (lod)
                {
                    DestroyImmediate(lod);
                }

                // Clear any owned GObjects that don't have colliders
                for (int i = colliderPrototype.transform.childCount - 1; i >= 0; i--)
                {
                    GameObject owned = colliderPrototype.transform.GetChild(i).gameObject;

                    if (owned.GetComponent <Collider>() == null)
                    {
                        DestroyImmediate(owned);
                    }
                }

                // Deactivate it
                colliderPrototype.SetActive(false);

                // Create the cache entry
                TreeCollisionCache cache = new TreeCollisionCache(colliderPrototype, m_ColliderHolder);

                // Add the collision cache to our dictionary
                m_Cache.Add(hash, cache);
                return(cache.RetrieveInstance());
            }
        }
        else if (m_Cache[hash] != null)
        {
            // We contain the cache, just retrieve an object
            return(m_Cache[hash].RetrieveInstance());
        }
        else
        {
            // If we contain the hash but it doesn't have anything in it, it means than it's a tree without collisions, like a bush or something
            return(null);
        }
    }
Exemple #4
0
    public void ExtractTreeLODPrototypeData()
    {
        TreeSystemPrototypeData[] proto = m_ManagedPrototypes;

        for (int i = 0; i < proto.Length; i++)
        {
            GameObject prefab            = proto[i].m_TreePrototype;
            TreeSystemPrototypeData data = proto[i];

            // Instantiate LOD data that is going to be populated at runtime
            LOD[] lods = prefab.GetComponent <LODGroup>().GetLODs();
            TreeSystemLODData[] lodData = new TreeSystemLODData[lods.Length];
            // Generate some partial LOD data that doesn't have to be calculated at runtime
            data.m_LODData = lodData;

            for (int lod = 0; lod < lodData.Length; lod++)
            {
                TreeSystemLODData d = new TreeSystemLODData();
                lodData[lod] = d;

                // Populate some data
                if (lod == lods.Length - 1)
                {
                    // It must be a billboard renderer tread it specially
                    d.m_IsBillboard = true;

                    d.m_Mesh = Instantiate(m_SystemQuad);
                    AssetDatabase.CreateAsset(d.m_Mesh,
                                              m_DataStorePath + "/" + prefab.name + "_Master_Billboard_Mesh_0.asset");

                    d.m_Materials = new Material[] { data.m_BillboardMasterMaterial };
                }
                else
                {
                    MeshRenderer rend   = lods[lod].renderers[0].gameObject.GetComponent <MeshRenderer>();
                    MeshFilter   filter = lods[lod].renderers[0].gameObject.GetComponent <MeshFilter>();

                    // Set the mesh we are drawing to the shared mesh
                    d.m_Mesh = Instantiate(filter.sharedMesh);
                    AssetDatabase.CreateAsset(d.m_Mesh,
                                              m_DataStorePath + "/" + prefab.name + "_Master_Tree_Mesh_LOD_" + lod + ".asset");

                    // Get the materials, create instances and set our SpeedTree master shader
                    d.m_Materials = rend.sharedMaterials;

                    for (int mat = 0; mat < d.m_Materials.Length; mat++)
                    {
                        d.m_Materials[mat]        = new Material(d.m_Materials[mat]);
                        d.m_Materials[mat].shader = m_TreeShaderMaster;

                        AssetDatabase.CreateAsset(d.m_Materials[mat],
                                                  m_DataStorePath + "/" + prefab.name + "_Master_Tree_Material_LOD" + lod + "_" + mat + ".mat");
                    }
                }
            }

            data.m_MaxLodIndex   = lodData.Length - 1;
            data.m_MaxLod3DIndex = lodData.Length - 2;
        }
    }
Exemple #5
0
    void Start()
    {
        system    = FindObjectOfType <TreeSystem>();
        protoData = system.m_ManagedPrototypes[0];

        THRESHOLD    = system.GetTreeTanzitionThreshold();
        procDistance = system.GetTreeDistance() - THRESHOLD;
    }
    private void GenerateRuntimePrototypeData(TreeSystemPrototypeData data)
    {
        // All stuff that we draw must have billboard renderers, that's why it's trees
        LOD[] lods = data.m_TreePrototype.GetComponent <LODGroup>().GetLODs();
        TreeSystemLODData[] lodData = data.m_LODData;

        // Set material's UV at runtime
        SetMaterialBillProps(data, data.m_BillboardBatchMaterial);
        SetMaterialBillProps(data, data.m_BillboardMasterMaterial);

        // Get the same tree stuff as the grass
        for (int i = 0; i < lodData.Length; i++)
        {
            if (lods[i].renderers.Length != 1)
            {
                Debug.LogError("Renderer length != 1! Problem!");
            }

            // Assume only one renderer
            TreeSystemLODData d = lodData[i];

            // If it's last element
            if (i == lods.Length - 1)
            {
                d.m_Block = new MaterialPropertyBlock();
            }
            else
            {
                d.m_Block = new MaterialPropertyBlock();

                GameObject weirdTree = Instantiate(lods[i].renderers[0].gameObject);
                weirdTree.hideFlags = HideFlags.HideAndDontSave;

                // Get the tree renderer
                d.m_TreeRenderer = weirdTree.GetComponent <MeshRenderer>();
                d.m_TreeRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;

                d.m_TreeRenderer.transform.SetParent(Camera.main.transform);
                d.m_TreeRenderer.transform.localPosition = new Vector3(0, 0, -2);
                d.m_TreeRenderer.transform.localScale    = new Vector3(0.1f, 0.1f, 0.1f);

                MeshFilter mf = d.m_TreeRenderer.GetComponent <MeshFilter>();

                // Get an instance mesh from the weird tree
                Mesh m = mf.mesh;

                // Set the new bounds so that it's always drawn
                Bounds b = m.bounds;
                b.Expand(50.0f);
                m.bounds = b;
                mf.mesh  = m;
            }
        }

        // Update the LOD distances
        UpdateLODDataDistances(data);
    }
Exemple #7
0
    public void ExtractTreeLODPrototypeData()
    {
        TreeSystemPrototypeData[] proto = m_ManagedPrototypes;

        for (int i = 0; i < proto.Length; i++)
        {
            GameObject prefab            = proto[i].m_TreePrototype;
            TreeSystemPrototypeData data = proto[i];

            if (prefab.GetComponent <LODGroup>() == null)
            {
                Debug.LogError("Prototype: " + prefab.name + " does not have a LOD group! Please fix and regenerate, or the system will break!");
                continue;
            }

            // Instantiate LOD data that is going to be populated at runtime
            LOD[] lods = prefab.GetComponent <LODGroup>().GetLODs();
            TreeSystemLODData[] lodData = new TreeSystemLODData[lods.Length];
            // Generate some partial LOD data that doesn't have to be calculated at runtime
            data.m_LODData = lodData;

            for (int lod = 0; lod < lodData.Length; lod++)
            {
                TreeSystemLODData d = new TreeSystemLODData();
                lodData[lod] = d;

                if (lods[lod].renderers[0].gameObject.GetComponent <BillboardRenderer>())
                {
                    continue;
                }

                MeshRenderer rend   = lods[lod].renderers[0].gameObject.GetComponent <MeshRenderer>();
                MeshFilter   filter = lods[lod].renderers[0].gameObject.GetComponent <MeshFilter>();

                // Set the mesh we are drawing to the shared mesh
                d.m_Mesh = Instantiate(filter.sharedMesh);
                AssetDatabase.CreateAsset(d.m_Mesh,
                                          m_DataStorePath + "/" + prefab.name + "_Master_Tree_Mesh_LOD_" + lod + ".asset");

                // Get the materials, create instances and set our SpeedTree master shader
                d.m_Materials = rend.sharedMaterials;

                for (int mat = 0; mat < d.m_Materials.Length; mat++)
                {
                    d.m_Materials[mat]        = new Material(d.m_Materials[mat]);
                    d.m_Materials[mat].shader = m_TreeShaderMaster;

                    AssetDatabase.CreateAsset(d.m_Materials[mat],
                                              m_DataStorePath + "/" + prefab.name + "_Master_Tree_Material_LOD" + lod + "_" + mat + ".mat");
                }
            }

            data.m_MaxLod3DIndex = lodData.Length - 2;
        }
    }
    public static void SetMaterialBillProps(TreeSystemPrototypeData data, Material m)
    {
        Vector2[] uv = data.m_VertBillboardUVs;

        Vector4[] UV_U = new Vector4[8];
        Vector4[] UV_V = new Vector4[8];

        for (int i = 0; i < 8; i++)
        {
            // 4 by 4 elements
            UV_U[i].x = uv[4 * i + 0].x;
            UV_U[i].y = uv[4 * i + 1].x;
            UV_U[i].z = uv[4 * i + 2].x;
            UV_U[i].w = uv[4 * i + 3].x;

            UV_V[i].x = uv[4 * i + 0].y;
            UV_V[i].y = uv[4 * i + 1].y;
            UV_V[i].z = uv[4 * i + 2].y;
            UV_V[i].w = uv[4 * i + 3].y;
        }

        m.SetVectorArray("_UVVert_U", UV_U);
        m.SetVectorArray("_UVVert_V", UV_V);

        uv = data.m_HorzBillboardUVs;

        for (int i = 0; i < 1; i++)
        {
            // 4 by 4 elements
            UV_U[i].x = uv[4 * i + 0].x;
            UV_U[i].y = uv[4 * i + 1].x;
            UV_U[i].z = uv[4 * i + 2].x;
            UV_U[i].w = uv[4 * i + 3].x;

            UV_V[i].x = uv[4 * i + 0].y;
            UV_V[i].y = uv[4 * i + 1].y;
            UV_V[i].z = uv[4 * i + 2].y;
            UV_V[i].w = uv[4 * i + 3].y;
        }

        m.SetVector("_UVHorz_U", UV_U[0]);
        m.SetVector("_UVHorz_V", UV_V[0]);
    }
Exemple #9
0
    public void ExtractXMLTreePrototypeData()
    {
        TreeSystemPrototypeData[] data = m_ManagedPrototypes;

        for (int i = 0; i < data.Length; i++)
        {
            TreeSystemPrototypeData d = data[i];

            if (d.m_TreePrototype == null)
            {
                Debug.LogError("Nothing set for data at index: " + i);
                continue;
            }

            // Get the protorype's billboard asset
            BillboardRenderer bill = d.m_TreePrototype.GetComponentInChildren <BillboardRenderer>();

            if (bill == null)
            {
                Debug.LogError("Prototype: " + d.m_TreePrototype.name + " does not contain a billboard renderer! Items without billboard renderers are not supported at the moment!");
                continue;
            }

            BillboardAsset billAsset = bill.billboard;

            // Set sizes
            d.m_Size = new Vector3(billAsset.width, billAsset.height, billAsset.bottom);


            Vector4[] uvs = billAsset.GetImageTexCoords();

            // Ussualy 16
            d.m_VertBillboardUVs = new Vector2[uvs.Length * 4];
            // Just set the first UV's just to have something
            d.m_HorzBillboardUVs = new Vector2[4];

            for (int uvIdx = 0, billUv = 0; uvIdx < uvs.Length; uvIdx++, billUv += 4)
            {
                Vector4 extract = uvs[uvIdx];

                if (uvIdx == 0)
                {
                    d.m_HorzBillboardUVs[0] = new Vector2(extract.x, extract.y);
                    d.m_HorzBillboardUVs[1] = new Vector2(extract.x, extract.y) + new Vector2(0, Mathf.Abs(extract.w));
                    d.m_HorzBillboardUVs[2] = new Vector2(extract.x, extract.y) + new Vector2(-extract.z, Mathf.Abs(extract.w));
                    d.m_HorzBillboardUVs[3] = new Vector2(extract.x, extract.y) + new Vector2(-extract.z, 0);
                }

                // We are rotated
                if (extract.w < 0)
                {
                    d.m_VertBillboardUVs[billUv + 0] = new Vector2(extract.x, extract.y);
                    d.m_VertBillboardUVs[billUv + 1] = new Vector2(extract.x, extract.y) + new Vector2(0, Mathf.Abs(extract.w));
                    d.m_VertBillboardUVs[billUv + 2] = new Vector2(extract.x, extract.y) + new Vector2(-extract.z, Mathf.Abs(extract.w));
                    d.m_VertBillboardUVs[billUv + 3] = new Vector2(extract.x, extract.y) + new Vector2(-extract.z, 0);
                }
                else
                {
                    d.m_VertBillboardUVs[billUv + 0] = new Vector2(extract.x, extract.y);
                    d.m_VertBillboardUVs[billUv + 1] = new Vector2(extract.x, extract.y) + new Vector2(extract.z, 0);
                    d.m_VertBillboardUVs[billUv + 2] = new Vector2(extract.x, extract.y) + new Vector2(extract.z, extract.w);
                    d.m_VertBillboardUVs[billUv + 3] = new Vector2(extract.x, extract.y) + new Vector2(0, extract.w);
                }
            }

            Vector4 size = d.m_Size;
            size.w = 1;

            // Create the material with the texture references
            Material billboardMaterialBatch = new Material(m_BillboardShaderBatch);
            billboardMaterialBatch.SetTexture("_MainTex", bill.billboard.material.GetTexture("_MainTex"));
            billboardMaterialBatch.SetTexture("_BumpMap", bill.billboard.material.GetTexture("_BumpMap"));
            billboardMaterialBatch.SetVector("_Size", size);

            // Replace, don't delete
            // AssetDatabase.DeleteAsset(m_DataStorePath + "/" + d.m_TreePrototype.name + "_Mat.mat");
            AssetDatabase.CreateAsset(billboardMaterialBatch,
                                      m_DataStorePath + "/" + d.m_TreePrototype.name + "_Bill_Batch_Mat.mat");

            // Set the material
            d.m_BillboardBatchMaterial = billboardMaterialBatch;

            // Set billboard data
            TreeSystem.SetMaterialBillProps(d, d.m_BillboardBatchMaterial);
        }

        AssetDatabase.Refresh();
    }
Exemple #10
0
    private void IssueDrawTrees(TreeSystemPrototypeData data, TreeSystemStructuredTrees trees, int[] indices, float[] dist, int count, bool shadowsOnly)
    {
        for (int i = 0; i < MAX_LOD_COUNT; i++)
        {
            m_MtxLODTempCount[i] = 0;
        }

        TreeSystemLODData[] lodData = data.m_LODData;

        TreeSystemStoredInstance[] treeInstances   = trees.m_Instances;
        TreeSystemLODInstance[]    lodInstanceData = trees.m_InstanceData;

        int maxLod3D = data.m_MaxLod3DIndex;

        // Build the batched stuff. We want to batch the same LOD and draw them using instancing
        for (int i = 0; i < count; i++)
        {
            ProcessLOD(ref lodData, ref maxLod3D, ref lodInstanceData[indices[i]], ref dist[i]);

            int idx             = indices[i];
            int currentLODLevel = lodInstanceData[idx].m_LODLevel;

            // Collect that LOD level
            if (currentLODLevel == 0)
            {
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m00 = treeInstances[idx].m_PositionMtx.m00;
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m01 = treeInstances[idx].m_PositionMtx.m01;
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m02 = treeInstances[idx].m_PositionMtx.m02;
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m03 = treeInstances[idx].m_PositionMtx.m03;
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m10 = treeInstances[idx].m_PositionMtx.m10;
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m11 = treeInstances[idx].m_PositionMtx.m11;
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m12 = treeInstances[idx].m_PositionMtx.m12;
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m13 = treeInstances[idx].m_PositionMtx.m13;
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m20 = treeInstances[idx].m_PositionMtx.m20;
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m21 = treeInstances[idx].m_PositionMtx.m21;
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m22 = treeInstances[idx].m_PositionMtx.m22;
                m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]].m23 = treeInstances[idx].m_PositionMtx.m23;

                m_MtxLODTranzDetail_0[m_MtxLODTempCount[currentLODLevel]] = lodInstanceData[idx].m_LODTransition;
                m_MtxLODTranzFull_0[m_MtxLODTempCount[currentLODLevel]]   = lodInstanceData[idx].m_LODFullFade;
            }
            else if (currentLODLevel == 1)
            {
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m00 = treeInstances[idx].m_PositionMtx.m00;
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m01 = treeInstances[idx].m_PositionMtx.m01;
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m02 = treeInstances[idx].m_PositionMtx.m02;
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m03 = treeInstances[idx].m_PositionMtx.m03;
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m10 = treeInstances[idx].m_PositionMtx.m10;
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m11 = treeInstances[idx].m_PositionMtx.m11;
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m12 = treeInstances[idx].m_PositionMtx.m12;
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m13 = treeInstances[idx].m_PositionMtx.m13;
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m20 = treeInstances[idx].m_PositionMtx.m20;
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m21 = treeInstances[idx].m_PositionMtx.m21;
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m22 = treeInstances[idx].m_PositionMtx.m22;
                m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]].m23 = treeInstances[idx].m_PositionMtx.m23;

                m_MtxLODTranzDetail_1[m_MtxLODTempCount[currentLODLevel]] = lodInstanceData[idx].m_LODTransition;
                m_MtxLODTranzFull_1[m_MtxLODTempCount[currentLODLevel]]   = lodInstanceData[idx].m_LODFullFade;
            }
            else if (currentLODLevel == 2)
            {
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m00 = treeInstances[idx].m_PositionMtx.m00;
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m01 = treeInstances[idx].m_PositionMtx.m01;
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m02 = treeInstances[idx].m_PositionMtx.m02;
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m03 = treeInstances[idx].m_PositionMtx.m03;
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m10 = treeInstances[idx].m_PositionMtx.m10;
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m11 = treeInstances[idx].m_PositionMtx.m11;
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m12 = treeInstances[idx].m_PositionMtx.m12;
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m13 = treeInstances[idx].m_PositionMtx.m13;
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m20 = treeInstances[idx].m_PositionMtx.m20;
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m21 = treeInstances[idx].m_PositionMtx.m21;
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m22 = treeInstances[idx].m_PositionMtx.m22;
                m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]].m23 = treeInstances[idx].m_PositionMtx.m23;

                m_MtxLODTranzDetail_2[m_MtxLODTempCount[currentLODLevel]] = lodInstanceData[idx].m_LODTransition;
                m_MtxLODTranzFull_2[m_MtxLODTempCount[currentLODLevel]]   = lodInstanceData[idx].m_LODFullFade;
            }
            else if (currentLODLevel == 3)
            {
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m00 = treeInstances[idx].m_PositionMtx.m00;
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m01 = treeInstances[idx].m_PositionMtx.m01;
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m02 = treeInstances[idx].m_PositionMtx.m02;
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m03 = treeInstances[idx].m_PositionMtx.m03;
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m10 = treeInstances[idx].m_PositionMtx.m10;
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m11 = treeInstances[idx].m_PositionMtx.m11;
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m12 = treeInstances[idx].m_PositionMtx.m12;
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m13 = treeInstances[idx].m_PositionMtx.m13;
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m20 = treeInstances[idx].m_PositionMtx.m20;
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m21 = treeInstances[idx].m_PositionMtx.m21;
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m22 = treeInstances[idx].m_PositionMtx.m22;
                m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]].m23 = treeInstances[idx].m_PositionMtx.m23;

                m_MtxLODTranzDetail_3[m_MtxLODTempCount[currentLODLevel]] = lodInstanceData[idx].m_LODTransition;
                m_MtxLODTranzFull_3[m_MtxLODTempCount[currentLODLevel]]   = lodInstanceData[idx].m_LODFullFade;
            }
            else if (currentLODLevel == 4)
            {
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m00 = treeInstances[idx].m_PositionMtx.m00;
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m01 = treeInstances[idx].m_PositionMtx.m01;
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m02 = treeInstances[idx].m_PositionMtx.m02;
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m03 = treeInstances[idx].m_PositionMtx.m03;
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m10 = treeInstances[idx].m_PositionMtx.m10;
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m11 = treeInstances[idx].m_PositionMtx.m11;
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m12 = treeInstances[idx].m_PositionMtx.m12;
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m13 = treeInstances[idx].m_PositionMtx.m13;
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m20 = treeInstances[idx].m_PositionMtx.m20;
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m21 = treeInstances[idx].m_PositionMtx.m21;
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m22 = treeInstances[idx].m_PositionMtx.m22;
                m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]].m23 = treeInstances[idx].m_PositionMtx.m23;

                m_MtxLODTranzDetail_4[m_MtxLODTempCount[currentLODLevel]] = lodInstanceData[idx].m_LODTransition;
                m_MtxLODTranzFull_4[m_MtxLODTempCount[currentLODLevel]]   = lodInstanceData[idx].m_LODFullFade;
            }

            m_MtxLODTempCount[currentLODLevel]++;
        }

        // Now that the data is built, issue it
        for (int i = 0; i <= maxLod3D; i++)
        {
            if (i == 0)
            {
                Draw3DLODInstanced(ref lodData[i], ref m_MtxLODTemp_0, ref m_MtxLODTranzDetail_0, ref m_MtxLODTranzFull_0, ref m_MtxLODTempCount[i], ref shadowsOnly);
            }
            else if (i == 1)
            {
                Draw3DLODInstanced(ref lodData[i], ref m_MtxLODTemp_1, ref m_MtxLODTranzDetail_1, ref m_MtxLODTranzFull_1, ref m_MtxLODTempCount[i], ref shadowsOnly);
            }
            else if (i == 2)
            {
                Draw3DLODInstanced(ref lodData[i], ref m_MtxLODTemp_2, ref m_MtxLODTranzDetail_2, ref m_MtxLODTranzFull_2, ref m_MtxLODTempCount[i], ref shadowsOnly);
            }
            else if (i == 3)
            {
                Draw3DLODInstanced(ref lodData[i], ref m_MtxLODTemp_3, ref m_MtxLODTranzDetail_3, ref m_MtxLODTranzFull_3, ref m_MtxLODTempCount[i], ref shadowsOnly);
            }
            else if (i == 4)
            {
                Draw3DLODInstanced(ref lodData[i], ref m_MtxLODTemp_4, ref m_MtxLODTranzDetail_4, ref m_MtxLODTranzFull_4, ref m_MtxLODTempCount[i], ref shadowsOnly);
            }
        }
    }
Exemple #11
0
    private void GenerateRuntimePrototypeData(TreeSystemPrototypeData data)
    {
        // All stuff that we draw must have billboard renderers, that's why it's trees
        LOD[] lods = data.m_TreePrototype.GetComponent <LODGroup>().GetLODs();
        TreeSystemLODData[] lodData = data.m_LODData;

        // Set material's UV at runtime
        SetMaterialBillProps(data, data.m_BillboardBatchMaterial);

        // Instantiate the last 3D lod
        GameObject weirdTree = Instantiate(lods[lods.Length - 2].renderers[0].gameObject);

        // weirdTree.hideFlags = HideFlags.HideAndDontSave;

        // Stick it to the camera
        weirdTree.transform.SetParent(Camera.main.transform);
        weirdTree.transform.localPosition = new Vector3(0, 0, -2);
        weirdTree.transform.localScale    = new Vector3(0.1f, 0.1f, 0.1f);

        // Expand the bounds
        MeshRenderer mr = weirdTree.GetComponent <MeshRenderer>();
        MeshFilter   mf = weirdTree.GetComponent <MeshFilter>();

        // Get an instance mesh from the weird tree
        Mesh m = mf.mesh;

        // Set the new bounds so that it's always drawn
        Bounds b = m.bounds; b.Expand(50.0f); m.bounds = b;

        mf.mesh = m;

        // Don't cast shadows
        mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;

        // Get the same tree stuff as the grass
        for (int i = 0; i < lodData.Length; i++)
        {
            if (lods[i].renderers.Length != 1)
            {
                Debug.LogError("Renderer length != 1! Problem!");
            }

            // Assume only one renderer
            TreeSystemLODData d = lodData[i];

            // Add a new MBP and set the renderer from which we are going to copy the wind data from
            d.m_Block        = new MaterialPropertyBlock();
            d.m_TreeRenderer = mr;

            // Set the full array here because based on the docs, i'm not sure when is this going to be refreshed
            // Quote:
            // "The array length can't be changed once it has been added to the block. If you subsequently try
            // to set a longer array into the same property, the length will be capped to the original length and
            // the extra items you tried to assign will be ignored."
            // So, we're making sure that we have those MAX_BATCH count of floats
            d.m_Block.SetFloatArray(m_ShaderIDFadeLODDetail, new float[MAX_BATCH]);
            d.m_Block.SetFloatArray(m_ShaderIDFadeLODFull, new float[MAX_BATCH]);
        }

        // Update the LOD distances
        UpdateLODDataDistances(data);
    }
Exemple #12
0
    public void GenerateTreePrototypeData()
    {
        if (TerrainUtils.TreeHashCheck(m_MainManagedTerrain))
        {
            Log.e("Tree name hash collision, fix!");
            return;
        }

        TreePrototype[] proto = m_MainManagedTerrain.terrainData.treePrototypes;

        List <TreeSystemPrototypeData> managed = new List <TreeSystemPrototypeData>();

        for (int i = 0; i < proto.Length; i++)
        {
            if (ShouldUsePrefab(proto[i].prefab) >= 0)
            {
                GameObject prefab = proto[i].prefab;

                TreeSystemPrototypeData data = new TreeSystemPrototypeData();
                data.m_TreePrototype = prefab;
                // Use hash here instead of the old index
                data.m_TreePrototypeHash = TUtils.GetStableHashCode(proto[i].prefab.name);

                if (m_UseXMLData)
                {
                    TextAsset textData = AssetDatabase.LoadAssetAtPath <TextAsset>(m_TreeXMLStorePath + "/" + proto[i].prefab.name + ".xml");

                    if (textData != null)
                    {
                        data.m_TreeBillboardData = textData;
                    }
                    else
                    {
                        Debug.LogError("Could not find XML data for: " + data.m_TreePrototype.name);
                    }
                }

                // Instantiate LOD data that is going to be populated at runtime
                LOD[] lods = prefab.GetComponent <LODGroup>().GetLODs();
                TreeSystemLODData[] lodData = new TreeSystemLODData[lods.Length];
                // Generate some partial LOD data that doesn't have to be calculated at runtime
                data.m_LODData = lodData;

                for (int lod = 0; lod < lodData.Length; lod++)
                {
                    TreeSystemLODData d = new TreeSystemLODData();
                    lodData[lod] = d;
                }

                data.m_MaxLodIndex   = lodData.Length - 1;
                data.m_MaxLod3DIndex = lodData.Length - 2;

                managed.Add(data);
            }
        }

        m_ManagedPrototypes = managed.ToArray();

        // Try and set the prototypes to our tree system
        TreeSystem t = FindObjectOfType <TreeSystem>();

        if (t)
        {
            t.m_ManagedPrototypes = m_ManagedPrototypes;
        }
    }
Exemple #13
0
    private void BuildTreeTypeCellMesh(GameObject owner, TreeSystemStructuredTrees cell, List <TreeSystemStoredInstance> trees, TreeSystemPrototypeData data)
    {
        int[] originalTriangles = m_SystemQuad.triangles;

        RowCol pos = cell.m_Position;

        GameObject mesh = new GameObject();

        // Mark object as static
        GameObjectUtility.SetStaticEditorFlags(mesh, StaticEditorFlags.OccludeeStatic | StaticEditorFlags.ReflectionProbeStatic);

        mesh.transform.SetParent(owner.transform);
        mesh.name = "MeshCell[" + pos.m_Row + "_" + pos.m_Col + "_" + data.m_TreePrototype.name + "]";

        Vector3 worldScale = new Vector3(data.m_Size.x, data.m_Size.y, data.m_Size.x);

        // Set material
        MeshRenderer rend = mesh.AddComponent <MeshRenderer>();

        rend.sharedMaterial = data.m_BillboardBatchMaterial;

        MeshFilter filter = mesh.AddComponent <MeshFilter>();

        Mesh treeMesh = new Mesh();

        treeMesh.name = "TreeCell[" + pos.m_Row + "_" + pos.m_Col + "_" + data.m_TreePrototype.name + "]";

        List <Vector4> m_TempWorldPositions = new List <Vector4>();
        List <Vector3> m_TempWorldScales    = new List <Vector3>();
        List <Vector3> m_TempQuadVertices   = new List <Vector3>();
        List <Vector4> m_TempQuadTangents   = new List <Vector4>();
        List <Vector3> m_TempQuadNormals    = new List <Vector3>();
        List <int>     m_TempQuadIndices    = new List <int>();

        Bounds newBounds = new Bounds();

        newBounds.center = cell.m_BoundsBox.center;

        // TODO: populate mesh data
        for (int treeIndex = 0; treeIndex < trees.Count; treeIndex++)
        {
            Vector3 position = trees[treeIndex].m_WorldPosition;
            Vector3 scale    = trees[treeIndex].m_WorldScale;
            float   rot      = trees[treeIndex].m_WorldRotation;

            // Offset world position, by the grounding factor
            Vector3 instancePos = position;
            instancePos.y += data.m_Size.z;

            // Scale by the world scale too so that we don't have to do an extra multip
            Vector3 instanceScale = scale;
            instanceScale.Scale(worldScale);

            // Encapsulate bottom and top also
            newBounds.Encapsulate(instancePos);
            newBounds.Encapsulate(instancePos + new Vector3(0, data.m_Size.y, 0));

            // Add the world and scale data
            for (int index = 0; index < 4; index++)
            {
                Vector4 posAndRot = instancePos;
                posAndRot.w = rot;

                m_TempWorldPositions.Add(posAndRot);
                m_TempWorldScales.Add(instanceScale);
            }

            // Add stanard quad data
            m_TempQuadVertices.AddRange(m_SystemQuad.vertices);
            m_TempQuadTangents.AddRange(m_SystemQuad.tangents);
            m_TempQuadNormals.AddRange(m_SystemQuad.normals);

            // Calculate triangle indixes
            m_TempQuadIndices.AddRange(originalTriangles);
            for (int triIndex = 0; triIndex < 6; triIndex++)
            {
                // Just add to the triangles the existing triangles + the new indices
                m_TempQuadIndices[triIndex + 6 * treeIndex] = originalTriangles[triIndex] + 4 * treeIndex;
            }
        }

        treeMesh.Clear();

        // Set standard data
        treeMesh.SetVertices(m_TempQuadVertices);
        treeMesh.SetNormals(m_TempQuadNormals);
        treeMesh.SetTangents(m_TempQuadTangents);

        // Set the custom data
        treeMesh.SetUVs(1, m_TempWorldPositions);
        treeMesh.SetUVs(2, m_TempWorldScales);

        // Set triangles and do not calculate bounds
        treeMesh.SetTriangles(m_TempQuadIndices, 0, false);

        // Set the manually calculated bounds
        treeMesh.bounds = newBounds;

        treeMesh.UploadMeshData(true);

        // Set the mesh
        filter.mesh = treeMesh;
    }
Exemple #14
0
    private TreeSystemTerrain ProcessTerrain(Terrain terrain, int cellSize, GameObject cellHolder)
    {
        TreeSystemTerrain systemTerrain = new TreeSystemTerrain();

        // Set system terrain data
        systemTerrain.m_ManagedTerrain       = terrain;
        systemTerrain.m_ManagedTerrainBounds = terrain.GetComponent <TerrainCollider>().bounds;
        systemTerrain.m_CellCount            = TerrainUtils.GetCellCount(terrain, cellSize);
        systemTerrain.m_CellSize             = cellSize;

        int cellCount;

        BoxCollider[,] collidersBox;
        SphereCollider[,] collidersSphere;

        // Gridify terrain
        TerrainUtils.Gridify(terrain, cellSize, out cellCount, out collidersBox, out collidersSphere, cellHolder, null);

        // Temporary structured data
        TreeSystemStructuredTrees[,] str           = new TreeSystemStructuredTrees[cellCount, cellCount];
        List <TreeSystemStoredInstance>[,] strInst = new List <TreeSystemStoredInstance> [cellCount, cellCount];
        List <TreeSystemStructuredTrees> list = new List <TreeSystemStructuredTrees>();

        // Insantiate the required data
        for (int r = 0; r < cellCount; r++)
        {
            for (int c = 0; c < cellCount; c++)
            {
                TreeSystemStructuredTrees s = new TreeSystemStructuredTrees();

                // Set the bounds, all in world space
                s.m_BoundsBox    = collidersBox[r, c].bounds;
                s.m_BoundsSphere = new TreeSystemBoundingSphere(s.m_BoundsBox.center, collidersSphere[r, c].radius);

                // Set it's new position
                s.m_Position = new RowCol(r, c);

                str[r, c]     = s;
                strInst[r, c] = new List <TreeSystemStoredInstance>();

                list.Add(s);
            }
        }

        TreeInstance[]  terrainTreeInstances = terrain.terrainData.treeInstances;
        TreePrototype[] terrainTreeProto     = terrain.terrainData.treePrototypes;

        Vector3 sizes = terrain.terrainData.size;

        for (int i = 0; i < terrainTreeInstances.Length; i++)
        {
            GameObject proto = terrainTreeProto[terrainTreeInstances[i].prototypeIndex].prefab;

            if (ShouldUsePrefab(proto) < 0)
            {
                continue;
            }

            // Get bounds for that mesh
            Bounds b = proto.transform.Find(proto.name + "_LOD0").gameObject.GetComponent <MeshFilter>().sharedMesh.bounds;

            // Calculate this from normalized terrain space to terrain's local space so that our row/col info are correct.
            // Do the same when testing for cell row/col in which the player is, transform to terrain local space
            Vector3 pos = TerrainUtils.TerrainToTerrainPos(terrainTreeInstances[i].position, terrain);
            int     row = Mathf.Clamp(Mathf.FloorToInt(pos.x / sizes.x * cellCount), 0, cellCount - 1);
            int     col = Mathf.Clamp(Mathf.FloorToInt(pos.z / sizes.z * cellCount), 0, cellCount - 1);

            pos = TerrainUtils.TerrainToWorldPos(terrainTreeInstances[i].position, terrain);
            Vector3 scale = new Vector3(terrainTreeInstances[i].widthScale, terrainTreeInstances[i].heightScale, terrainTreeInstances[i].widthScale);
            float   rot   = terrainTreeInstances[i].rotation;
            int     hash  = TUtils.GetStableHashCode(proto.name);

            Matrix4x4 mtx = Matrix4x4.TRS(pos, Quaternion.Euler(0, rot * Mathf.Rad2Deg, 0), scale);

            TreeSystemStoredInstance inst = new TreeSystemStoredInstance();

            inst.m_TreeHash      = hash;
            inst.m_PositionMtx   = mtx;
            inst.m_WorldPosition = pos;
            inst.m_WorldScale    = scale;
            inst.m_WorldRotation = rot;
            inst.m_WorldBounds   = TUtils.LocalToWorld(ref b, ref mtx);

            strInst[row, col].Add(inst);
        }

        // Generate the mesh that contain all the billboards
        for (int r = 0; r < cellCount; r++)
        {
            for (int c = 0; c < cellCount; c++)
            {
                if (strInst[r, c].Count <= 0)
                {
                    continue;
                }

                // Sort based on the tree hash so that we don't have to do many dictionary look-ups
                strInst[r, c].Sort((x, y) => x.m_TreeHash.CompareTo(y.m_TreeHash));

                // Set the new instances
                str[r, c].m_Instances = strInst[r, c].ToArray();

                // Build the meshes for each cell based on tree type
                List <TreeSystemStoredInstance> singleType = new List <TreeSystemStoredInstance>();
                int lastHash = strInst[r, c][0].m_TreeHash;

                foreach (TreeSystemStoredInstance inst in strInst[r, c])
                {
                    // If we have a new hash, consume all the existing instances
                    if (inst.m_TreeHash != lastHash)
                    {
                        TreeSystemPrototypeData data = GetPrototypeWithHash(lastHash);
                        BuildTreeTypeCellMesh(cellHolder, str[r, c], singleType, data);
                        singleType.Clear();

                        // Update the hash
                        lastHash = inst.m_TreeHash;
                    }

                    // Add them to a list and when the hash changes begin the next generation
                    singleType.Add(inst);
                }

                if (singleType.Count > 0)
                {
                    TreeSystemPrototypeData data = GetPrototypeWithHash(singleType[0].m_TreeHash);
                    BuildTreeTypeCellMesh(cellHolder, str[r, c], singleType, data);
                    singleType.Clear();
                }
            }
        }

        // Set the cells that contain the trees to the system terrain
        systemTerrain.m_Cells = list.ToArray();

        // Return it
        return(systemTerrain);
    }
Exemple #15
0
    public void ExtractXMLTreePrototypeData()
    {
        TreeSystemPrototypeData[] data = m_ManagedPrototypes;

        for (int i = 0; i < data.Length; i++)
        {
            TreeSystemPrototypeData d = data[i];

            if (d.m_TreePrototype == null)
            {
                Log.e("Nothing set for data at index: " + i);
                continue;
            }

            // Get the protorype's billboard asset
            BillboardRenderer bill      = d.m_TreePrototype.GetComponentInChildren <BillboardRenderer>();
            BillboardAsset    billAsset = bill.billboard;

            // Set sizes
            d.m_Size = new Vector3(billAsset.width, billAsset.height, billAsset.bottom);

            // Parse the XML
            if (!d.m_TreeBillboardData && m_UseXMLData)
            {
                Debug.LogError("We are using XML data and we don't have any custom XML data! Switch 'UseXMLData' off!");
                continue;
            }

            if (m_UseXMLData)
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(d.m_TreeBillboardData.text);

                if (doc["SpeedTreeRaw"]["Billboards"]["Vertical"] == null || doc["SpeedTreeRaw"]["Billboards"]["Horizontal"] == null)
                {
                    Debug.Log("Missing tree XML data for: " + d.m_TreePrototype.name);
                }
                else
                {
                    // Should be multiple of 4
                    d.m_VertBillboardUVs = ExtractBillboards(doc["SpeedTreeRaw"]["Billboards"]["Vertical"], true).ToArray();
                    d.m_HorzBillboardUVs = ExtractBillboards(doc["SpeedTreeRaw"]["Billboards"]["Horizontal"], false).ToArray();
                }
            }
            else
            {
                // TODO: support for non-XML
                Vector4[] uvs = billAsset.GetImageTexCoords();

                // Ussualy 16
                d.m_VertBillboardUVs = new Vector2[uvs.Length * 4];
                // Just set the first UV's just to have something
                d.m_HorzBillboardUVs = new Vector2[4];

                for (int uvIdx = 0, billUv = 0; uvIdx < uvs.Length; uvIdx++, billUv += 4)
                {
                    Vector4 extract = uvs[uvIdx];

                    if (uvIdx == 0)
                    {
                        d.m_HorzBillboardUVs[0] = new Vector2(extract.x, extract.y);
                        d.m_HorzBillboardUVs[1] = new Vector2(extract.x, extract.y) + new Vector2(0, Mathf.Abs(extract.w));
                        d.m_HorzBillboardUVs[2] = new Vector2(extract.x, extract.y) + new Vector2(-extract.z, Mathf.Abs(extract.w));
                        d.m_HorzBillboardUVs[3] = new Vector2(extract.x, extract.y) + new Vector2(-extract.z, 0);
                    }

                    // We are rotated
                    if (extract.w < 0)
                    {
                        d.m_VertBillboardUVs[billUv + 0] = new Vector2(extract.x, extract.y);
                        d.m_VertBillboardUVs[billUv + 1] = new Vector2(extract.x, extract.y) + new Vector2(0, Mathf.Abs(extract.w));
                        d.m_VertBillboardUVs[billUv + 2] = new Vector2(extract.x, extract.y) + new Vector2(-extract.z, Mathf.Abs(extract.w));
                        d.m_VertBillboardUVs[billUv + 3] = new Vector2(extract.x, extract.y) + new Vector2(-extract.z, 0);
                    }
                    else
                    {
                        d.m_VertBillboardUVs[billUv + 0] = new Vector2(extract.x, extract.y);
                        d.m_VertBillboardUVs[billUv + 1] = new Vector2(extract.x, extract.y) + new Vector2(extract.z, 0);
                        d.m_VertBillboardUVs[billUv + 2] = new Vector2(extract.x, extract.y) + new Vector2(extract.z, extract.w);
                        d.m_VertBillboardUVs[billUv + 3] = new Vector2(extract.x, extract.y) + new Vector2(0, extract.w);
                    }
                }
            }

            Vector4 size = d.m_Size;
            size.w = 1;

            // Create the material with the texture references
            Material billboardMaterialBatch = new Material(m_BillboardShaderBatch);
            billboardMaterialBatch.SetTexture("_MainTex", bill.billboard.material.GetTexture("_MainTex"));
            billboardMaterialBatch.SetTexture("_BumpMap", bill.billboard.material.GetTexture("_BumpMap"));
            billboardMaterialBatch.SetVector("_Size", size);
            Material billboardMaterialMaster = new Material(m_BillboardShaderMaster);
            billboardMaterialMaster.SetTexture("_MainTex", bill.billboard.material.GetTexture("_MainTex"));
            billboardMaterialMaster.SetTexture("_BumpMap", bill.billboard.material.GetTexture("_BumpMap"));
            billboardMaterialMaster.SetVector("_Size", size);

            // Replace, don't delete
            // AssetDatabase.DeleteAsset(m_DataStorePath + "/" + d.m_TreePrototype.name + "_Mat.mat");
            AssetDatabase.CreateAsset(billboardMaterialBatch,
                                      m_DataStorePath + "/" + d.m_TreePrototype.name + "_Bill_Batch_Mat.mat");
            AssetDatabase.CreateAsset(billboardMaterialMaster,
                                      m_DataStorePath + "/" + d.m_TreePrototype.name + "_Bill_Master_Mat.mat");

            // Set the material
            d.m_BillboardBatchMaterial  = billboardMaterialBatch;
            d.m_BillboardMasterMaterial = billboardMaterialMaster;

            // Set billboard data
            TreeSystem.SetMaterialBillProps(d, d.m_BillboardBatchMaterial);
            TreeSystem.SetMaterialBillProps(d, d.m_BillboardMasterMaterial);
        }

        AssetDatabase.Refresh();
    }
Exemple #16
0
    private void IssueDrawTrees(TreeSystemPrototypeData data, TreeSystemStructuredTrees trees, int[] indices, float[] dist, int count, bool shadowsOnly)
    {
        for (int i = 0; i < MAX_LOD_COUNT; i++)
        {
            m_MtxLODTempCount[i] = 0;
        }

        TreeSystemLODData[] lodData = data.m_LODData;

        TreeSystemStoredInstance[] treeInstances   = trees.m_Instances;
        TreeSystemLODInstance[]    lodInstanceData = trees.m_InstanceData;

        int maxLod3D = data.m_MaxLod3DIndex;

        // Process LOD so that we know what to batch
        for (int i = 0; i < count; i++)
        {
            ProcessLOD(ref lodData, ref maxLod3D, ref lodInstanceData[indices[i]], ref dist[i]);
        }

        if (m_Settings.m_UseInstancing)
        {
            // Build the batched stuff. We want to batch the same LOD and draw them using instancing
            for (int i = 0; i < count; i++)
            {
                int idx             = indices[i];
                int currentLODLevel = lodInstanceData[idx].m_LODLevel;

                // Collect that LOD level
                if (currentLODLevel == 0)
                {
                    m_MtxLODTemp_0[m_MtxLODTempCount[currentLODLevel]]        = treeInstances[idx].m_PositionMtx;
                    m_MtxLODTranzDetail_0[m_MtxLODTempCount[currentLODLevel]] = lodInstanceData[idx].m_LODTransition;
                    m_MtxLODTranzFull_0[m_MtxLODTempCount[currentLODLevel]]   = lodInstanceData[idx].m_LODFullFade;
                }
                else if (currentLODLevel == 1)
                {
                    m_MtxLODTemp_1[m_MtxLODTempCount[currentLODLevel]]        = treeInstances[idx].m_PositionMtx;
                    m_MtxLODTranzDetail_1[m_MtxLODTempCount[currentLODLevel]] = lodInstanceData[idx].m_LODTransition;
                    m_MtxLODTranzFull_1[m_MtxLODTempCount[currentLODLevel]]   = lodInstanceData[idx].m_LODFullFade;
                }
                else if (currentLODLevel == 2)
                {
                    m_MtxLODTemp_2[m_MtxLODTempCount[currentLODLevel]]        = treeInstances[idx].m_PositionMtx;
                    m_MtxLODTranzDetail_2[m_MtxLODTempCount[currentLODLevel]] = lodInstanceData[idx].m_LODTransition;
                    m_MtxLODTranzFull_2[m_MtxLODTempCount[currentLODLevel]]   = lodInstanceData[idx].m_LODFullFade;
                }
                else if (currentLODLevel == 3)
                {
                    m_MtxLODTemp_3[m_MtxLODTempCount[currentLODLevel]]        = treeInstances[idx].m_PositionMtx;
                    m_MtxLODTranzDetail_3[m_MtxLODTempCount[currentLODLevel]] = lodInstanceData[idx].m_LODTransition;
                    m_MtxLODTranzFull_3[m_MtxLODTempCount[currentLODLevel]]   = lodInstanceData[idx].m_LODFullFade;
                }
                else if (currentLODLevel == 4)
                {
                    m_MtxLODTemp_4[m_MtxLODTempCount[currentLODLevel]]        = treeInstances[idx].m_PositionMtx;
                    m_MtxLODTranzDetail_4[m_MtxLODTempCount[currentLODLevel]] = lodInstanceData[idx].m_LODTransition;
                    m_MtxLODTranzFull_4[m_MtxLODTempCount[currentLODLevel]]   = lodInstanceData[idx].m_LODFullFade;
                }

                m_MtxLODTempCount[currentLODLevel]++;

                // We don't instantiate the trees that are in a transition since they should be an exception
                if (currentLODLevel == maxLod3D && lodInstanceData[idx].m_LODFullFade < 1)
                {
                    DrawBillboardLOD(ref lodData[currentLODLevel + 1], ref lodInstanceData[idx], ref treeInstances[idx]);
                }
            }

            // Now that the data is built, issue it
            for (int i = 0; i <= maxLod3D; i++)
            {
                if (i == 0)
                {
                    Draw3DLODInstanced(ref lodData[i], ref m_MtxLODTemp_0, ref m_MtxLODTranzDetail_0, ref m_MtxLODTranzFull_0, ref m_MtxLODTempCount[i], ref shadowsOnly);
                }
                else if (i == 1)
                {
                    Draw3DLODInstanced(ref lodData[i], ref m_MtxLODTemp_1, ref m_MtxLODTranzDetail_1, ref m_MtxLODTranzFull_1, ref m_MtxLODTempCount[i], ref shadowsOnly);
                }
                else if (i == 2)
                {
                    Draw3DLODInstanced(ref lodData[i], ref m_MtxLODTemp_2, ref m_MtxLODTranzDetail_2, ref m_MtxLODTranzFull_2, ref m_MtxLODTempCount[i], ref shadowsOnly);
                }
                else if (i == 3)
                {
                    Draw3DLODInstanced(ref lodData[i], ref m_MtxLODTemp_3, ref m_MtxLODTranzDetail_3, ref m_MtxLODTranzFull_3, ref m_MtxLODTempCount[i], ref shadowsOnly);
                }
                else if (i == 4)
                {
                    Draw3DLODInstanced(ref lodData[i], ref m_MtxLODTemp_4, ref m_MtxLODTranzDetail_4, ref m_MtxLODTranzFull_4, ref m_MtxLODTempCount[i], ref shadowsOnly);
                }
            }
        }
        else
        {
            // Draw the processed lod
            for (int i = 0; i < count; i++)
            {
                int idx = indices[i];
                DrawProcessedLOD(ref lodData, ref maxLod3D, ref lodInstanceData[idx], ref treeInstances[idx], ref shadowsOnly);
            }
        }
    }
Exemple #17
0
    // TODO: see from the list which trees fit in here
    private TreeSystemTerrain ProcessTerrain(Terrain terrain, int cellSize, GameObject cellHolder, List <GameObject> extraTrees)
    {
        TreeSystemTerrain systemTerrain = new TreeSystemTerrain();

        // Set system terrain data
        systemTerrain.m_ManagedTerrain             = terrain;
        systemTerrain.m_ManagedTerrainBounds       = terrain.GetComponent <TerrainCollider>().bounds;
        systemTerrain.m_ManagedTerrainLocalToWorld = terrain.transform.localToWorldMatrix;
        systemTerrain.m_ManagedTerrainWorldToLocal = terrain.transform.worldToLocalMatrix;
        systemTerrain.m_ManagedTerrainSizes        = terrain.terrainData.size;

        systemTerrain.m_CellCount = TerrainUtils.GetCellCount(terrain, cellSize);
        systemTerrain.m_CellSize  = cellSize;

        int cellCount;

        BoxCollider[,] collidersBox;
        SphereCollider[,] collidersSphere;

        // Gridify terrain
        TerrainUtils.Gridify(terrain, cellSize, out cellCount, out collidersBox, out collidersSphere, cellHolder, null);

        // Temporary structured data
        TreeSystemStructuredTrees[,] str           = new TreeSystemStructuredTrees[cellCount, cellCount];
        List <TreeSystemStoredInstance>[,] strInst = new List <TreeSystemStoredInstance> [cellCount, cellCount];
        List <TreeSystemStructuredTrees> list = new List <TreeSystemStructuredTrees>();

        // Insantiate the required data
        for (int r = 0; r < cellCount; r++)
        {
            for (int c = 0; c < cellCount; c++)
            {
                TreeSystemStructuredTrees s = new TreeSystemStructuredTrees();

                // Set the bounds, all in world space
                s.m_BoundsBox    = collidersBox[r, c].bounds;
                s.m_BoundsSphere = new TreeSystemBoundingSphere(s.m_BoundsBox.center, collidersSphere[r, c].radius);

                // Set it's new position
                s.m_Position = new RowCol(r, c);

                str[r, c]     = s;
                strInst[r, c] = new List <TreeSystemStoredInstance>();

                list.Add(s);
            }
        }

        // Delete cells since they might cause physics problems
        if (m_DeleteCellsAfterGridify)
        {
            for (int i = 0; i < cellCount; i++)
            {
                for (int j = 0; j < cellCount; j++)
                {
                    DestroyImmediate(collidersBox[i, j].gameObject);
                }
            }
        }

        int treeInstancesCount = 0, treeExtraCount = 0;

        TreeInstance[]  terrainTreeInstances = terrain.terrainData.treeInstances;
        TreePrototype[] terrainTreeProto     = terrain.terrainData.treePrototypes;

        Vector3 sizes = terrain.terrainData.size;

        for (int i = 0; i < terrainTreeInstances.Length; i++)
        {
            GameObject proto = terrainTreeProto[terrainTreeInstances[i].prototypeIndex].prefab;

            if (ShouldUsePrefab(proto) < 0)
            {
                continue;
            }

            treeInstancesCount++;

            // Get bounds for that mesh
            Bounds b = proto.transform.Find(proto.name + "_LOD0").gameObject.GetComponent <MeshFilter>().sharedMesh.bounds;

            // Calculate this from normalized terrain space to terrain's local space so that our row/col info are correct.
            // Do the same when testing for cell row/col in which the player is, transform to terrain local space
            Vector3 pos = TerrainUtils.TerrainToTerrainPos(terrainTreeInstances[i].position, terrain);
            int     row = Mathf.Clamp(Mathf.FloorToInt(pos.x / sizes.x * cellCount), 0, cellCount - 1);
            int     col = Mathf.Clamp(Mathf.FloorToInt(pos.z / sizes.z * cellCount), 0, cellCount - 1);

            pos = TerrainUtils.TerrainToWorldPos(terrainTreeInstances[i].position, terrain);
            Vector3 scale = new Vector3(terrainTreeInstances[i].widthScale, terrainTreeInstances[i].heightScale, terrainTreeInstances[i].widthScale);
            float   rot   = terrainTreeInstances[i].rotation;
            int     hash  = TUtils.GetStableHashCode(proto.name);

            Matrix4x4 mtx = Matrix4x4.TRS(pos, Quaternion.Euler(0, rot * Mathf.Rad2Deg, 0), scale);

            TreeSystemStoredInstance inst = new TreeSystemStoredInstance();

            inst.m_TreeHash      = hash;
            inst.m_PositionMtx   = mtx;
            inst.m_WorldPosition = pos;
            inst.m_WorldScale    = scale;
            inst.m_WorldRotation = rot;
            inst.m_WorldBounds   = TUtils.LocalToWorld(ref b, ref mtx);

            strInst[row, col].Add(inst);
        }

        List <GameObject> containedTrees = new List <GameObject>();

        // Change if we're going to use something diff than 50 for max extent
        Bounds terrainExtendedBounds = systemTerrain.m_ManagedTerrainBounds;

        terrainExtendedBounds.Expand(new Vector3(0, 50, 0));

        // Same as a instance with minor diferences
        for (int i = 0; i < extraTrees.Count; i++)
        {
            GameObject treeInstance = extraTrees[i];

            // If the terrain contains the stuff
            if (terrainExtendedBounds.Contains(treeInstance.transform.position) == false)
            {
                continue;
            }

            treeExtraCount++;

            // Add the tree to the list of trees for removal
            containedTrees.Add(treeInstance);

            // Owner
            GameObject proto = GetPrefabOwner(treeInstance);

            // Get bounds for that mesh
            Bounds b = proto.transform.Find(proto.name + "_LOD0").gameObject.GetComponent <MeshFilter>().sharedMesh.bounds;

            // Calculate this from normalized terrain space to terrain's local space so that our row/col info are correct.
            // Do the same when testing for cell row/col in which the player is, transform to terrain local space
            Vector3 pos = TerrainUtils.TerrainToTerrainPos(TerrainUtils.WorldPosToTerrain(treeInstance.transform.position, terrain), terrain);

            int row = Mathf.Clamp(Mathf.FloorToInt(pos.x / sizes.x * cellCount), 0, cellCount - 1);
            int col = Mathf.Clamp(Mathf.FloorToInt(pos.z / sizes.z * cellCount), 0, cellCount - 1);

            pos = treeInstance.transform.position;
            Vector3 scale = treeInstance.transform.localScale;
            float   rot   = treeInstance.transform.rotation.eulerAngles.y * Mathf.Deg2Rad;

            // Set the hash
            int hash = TUtils.GetStableHashCode(proto.name);

            // Set the mtx
            Matrix4x4 mtx = Matrix4x4.TRS(pos, Quaternion.Euler(0, rot * Mathf.Rad2Deg, 0), scale);

            TreeSystemStoredInstance inst = new TreeSystemStoredInstance();

            inst.m_TreeHash      = hash;
            inst.m_PositionMtx   = mtx;
            inst.m_WorldPosition = pos;
            inst.m_WorldScale    = scale;
            inst.m_WorldRotation = rot;
            inst.m_WorldBounds   = TUtils.LocalToWorld(ref b, ref mtx);

            strInst[row, col].Add(inst);
        }

        // Remove the items from the extra trees
        foreach (GameObject tree in containedTrees)
        {
            extraTrees.Remove(tree);
        }

        // Generate the mesh that contain all the billboards
        for (int r = 0; r < cellCount; r++)
        {
            for (int c = 0; c < cellCount; c++)
            {
                if (strInst[r, c].Count <= 0)
                {
                    continue;
                }

                // Sort based on the tree hash so that we don't have to do many dictionary look-ups
                strInst[r, c].Sort((x, y) => x.m_TreeHash.CompareTo(y.m_TreeHash));

                // Set the new instances
                str[r, c].m_Instances = strInst[r, c].ToArray();

                // Build the meshes for each cell based on tree type
                List <TreeSystemStoredInstance> singleType = new List <TreeSystemStoredInstance>();
                int lastHash = strInst[r, c][0].m_TreeHash;

                foreach (TreeSystemStoredInstance inst in strInst[r, c])
                {
                    // If we have a new hash, consume all the existing instances
                    if (inst.m_TreeHash != lastHash)
                    {
                        TreeSystemPrototypeData data = GetPrototypeWithHash(lastHash);

                        if (ShouldBuildBillboardBatch(data.m_TreePrototype))
                        {
                            BuildTreeTypeCellMesh(cellHolder, str[r, c], singleType, data);
                        }

                        singleType.Clear();

                        // Update the hash
                        lastHash = inst.m_TreeHash;
                    }

                    // Add them to a list and when the hash changes begin the next generation
                    singleType.Add(inst);
                }

                if (singleType.Count > 0)
                {
                    TreeSystemPrototypeData data = GetPrototypeWithHash(singleType[0].m_TreeHash);

                    if (ShouldBuildBillboardBatch(data.m_TreePrototype))
                    {
                        BuildTreeTypeCellMesh(cellHolder, str[r, c], singleType, data);
                    }

                    singleType.Clear();
                }
            }
        }

        // Set the cells that contain the trees to the system terrain
        systemTerrain.m_Cells = list.ToArray();

        // Print extraction data
        Debug.Log("Extracted for terrain: " + terrain.name + " instance trees: " + treeInstancesCount + " extra trees: " + treeExtraCount);

        // Return it
        return(systemTerrain);
    }
Exemple #18
0
    public void GenerateTreePrototypeData()
    {
        if (TerrainUtils.TreeHashCheck(m_MainManagedTerrain))
        {
            Debug.LogError("Tree name hash collision, fix!");
            return;
        }

        GameObject[] proto = m_TreeToExtractPrefabs;
        List <TreeSystemPrototypeData> managed = new List <TreeSystemPrototypeData>();

        for (int i = 0; i < proto.Length; i++)
        {
            GameObject prefab = proto[i];

            if (PrefabUtility.GetPrefabType(prefab) != PrefabType.ModelPrefab ||
                prefab.GetComponent <LODGroup>() == null ||
                prefab.GetComponentInChildren <BillboardRenderer>() == null)
            {
                Debug.LogError("Invalid prefab: " + prefab.name + ". Make sure that it is a SpeedTree, that it contains a 'LODGroup' and that it has a 'BillboardRenderer' component.");
                continue;
            }

            TreeSystemPrototypeData data = new TreeSystemPrototypeData();
            data.m_TreePrototype = prefab;
            // Use hash here instead of the old index
            data.m_TreePrototypeHash = TUtils.GetStableHashCode(prefab.name);

            if (m_UseXMLData)
            {
                TextAsset textData = AssetDatabase.LoadAssetAtPath <TextAsset>(m_TreeXMLStorePath + "/" + prefab.name + ".xml");

                if (textData != null)
                {
                    data.m_TreeBillboardData = textData;
                }
                else
                {
                    Debug.LogError("Could not find XML data for: " + data.m_TreePrototype.name);
                }
            }

            // Instantiate LOD data that is going to be populated at runtime
            LOD[] lods = prefab.GetComponent <LODGroup>().GetLODs();
            TreeSystemLODData[] lodData = new TreeSystemLODData[lods.Length];
            // Generate some partial LOD data that doesn't have to be calculated at runtime
            data.m_LODData = lodData;

            for (int lod = 0; lod < lodData.Length; lod++)
            {
                TreeSystemLODData d = new TreeSystemLODData();
                lodData[lod] = d;
            }

            data.m_MaxLodIndex   = lodData.Length - 1;
            data.m_MaxLod3DIndex = lodData.Length - 2;

            managed.Add(data);
        }

        m_ManagedPrototypes = managed.ToArray();

        // Try and set the prototypes to our tree system
        TreeSystem t = FindObjectOfType <TreeSystem>();

        if (t)
        {
            t.m_ManagedPrototypes = m_ManagedPrototypes;
        }
    }