public static GTreeInstance Create(int prototypeIndex) { GTreeInstance tree = new GTreeInstance(); tree.PrototypeIndex = prototypeIndex; tree.Position = Vector3.zero; tree.Rotation = Quaternion.identity; tree.Scale = Vector3.one; return(tree); }
private static void RenderTreesInstanced_NonInstanced(GStylizedTerrain t, Camera cam, List <int> flags, List <Vector3> worldPositions) { List <GTreePrototype> prototypes = t.TerrainData.Foliage.Trees.Prototypes; List <GTreeInstance> instances = t.TerrainData.Foliage.TreeInstances; int instanceCount = instances.Count; int subMeshCount = 0; int materialCount = 0; int drawCallCount = 0; int d; for (int i = 0; i < instanceCount; ++i) { if (flags[i] != FLAG_NON_INSTANCED) { continue; } GTreeInstance tree = instances[i]; GTreePrototype proto = prototypes[tree.PrototypeIndex]; subMeshCount = proto.SharedMesh.subMeshCount; materialCount = proto.SharedMaterials.Length; drawCallCount = Mathf.Min(subMeshCount, materialCount); for (d = 0; d < drawCallCount; ++d) { Graphics.DrawMesh( proto.SharedMesh, Matrix4x4.TRS(worldPositions[i] + Vector3.up * proto.PivotOffset, tree.Rotation * proto.BaseRotation, tree.Scale.Mul(proto.BaseScale)), proto.SharedMaterials[d], proto.Layer, cam, //camera d, //sub mesh index null, //properties block proto.ShadowCastingMode, proto.ReceiveShadow, null, //probe anchor LightProbeUsage.BlendProbes, null); //proxy volume } } }
private void CreateStaticObstacles(GStylizedTerrain t) { if (t.TerrainData == null) { return; } if (t.TerrainData.Foliage.Trees == null) { return; } if (t.TerrainData.Foliage.Trees.Prototypes.Count == 0) { return; } #if UNITY_EDITOR string title = "Creating static obstacles"; string info = t.name; GCommonGUI.CancelableProgressBar(title, info, 0); #endif Vector3 terrainSize = new Vector3( t.TerrainData.Geometry.Width, t.TerrainData.Geometry.Height, t.TerrainData.Geometry.Length); Transform root = GUtilities.GetChildrenWithName(transform, string.Format("{0}_{1}", t.name, t.GetInstanceID())); List <GTreePrototype> prototypes = t.TerrainData.Foliage.Trees.Prototypes; GameObject[] templates = new GameObject[prototypes.Count]; for (int i = 0; i < prototypes.Count; ++i) { if (!prototypes[i].IsValid) { continue; } GameObject template = Instantiate(prototypes[i].Prefab) as GameObject; Component[] components = template.GetComponentsInChildren <Component>(); for (int j = 0; j < components.Length; ++j) { if (components[j] is Collider) { GUtilities.DestroyObject(components[j]); } if (components[j] is MeshRenderer) { MeshRenderer mr = components[j] as MeshRenderer; mr.sharedMaterials = new Material[] { GInternalMaterials.NavHelperDummyGameObjectMaterial }; mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; mr.receiveShadows = false; } } #if UNITY_EDITOR GameObjectUtility.SetStaticEditorFlags(template, StaticEditorFlags.NavigationStatic); #endif template.name = prototypes[i].Prefab.name; templates[i] = template; } List <GTreeInstance> instances = t.TerrainData.Foliage.TreeInstances; for (int i = 0; i < instances.Count; ++i) { #if UNITY_EDITOR GCommonGUI.CancelableProgressBar(title, info, i * 1.0f / instances.Count); #endif GTreeInstance tree = instances[i]; if (templates[tree.PrototypeIndex] == null) { continue; } GameObject g = Instantiate(templates[tree.PrototypeIndex]) as GameObject; g.transform.parent = root; Vector3 localPos = new Vector3( tree.Position.x * terrainSize.x, tree.Position.y * terrainSize.y, tree.Position.z * terrainSize.z); Vector3 worldPos = t.transform.TransformPoint(localPos); g.transform.position = worldPos; g.transform.rotation = tree.Rotation; g.transform.localScale = tree.Scale; g.name = templates[tree.PrototypeIndex].name; } for (int i = 0; i < templates.Length; ++i) { GUtilities.DestroyGameobject(templates[i]); } #if UNITY_EDITOR GCommonGUI.ClearProgressBar(); #endif }
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; } GameObject actualTarget = null; if (Target != null) { actualTarget = Target; } else if (Camera.main != null) { actualTarget = Camera.main.gameObject; } if (actualTarget == null) { return; } Vector3 terrainSize = new Vector3( Terrain.TerrainData.Geometry.Width, Terrain.TerrainData.Geometry.Height, Terrain.TerrainData.Geometry.Length); Vector3 targetLocalPos = Terrain.transform.InverseTransformPoint(actualTarget.transform.position); Vector3 treeLocalPos = Vector3.zero; float sqrDistance = distance * distance; TreeInstances.Clear(); List <GTreeInstance> instances = Terrain.TerrainData.Foliage.TreeInstances; for (int i = 0; i < instances.Count; ++i) { GTreeInstance tree = instances[i]; treeLocalPos.Set( tree.Position.x * terrainSize.x, tree.Position.y * terrainSize.y, tree.Position.z * terrainSize.z); if (Vector3.SqrMagnitude(targetLocalPos - treeLocalPos) <= sqrDistance) { TreeInstances.Add(tree); } } Vector3 targetNormalizePos = Terrain.WorldPointToNormalized(actualTarget.transform.position); TreeInstances.Sort((t0, t1) => { float d0 = Vector3.SqrMagnitude(targetNormalizePos - t0.Position); float d1 = Vector3.SqrMagnitude(targetNormalizePos - t1.Position); return(d0.CompareTo(d1)); }); List <GTreePrototype> prototypes = Terrain.TerrainData.Foliage.Trees.Prototypes; int colliderIndex = 0; for (int i = 0; i < TreeInstances.Count; ++i) { GTreeInstance tree = TreeInstances[i]; GTreePrototype prototype = prototypes[tree.PrototypeIndex]; if (!prototype.HasCollider) { continue; } if (colliderIndex >= ColliderBudget) { break; } CapsuleCollider col = GetCollider(colliderIndex); colliderIndex += 1; Vector3 localPos = new Vector3( terrainSize.x * tree.Position.x, terrainSize.y * tree.Position.y, terrainSize.z * tree.Position.z); Vector3 worldPos = Terrain.transform.TransformPoint(localPos); 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; col.gameObject.tag = prototype.Prefab.tag; col.gameObject.SetActive(true); } for (int i = colliderIndex; i < ColliderBudget; ++i) { CapsuleCollider col = GetCollider(i); col.gameObject.SetActive(false); } }
private static void RenderTreesNonInstanced(GStylizedTerrain t, Camera cam) { if (t.TerrainData.Foliage.Trees == null || t.TerrainData.Foliage.Trees.Prototypes.Count == 0) { return; } List <GTreePrototype> prototypes = t.TerrainData.Foliage.Trees.Prototypes; List <GTreeInstance> instances = t.TerrainData.Foliage.TreeInstances; int prototypeCount = prototypes.Count; int instanceCount = instances.Count; bool[] prototypeValidation = new bool[prototypeCount]; int[] prototypeSubMeshCount = new int[prototypeCount]; for (int pIndex = 0; pIndex < prototypeCount; ++pIndex) { prototypeValidation[pIndex] = prototypes[pIndex].IsValid; prototypeSubMeshCount[pIndex] = prototypeValidation[pIndex] ? prototypes[pIndex].SharedMesh.subMeshCount : 0; if (prototypeValidation[pIndex] == true && prototypes[pIndex].Billboard != null && prototypes[pIndex].Billboard.material != null) { try { Material mat = prototypes[pIndex].Billboard.material; mat.SetVectorArray(IMAGE_TEXCOORDS_PROPERTY, prototypes[pIndex].Billboard.GetImageTexCoords()); mat.SetInt(IMAGE_COUNT_PROPERTY, prototypes[pIndex].Billboard.imageCount); } catch { } } } Quaternion billboardShadowCasterRotation = Quaternion.identity; Light[] directionalLights = Light.GetLights(LightType.Directional, 0); Light firstDirLight = null; if (directionalLights.Length > 0) { firstDirLight = directionalLights[0]; billboardShadowCasterRotation = Quaternion.Euler(0, firstDirLight.transform.root.eulerAngles.y, 0); } float sqrBillboardStart = t.TerrainData.Rendering.BillboardStart * t.TerrainData.Rendering.BillboardStart; float sqrTreeDistance = t.TerrainData.Rendering.TreeDistance * t.TerrainData.Rendering.TreeDistance; float sqrDistance = 0; Vector3 dimension = new Vector3( t.TerrainData.Geometry.Width, t.TerrainData.Geometry.Height, t.TerrainData.Geometry.Length); Vector3 localPos = Vector3.zero; Vector3 worldPos = Vector3.zero; int prototypeMaxIndex = prototypeCount - 1; GTreePrototype p = null; int subMeshCount = 0; int materialCount = 0; int drawCallCount = 0; int i = 0; int d = 0; Vector3 camLocalPos = t.transform.InverseTransformPoint(cam.transform.position); for (i = 0; i < instanceCount; ++i) { GTreeInstance tree = instances[i]; if (tree.PrototypeIndex < 0 || tree.PrototypeIndex > prototypeMaxIndex) { continue; } if (!prototypeValidation[tree.PrototypeIndex]) { continue; } localPos.Set( tree.Position.x * dimension.x, tree.Position.y * dimension.y, tree.Position.z * dimension.z); sqrDistance = Vector3.SqrMagnitude(localPos - camLocalPos); if (sqrDistance > sqrTreeDistance) { continue; } worldPos = t.transform.TransformPoint(localPos); p = prototypes[tree.PrototypeIndex]; if (sqrDistance < sqrBillboardStart) { subMeshCount = prototypeSubMeshCount[tree.PrototypeIndex]; materialCount = p.SharedMaterials.Length; drawCallCount = Mathf.Min(subMeshCount, materialCount); for (d = 0; d < drawCallCount; ++d) { Graphics.DrawMesh( p.SharedMesh, Matrix4x4.TRS(worldPos + Vector3.up * p.PivotOffset, p.BaseRotation * tree.Rotation, tree.Scale.Mul(p.BaseScale)), p.SharedMaterials[d], p.Layer, cam, //camera d, //sub mesh index null, //properties block p.ShadowCastingMode, p.ReceiveShadow, null, //probe anchor LightProbeUsage.BlendProbes, null); //proxy volume } } else { if (p.Billboard == null) { continue; } if (p.Billboard.material == null) { continue; } Vector3 lookDir = cam.transform.position - worldPos; lookDir.y = 0; Quaternion rotation = Quaternion.LookRotation(-lookDir, Vector3.up); Mesh billboardMesh = ResourceManager.GetBillboardMesh(p.Billboard); Graphics.DrawMesh( billboardMesh, Matrix4x4.TRS(worldPos + Vector3.up * p.PivotOffset, rotation, tree.Scale), p.Billboard.material, p.Layer, cam, //camera 0, //sub mesh index null, //properties block ShadowCastingMode.Off, false, //receive shadow null, //probe anchor LightProbeUsage.BlendProbes, null); //proxy volume if (p.ShadowCastingMode == ShadowCastingMode.Off) { continue; } if (firstDirLight == null) { continue; } Graphics.DrawMesh( billboardMesh, Matrix4x4.TRS(worldPos + Vector3.up * p.PivotOffset, billboardShadowCasterRotation, tree.Scale), p.Billboard.material, p.Layer, cam, //camera 0, //sub mesh index null, //properties block ShadowCastingMode.ShadowsOnly, false, //receive shadow null, //probe anchor LightProbeUsage.Off, null); //proxy volume } } }
private static void RenderTreesInstanced_InstancedBillboard(GStylizedTerrain t, Camera cam, List <int> flags, List <Vector3> worldPositions) { List <GTreePrototype> prototypes = t.TerrainData.Foliage.Trees.Prototypes; List <GTreeInstance> instances = t.TerrainData.Foliage.TreeInstances; int prototypeCount = prototypes.Count; int instanceCount = instances.Count; int batchInstanceCount = 0; Light[] directionalLights = Light.GetLights(LightType.Directional, 0); Light firstDirLight = null; if (directionalLights.Length > 0) { firstDirLight = directionalLights[0]; } Quaternion billboardShadowCasterRotation = Quaternion.Euler(0, firstDirLight.transform.root.eulerAngles.y, 0); for (int p = 0; p < prototypeCount; ++p) { for (int i = 0; i <= instanceCount; ++i) { //render batch if ((instanceCount == i || batchInstanceCount == MAX_INSTANCE_PER_BATCH) && batchInstanceCount > 0) { Mesh billboardMesh = ResourceManager.GetBillboardMesh(prototypes[p].Billboard); Graphics.DrawMeshInstanced( billboardMesh, 0, //submesh index prototypes[p].Billboard.material, batchMatrices, batchInstanceCount, null, //properties ShadowCastingMode.Off, false, //receive shadow prototypes[p].Layer, cam, LightProbeUsage.BlendProbes, null); //proxy volume if (prototypes[p].ShadowCastingMode == ShadowCastingMode.Off) { continue; } if (firstDirLight == null) { continue; } Graphics.DrawMeshInstanced( billboardMesh, 0, //submesh index prototypes[p].Billboard.material, batchMatricesShadowCaster, batchInstanceCount, null, //properties ShadowCastingMode.ShadowsOnly, false, //receive shadow prototypes[p].Layer, cam, LightProbeUsage.BlendProbes, null); //proxy volume batchInstanceCount = 0; } if (instanceCount == i) { break; } if (flags[i] != FLAG_INSTANCED_BILLBOARD) { continue; } GTreeInstance tree = instances[i]; if (tree.PrototypeIndex != p) { continue; } Vector3 lookDir = cam.transform.position - worldPositions[i]; lookDir.y = 0; Quaternion rotation = Quaternion.LookRotation(-lookDir, Vector3.up); batchMatrices[batchInstanceCount] = Matrix4x4.TRS(worldPositions[i] + Vector3.up * prototypes[p].PivotOffset, rotation, tree.Scale); batchMatricesShadowCaster[batchInstanceCount] = Matrix4x4.TRS(worldPositions[i] + Vector3.up * prototypes[p].PivotOffset, billboardShadowCasterRotation, tree.Scale); batchInstanceCount += 1; } } }
private static void RenderTreesInstanced_Instanced(GStylizedTerrain t, Camera cam, List <int> flags, List <Vector3> worldPositions) { List <GTreePrototype> prototypes = t.TerrainData.Foliage.Trees.Prototypes; List <GTreeInstance> instances = t.TerrainData.Foliage.TreeInstances; int prototypeCount = prototypes.Count; int instanceCount = instances.Count; int subMeshCount = 0; int materialCount = 0; int drawCallCount = 0; int batchInstanceCount = 0; for (int p = 0; p < prototypeCount; ++p) { for (int i = 0; i <= instanceCount; ++i) { //render batch if ((instanceCount == i || batchInstanceCount == MAX_INSTANCE_PER_BATCH) && batchInstanceCount > 0) { subMeshCount = prototypes[p].SharedMesh.subMeshCount; materialCount = prototypes[p].SharedMaterials.Length; drawCallCount = Mathf.Min(subMeshCount, materialCount); for (int d = 0; d < drawCallCount; ++d) { Graphics.DrawMeshInstanced( prototypes[p].SharedMesh, d, prototypes[p].SharedMaterials[d], batchMatrices, batchInstanceCount, null, //properties prototypes[p].ShadowCastingMode, prototypes[p].ReceiveShadow, prototypes[p].Layer, cam, LightProbeUsage.BlendProbes, null); //proxy volume } batchInstanceCount = 0; } if (instanceCount == i) { break; } if (flags[i] != FLAG_INSTANCED) { continue; } GTreeInstance tree = instances[i]; if (tree.PrototypeIndex != p) { continue; } batchMatrices[batchInstanceCount] = Matrix4x4.TRS( worldPositions[i] + Vector3.up * prototypes[p].PivotOffset, tree.Rotation * prototypes[p].BaseRotation, tree.Scale.Mul(prototypes[p].BaseScale)); batchInstanceCount += 1; } } }
private static void RenderTreesInstanced_NonInstancedBillboard(GStylizedTerrain t, Camera cam, List <int> flags, List <Vector3> worldPositions) { List <GTreePrototype> prototypes = t.TerrainData.Foliage.Trees.Prototypes; List <GTreeInstance> instances = t.TerrainData.Foliage.TreeInstances; int instanceCount = instances.Count; Light[] directionalLights = Light.GetLights(LightType.Directional, 0); Light firstDirLight = null; if (directionalLights.Length > 0) { firstDirLight = directionalLights[0]; } Quaternion billboardShadowCasterRotation = Quaternion.Euler(0, firstDirLight.transform.root.eulerAngles.y, 0); for (int i = 0; i < instanceCount; ++i) { if (flags[i] != FLAG_NON_INSTANCED_BILLBOARD) { continue; } GTreeInstance tree = instances[i]; GTreePrototype proto = prototypes[tree.PrototypeIndex]; Vector3 lookDir = cam.transform.position - worldPositions[i]; lookDir.y = 0; Quaternion rotation = Quaternion.LookRotation(-lookDir, Vector3.up); Mesh billboardMesh = ResourceManager.GetBillboardMesh(proto.Billboard); Graphics.DrawMesh( billboardMesh, Matrix4x4.TRS(worldPositions[i] + Vector3.up * proto.PivotOffset, rotation, instances[i].Scale), proto.Billboard.material, proto.Layer, cam, //camera 0, //sub mesh index null, //properties block ShadowCastingMode.Off, false, //receive shadow null, //probe anchor LightProbeUsage.BlendProbes, null); //proxy volume if (proto.ShadowCastingMode == ShadowCastingMode.Off) { continue; } if (firstDirLight == null) { continue; } Graphics.DrawMesh( billboardMesh, Matrix4x4.TRS(worldPositions[i], billboardShadowCasterRotation, instances[i].Scale), proto.Billboard.material, proto.Layer, cam, //camera 0, //sub mesh index null, //properties block ShadowCastingMode.ShadowsOnly, false, //receive shadow null, //probe anchor LightProbeUsage.Off, null); //proxy volume } }
private static void PreprocessRendering(GStylizedTerrain t, Camera cam, List <int> flags, List <Vector3> worldPositions) { flags.Clear(); worldPositions.Clear(); List <GTreePrototype> prototypes = t.TerrainData.Foliage.Trees.Prototypes; List <GTreeInstance> instances = t.TerrainData.Foliage.TreeInstances; int prototypeCount = prototypes.Count; int instanceCount = instances.Count; bool[] prototypeValidation = new bool[prototypeCount]; for (int pIndex = 0; pIndex < prototypeCount; ++pIndex) { prototypeValidation[pIndex] = prototypes[pIndex].IsValid; } float sqrTreeDistance = t.TerrainData.Rendering.TreeDistance * t.TerrainData.Rendering.TreeDistance; float sqrBillboardDistance = t.TerrainData.Rendering.BillboardStart * t.TerrainData.Rendering.BillboardStart; float sqrDistance = 0; Bounds bound = new Bounds(); Vector3 boundSize = Vector3.zero; Vector3 boundCenter = Vector3.zero; Vector3 dimension = new Vector3( t.TerrainData.Geometry.Width, t.TerrainData.Geometry.Height, t.TerrainData.Geometry.Length); Vector3 localPos = Vector3.zero; Vector3 worldPos = Vector3.zero; bool isInstancingEnabledForAllSharedMaterials = true; frustumPlanes = GeometryUtility.CalculateFrustumPlanes(cam); for (int i = 0; i < instanceCount; ++i) { GTreeInstance tree = instances[i]; //invalid prototype index if (tree.PrototypeIndex < 0 || tree.PrototypeIndex >= prototypeCount) { flags.Add(FLAG_CULLED); worldPositions.Add(Vector3.zero); continue; } if (prototypeValidation[tree.PrototypeIndex] == false) { flags.Add(FLAG_CULLED); worldPositions.Add(Vector3.zero); continue; } GTreePrototype proto = prototypes[tree.PrototypeIndex]; //cull layer if (cam.cullingMask >= 0 && (cam.cullingMask & (1 << proto.Layer)) == 0) { flags.Add(FLAG_CULLED); worldPositions.Add(Vector3.zero); continue; } localPos.Set( dimension.x * tree.Position.x, dimension.y * tree.Position.y, dimension.z * tree.Position.z); worldPos = t.transform.TransformPoint(localPos); sqrDistance = Vector3.SqrMagnitude(worldPos - cam.transform.position); //cull distance if (sqrDistance > sqrTreeDistance) { flags.Add(FLAG_CULLED); worldPositions.Add(Vector3.zero); continue; } //cull frustum boundSize.Set( proto.SharedMesh.bounds.size.x * tree.Scale.x, proto.SharedMesh.bounds.size.y * tree.Scale.y, proto.SharedMesh.bounds.size.z * tree.Scale.z); boundCenter = worldPos; bound.size = boundSize; bound.center = boundCenter; if (!GeometryUtility.TestPlanesAABB(frustumPlanes, bound)) { flags.Add(FLAG_CULLED); worldPositions.Add(Vector3.zero); continue; } //cull no billboard if (sqrDistance >= sqrBillboardDistance && (proto.Billboard == null || proto.Billboard.material == null)) { flags.Add(FLAG_CULLED); worldPositions.Add(Vector3.zero); continue; } //the object will be rendered worldPositions.Add(worldPos); //determine render mode if (sqrDistance >= sqrBillboardDistance) { if (proto.Billboard.material.enableInstancing) { flags.Add(FLAG_INSTANCED_BILLBOARD); } else { flags.Add(FLAG_NON_INSTANCED_BILLBOARD); } } else { isInstancingEnabledForAllSharedMaterials = true; for (int mIndex = 0; mIndex < proto.SharedMaterials.Length; ++mIndex) { if (!proto.SharedMaterials[mIndex].enableInstancing) { isInstancingEnabledForAllSharedMaterials = false; break; } } if (isInstancingEnabledForAllSharedMaterials) { flags.Add(FLAG_INSTANCED); } else { flags.Add(FLAG_NON_INSTANCED); } } } }
public int GetTreeMemStats() { return(TreeInstances.Count * GTreeInstance.GetStructSize()); }