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; } }