public void Add(GGrassPrototype p) { if (p == null) { return; } if (p.Shape == GGrassShape.DetailObject) { if (p.Prefab == null) { return; } Texture t = AssetPreview.GetAssetPreview(p.Prefab); if (t != null) { Textures.Add(t); Colors.Add(Color.white); } } else { if (p.Texture == null) { return; } Textures.Add(p.Texture); Colors.Add(p.Color); } }
private void DrawAddPrototypeGUI() { EditorGUILayout.GetControlRect(GUILayout.Height(1)); Rect r0 = EditorGUILayout.GetControlRect(GUILayout.Height(GEditorCommon.objectSelectorDragDropHeight)); Texture2D t = GEditorCommon.ObjectSelectorDragDrop <Texture2D>(r0, "Drop a Texture here!", "t:Texture2D"); if (t != null) { GGrassPrototype g = GGrassPrototype.Create(t); instance.Prototypes.Add(g); RefreshInstanceList(); EditorUtility.SetDirty(instance); } EditorGUILayout.GetControlRect(GUILayout.Height(1)); Rect r1 = EditorGUILayout.GetControlRect(GUILayout.Height(GEditorCommon.objectSelectorDragDropHeight)); GameObject prefab = GEditorCommon.ObjectSelectorDragDrop <GameObject>(r1, "Drop a Game Object here!", "t:GameObject"); if (prefab != null) { GGrassPrototype p = GGrassPrototype.Create(prefab); instance.Prototypes.Add(p); RefreshInstanceList(); EditorUtility.SetDirty(instance); } }
private void ConfirmAndRemovePrototypeAtIndex(int index) { GGrassPrototype p = instance.Prototypes[index]; string label = p.Texture != null ? p.Texture.name : "Grass " + index; if (EditorUtility.DisplayDialog( "Confirm", "Remove " + label, "OK", "Cancel")) { instance.Prototypes.RemoveAt(index); } }
public static GGrassPrototype Create(GameObject prefab) { GGrassPrototype prototype = new GGrassPrototype(); prototype.Shape = GGrassShape.DetailObject; prototype.Prefab = prefab; prototype.Size = Vector3.one; prototype.Layer = LayerMask.NameToLayer("Default"); prototype.AlignToSurface = false; prototype.PivotOffset = 0; prototype.BendFactor = 1; prototype.Color = Color.white; return(prototype); }
private void DrawPrototypesListGUI() { for (int i = 0; i < instance.Prototypes.Count; ++i) { GGrassPrototype p = instance.Prototypes[i]; string label = string.Empty; if (p.Shape != GGrassShape.DetailObject) { label = p.Texture != null && !string.IsNullOrEmpty(p.Texture.name) ? p.Texture.name : "Grass " + i; } else { label = p.Prefab != null && !string.IsNullOrEmpty(p.Prefab.name) ? p.Prefab.name : "Grass " + i; } string id = "grassprototype" + i + instance.GetInstanceID().ToString(); int index = i; GenericMenu menu = new GenericMenu(); menu.AddItem( new GUIContent("Remove"), false, () => { ConfirmAndRemovePrototypeAtIndex(index); }); GEditorCommon.Foldout(label, false, id, () => { if (p.Shape != GGrassShape.DetailObject) { p.Texture = EditorGUILayout.ObjectField("Texture", p.Texture, typeof(Texture2D), false) as Texture2D; if (p.Shape == GGrassShape.CustomMesh) { p.CustomMesh = EditorGUILayout.ObjectField("Mesh", p.CustomMesh, typeof(Mesh), false) as Mesh; } } else { DrawPreview(p.Prefab); p.Prefab = EditorGUILayout.ObjectField("Prefab", p.Prefab, typeof(GameObject), false) as GameObject; } p.Color = EditorGUILayout.ColorField("Color", p.Color); p.Size = GEditorCommon.InlineVector3Field("Size", p.Size); p.BendFactor = EditorGUILayout.FloatField("Bend Factor", p.BendFactor); p.PivotOffset = EditorGUILayout.Slider("Pivot Offset", p.PivotOffset, -1f, 1f); p.Layer = EditorGUILayout.LayerField("Layer", p.Layer); p.Shape = (GGrassShape)EditorGUILayout.EnumPopup("Shape", p.Shape); p.AlignToSurface = EditorGUILayout.Toggle("Align To Surface", p.AlignToSurface); }, menu); } }
public static GGrassPrototype Create(Texture2D tex) { GGrassPrototype prototype = new GGrassPrototype(); prototype.Shape = GGrassShape.Quad; prototype.Texture = tex; prototype.Size = Vector3.one; prototype.Layer = LayerMask.NameToLayer("Default"); prototype.AlignToSurface = false; prototype.PivotOffset = 0; prototype.BendFactor = 1; prototype.Color = Color.white; return(prototype); }
private void DrawPrototypesListGUI() { for (int i = 0; i < instance.Prototypes.Count; ++i) { GGrassPrototype p = instance.Prototypes[i]; string label = string.Empty; if (p.Shape != GGrassShape.DetailObject) { label = p.Texture != null && !string.IsNullOrEmpty(p.Texture.name) ? p.Texture.name : "Grass " + i; } else { label = p.Prefab != null && !string.IsNullOrEmpty(p.Prefab.name) ? p.Prefab.name : "Grass " + i; } string id = "grassprototype" + i + instance.GetInstanceID().ToString(); int index = i; GenericMenu menu = new GenericMenu(); menu.AddItem( new GUIContent("Remove"), false, () => { ConfirmAndRemovePrototypeAtIndex(index); }); GEditorCommon.Foldout(label, false, id, () => { EditorGUI.BeginChangeCheck(); if (p.Shape != GGrassShape.DetailObject) { p.Texture = EditorGUILayout.ObjectField("Texture", p.Texture, typeof(Texture2D), false) as Texture2D; p.Shape = (GGrassShape)EditorGUILayout.EnumPopup("Shape", p.Shape); if (p.Shape == GGrassShape.CustomMesh) { p.CustomMesh = EditorGUILayout.ObjectField("Mesh", p.CustomMesh, typeof(Mesh), false) as Mesh; } } else { DrawPreview(p.Prefab); p.Shape = (GGrassShape)EditorGUILayout.EnumPopup("Shape", p.Shape); p.Prefab = EditorGUILayout.ObjectField("Prefab", p.Prefab, typeof(GameObject), false) as GameObject; GUI.enabled = false; EditorGUILayout.ObjectField("Material", p.DetailMaterial, typeof(Material), false); GUI.enabled = true; } p.Color = EditorGUILayout.ColorField("Color", p.Color); if (EditorGUI.EndChangeCheck()) { EditorUtility.SetDirty(instance); } EditorGUI.BeginChangeCheck(); p.Size = GEditorCommon.InlineVector3Field("Size", p.Size); p.PivotOffset = EditorGUILayout.DelayedFloatField("Pivot Offset", p.PivotOffset); if (EditorGUI.EndChangeCheck()) { ResetNativeArrays(); EditorUtility.SetDirty(instance); } p.BendFactor = EditorGUILayout.FloatField("Bend Factor", p.BendFactor); p.Layer = EditorGUILayout.LayerField("Layer", p.Layer); p.AlignToSurface = EditorGUILayout.Toggle("Align To Surface", p.AlignToSurface); p.ShadowCastingMode = (ShadowCastingMode)EditorGUILayout.EnumPopup("Cast Shadow", p.ShadowCastingMode); p.ReceiveShadow = EditorGUILayout.Toggle("Receive Shadow", p.ReceiveShadow); if (p.Shape != GGrassShape.DetailObject) { p.IsBillboard = EditorGUILayout.Toggle("Billboard", p.IsBillboard); } if (p.Shape == GGrassShape.DetailObject) { EditorGUILayout.LabelField("Detail Object uses the first sub-mesh and material found in its prefab, and may NOT affected by wind.", GEditorCommon.WarningLabel); if (!p.DetailMaterial.enableInstancing) { EditorGUILayout.LabelField("Prototype's material has GPU Instancing option turned off. This prototype will not be rendered.", GEditorCommon.WarningLabel); } } if (p.Shape != GGrassShape.DetailObject && p.IsBillboard) { EditorGUILayout.LabelField("Billboard will not work if Interactive Grass is enabled.", GEditorCommon.WarningLabel); } }, menu); } }
private static void RenderPreviewGrass(GStylizedTerrain t, Camera cam) { if (t.TerrainData.Foliage.Grasses == null || t.TerrainData.Foliage.Grasses.Prototypes.Count == 0) { return; } List <GGrassPrototype> prototypes = t.TerrainData.Foliage.Grasses.Prototypes; Vector3 dimension = new Vector3( t.TerrainData.Geometry.Width, t.TerrainData.Geometry.Height, t.TerrainData.Geometry.Length); Vector3 localPos = Vector3.zero; Vector3 worldPos = Vector3.zero; Vector3 scale = Vector3.zero; Material mat = GGriffinSettings.Instance.TerrainDataDefault.Foliage.GrassPreviewMaterial; Rect[] dirtyRects = t.TerrainData.Foliage.GetGrassDirtyRegions(); GGrassPatch[] patches = t.TerrainData.Foliage.GrassPatches; bool[] dirtyFlags = new bool[patches.Length]; for (int p = 0; p < patches.Length; ++p) { dirtyFlags[p] = false; Rect uvRange = patches[p].GetUvRange(); for (int r = 0; r < dirtyRects.Length; ++r) { if (uvRange.Overlaps(dirtyRects[r])) { dirtyFlags[p] = true; break; } } } for (int pIndex = 0; pIndex < prototypes.Count; ++pIndex) { GGrassPrototype proto = prototypes[pIndex]; //mat.SetTexture(MAINTEX_PROPERTY, proto.Texture); mat.SetPass(0); for (int p = 0; p < patches.Length; ++p) { if (dirtyFlags[p] == false) { continue; } int instanceCount = patches[p].Instances.Count; for (int j = 0; j < instanceCount; ++j) { GGrassInstance grass = patches[p].Instances[j]; if (grass.PrototypeIndex != pIndex) { continue; } localPos.Set( dimension.x * grass.Position.x, dimension.y * grass.Position.y, dimension.z * grass.Position.z); worldPos = t.transform.TransformPoint(localPos); scale.Set( prototypes[grass.PrototypeIndex].Size.x * grass.Scale.x, prototypes[grass.PrototypeIndex].Size.y * grass.Scale.y, prototypes[grass.PrototypeIndex].Size.z * grass.Scale.z); Mesh mesh = prototypes[grass.PrototypeIndex].GetBaseMesh(); Graphics.DrawMeshNow( mesh, Matrix4x4.TRS(worldPos, grass.Rotation, scale)); } } } }
private static void RenderGrass(GStylizedTerrain t, Camera cam) { if (t.TerrainData.Foliage.Grasses == null || t.TerrainData.Foliage.Grasses.Prototypes.Count == 0) { return; } List <GGrassPrototype> prototypes = t.TerrainData.Foliage.Grasses.Prototypes; GGrassPatch[] patches = t.TerrainData.Foliage.GrassPatches; if (patches == null) { return; } float grassDistance = t.TerrainData.Rendering.GrassDistance; Vector3 dimension = new Vector3( t.TerrainData.Geometry.Width, t.TerrainData.Geometry.Height, t.TerrainData.Geometry.Length); float patchWorldRadius = dimension.x * 0.5f / t.TerrainData.Foliage.PatchGridSize; Vector3 boundLocalPos = Vector3.zero; Vector3 boundWorldPos = Vector3.zero; int FLAG_CULLED = 0; int FLAG_DIRTY = 1; int FLAG_BATCHED = 2; int[] flags = new int[patches.Length]; Rect[] dirtyRect = t.TerrainData.Foliage.GetGrassDirtyRegions(); for (int i = 0; i < patches.Length; ++i) { Rect uvRect = patches[i].GetUvRange(); boundLocalPos.Set( uvRect.center.x * dimension.x, 0, uvRect.center.y * dimension.z); boundWorldPos = t.transform.TransformPoint(boundLocalPos); boundWorldPos.y = cam.transform.position.y; if (Vector3.Distance(boundWorldPos, cam.transform.position) - patchWorldRadius > grassDistance) { flags[i] = FLAG_CULLED; continue; } for (int r = 0; r < dirtyRect.Length; ++r) { if (dirtyRect[r].Overlaps(uvRect)) { flags[i] = FLAG_DIRTY; break; } } if (flags[i] == FLAG_DIRTY) { continue; } flags[i] = FLAG_BATCHED; } Vector3 meshLocalPos = Vector3.zero; Vector3 meshWorldPos = Vector3.zero; grassMaterialPropertyBlock.Clear(); if (t.TerrainData.Foliage.EnableInteractiveGrass) { grassMaterialPropertyBlock.SetTexture(VECTOR_FIELD_PROPERTY, t.GetGrassVectorFieldRenderTexture()); grassMaterialPropertyBlock.SetMatrix(WORLD_TO_NORMALIZED_PROPERTY, t.GetWorldToNormalizedMatrix()); } grassMaterialPropertyBlock.SetTexture(NOISETEX_PROPERTY, GGriffinSettings.Instance.DefaultNoiseTexture); IEnumerator <GWindZone> windZone = GWindZone.ActiveWindZones.GetEnumerator(); if (windZone.MoveNext()) { GWindZone w = windZone.Current; grassMaterialPropertyBlock.SetVector(WIND_PROPERTY, w.GetWindParams()); } //draw batched //Material grassMat = GCommon.CurrentRenderPipeline == GRenderPipelineType.Lightweight ? // GGriffinSettings.Instance.TerrainDataDefault.Foliage.GrassMaterialLW : // GGriffinSettings.Instance.TerrainDataDefault.Foliage.GrassMaterial; Material grassMat = null; if (GCommon.CurrentRenderPipeline == GRenderPipelineType.Builtin) { grassMat = t.TerrainData.Foliage.EnableInteractiveGrass ? GGriffinSettings.Instance.TerrainDataDefault.Foliage.GrassInteractiveMaterial : GGriffinSettings.Instance.TerrainDataDefault.Foliage.GrassMaterial; if (grassMat == null) { Debug.Log("Grass material missing. Try re-installing the Polaris V2 package."); } } else if (GCommon.CurrentRenderPipeline == GRenderPipelineType.Lightweight) { grassMat = t.TerrainData.Foliage.EnableInteractiveGrass ? GGriffinSettings.Instance.TerrainDataDefault.Foliage.GrassInteractiveMaterialLWRP : GGriffinSettings.Instance.TerrainDataDefault.Foliage.GrassMaterialLWRP; if (grassMat == null) { Debug.Log("Grass material missing. Try installing the 'Lightweight Render Pipeline Support' extension from the Extension Window (Window>Griffin>Tools>Extension)"); } } else if (GCommon.CurrentRenderPipeline == GRenderPipelineType.Universal) { grassMat = t.TerrainData.Foliage.EnableInteractiveGrass ? GGriffinSettings.Instance.TerrainDataDefault.Foliage.GrassInteractiveMaterialURP : GGriffinSettings.Instance.TerrainDataDefault.Foliage.GrassMaterialURP; if (grassMat == null) { Debug.Log("Grass material missing. Try installing the 'Universal Render Pipeline Support' extension from the Extension Window (Window>Griffin>Tools>Extension)"); } } if (grassMat == null) { return; } for (int i = 0; i < patches.Length; ++i) { if (flags[i] != FLAG_BATCHED) { continue; } for (int p = 0; p < prototypes.Count; ++p) { grassMaterialPropertyBlock.SetColor(COLOR_PROPERTY, prototypes[p].Color); grassMaterialPropertyBlock.SetFloat(BEND_FACTOR_PROPERTY, prototypes[p].BendFactor); if (prototypes[p].Shape != GGrassShape.DetailObject && prototypes[p].Texture != null) { grassMaterialPropertyBlock.SetTexture(MAINTEX_PROPERTY, prototypes[p].Texture); } Mesh mesh = patches[i].GetMesh(p); if (mesh != null) { meshLocalPos = mesh.bounds.center; meshWorldPos = t.transform.TransformPoint(meshLocalPos); if (Vector3.Distance(meshWorldPos, cam.transform.position) - patchWorldRadius > grassDistance) { continue; } Graphics.DrawMesh( mesh, Matrix4x4.TRS(t.transform.position, Quaternion.identity, Vector3.one), prototypes[p].Shape == GGrassShape.DetailObject ? prototypes[p].DetailMaterial : grassMat, prototypes[p].Layer, cam, 0, prototypes[p].Shape == GGrassShape.DetailObject ? null : grassMaterialPropertyBlock, prototypes[p].Shape == GGrassShape.DetailObject ? prototypes[p].ShadowCastingMode : ShadowCastingMode.On, prototypes[p].Shape == GGrassShape.DetailObject ? prototypes[p].ReceiveShadow : true, null, LightProbeUsage.BlendProbes, null); } } } if (GCommon.CurrentRenderPipeline == GRenderPipelineType.Builtin) { return; } Vector3 localPos = Vector3.zero; Vector3 worldPos = Vector3.zero; Vector3 scale = Vector3.zero; Material previewMat = GGriffinSettings.Instance.TerrainDataDefault.Foliage.GrassPreviewMaterial; for (int pIndex = 0; pIndex < prototypes.Count; ++pIndex) { GGrassPrototype proto = prototypes[pIndex]; //grassMaterialPropertyBlock.Clear(); //grassMaterialPropertyBlock.SetTexture(MAINTEX_PROPERTY, proto.Texture); for (int i = 0; i < patches.Length; ++i) { if (flags[i] != FLAG_DIRTY) { continue; } GGrassPatch patch = patches[i]; int instanceCount = patch.Instances.Count; for (int j = 0; j < instanceCount; ++j) { GGrassInstance grass = patch.Instances[j]; if (grass.PrototypeIndex != pIndex) { continue; } localPos.Set( dimension.x * grass.Position.x, dimension.y * grass.Position.y, dimension.z * grass.Position.z); worldPos = t.transform.TransformPoint(localPos); scale.Set( prototypes[grass.PrototypeIndex].Size.x * grass.Scale.x, prototypes[grass.PrototypeIndex].Size.y * grass.Scale.y, prototypes[grass.PrototypeIndex].Size.z * grass.Scale.z); Mesh mesh = prototypes[grass.PrototypeIndex].GetBaseMesh(); Graphics.DrawMesh( mesh, Matrix4x4.TRS(worldPos, grass.Rotation, scale), previewMat, prototypes[grass.PrototypeIndex].Layer, cam, 0, null, //grassMaterialPropertyBlock ShadowCastingMode.On, true, null, LightProbeUsage.BlendProbes, null); } } } }
public void UpdateMesh(int prototypeIndex) { if (Foliage.Grasses == null) { return; } if (prototypeIndex < 0 || prototypeIndex >= Foliage.Grasses.Prototypes.Count) { return; } StripInstances(prototypeIndex); string key = GetPatchMeshName(Index, prototypeIndex); Mesh m = Foliage.TerrainData.FoliageData.GetMesh(key); if (m != null && Foliage.TerrainData.Foliage.GrassInstanceCount == 0) { Foliage.TerrainData.FoliageData.DeleteMesh(key); return; } else if (m == null) { m = new Mesh(); m.MarkDynamic(); m.name = key; Foliage.TerrainData.FoliageData.SetMesh(key, m); } GGrassPrototype prototype = Foliage.Grasses.Prototypes[prototypeIndex]; Mesh baseMesh = prototype.GetBaseMesh(); Vector3 terrainSize = new Vector3( Foliage.TerrainData.Geometry.Width, Foliage.TerrainData.Geometry.Height, Foliage.TerrainData.Geometry.Length); GCombineInfo meshTemplate = new GCombineInfo(baseMesh); List <Matrix4x4> transforms = new List <Matrix4x4>(); Rect uvRange = GetUvRange(); for (int i = 0; i < Instances.Count; ++i) { GGrassInstance grass = Instances[i]; if (grass.PrototypeIndex != prototypeIndex) { continue; } if (!uvRange.Contains(new Vector2(grass.Position.x, grass.Position.z))) { continue; } Matrix4x4 t = Matrix4x4.TRS( new Vector3(grass.Position.x * terrainSize.x, grass.Position.y * terrainSize.y + prototype.PivotOffset, grass.Position.z * terrainSize.z), grass.Rotation, new Vector3(prototype.Size.x * grass.Scale.x, prototype.Size.y * grass.Scale.y, prototype.Size.z * grass.Scale.z)); transforms.Add(t); } int vertexCount = meshTemplate.Vertices.Length * transforms.Count; if (vertexCount > 65000) { string warning = string.Format("Failed to batch grass meshes at patch {0}, prototypeIndex {1} due to vertices limit (batch: {2}, limit: 65000). Consider removing some instances or increase Patch Grid Size and try again!", Index.ToString("0"), prototypeIndex, vertexCount); Debug.LogWarning(warning); } GJobSystem.RunOnBackground(() => { GCombineInfo result = GCombiner.Combine(meshTemplate, transforms); System.Action task = () => { if (result.Vertices.Length == 0) { Foliage.TerrainData.FoliageData.DeleteMesh(key); } else { m.Clear(); m.vertices = result.Vertices; m.uv = result.UVs; //m.colors32 = result.Colors; m.triangles = result.Triangles; m.RecalculateBounds(); //Reduce mesh storage space m.colors32 = null; m.normals = null; m.tangents = null; m.uv2 = null; //m.RecalculateNormals(); //m.RecalculateTangents(); GCommon.SetDirty(m); //System.GC.Collect(); ==>This line will cause the editor to hang soooooo long! } }; if (result.Vertices.Length < 1000) { GJobSystem.RunOnMainThread(task); } else { GJobSystem.ScheduleOnMainThread(task, 0); } }); }