public void AddGrassInstances(List <GGrassInstance> instances) { Rect[] uvRects = new Rect[GrassPatches.Length]; for (int r = 0; r < uvRects.Length; ++r) { uvRects[r] = GrassPatches[r].GetUvRange(); } int[] dirty = new int[GrassPatches.Length]; for (int i = 0; i < instances.Count; ++i) { GGrassInstance grass = instances[i]; for (int r = 0; r < uvRects.Length; ++r) { if (uvRects[r].Contains(new Vector2(grass.Position.x, grass.Position.z))) { grassPatches[r].Instances.Add(grass); dirty[r] = 1; break; } } } for (int i = 0; i < dirty.Length; ++i) { if (dirty[i] == 1) { GrassPatches[i].RecalculateBounds(); GrassPatches[i].Changed(); } } }
public static GGrassInstance Create(int prototypeIndex) { GGrassInstance instance = new GGrassInstance(); instance.PrototypeIndex = prototypeIndex; instance.Position = Vector3.zero; instance.Rotation = Quaternion.identity; instance.Scale = Vector3.one; return(instance); }
internal void Deserialize() { if (prototypeIndexSerializeData != null && positionSerializeData != null && rotationSerializeData != null && scaleSerializeData != null) { prototypeIndexSerializeData = GCompressor.Decompress(prototypeIndexSerializeData); positionSerializeData = GCompressor.Decompress(positionSerializeData); rotationSerializeData = GCompressor.Decompress(rotationSerializeData); scaleSerializeData = GCompressor.Decompress(scaleSerializeData); int[] indices = new int[prototypeIndexSerializeData.Length / sizeof(int)]; float[] positions = new float[positionSerializeData.Length / sizeof(float)]; float[] rotations = new float[rotationSerializeData.Length / sizeof(float)]; float[] scales = new float[scaleSerializeData.Length / sizeof(float)]; Buffer.BlockCopy(prototypeIndexSerializeData, 0, indices, 0, prototypeIndexSerializeData.Length); Buffer.BlockCopy(positionSerializeData, 0, positions, 0, positionSerializeData.Length); Buffer.BlockCopy(rotationSerializeData, 0, rotations, 0, rotationSerializeData.Length); Buffer.BlockCopy(scaleSerializeData, 0, scales, 0, scaleSerializeData.Length); Instances.Clear(); for (int i = 0; i < indices.Length; ++i) { GGrassInstance grass = GGrassInstance.Create(indices[i]); grass.Position = new Vector3( positions[i * 3 + 0], positions[i * 3 + 1], positions[i * 3 + 2]); grass.Rotation = new Quaternion( rotations[i * 4 + 0], rotations[i * 4 + 1], rotations[i * 4 + 2], rotations[i * 4 + 3]); grass.Scale = new Vector3( scales[i * 3 + 0], scales[i * 3 + 1], scales[i * 3 + 2]); Instances.Add(grass); } } }
internal void Serialize() { int[] protoIndices = new int[Instances.Count]; float[] positions = new float[Instances.Count * 3]; float[] rotations = new float[Instances.Count * 4]; float[] scales = new float[Instances.Count * 3]; for (int i = 0; i < Instances.Count; ++i) { GGrassInstance grass = Instances[i]; protoIndices[i] = grass.PrototypeIndex; positions[i * 3 + 0] = grass.Position.x; positions[i * 3 + 1] = grass.Position.y; positions[i * 3 + 2] = grass.Position.z; rotations[i * 4 + 0] = grass.Rotation.x; rotations[i * 4 + 1] = grass.Rotation.y; rotations[i * 4 + 2] = grass.Rotation.z; rotations[i * 4 + 3] = grass.Rotation.w; scales[i * 3 + 0] = grass.Scale.x; scales[i * 3 + 1] = grass.Scale.y; scales[i * 3 + 2] = grass.Scale.z; } prototypeIndexSerializeData = new byte[Buffer.ByteLength(protoIndices)]; Buffer.BlockCopy(protoIndices, 0, prototypeIndexSerializeData, 0, prototypeIndexSerializeData.Length); prototypeIndexSerializeData = GCompressor.Compress(prototypeIndexSerializeData); positionSerializeData = new byte[Buffer.ByteLength(positions)]; Buffer.BlockCopy(positions, 0, positionSerializeData, 0, positionSerializeData.Length); positionSerializeData = GCompressor.Compress(positionSerializeData); rotationSerializeData = new byte[Buffer.ByteLength(rotations)]; Buffer.BlockCopy(rotations, 0, rotationSerializeData, 0, rotationSerializeData.Length); rotationSerializeData = GCompressor.Compress(rotationSerializeData); scaleSerializeData = new byte[Buffer.ByteLength(scales)]; Buffer.BlockCopy(scales, 0, scaleSerializeData, 0, scaleSerializeData.Length); scaleSerializeData = GCompressor.Compress(scaleSerializeData); }
public void AddGrassInstances(List <GGrassInstance> instances) { Rect[] uvRects = new Rect[GrassPatches.Length]; for (int r = 0; r < uvRects.Length; ++r) { uvRects[r] = GrassPatches[r].GetUvRange(); } for (int i = 0; i < instances.Count; ++i) { GGrassInstance grass = instances[i]; for (int r = 0; r < uvRects.Length; ++r) { if (uvRects[r].Contains(new Vector2(grass.Position.x, grass.Position.z))) { grassPatches[r].Instances.Add(grass); break; } } } CalculateEstimatedGrassStorage(); }
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); } }); }
private void StripInstances(int prototypeIndex) { if (Foliage.Grasses == null) { return; } int count = 0; for (int i = 0; i < Instances.Count; ++i) { if (Instances[i].PrototypeIndex == prototypeIndex) { count += 1; } } if (count == 0) { return; } Mesh baseMesh = Foliage.Grasses.Prototypes[prototypeIndex].GetBaseMesh(); if (baseMesh == null) { return; } int baseVertexCount = baseMesh.vertexCount; int vertexCount = baseVertexCount * count; if (vertexCount < 65000) { return; } int toRemoveCount = 1 + (vertexCount - 65000) / baseVertexCount; List <int> indices = new List <int>(); for (int i = 0; i < Instances.Count; ++i) { if (Instances[i].PrototypeIndex == prototypeIndex) { indices.Add(i); } } GUtilities.ShuffleList(indices); for (int i = 0; i < toRemoveCount; ++i) { GGrassInstance instance = Instances[indices[i]]; instance.PrototypeIndex = -1; Instances[indices[i]] = instance; } int removedCount = Instances.RemoveAll(g => g.PrototypeIndex < 0); Debug.Log(string.Format( "Stripped {0} instance{1} at patch {2}, prototype {3}.", removedCount, removedCount > 1 ? "s" : "", Index.ToString("0"), prototypeIndex)); }