private void DoImportTrees() { if (!ImportTreeInstancesOnly) { GTreePrototypeGroup treeGroup = DesData.Foliage.Trees; if (treeGroup == null || treeGroup == GRuntimeSettings.Instance.foliageDefault.trees) { CreateNewTreePrototypesGroup = true; } if (CreateNewTreePrototypesGroup) { treeGroup = ScriptableObject.CreateInstance <GTreePrototypeGroup>(); #if UNITY_EDITOR if (!Application.isPlaying) { string path = AssetDatabase.GetAssetPath(DesData); string directory = Path.GetDirectoryName(path); string filePath = Path.Combine(directory, string.Format("Trees_{0}_{1}.asset", DesData.Id, System.DateTime.Now.Ticks)); AssetDatabase.CreateAsset(treeGroup, filePath); } #endif DesData.Foliage.Trees = treeGroup; } treeGroup.Prototypes.Clear(); TreePrototype[] treePrototypes = SrcData.treePrototypes; for (int i = 0; i < treePrototypes.Length; ++i) { GTreePrototype proto = (GTreePrototype)treePrototypes[i]; treeGroup.Prototypes.Add(proto); } GCommon.SetDirty(treeGroup); } DesData.Foliage.TreeInstances.Clear(); TreeInstance[] treeInstances = SrcData.treeInstances; for (int i = 0; i < treeInstances.Length; ++i) { GTreeInstance t = (GTreeInstance)treeInstances[i]; DesData.Foliage.TreeInstances.Add(t); } if (DesTerrain != null) { DesData.Foliage.SetTreeRegionDirty(GCommon.UnitRect); DesTerrain.UpdateTreesPosition(); DesData.Foliage.ClearTreeDirtyRegions(); } DesData.SetDirty(GTerrainData.DirtyFlags.Foliage); //GC.Collect(); }
private bool IsInstancingEnabledForAllMaterials(GTreePrototype p) { if (!enableInstancing) { return(false); } if (p.SharedMaterials == null || p.SharedMaterials.Length == 0) { return(false); } for (int i = 0; i < p.SharedMaterials.Length; ++i) { if (!p.SharedMaterials[i].enableInstancing) { return(false); } } return(true); }
private void ConvertToTreePrototypeGroup() { GTreePrototypeGroup group = ScriptableObject.CreateInstance <GTreePrototypeGroup>(); for (int i = 0; i < instance.Prototypes.Count; ++i) { GameObject prefab = instance.Prototypes[i]; if (prefab != null) { group.Prototypes.Add(GTreePrototype.Create(prefab)); } } string path = AssetDatabase.GetAssetPath(instance); string directory = Path.GetDirectoryName(path); string filePath = Path.Combine(directory, string.Format("{0}_{1}_{2}.asset", instance.name, "Trees", GCommon.GetUniqueID())); AssetDatabase.CreateAsset(group, filePath); Selection.activeObject = group; }
private void LateUpdate() { if (Terrain == null) { return; } if (Terrain.TerrainData == null) { return; } if (Terrain.TerrainData.Foliage.Trees == null) { return; } if (Terrain.TerrainData.Foliage.Trees.Prototypes.Count == 0) { return; } if (treeInstances == null || treeInstances.Length == 0) { return; } GameObject actualTarget = null; if (Target != null) { actualTarget = Target; } else if (Camera.main != null) { actualTarget = Camera.main.gameObject; } if (actualTarget == null) { return; } Vector3 targetLocalPos = Terrain.transform.InverseTransformPoint(actualTarget.transform.position); GTreeColliderCullJob job = new GTreeColliderCullJob() { instances = nativeTreeInstances, cullResults = nativeCullResults, maxDistance = distance, targetPos = targetLocalPos }; JobHandle handle = job.Schedule(nativeTreeInstances.Length, 100); handle.Complete(); if (cullResults == null || cullResults.Length != nativeCullResults.Length) { cullResults = new bool[nativeCullResults.Length]; } nativeCullResults.CopyTo(cullResults); List <GTreePrototype> prototypes = Terrain.TerrainData.Foliage.Trees.Prototypes; int colliderIndex = 0; Vector3 terrainPos = Terrain.transform.position; Vector3 worldPos = Vector3.zero; if (terrain.TerrainData.Rendering.DrawTrees) { for (int i = 0; i < treeInstances.Length; ++i) { if (cullResults[i] == false) { continue; } GTreeInstance tree = treeInstances[i]; GTreePrototype prototype = prototypes[tree.prototypeIndex]; if (prototype.prefab == null) { continue; } if (!prototype.hasCollider) { continue; } CapsuleCollider col = GetCollider(colliderIndex); colliderIndex += 1; worldPos.Set( tree.position.x + terrainPos.x, tree.position.y + terrainPos.y, tree.position.z + terrainPos.z); col.transform.position = worldPos; col.transform.rotation = tree.rotation; col.transform.localScale = tree.scale; GTreeColliderInfo colliderInfo = prototype.colliderInfo; col.center = colliderInfo.center; col.radius = colliderInfo.radius; col.height = colliderInfo.height; col.direction = colliderInfo.direction; col.gameObject.layer = prototype.layer; if (CopyTreeTag) { col.gameObject.tag = prototype.prefab.tag; } col.gameObject.SetActive(true); } } int colliderCount = Colliders.Count; for (int i = colliderIndex; i < colliderCount; ++i) { CapsuleCollider col = GetCollider(i); col.gameObject.SetActive(false); } }
private void DrawInstanced(int prototypeIndex) { GTreePrototype proto = prototypes[prototypeIndex]; Mesh mesh = proto.sharedMesh; Material[] materials = proto.sharedMaterials; int submeshCount = prototypeCache[prototypeIndex].subMeshCount; int drawCount = Mathf.Min(submeshCount, materials.Length); Mesh billboardMesh = null; Material billboardMaterial = null; BillboardAsset billboard = proto.billboard; if (billboard != null) { billboardMesh = GBillboardUtilities.GetMesh(billboard); billboardMaterial = billboard.material; } bool canDrawInstanced = prototypeCache[prototypeIndex].canDrawInstanced; bool canDrawBillboardInstanced = prototypeCache[prototypeIndex].canDrawBillboardInstanced; batchInstanceCount = 0; billboardBatchInstanceCount = 0; int count = instancePrototypeIndices.Length; for (int i = 0; i <= count; ++i) { if (i == count || batchInstanceCount == BATCH_MAX_INSTANCE_COUNT) { if (canDrawInstanced) { for (int d = 0; d < drawCount; ++d) { Graphics.DrawMeshInstanced( mesh, d, materials[d], batchContainer, batchInstanceCount, null, proto.shadowCastingMode, proto.receiveShadow, proto.layer, camera, LightProbeUsage.BlendProbes); } batchInstanceCount = 0; } } if (i == count || billboardBatchInstanceCount == BATCH_MAX_INSTANCE_COUNT) { if (billboard != null && canDrawBillboardInstanced) { Graphics.DrawMeshInstanced( billboardMesh, 0, billboardMaterial, billboardBatchContainer, billboardBatchInstanceCount, null, proto.billboardShadowCastingMode, proto.BillboardReceiveShadow, proto.layer, camera, LightProbeUsage.BlendProbes); billboardBatchInstanceCount = 0; } } if (i == count) { break; } if (instanceCullResults[i] == CULLED) { continue; } if (instancePrototypeIndices[i] != prototypeIndex) { continue; } if (instanceCullResults[i] == VISIBLE) { if (canDrawInstanced) { batchContainer[batchInstanceCount] = instanceTransforms[i]; batchInstanceCount += 1; } else { for (int d = 0; d < drawCount; ++d) { Graphics.DrawMesh( mesh, instanceTransforms[i], materials[d], proto.layer, camera, d, null, proto.shadowCastingMode, proto.receiveShadow, null, LightProbeUsage.BlendProbes, null); } } } else if (instanceCullResults[i] == BILLBOARD && billboard != null) { if (canDrawBillboardInstanced) { billboardBatchContainer[billboardBatchInstanceCount] = instanceTransforms[i]; billboardBatchInstanceCount += 1; } else { Graphics.DrawMesh( billboardMesh, instanceTransforms[i], billboardMaterial, proto.layer, camera, 0, null, proto.billboardShadowCastingMode, proto.BillboardReceiveShadow, null, LightProbeUsage.BlendProbes, null); } } } }
private void InitFrame(Camera cam) { foliage = terrain.TerrainData.Foliage; terrainPosition = terrain.transform.position; terrainSize = terrain.TerrainData.Geometry.Size; treeDistance = terrain.TerrainData.Rendering.TreeDistance; billboardStart = terrain.TerrainData.Rendering.BillboardStart; cullVolumeBias = GRuntimeSettings.Instance.renderingDefault.cullVolumeBias; if (terrain.TerrainData.Foliage.Trees != null) { prototypes = terrain.TerrainData.Foliage.Trees.Prototypes; } else { prototypes = new List <GTreePrototype>(); } if (prototypeCache == null || prototypeCache.Length != prototypes.Count) { prototypeCache = new PrototypeCache[prototypes.Count]; } for (int i = 0; i < prototypes.Count; ++i) { GTreePrototype p = prototypes[i]; PrototypeCache cache = prototypeCache[i]; bool valid = prototypes[i].IsValid; cache.validation = valid; if (valid) { cache.subMeshCount = p.sharedMesh.subMeshCount; cache.canDrawInstanced = IsInstancingEnabledForAllMaterials(p); cache.canDrawBillboardInstanced = p.billboard != null && p.billboard.material != null && p.billboard.material.enableInstancing; } if (p.billboard != null) { cache.billboardMesh = GBillboardUtilities.GetMesh(p.billboard); } if (p.billboard != null && p.billboard.material != null) { if (cache.billboardImageTexcoords == null || cache.billboardImageTexcoords.Length != p.billboard.imageCount) { cache.billboardImageTexcoords = p.billboard.GetImageTexCoords(); } Material mat = p.billboard.material; mat.SetVectorArray(BILLBOARD_IMAGE_TEXCOORDS, cache.billboardImageTexcoords); mat.SetInt(BILLBOARD_IMAGE_COUNT, p.billboard.imageCount); } prototypeCache[i] = cache; } enableInstancing = terrain.TerrainData.Rendering.EnableInstancing && SystemInfo.supportsInstancing; normalizedToLocalMatrix = Matrix4x4.Scale(terrainSize); localToWorldMatrix = terrain.transform.localToWorldMatrix; normalizedToWorldMatrix = localToWorldMatrix * normalizedToLocalMatrix; camera = cam; if (frustum == null) { frustum = new Plane[6]; } GFrustumUtilities.Calculate(camera, frustum, treeDistance); if (nearFrustumCorners == null) { nearFrustumCorners = new Vector3[4]; } if (farFrustumCorners == null) { farFrustumCorners = new Vector3[4]; } if (batchContainer == null) { batchContainer = new Matrix4x4[BATCH_MAX_INSTANCE_COUNT]; } if (billboardBatchContainer == null) { billboardBatchContainer = new Matrix4x4[BATCH_MAX_INSTANCE_COUNT]; } if (!isWarningLogged) { for (int i = 0; i < prototypes.Count; ++i) { if (!prototypes[i].IsValid) { string msg = string.Format( "Tree prototye {0}: " + "The prototype is not valid, make sure you've assigned a prefab with correct mesh and materials setup.", i); Debug.LogWarning(msg); } if (enableInstancing && prototypes[i].IsValid) { if (!IsInstancingEnabledForAllMaterials(prototypes[i])) { string msg = string.Format( "Tree prototype {0} ({1}): " + "Instancing need to be enabled for all materials for the renderer to work at its best. " + "Otherwise it will fallback to non-instanced for this prototype.", i, prototypes[i].Prefab.name); Debug.LogWarning(msg); } if (prototypes[i].billboard != null && prototypes[i].billboard.material != null && prototypes[i].billboard.material.enableInstancing == false) { string msg = string.Format( "Tree prototype {0} ({1}): " + "Instancing need to be enabled for billboard material for the renderer to work at its best. " + "Otherwise it will fallback to non-instanced for this prototype when render billboards.", i, prototypes[i].Prefab.name); Debug.LogWarning(msg); } } } isWarningLogged = true; } }