public static void FixMissingPrefab() { Object o = Selection.activeObject; if (o is GTreePrototypeGroup) { string[] prefabAssetPaths = new string[] { "Assets/Griffin - PolarisV2/_Demo/Prefabs/AutumnTree1.prefab", "Assets/Griffin - PolarisV2/_Demo/Prefabs/SpringTree1.prefab", "Assets/Griffin - PolarisV2/_Demo/Prefabs/Pine_00.prefab", "Assets/Griffin - PolarisV2/_Demo/Prefabs/Pine_01.prefab", "Assets/Griffin - PolarisV2/_Demo/Prefabs/Dead.prefab", "Assets/Griffin - PolarisV2/_Demo/Prefabs/Dead_Break.prefab" }; GTreePrototypeGroup group = o as GTreePrototypeGroup; group.Prototypes.Clear(); for (int i = 0; i < prefabAssetPaths.Length; ++i) { GameObject p = AssetDatabase.LoadAssetAtPath <GameObject>(prefabAssetPaths[i]); GTreePrototype proto = GTreePrototype.Create(p); proto.Refresh(); group.Prototypes.Add(proto); } EditorUtility.SetDirty(group); } }
public static void FixMissingTreeDemo00() { Object o = Selection.activeObject; if (o is GTreePrototypeGroup) { string[] prefabAssetPaths = new string[] { "Assets/Polaris - Low Poly Ecosystem/Polaris - Low Poly Terrain Engine/Samples/Pinwheel Studio/Prefabs/Pine_00.prefab", "Assets/Polaris - Low Poly Ecosystem/Polaris - Low Poly Terrain Engine/Samples/Pinwheel Studio/Prefabs/Dead.prefab", "Assets/Polaris - Low Poly Ecosystem/Polaris - Low Poly Terrain Engine/Samples/Pinwheel Studio/Prefabs/Pine_01.prefab", "Assets/Polaris - Low Poly Ecosystem/Polaris - Low Poly Terrain Engine/Samples/Pinwheel Studio/Prefabs/Dead_Break.prefab" }; GTreePrototypeGroup group = o as GTreePrototypeGroup; group.Prototypes.Clear(); for (int i = 0; i < prefabAssetPaths.Length; ++i) { GameObject p = AssetDatabase.LoadAssetAtPath <GameObject>(prefabAssetPaths[i]); GTreePrototype proto = GTreePrototype.Create(p); proto.Refresh(); group.Prototypes.Add(proto); } EditorUtility.SetDirty(group); } }
private void DrawPrototypesListGUI() { string label, id; for (int i = 0; i < instance.Prototypes.Count; ++i) { GTreePrototype p = instance.Prototypes[i]; CachePrefabPath(p); label = p.Prefab != null && !string.IsNullOrEmpty(p.Prefab.name) ? p.Prefab.name : "Tree " + i; id = "treeprototype" + i + instance.GetInstanceID().ToString(); int index = i; GenericMenu menu = new GenericMenu(); menu.AddItem( new GUIContent("Remove"), false, () => { ConfirmAndRemovePrototypeAtIndex(index); }); menu.AddItem( new GUIContent("Sync with Prefab"), false, () => { p.Refresh(); }); GEditorCommon.Foldout(label, false, id, () => { if (p.Prefab != null) { DrawPreview(p.Prefab); } p.Prefab = EditorGUILayout.ObjectField("Prefab", p.Prefab, typeof(GameObject), false) as GameObject; p.Billboard = EditorGUILayout.ObjectField("Billboard", p.Billboard, typeof(BillboardAsset), false) as BillboardAsset; EditorGUI.BeginChangeCheck(); p.PivotOffset = EditorGUILayout.Slider("Pivot Offset", p.PivotOffset, -1f, 1f); p.BaseRotation = Quaternion.Euler(GEditorCommon.InlineVector3Field("Base Rotation", p.BaseRotation.eulerAngles)); p.BaseScale = GEditorCommon.InlineVector3Field("Base Scale", p.BaseScale); if (EditorGUI.EndChangeCheck()) { ResetNativeArrays(); } GUI.enabled = !p.KeepPrefabLayer; p.Layer = EditorGUILayout.LayerField("Layer", p.Layer); GUI.enabled = true; p.KeepPrefabLayer = EditorGUILayout.Toggle("Keep Prefab Layer", p.KeepPrefabLayer); p.ShadowCastingMode = (ShadowCastingMode)EditorGUILayout.EnumPopup("Cast Shadow", p.ShadowCastingMode); p.ReceiveShadow = EditorGUILayout.Toggle("Receive Shadow", p.ReceiveShadow); p.BillboardShadowCastingMode = (ShadowCastingMode)EditorGUILayout.EnumPopup("Billboard Cast Shadow", p.BillboardShadowCastingMode); p.BillboardReceiveShadow = EditorGUILayout.Toggle("Billboard Receive Shadow", p.BillboardReceiveShadow); GUI.enabled = false; EditorGUILayout.Toggle("Has Collider", p.HasCollider); GUI.enabled = true; }, menu); } }
public static GTreePrototype Create(GameObject g) { GTreePrototype prototype = new GTreePrototype(); prototype.Prefab = g; prototype.PivotOffset = 0; prototype.BaseRotation = Quaternion.identity; prototype.BaseScale = Vector3.one; return(prototype); }
private void CachePrefabPath(GTreePrototype p) { if (p.Prefab == null) { p.Editor_PrefabAssetPath = null; } else { p.Editor_PrefabAssetPath = AssetDatabase.GetAssetPath(p.Prefab); } }
private void DrawAddPrototypeGUI() { EditorGUILayout.GetControlRect(GUILayout.Height(1)); Rect r = EditorGUILayout.GetControlRect(GUILayout.Height(GEditorCommon.objectSelectorDragDropHeight)); GameObject g = GEditorCommon.ObjectSelectorDragDrop <GameObject>(r, "Drop a Game Object here!", "t:GameObject"); if (g != null) { GTreePrototype p = GTreePrototype.Create(g); instance.Prototypes.Add(p); } }
private void ConfirmAndRemovePrototypeAtIndex(int index) { GTreePrototype p = instance.Prototypes[index]; string label = p.Prefab != null ? p.Prefab.name : "Tree " + index; if (EditorUtility.DisplayDialog( "Confirm", "Remove " + label, "OK", "Cancel")) { instance.Prototypes.RemoveAt(index); } }
public void Add(GTreePrototype p) { if (p == null) { return; } if (p.Prefab == null) { return; } Texture t = AssetPreview.GetAssetPreview(p.Prefab); if (t != null) { Textures.Add(t); Colors.Add(Color.white); } }
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 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_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); } } } }