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; } }
void Awake() { Instance = this; // Get the non-alloc version of the plane extraction MethodInfo info = typeof(GeometryUtility).GetMethod("Internal_ExtractPlanes", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Plane[]), typeof(Matrix4x4) }, null); ExtractPlanes = Delegate.CreateDelegate(typeof(Action <Plane[], Matrix4x4>), info) as Action <Plane[], Matrix4x4>; // Set maximum tree distance Shader.SetGlobalFloat("_TreeSystemDistance", m_Settings.m_MaxTreeDistance); m_ShaderIDFadeLOD = Shader.PropertyToID("master_LODFade"); m_ShaderIDBillboardScaleRotation = Shader.PropertyToID("_InstanceScaleRotation"); // Generate runtime data for (int i = 0; i < m_ManagedPrototypes.Length; i++) { GenerateRuntimePrototypeData(m_ManagedPrototypes[i]); } // Build the dictionary based on the index m_ManagedPrototypesIndexed = new Dictionary <int, TreeSystemPrototypeData>(); for (int i = 0; i < m_ManagedPrototypes.Length; i++) { m_ManagedPrototypesIndexed.Add(m_ManagedPrototypes[i].m_TreePrototypeHash, m_ManagedPrototypes[i]); } }
void Start() { system = FindObjectOfType <TreeSystem>(); protoData = system.m_ManagedPrototypes[0]; THRESHOLD = system.GetTreeTanzitionThreshold(); procDistance = system.GetTreeDistance() - THRESHOLD; }
void Awake() { Instance = this; m_UsedLayerId = LayerMask.NameToLayer(m_Settings.m_UsedLayer); // Get the non-alloc version of the plane extraction MethodInfo info = typeof(GeometryUtility).GetMethod("Internal_ExtractPlanes", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(Plane[]), typeof(Matrix4x4) }, null); ExtractPlanes = Delegate.CreateDelegate(typeof(Action <Plane[], Matrix4x4>), info) as Action <Plane[], Matrix4x4>; // Set maximum tree distance Shader.SetGlobalFloat("_TreeSystemDistance", m_Settings.m_MaxTreeDistance); m_ShaderIDFadeLODFull = Shader.PropertyToID("master_LODFadeFull"); m_ShaderIDFadeLODDetail = Shader.PropertyToID("master_LODFadeDetail"); m_ShaderIDFadeBillboard = Shader.PropertyToID("master_LODFade"); m_ShaderIDBillboardScaleRotation = Shader.PropertyToID("_InstanceScaleRotation"); // We need matrices as 'indentity' m_MtxLODTemp_0 = new Matrix4x4[MAX_BATCH]; m_MtxLODTemp_1 = new Matrix4x4[MAX_BATCH]; m_MtxLODTemp_2 = new Matrix4x4[MAX_BATCH]; m_MtxLODTemp_3 = new Matrix4x4[MAX_BATCH]; m_MtxLODTemp_4 = new Matrix4x4[MAX_BATCH]; for (int i = 0; i < MAX_BATCH; i++) { m_MtxLODTemp_0[i] = Matrix4x4.identity; m_MtxLODTemp_1[i] = Matrix4x4.identity; m_MtxLODTemp_2[i] = Matrix4x4.identity; m_MtxLODTemp_3[i] = Matrix4x4.identity; m_MtxLODTemp_4[i] = Matrix4x4.identity; } // Generate runtime data for (int i = 0; i < m_ManagedPrototypes.Length; i++) { GenerateRuntimePrototypeData(m_ManagedPrototypes[i]); } // Build the dictionary based on the index m_ManagedPrototypesIndexed = new Dictionary <int, TreeSystemPrototypeData>(); for (int i = 0; i < m_ManagedPrototypes.Length; i++) { m_ManagedPrototypesIndexed.Add(m_ManagedPrototypes[i].m_TreePrototypeHash, m_ManagedPrototypes[i]); } // If we don't have tree colliders initialize them if (!m_TreeColliders) { m_TreeColliders = gameObject.AddComponent <TreeColliders>(); m_TreeColliders.m_OwnerSystem = this; } }
// Constructor public Tree(TreeSystem treeSystem, string levelUid, Texture2D barkTexture, List <List <Texture2D> > leafTextures, XElement data) { _treeSystem = treeSystem; _levelUid = levelUid; _leafTextures = leafTextures; _barkTexture = barkTexture; _angle = Loader.loadFloat(data.Attribute("angle"), 0f); _seed = Loader.loadInt(data.Attribute("seed"), 12345); _age = Loader.loadFloat(data.Attribute("age"), 0f); _internodeHalfLength = Loader.loadFloat(data.Attribute("internode_half_length"), 0.5f); _internodeLength = _internodeHalfLength * 2f; _maxShootLength = Loader.loadInt(data.Attribute("max_shoot_length"), 4); _maxBaseHalfWidth = Loader.loadFloat(data.Attribute("max_base_half_width"), 0.25f); _perceptionAngle = Loader.loadFloat(data.Attribute("perception_angle"), 0.6f); _perceptionRadius = Loader.loadFloat(data.Attribute("perception_radius"), 4f); _occupancyRadius = Loader.loadFloat(data.Attribute("occupancy_radius"), 1f); _lateralAngle = Loader.loadFloat(data.Attribute("lateral_angle"), 0.6f); _fullExposure = Loader.loadFloat(data.Attribute("full_exposure"), 1f); _penumbraA = Loader.loadFloat(data.Attribute("penumbra_a"), 1f); _penumbraB = Loader.loadFloat(data.Attribute("penumbra_b"), 2f); _optimalGrowthWeight = Loader.loadFloat(data.Attribute("optimal_growth_weight"), 1f); _tropismWeight = Loader.loadFloat(data.Attribute("tropism_weight"), 1f); _tropism = Loader.loadVector2(data.Attribute("tropism"), Vector2.Zero); _position = Loader.loadVector2(data.Attribute("position"), Vector2.Zero); _layerDepth = Loader.loadFloat(data.Attribute("layer_depth"), 0.1f); _entityId = int.Parse(data.Attribute("id").Value); _vertices = new VertexPositionColorTexture[MAX_VERTICES]; for (int i = 0; i < MAX_VERTICES; i++) { _vertices[i].Color = Color.White; } _random = new Random(_seed); _internodeLengthSq = _internodeLength * _internodeLength; _aabb = new AABB(); _aabb.LowerBound = _position; _aabb.UpperBound = _position; // Calculate root position float rootAngle = _angle + (StasisMathHelper.pi); _rootPosition = _position + new Vector2((float)Math.Cos(rootAngle), (float)Math.Sin(rootAngle)) * 5f; // Calculate anchor normals float anchorAngle = _angle - (StasisMathHelper.pi * 0.5f); _anchorNormal = new Vector2((float)Math.Cos(anchorAngle), (float)Math.Sin(anchorAngle)); // Create first metamer _rootMetamer = new Metamer(this, null, BudType.TERMINAL, BudState.DORMANT, BudState.DEAD, _angle, true); _rootMetamer.isTail = true; }
// Constructor public Tree(TreeSystem treeSystem, string levelUid, Texture2D barkTexture, List<List<Texture2D>> leafTextures, XElement data) { _treeSystem = treeSystem; _levelUid = levelUid; _leafTextures = leafTextures; _barkTexture = barkTexture; _angle = Loader.loadFloat(data.Attribute("angle"), 0f); _seed = Loader.loadInt(data.Attribute("seed"), 12345); _age = Loader.loadFloat(data.Attribute("age"), 0f); _internodeHalfLength = Loader.loadFloat(data.Attribute("internode_half_length"), 0.5f); _internodeLength = _internodeHalfLength * 2f; _maxShootLength = Loader.loadInt(data.Attribute("max_shoot_length"), 4); _maxBaseHalfWidth = Loader.loadFloat(data.Attribute("max_base_half_width"), 0.25f); _perceptionAngle = Loader.loadFloat(data.Attribute("perception_angle"), 0.6f); _perceptionRadius = Loader.loadFloat(data.Attribute("perception_radius"), 4f); _occupancyRadius = Loader.loadFloat(data.Attribute("occupancy_radius"), 1f); _lateralAngle = Loader.loadFloat(data.Attribute("lateral_angle"), 0.6f); _fullExposure = Loader.loadFloat(data.Attribute("full_exposure"), 1f); _penumbraA = Loader.loadFloat(data.Attribute("penumbra_a"), 1f); _penumbraB = Loader.loadFloat(data.Attribute("penumbra_b"), 2f); _optimalGrowthWeight = Loader.loadFloat(data.Attribute("optimal_growth_weight"), 1f); _tropismWeight = Loader.loadFloat(data.Attribute("tropism_weight"), 1f); _tropism = Loader.loadVector2(data.Attribute("tropism"), Vector2.Zero); _position = Loader.loadVector2(data.Attribute("position"), Vector2.Zero); _layerDepth = Loader.loadFloat(data.Attribute("layer_depth"), 0.1f); _entityId = int.Parse(data.Attribute("id").Value); _vertices = new VertexPositionColorTexture[MAX_VERTICES]; for (int i = 0; i < MAX_VERTICES; i++) { _vertices[i].Color = Color.White; } _random = new Random(_seed); _internodeLengthSq = _internodeLength * _internodeLength; _aabb = new AABB(); _aabb.LowerBound = _position; _aabb.UpperBound = _position; // Calculate root position float rootAngle = _angle + (StasisMathHelper.pi); _rootPosition = _position + new Vector2((float)Math.Cos(rootAngle), (float)Math.Sin(rootAngle)) * 5f; // Calculate anchor normals float anchorAngle = _angle - (StasisMathHelper.pi * 0.5f); _anchorNormal = new Vector2((float)Math.Cos(anchorAngle), (float)Math.Sin(anchorAngle)); // Create first metamer _rootMetamer = new Metamer(this, null, BudType.TERMINAL, BudState.DORMANT, BudState.DEAD, _angle, true); _rootMetamer.isTail = true; }
void Start() { if (!m_OwnerSystem) { m_OwnerSystem = FindObjectOfType <TreeSystem>(); } if (!m_WatchedTransform) { m_WatchedTransform = Camera.main.transform; } m_CollisionDistance = m_OwnerSystem.m_Settings.m_ColliderSetDistance; m_CollisionRefreshDistance = m_OwnerSystem.m_Settings.m_ColliderRefreshDistance; m_LastPosition = m_WatchedTransform.position; m_ColliderHolder = new GameObject("TreeSystemColliderHolder"); }
public override void OnInspectorGUI() { DrawDefaultInspector(); TreeSystem system = target as TreeSystem; GUILayout.Space(20); if (GUILayout.Button("Apply Settings")) { system.SetTreeDistance(system.m_Settings.m_MaxTreeDistance); system.SetMandatoryShadowDistance(system.m_Settings.m_ShadowDrawDistance); system.SetApplyColliders(system.m_Settings.m_ApplyTreeColliders); system.SetCollisionDistance(system.m_Settings.m_ColliderSetDistance); system.SetCollisionRefreshDistance(system.m_Settings.m_ColliderRefreshDistance); } }
public void RunForDepth2Other() { var system = new TreeSystem(); var gate = new Gate(false); var lgate = new Gate(true); gate.LeftNode = lgate; var rgate = new Gate(true); gate.RightNode = rgate; var c1 = new Container(); lgate.LeftNode = c1; var c2 = new Container() { ContainerNumber = 1 }; lgate.RightNode = c2; var c3 = new Container() { ContainerNumber = 2 }; rgate.LeftNode = c3; var c4 = new Container() { ContainerNumber = 3 }; rgate.RightNode = c4; system.Tree = gate; system.Containers.Add(c1); system.Containers.Add(c2); system.Containers.Add(c3); system.Containers.Add(c4); GateRunner gr = new GateRunner(); int s = gr.RunSystem(system); s.Should().Be(1); }
public void RunForDepth1() { var system = new TreeSystem(); var gate = new Gate(true); var lcontainer = new Container(); gate.LeftNode = lcontainer; var rcontainer = new Container() { ContainerNumber = 1 }; gate.RightNode = rcontainer; system.Tree = gate; system.Containers.Add(lcontainer); system.Containers.Add(rcontainer); GateRunner gr = new GateRunner(); int s = gr.RunSystem(system); s.Should().Be(1); }
private void CopyDataTo(long position, DataFile targetDataFile, long targetPosition, long size) { // If transactions are the same (data is being copied within the same // transaction context). TreeSystemStack targetStack; TreeSystemStack sourceStack; // Keys Key targetKey = targetDataFile.key; Key sourceKey = key; bool modifyPosOnShift = false; if (targetDataFile.Transaction == Transaction) { // We set the source and target stack to the same sourceStack = targetDataFile.stack; targetStack = sourceStack; // If same transaction and target_position is before the position we // set the modify_pos_on_shift boolean. This will update the absolute // position when data is copied. modifyPosOnShift = (targetPosition <= position); } else { // Otherwise, set the target stack to the target file's stack sourceStack = stack; targetStack = targetDataFile.stack; } // Compact the key we are copying from, and in the destination, transaction.CompactNodeKey(sourceKey); targetDataFile.CompactNodeKey(targetKey); // The process works as follows; // 1. If we are not positioned at the start of a leaf, copy all data up // to the next leaf to the target. // 2. Split the target leaf at the new position if the leaf can be // split into 2 leaf nodes. // 3. Copy every full leaf to the target as a new leaf element. // 4. If there is any remaining data to copy, insert it into the target. // Set up for the position sourceStack.SetupForPosition(sourceKey, position); // If we aren't at the start of the leaf, then copy the data to the // target. int leafOff = sourceStack.LeafOffset; if (leafOff > 0) { // We copy the remaining data in the leaf to the target // The amount of data to copy from the leaf to the target int to_copy = (int)Math.Min(size, sourceStack.LeafSize - leafOff); if (to_copy > 0) { // Read into a buffer byte[] buf = new byte[to_copy]; sourceStack.CurrentLeaf.Read(leafOff, buf, 0, to_copy); // Make enough room to insert this data in the target targetStack.ShiftData(targetKey, targetPosition, to_copy); // Update the position if necessary if (modifyPosOnShift) { position += to_copy; } // Write the data to the target stack targetStack.WriteFrom(targetKey, targetPosition, buf, 0, to_copy); // Increment the pointers position += to_copy; targetPosition += to_copy; size -= to_copy; } } // If this is true, the next iteration will use the byte buffer leaf copy // routine. Set if a link to a node failed for whatever reason. bool useByteBufferCopyForNext = false; // The loop while (size > 0) { // We now know we are at the start of a leaf with data left to copy. sourceStack.SetupForPosition(sourceKey, position); // Lets assert that if (sourceStack.LeafOffset != 0) { throw new ApplicationException("Expected to be at the start of a leaf."); } // If the source is a heap node or we are copying less than the data // that's in the leaf then we use the standard shift and write. TreeLeaf currentLeaf = sourceStack.CurrentLeaf; // Check the leaf size isn't 0 if (currentLeaf.Length <= 0) { throw new ApplicationException("Leaf is empty."); } // If the remaining copy is less than the size of the leaf we are // copying from, we just do a byte array copy if (useByteBufferCopyForNext || size < currentLeaf.Length) { // Standard copy through a byte[] buf, useByteBufferCopyForNext = false; int toCopy = (int)Math.Min(size, currentLeaf.Length); // Read into a buffer byte[] buf = new byte[toCopy]; currentLeaf.Read(0, buf, 0, toCopy); // Make enough room in the target targetStack.ShiftData(targetKey, targetPosition, toCopy); if (modifyPosOnShift) { position += toCopy; } // Write the data and finish targetStack.WriteFrom(targetKey, targetPosition, buf, 0, toCopy); // Update pointers position += toCopy; targetPosition += toCopy; size -= toCopy; } else { // We need to copy a complete leaf node, // If the leaf is on the heap, write it out if (transaction.IsHeapNode(currentLeaf.Id)) { sourceStack.WriteLeafOnly(sourceKey); // And update any vars currentLeaf = sourceStack.CurrentLeaf; } // Ok, source current leaf isn't on the heap, and we are copying a // complete leaf node, so we are elegible to play with pointers to // copy the data. targetStack.SetupForPosition(targetKey, targetPosition); bool insertNextBefore = false; // Does the target key exist? bool targetKeyExists = targetStack.CurrentLeafKey.Equals(targetKey); if (targetKeyExists) { // If the key exists, is target_position at the end of the span? insertNextBefore = targetStack.LeafOffset < targetStack.CurrentLeaf.Length; } // If target isn't currently on a boundary if (!targetStack.IsAtEndOfKeyData && targetStack.LeafOffset != 0) { // If we aren't on a boundary we need to split the target leaf targetStack.SplitLeaf(targetKey, targetPosition); } // If the key exists we set up the position to the previous left // to insert the new leaf, otherwise we set it up to the default // position to insert. // Copy the leaf, // Try to link to this leaf bool linkSuccessful = TreeSystem.LinkLeaf(targetKey, currentLeaf.Id); // If the link was successful, if (linkSuccessful) { // Insert the leaf into the tree targetStack.InsertLeaf(targetKey, currentLeaf, insertNextBefore); // Update the pointers int copiedSize = currentLeaf.Length; // Update if we inserting stuff before if (modifyPosOnShift) { position += copiedSize; } position += copiedSize; targetPosition += copiedSize; size -= copiedSize; } // If the link was not successful, else { // We loop back and use the byte buffer copy, useByteBufferCopyForNext = true; } } } }
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; } }
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(); }
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; } }
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(); }