示例#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;
        }
    }
示例#2
0
    private static void testArray <T>(Action <T[]> write, Func <T[]> read, T[] input, LengthPrefix lengthPrefix = LengthPrefix.Default)
    {
        string typ = typeof(T).Name;

        TUtils.Test("read/write " + typ + "[] (" + TUtils.IListToString(input) + ")", () =>
        {
            bw.Position = 0;
            write(input);
            bw.Position = 0;
            T[] result  = read();
            if (input.Length != result.Length)
            {
                TUtils.WriteFail($"FAIL length not equal{input.Length}!={result.Length}");
                return(TestResult.Failure);
            }
            if (TUtils.IsIListEqual(input, result))
            {
                TUtils.WriteSucces($"OK");
                return(TestResult.Success);
            }
            else
            {
                TUtils.WriteFail($"FAIL array({TUtils.IListToString(result)})");
                return(TestResult.Failure);
            }
        });
    }
示例#3
0
    private static void testGArray <T>(T[] input, LengthPrefix lengthPrefix = LengthPrefix.Int32) where T : unmanaged
    {
        string typ = typeof(T).Name;

        TUtils.Test($"read/write {typ}[].length:{lengthPrefix} ({TUtils.IListToString(input)})", () =>
        {
            bw.Position = 0;
            bw.WriteArray(input, lengthPrefix);
            bw.Position = 0;
            T[] result  = br.ReadArray <T>(lengthPrefix);
            if (input.Length != result.Length)
            {
                TUtils.WriteFail($"FAIL length not equal{input.Length}!={result.Length}");
                return(TestResult.Failure);
            }
            if (TUtils.IsIListEqual(input, result))
            {
                TUtils.WriteSucces($"OK");
                return(TestResult.Success);
            }
            else
            {
                TUtils.WriteFail($"FAIL array({TUtils.IListToString(result)})");
                return(TestResult.Failure);
            }
        });
    }
示例#4
0
    private static void testLists()
    {
        int size = 8;

        Random rnd = new Random(1);

        byte[] data0 = new byte[size];
        for (int i = 0; i < size; i++)
        {
            data0[i] = (byte)(rnd.NextDouble() * 255f);
        }

        TUtils.Test("Read to new List", () =>
        {
            var bw = new BinaryViewWriter();
            bw.WriteIList(data0);
            bw.Dispose();
            var file = bw.ToArray();

            var br   = new BinaryViewReader(file);
            var list = new List <byte>();
            br.ReadToIList(list);
            br.Dispose();

            if (!TUtils.IsIListEqual(data0, list))
            {
                TUtils.WriteFail($"FAIL data: {TUtils.IListToString(list)}, expected: {TUtils.IListToString(data0)}");
                return(TestResult.Failure);
            }

            TUtils.WriteSucces($"OK");
            return(TestResult.Success);
        });

        TUtils.Test("Read no Prefix", () =>
        {
            var bw = new BinaryViewWriter();
            bw.WriteIList(data0, LengthPrefix.None);
            bw.Dispose();
            var file = bw.ToArray();

            var br   = new BinaryViewReader(file);
            var list = new List <byte>();
            br.ReadToIList(list, 0, size);
            br.Dispose();

            if (!TUtils.IsIListEqual(data0, list))
            {
                TUtils.WriteFail($"FAIL data: {TUtils.IListToString(list)}, expected: {TUtils.IListToString(data0)}");
                return(TestResult.Failure);
            }

            TUtils.WriteSucces($"OK");
            return(TestResult.Success);
        });
    }
    /**
     * Checks whether there is a collision within the tree's hashes that has to be remedied
     *
     * @return False in case there is no collision and true if there is a collision
     */
    public static bool TreeHashCheck(string[] prefabNames)
    {
        HashSet <int> set = new HashSet <int>();

        foreach (string prefab in prefabNames)
        {
            if (set.Contains(TUtils.GetStableHashCode(prefab)))
            {
                return(true);
            }

            set.Add(TUtils.GetStableHashCode(prefab));
        }

        return(false);
    }
示例#6
0
    /**
     * Checks whether there is a collision within the tree's hashes that has to be remedied
     *
     * @return False in case there is no collision and true if there is a collision
     */
    public static bool TreeHashCheck(Terrain mainTerrain)
    {
        HashSet <int> set = new HashSet <int>();

        TreePrototype[] p = mainTerrain.terrainData.treePrototypes;

        for (int i = 0; i < p.Length; i++)
        {
            if (set.Contains(TUtils.GetStableHashCode(p[i].prefab.name)))
            {
                return(true);
            }

            set.Add(TUtils.GetStableHashCode(p[i].prefab.name));
        }

        return(false);
    }
示例#7
0
    private void fillBeziers()
    {
        beziers = new Bezier <T> [controlPoints.Length];

        Bezier <T> first = new Bezier <T>();

        first.b0 = controlPoints[0];
        first.b1 = TUtils <T> .Additate(TUtils <T> .Scalar((TUtils <T> .Subtract(controlPoints[1], controlPoints[0])), (1f / 3f)), controlPoints[0]);

        first.b2 = TUtils <T> .Additate(TUtils <T> .Scalar((TUtils <T> .Subtract(controlPoints[1], controlPoints[0])), (2f / 3f)), controlPoints[0]);

        beziers[0] = first;

        Bezier <T> last = new Bezier <T>();

        last.b1 = TUtils <T> .Additate(TUtils <T> .Scalar((TUtils <T> .Subtract(controlPoints[0], controlPoints[controlPoints.Length - 1])), (1f / 3f)), controlPoints[controlPoints.Length - 1]);

        last.b2 = TUtils <T> .Additate(TUtils <T> .Scalar((TUtils <T> .Subtract(controlPoints[0], controlPoints[controlPoints.Length - 1])), (2f / 3f)), controlPoints[controlPoints.Length - 1]);

        last.b3 = controlPoints[0];
        beziers[beziers.Length - 1] = last;

        for (int i = 1; i < beziers.Length - 1; i++)
        {
            beziers[i]    = new Bezier <T>();
            beziers[i].b1 = TUtils <T> .Additate(TUtils <T> .Scalar((TUtils <T> .Subtract(controlPoints[i + 1], controlPoints[i])), (1f / 3f)), controlPoints[i]);

            beziers[i].b2 = TUtils <T> .Additate(TUtils <T> .Scalar((TUtils <T> .Subtract(controlPoints[i + 1], controlPoints[i])), (2f / 3f)), controlPoints[i]);
        }

        beziers[beziers.Length - 1].b3 = TUtils <T> .Additate(TUtils <T> .Scalar((TUtils <T> .Subtract(beziers[0].b1, beziers[beziers.Length - 1].b2)), 0.5f), beziers[beziers.Length - 1].b2);

        beziers[0].b0 = beziers[beziers.Length - 1].b3;

        for (int i = 0; i < beziers.Length - 1; i++)
        {
            beziers[i].b3 = TUtils <T> .Additate(TUtils <T> .Scalar((TUtils <T> .Subtract(beziers[i + 1].b1, beziers[i].b2)), 0.5f), beziers[i].b2);

            beziers[i + 1].b0 = beziers[i].b3;
        }
    }
示例#8
0
    private static void testString(string str, LengthPrefix lengthPrefix = LengthPrefix.Default, CharSizePrefix charSizePrefix = CharSizePrefix.Default)
    {
        TUtils.Test($"read/write string[{charSizePrefix}].length:{lengthPrefix} ({str})", () =>
        {
            bw.Position = 0;
            bw.WriteString(str, lengthPrefix, charSizePrefix);
            bw.Position   = 0;
            string result = br.ReadString(lengthPrefix, charSizePrefix);
            if (result.Equals(str))
            {
                TUtils.WriteSucces("OK");
                return(TestResult.Success);
            }

            else
            {
                TUtils.WriteFail($"FAIL \"{result}\"");
                return(TestResult.Failure);
            }
        });
    }
示例#9
0
    private static void testSTyp <T>(T input)
    {
        string typ = typeof(T).Name;

        TUtils.Test("read/write " + typ + " (" + input + ")", () =>
        {
            bw.Position = 0;
            bw.Serialize(input);
            var size    = bw.Position;
            bw.Position = 0;
            T result    = br.Deserialize <T>();
            if (result.Equals(input))
            {
                TUtils.WriteSucces($"OK {size}b");
                return(TestResult.Success);
            }
            else
            {
                TUtils.WriteFail($"{result}");
                return(TestResult.Failure);
            }
        });
    }
示例#10
0
    private static void testTyp <T>(Action <T> write, Func <T> read, T input)
    {
        string typ = typeof(T).Name;

        TUtils.Test("read/write " + typ + " (" + input + ")", () =>
        {
            bw.Position = 0;
            write(input);
            bw.Position = 0;
            T result    = read();
            if (result.Equals(input))
            {
                TUtils.WriteSucces("OK");
                return(TestResult.Success);
            }

            else
            {
                TUtils.WriteFail($"{result}");
                return(TestResult.Failure);
            }
        });
    }
示例#11
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);
    }
示例#12
0
    public static void Run()
    {
        stream = new MemoryStream();
        bw     = new BinaryViewWriter(stream);
        br     = new BinaryViewReader(stream);

        TUtils.WriteTitle("Run tests...\n");

        TUtils.WriteTitle("test types");
        testTyp(bw.WriteBoolean, br.ReadBoolean, false, true);
        testTyp(bw.WriteChar, br.ReadChar, char.MinValue, char.MaxValue);
        testTyp(bw.WriteByte, br.ReadByte, byte.MinValue, byte.MaxValue);
        testTyp(bw.WriteSByte, br.ReadSByte, sbyte.MinValue, sbyte.MaxValue);
        testTyp(bw.WriteUInt16, br.ReadUInt16, ushort.MinValue, ushort.MaxValue);
        testTyp(bw.WriteInt16, br.ReadInt16, short.MinValue, short.MaxValue);
        testTyp(bw.WriteUInt32, br.ReadUInt32, uint.MinValue, uint.MaxValue);
        testTyp(bw.WriteInt32, br.ReadInt32, int.MinValue, int.MaxValue);
        testTyp(bw.WriteUInt64, br.ReadUInt64, ulong.MinValue, ulong.MaxValue);
        testTyp(bw.WriteInt64, br.ReadInt64, long.MinValue, long.MaxValue);
        testTyp(bw.WriteSingle, br.ReadSingle, float.MinValue, float.MaxValue);
        testTyp(bw.WriteDouble, br.ReadDouble, double.MinValue, double.MaxValue);
        testTyp(bw.WriteDecimal, br.ReadDecimal, decimal.MinValue, decimal.MaxValue);

        TUtils.WriteTitle("test string");
        testString("TestString123", LengthPrefix.Default, CharSizePrefix.Default);
        testString("TestString123", LengthPrefix.Byte, CharSizePrefix.Byte);
        testString("TestString123", LengthPrefix.UInt32, CharSizePrefix.Char);
        testString("Ä'*Ü-.,><%§ÃoÜ╝ô○╝+");

        TUtils.WriteTitle("test unmanaged types");
        testGTyp(false, true);
        testGTyp(char.MinValue, char.MaxValue);
        testGTyp(byte.MinValue, byte.MaxValue);
        testGTyp(sbyte.MinValue, sbyte.MaxValue);
        testGTyp(ushort.MinValue, ushort.MaxValue);
        testGTyp(short.MinValue, short.MaxValue);
        testGTyp(uint.MinValue, uint.MaxValue);
        testGTyp(int.MinValue, int.MaxValue);
        testGTyp(ulong.MinValue, ulong.MaxValue);
        testGTyp(long.MinValue, long.MaxValue);
        testGTyp(float.MinValue, float.MaxValue);
        testGTyp(double.MinValue, double.MaxValue);
        testGTyp(decimal.MinValue, decimal.MaxValue);
        testGTyp(new TUtils.Struct()
        {
            A = 42, B = 3.6f
        });
        testGTyp(new DateTime(2020, 07, 20, 15, 54, 24));
        testGTyp(new Point(10, 42));
        testGTyp(new RectangleF(10, 42, 25.5f, 23));

        TUtils.WriteTitle("test serializable types");
        testSTyp(42);
        testSTyp("Hello World");
        testSTyp(new DateTime(2000, 10, 20));
        testSTyp(new DateTime(2020, 07, 20, 15, 54, 24));
        testSTyp(new Point(2000, 10));
        testSTyp(new RectangleF(10, 42, 25.5f, 23));

        TUtils.WriteTitle("test arrays");
        testGArray(new byte[] { 0, 2, 4, 6 });
        testGArray(new byte[] { 0, 2, 4, 6 }, LengthPrefix.Int64);
        testGArray(new int[] { 0, -2, 4, -6 });
        testGArray(new float[] { 0, -2.5f, 4.25f, -6.66f });
        testGArray(new TUtils.Struct[] { new TUtils.Struct()
                                         {
                                             A = 42, B = 3.6f
                                         }, new TUtils.Struct()
                                         {
                                             A = 36, B = 1.666f
                                         } });
        testArray(bw.WriteStringArray, br.ReadStringArray, new string[] { "ab", "cd", "ef", "gh" });

        TUtils.WriteTitle("test IList");
        testLists();

        TUtils.WriteTitle("test compresion");
        testCompression();

        TUtils.WriteTitle("test map");
        testMap(64, false);

        TUtils.WriteTitle("test map compressed");
        testMap(64, true);

        TUtils.WriteTitle("test speed");
        testSpeed();

        TUtils.WriteResults();
    }
示例#13
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;
        }
    }
示例#14
0
    private static void testMap(int size, bool compressed)
    {
        for (int it = 0; it < 6; it++)
        {
            byte[] mapLayer1 = new byte[size];
            byte[] mapLayer2 = new byte[size];
            byte[] mapLayer3 = new byte[size];
            Random rnd       = new Random(1);
            for (int i = 0; i < size; i++)
            {
                mapLayer1[i] = (byte)(rnd.NextDouble() * 255f);
            }
            rnd = new Random(2);
            for (int i = 0; i < size; i++)
            {
                mapLayer2[i] = (byte)(rnd.NextDouble() * 2f);
            }

            TUtils.Test($"save map {size}x{size}", () =>
            {
                using (var binaryView = new BinaryViewWriter("test.dat"))
                {
                    if (compressed)
                    {
                        binaryView.CompressAll();
                    }
                    binaryView.WriteString("map");
                    binaryView.WriteInt32(size);
                    binaryView.WriteSingle(0.45f);
                    binaryView.WriteArray(mapLayer1);
                    binaryView.WriteArray(mapLayer2);
                    binaryView.WriteArray(mapLayer3);
                }

                TUtils.WriteSucces($"OK {new FileInfo("test.dat").Length}b");
                return(TestResult.Success);
            });
            TUtils.Test($"load map {size}x{size}", () =>
            {
                bool result = true;
                using (var binaryView = new BinaryViewReader("test.dat"))
                {
                    if (compressed)
                    {
                        binaryView.DecompressAll();
                    }
                    result &= binaryView.ReadString() == "map";
                    result &= binaryView.ReadInt32() == size;
                    result &= binaryView.ReadSingle() == 0.45f;
                    result &= TUtils.IsIListEqual(mapLayer1, binaryView.ReadArray <byte>());
                    result &= TUtils.IsIListEqual(mapLayer2, binaryView.ReadArray <byte>());
                    result &= TUtils.IsIListEqual(mapLayer3, binaryView.ReadArray <byte>());
                }
                if (result)
                {
                    TUtils.WriteSucces("OK");
                    return(TestResult.Success);
                }
                else
                {
                    TUtils.WriteFail("FAIL");
                    return(TestResult.Failure);
                }
            });
            size *= 2;
        }
    }
示例#15
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;
        }
    }
示例#16
0
    private List <Vector2> ExtractBillboards(XmlElement bills, bool vertical)
    {
        XmlElement verticalBills = bills;

        List <Vector2> allUv = new List <Vector2>();

        if (vertical)
        {
            foreach (XmlElement node in verticalBills.ChildNodes)
            {
                XmlElement elem = node;

                bool rotated = bool.Parse(elem.GetAttribute("Rotated"));

                Log.i("Rotated: " + rotated);

                string[] u = node["TexcoordU"].InnerText.Trim().Split(' ');
                string[] v = node["TexcoordV"].InnerText.Trim().Split(' ');

                Log.i("UV data: " + TUtils.ToString(u) + " " + TUtils.ToString(v));

                if (v.Length != u.Length || v.Length != 4)
                {
                    Debug.LogError("Something bad went parsing: " + u + " " + v);
                    continue;
                }

                List <Vector2> uv = new List <Vector2>();

                for (int j = 0; j < u.Length; j++)
                {
                    uv.Add(new Vector2(float.Parse(u[j]), float.Parse(v[j])));
                }

                Log.i("Extracted uv: " + TUtils.ToString(uv));
                allUv.AddRange(uv);
            }
        }
        else
        {
            string[] u = bills["TexcoordU"].InnerText.Trim().Split(' ');
            string[] v = bills["TexcoordV"].InnerText.Trim().Split(' ');

            Log.i("UV data: " + TUtils.ToString(u) + " " + TUtils.ToString(v));

            if (v.Length != u.Length || v.Length != 4)
            {
                Debug.LogError("Something bad went parsing: " + u + " " + v);
            }

            List <Vector2> uv = new List <Vector2>();

            for (int j = 0; j < u.Length; j++)
            {
                uv.Add(new Vector2(float.Parse(u[j]), float.Parse(v[j])));
            }

            Log.i("Extracted uv: " + TUtils.ToString(uv));
            allUv.AddRange(uv);
        }

        return(allUv);
    }
示例#17
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);
    }
示例#18
0
    private static void testCompression()
    {
        int size = 8;

        Random rnd = new Random(1);

        byte[] data0 = new byte[size];
        for (int i = 0; i < size; i++)
        {
            data0[i] = (byte)(rnd.NextDouble() * 255f);
        }
        byte[] data1 = new byte[size];
        for (int i = 0; i < size; i++)
        {
            data1[i] = (byte)(rnd.NextDouble() * 255f);
        }
        byte[] data2 = new byte[size];
        for (int i = 0; i < size; i++)
        {
            data2[i] = (byte)(rnd.NextDouble() * 255f);
        }

        TUtils.Test("Compress All", () =>
        {
            var bw = new BinaryViewWriter();
            bw.CompressAll();
            bw.WriteArray(data0);
            bw.Dispose();
            var file = bw.ToArray();

            if (file.Length == 0)
            {
                TUtils.WriteFail($"FAIL file length is 0");
                return(TestResult.Failure);
            }

            var br = new BinaryViewReader(file);
            br.DecompressAll();
            var rdata0 = br.ReadArray <byte>();
            br.Dispose();

            if (!TUtils.IsIListEqual(data0, rdata0))
            {
                TUtils.WriteFail($"FAIL data: {TUtils.IListToString(rdata0)}, expected: {TUtils.IListToString(data0)}");
                return(TestResult.Failure);
            }
            TUtils.WriteSucces($"OK");
            return(TestResult.Success);
        });

        TUtils.Test("Compress Section", () =>
        {
            var bw = new BinaryViewWriter();
            bw.BeginDeflateSection();
            bw.WriteArray(data0);
            bw.EndDeflateSection();
            bw.Dispose();
            var file = bw.ToArray();

            if (file.Length == 0)
            {
                TUtils.WriteFail($"FAIL file length is 0");
                return(TestResult.Failure);
            }

            var br = new BinaryViewReader(file);
            br.BeginDeflateSection();
            var rdata0 = br.ReadArray <byte>();
            br.EndDeflateSection();
            br.Dispose();

            if (!TUtils.IsIListEqual(data0, rdata0))
            {
                TUtils.WriteFail($"FAIL data: {TUtils.IListToString(rdata0)}, expected: {TUtils.IListToString(data0)}");
                return(TestResult.Failure);
            }
            TUtils.WriteSucces($"OK");
            return(TestResult.Success);
        });

        TUtils.Test("Compress 2 Sections", () =>
        {
            var bw = new BinaryViewWriter();
            bw.BeginDeflateSection();
            bw.WriteArray(data0);
            bw.EndDeflateSection();
            bw.WriteArray(data1);
            bw.BeginDeflateSection();
            bw.WriteArray(data2);
            bw.EndDeflateSection();
            bw.Dispose();
            var file = bw.ToArray();

            if (file.Length == 0)
            {
                TUtils.WriteFail($"FAIL file length is 0");
                return(TestResult.Failure);
            }

            var br = new BinaryViewReader(file);
            br.BeginDeflateSection();
            var rdata0 = br.ReadArray <byte>();
            br.EndDeflateSection();
            var rdata1 = br.ReadArray <byte>();
            br.BeginDeflateSection();
            var rdata2 = br.ReadArray <byte>();
            br.EndDeflateSection();
            br.Dispose();

            if (!TUtils.IsIListEqual(data0, rdata0))
            {
                TUtils.WriteFail($"FAIL data0: {TUtils.IListToString(rdata0)}, expected: {TUtils.IListToString(data0)}");
                return(TestResult.Failure);
            }
            if (!TUtils.IsIListEqual(data1, rdata1))
            {
                TUtils.WriteFail($"FAIL data1: {TUtils.IListToString(rdata1)}, expected: {TUtils.IListToString(data1)}");
                return(TestResult.Failure);
            }
            if (!TUtils.IsIListEqual(data2, rdata2))
            {
                TUtils.WriteFail($"FAIL data2: {TUtils.IListToString(rdata2)}, expected: {TUtils.IListToString(data2)}");
                return(TestResult.Failure);
            }
            TUtils.WriteSucces($"OK");
            return(TestResult.Success);
        });
    }
示例#19
0
    private void ProcessTerrainCell(TreeSystemStructuredTrees cell, ref float treeDistSqr, ref float shadowDistSqr)
    {
        // Draw all trees instanced in MAX_BATCH chunks
        int   tempIndexTree = 0;
        int   tempIndexShadow = 0;
        float x, y, z;

        // If we are completely inside frustum we don't need to AABB test each tree
        bool insideFrustum = TUtils.IsCompletelyInsideFrustum(m_PlanesTemp, TUtils.BoundsCorners(ref cell.m_BoundsBox, ref m_TempCorners));

        // If it is completely inside the frustum notify us
        if (insideFrustum)
        {
            m_DataIssuedTerrainCellsFull++;
        }

        // Tree instances
        TreeSystemStoredInstance[] treeInstances = cell.m_Instances;

        // TODO: Hm... if we take that hash it doesn't mean that it's the first visible one...
        int treeHash        = treeInstances[0].m_TreeHash;
        int currentTreeHash = treeHash;

        int shadowHash        = treeHash;
        int currentShadowHash = shadowHash;

        for (int treeIndex = 0; treeIndex < treeInstances.Length; treeIndex++)
        {
            // This is an order of magnitude faster than (treeInstances[treeIndex].m_WorldPosition - pos).sqrMagnitude
            // since it does not initiate with a ctor an extra vector during the computation
            x = treeInstances[treeIndex].m_WorldPosition.x - m_CameraPosTemp.x;
            y = treeInstances[treeIndex].m_WorldPosition.y - m_CameraPosTemp.y;
            z = treeInstances[treeIndex].m_WorldPosition.z - m_CameraPosTemp.z;

            float distToTree = x * x + y * y + z * z;

            if (insideFrustum)
            {
                // If we are completely inside the frustum we don't need to check each individual tree's bounds
                if (distToTree <= treeDistSqr)
                {
                    currentTreeHash = treeInstances[treeIndex].m_TreeHash;

                    if (tempIndexTree >= MAX_BATCH || treeHash != currentTreeHash)
                    {
                        // We're sure that they will not be shadows only
                        IssueDrawTrees(m_ManagedPrototypesIndexed[treeHash], cell, m_IdxTempMesh, m_DstTempMesh, tempIndexTree, false);
                        tempIndexTree = 0;

                        // Update the hash
                        treeHash = currentTreeHash;
                    }

                    m_IdxTempMesh[tempIndexTree] = treeIndex;
                    m_DstTempMesh[tempIndexTree] = Mathf.Sqrt(distToTree);
                    tempIndexTree++;

                    m_DataIssuedMeshTrees++;
                }
            }
            else
            {
                // TODO: In the future instead of 'GeometryUtility.TestPlanesAABB' have our own function that
                // checks if the object is within the shadow volume, that is if it casts shadow inside our frustum
                // Just generate our own set of planes that we'll use...

                // If we are not completely inside the frustum we need to check the bounds of each individual tree
                if (distToTree <= treeDistSqr && GeometryUtility.TestPlanesAABB(m_PlanesTemp, treeInstances[treeIndex].m_WorldBounds))
                {
                    currentTreeHash = treeInstances[treeIndex].m_TreeHash;

                    if (tempIndexTree >= MAX_BATCH || treeHash != currentTreeHash)
                    {
                        IssueDrawTrees(m_ManagedPrototypesIndexed[treeHash], cell, m_IdxTempMesh, m_DstTempMesh, tempIndexTree, false);
                        tempIndexTree = 0;

                        // Update the hash
                        treeHash = currentTreeHash;
                    }

                    m_IdxTempMesh[tempIndexTree] = treeIndex;
                    m_DstTempMesh[tempIndexTree] = Mathf.Sqrt(distToTree);
                    tempIndexTree++;

                    m_DataIssuedMeshTrees++;
                }
                // Same operation as above but with different matrices
                else if (m_Settings.m_ApplyShadowPoppingCorrection && distToTree < shadowDistSqr)
                {
                    // Even if we are invisible but within the shadow sitance still cast shadows...
                    // (not the most efficient method though, waiting for the complete one)

                    currentShadowHash = treeInstances[treeIndex].m_TreeHash;

                    if (tempIndexShadow >= MAX_BATCH || shadowHash != currentShadowHash)
                    {
                        IssueDrawTrees(m_ManagedPrototypesIndexed[shadowHash], cell, m_IdxTempShadow, m_DstTempShadow, tempIndexShadow, true);
                        tempIndexShadow = 0;

                        // Update the shadow hash
                        shadowHash = currentShadowHash;
                    }

                    m_IdxTempShadow[tempIndexShadow] = treeIndex;
                    m_DstTempShadow[tempIndexShadow] = Mathf.Sqrt(distToTree);
                    tempIndexShadow++;

                    m_DataIssuedShadows++;
                }
            }
        } // End cell tree iteration

        if (tempIndexTree > 0)
        {
            // Get a tree hash from the first element of the array so that we know for sure that we use the correct prototype data
            IssueDrawTrees(m_ManagedPrototypesIndexed[treeInstances[m_IdxTempMesh[0]].m_TreeHash], cell,
                           m_IdxTempMesh, m_DstTempMesh, tempIndexTree, false);

            tempIndexTree = 0;
        }

        if (tempIndexShadow > 0)
        {
            IssueDrawTrees(m_ManagedPrototypesIndexed[treeInstances[m_IdxTempShadow[0]].m_TreeHash], cell,
                           m_IdxTempShadow, m_DstTempShadow, tempIndexShadow, true);

            tempIndexShadow = 0;
        }
    }
示例#20
0
    private static void testSpeed()
    {
        var rnd   = new Random();
        var watch = new Stopwatch();

        TUtils.Test("WriteByte x100000 time", () =>
        {
            stream.Seek(0, SeekOrigin.Begin);
            watch.Restart();
            for (int i = 0; i < 100000; i++)
            {
                bw.WriteByte((byte)rnd.NextDouble());
            }
            watch.Stop();

            TUtils.WriteSucces($"OK {watch.Elapsed.TotalMilliseconds}ms");
            return(TestResult.Success);
        });
        TUtils.Test("ReadByte x100000 time", () =>
        {
            stream.Seek(0, SeekOrigin.Begin);
            watch.Restart();
            for (int i = 0; i < 100000; i++)
            {
                br.ReadByte();
            }
            watch.Stop();

            TUtils.WriteSucces($"OK {watch.Elapsed.TotalMilliseconds}ms");
            return(TestResult.Success);
        });

        TUtils.Test("Write<byte> x100000 time", () =>
        {
            stream.Seek(0, SeekOrigin.Begin);
            watch.Restart();
            for (int i = 0; i < 100000; i++)
            {
                bw.Write <byte>((byte)rnd.NextDouble());
            }
            watch.Stop();

            TUtils.WriteSucces($"OK {watch.Elapsed.TotalMilliseconds}ms");
            return(TestResult.Success);
        });

        TUtils.Test("Read<byte> x100000 time", () =>
        {
            stream.Seek(0, SeekOrigin.Begin);
            watch.Restart();
            for (int i = 0; i < 100000; i++)
            {
                br.Read <byte>();
            }
            watch.Stop();

            TUtils.WriteSucces($"OK {watch.Elapsed.TotalMilliseconds}ms");
            return(TestResult.Success);
        });

        TUtils.Test("WriteDouble x100000 time", () =>
        {
            stream.Seek(0, SeekOrigin.Begin);
            watch.Restart();
            for (int i = 0; i < 100000; i++)
            {
                bw.WriteDouble(rnd.NextDouble());
            }
            watch.Stop();

            TUtils.WriteSucces($"OK {watch.Elapsed.TotalMilliseconds}ms");
            return(TestResult.Success);
        });
        TUtils.Test("ReadDouble x100000 time", () =>
        {
            stream.Seek(0, SeekOrigin.Begin);
            watch.Restart();
            for (int i = 0; i < 100000; i++)
            {
                br.ReadDouble();
            }
            watch.Stop();

            TUtils.WriteSucces($"OK {watch.Elapsed.TotalMilliseconds}ms");
            return(TestResult.Success);
        });

        TUtils.Test("Write<double> x100000 time", () =>
        {
            stream.Seek(0, SeekOrigin.Begin);
            watch.Restart();
            for (int i = 0; i < 100000; i++)
            {
                bw.Write <double>(rnd.NextDouble());
            }
            watch.Stop();

            TUtils.WriteSucces($"OK {watch.Elapsed.TotalMilliseconds}ms");
            return(TestResult.Success);
        });

        TUtils.Test("Read<double> x100000 time", () =>
        {
            stream.Seek(0, SeekOrigin.Begin);
            watch.Restart();
            for (int i = 0; i < 100000; i++)
            {
                br.Read <double>();
            }
            watch.Stop();

            TUtils.WriteSucces($"OK {watch.Elapsed.TotalMilliseconds}ms");
            return(TestResult.Success);
        });
    }
示例#21
0
    private void ProcessTerrainCell(TreeSystemStructuredTrees cell, ref float treeDistSqr)
    {
        // Draw all trees instanced in MAX_BATCH chunks
        int   tempIndex = 0;
        float x, y, z;

        // If we are completely inside frustum we don't need to AABB test each tree
        bool insideFrustum = TUtils.IsCompletelyInsideFrustum(m_PlanesTemp, TUtils.BoundsCorners(ref cell.m_Bounds, ref m_TempCorners));

        // Tree instances
        TreeSystemStoredInstance[] treeInstances = cell.m_Instances;

        // TODO: Hm... if we take that hash it doesn't mean that it's the first visible one...
        int treeHash        = treeInstances[0].m_TreeHash;
        int currentTreeHash = treeHash;

        for (int treeIndex = 0; treeIndex < treeInstances.Length; treeIndex++)
        {
            // 1.33 ms for 110k trees
            // This is an order of magnitude faster than (treeInstances[treeIndex].m_WorldPosition - pos).sqrMagnitude
            // since it does not initiate with a ctor an extra vector during the computation
            x = treeInstances[treeIndex].m_WorldPosition.x - m_CameraPosTemp.x;
            y = treeInstances[treeIndex].m_WorldPosition.y - m_CameraPosTemp.y;
            z = treeInstances[treeIndex].m_WorldPosition.z - m_CameraPosTemp.z;

            float distToTree = x * x + y * y + z * z;

            // 17 ms for 110k trees
            // float distToTree = (treeInstances[treeIndex].m_WorldPosition - pos).sqrMagnitude;

            if (insideFrustum)
            {
                // If we are completely inside the frustum we don't need to check each individual tree's bounds
                if (distToTree <= treeDistSqr)
                {
                    currentTreeHash = treeInstances[treeIndex].m_TreeHash;

                    if (tempIndex >= MAX_BATCH || treeHash != currentTreeHash)
                    {
                        IssueDrawTrees(m_ManagedPrototypesIndexed[treeHash], cell, m_IdxTemp, m_DstTemp, tempIndex);
                        tempIndex = 0;

                        // Update the hash
                        treeHash = currentTreeHash;
                    }

                    m_IdxTemp[tempIndex] = treeIndex;
                    m_DstTemp[tempIndex] = Mathf.Sqrt(distToTree);
                    tempIndex++;

                    m_DataIssuedMeshTrees++;
                }
            }
            else
            {
                // If we are not completely inside the frustum we need to check the bounds of each individual tree
                if (distToTree <= treeDistSqr && GeometryUtility.TestPlanesAABB(m_PlanesTemp, treeInstances[treeIndex].m_WorldBounds))
                {
                    currentTreeHash = treeInstances[treeIndex].m_TreeHash;

                    if (tempIndex >= MAX_BATCH || treeHash != currentTreeHash)
                    {
                        IssueDrawTrees(m_ManagedPrototypesIndexed[treeHash], cell, m_IdxTemp, m_DstTemp, tempIndex);
                        tempIndex = 0;

                        // Update the hash
                        treeHash = currentTreeHash;
                    }

                    m_IdxTemp[tempIndex] = treeIndex;
                    m_DstTemp[tempIndex] = Mathf.Sqrt(distToTree);
                    tempIndex++;

                    m_DataIssuedMeshTrees++;
                }
            }
        } // End cell tree iteration

        if (tempIndex > 0)
        {
            // Get a tree hash from the first element of the array so that we know for sure that we use the correc prototype data
            IssueDrawTrees(m_ManagedPrototypesIndexed[treeInstances[m_IdxTemp[0]].m_TreeHash], cell,
                           m_IdxTemp, m_DstTemp, tempIndex);

            tempIndex = 0;
        }
    }