using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; [CreateAssetMenu(fileName = "VisualStyle", menuName = "UniQuake/Visual Style", order = 1)] public class VisualStyle : ScriptableObject { [SerializeField] protected Material entityMaterial; [SerializeField] protected Material worldMaterial; // TODO: split into wall, liquid, sky, etc [SerializeField] protected Material liquidMaterial; [SerializeField] protected bool pointSampling; [SerializeField] protected bool affineTexturing; [SerializeField] protected LiquidProperties liquidProperties = new LiquidProperties(); [SerializeField] protected ParticleSystems particles = new ParticleSystems(); public ParticleSystems Particles => particles; public virtual void Activate() { particles.Init(); if (pointSampling) Shader.EnableKeyword("_POINT_SAMPLING"); else Shader.DisableKeyword("_POINT_SAMPLING"); } public virtual Material CreateEntityMaterial(bool aliasModel) { var material = new Material(entityMaterial); if (aliasModel && affineTexturing) material.EnableKeyword("_AFFINE_ON"); else material.DisableKeyword("_AFFINE_ON"); return material; } public virtual Material CreateWorldMaterial(QSurfaceFlags surfaceFlags) { Material material; if (surfaceFlags.HasFlag(QSurfaceFlags.DrawTurbulence) && liquidMaterial != null) { float alpha = 1f; if (surfaceFlags.HasFlag(QSurfaceFlags.DrawWater)) alpha = liquidProperties.waterAlpha; else if (surfaceFlags.HasFlag(QSurfaceFlags.DrawSlime)) alpha = liquidProperties.slimeAlpha; else if (surfaceFlags.HasFlag(QSurfaceFlags.DrawLava)) alpha = liquidProperties.lavaAlpha; else if (surfaceFlags.HasFlag(QSurfaceFlags.DrawTeleporter)) alpha = liquidProperties.teleAlpha; material = new Material(liquidMaterial); material.SetColor("_BaseColor", new Color(1, 1, 1, alpha)); } else { material = new Material(worldMaterial); } if (surfaceFlags.HasFlag(QSurfaceFlags.DrawFence)) { material.EnableKeyword("_ALPHATEST_ON"); } return material; } public virtual void SetupEntityRenderer(MeshRenderer meshRenderer) { meshRenderer.shadowCastingMode = ShadowCastingMode.Off; meshRenderer.receiveShadows = false; meshRenderer.lightProbeUsage = LightProbeUsage.Off; meshRenderer.reflectionProbeUsage = ReflectionProbeUsage.Off; } public virtual void SetupEntityRenderer(SkinnedMeshRenderer skinnedMeshRenderer) { skinnedMeshRenderer.shadowCastingMode = ShadowCastingMode.Off; skinnedMeshRenderer.receiveShadows = false; skinnedMeshRenderer.lightProbeUsage = LightProbeUsage.Off; skinnedMeshRenderer.reflectionProbeUsage = ReflectionProbeUsage.Off; } public virtual void SetupWorldRenderer(MeshRenderer meshRenderer) { meshRenderer.shadowCastingMode = ShadowCastingMode.Off; meshRenderer.receiveShadows = false; meshRenderer.lightProbeUsage = LightProbeUsage.Off; meshRenderer.reflectionProbeUsage = ReflectionProbeUsage.Off; } public virtual void SetEntityTextures(Material material, Texture2D mainTexture, Texture2D fullBright) { material.mainTexture = mainTexture; material.SetTexture("_EmissionMap", fullBright); material.SetColor("_EmissionColor", Color.white); if (fullBright != null) material.EnableKeyword("_EMISSION"); else material.DisableKeyword("_EMISSION"); } public virtual void SetWorldTextures(Material material, Texture2D mainTexture, Texture2D fullBright, Texture2D lightmap) { material.mainTexture = mainTexture; material.SetTexture("_EmissionMap", fullBright); material.SetColor("_EmissionColor", Color.white); if (fullBright != null) material.EnableKeyword("_EMISSION"); else material.DisableKeyword("_EMISSION"); material.SetTexture("_LightMap", lightmap); if (lightmap != null) material.EnableKeyword("_QLIGHTMAP_ON"); else material.DisableKeyword("_QLIGHTMAP_ON"); } } [System.Serializable] public class LiquidProperties { [Range(0, 1)] public float waterAlpha = 0.85f, slimeAlpha = 0.9f, lavaAlpha = 0.95f, teleAlpha = 1.0f; } [System.Serializable] public class ParticleSystems { [SerializeField] protected Material particleMaterial; [SerializeField] protected float particleSize = 1.0f; [SerializeField] protected ParticleSystem particleEffect; [SerializeField] protected ParticleSystem explosion; [SerializeField] protected ParticleSystem rogueExplosion; [SerializeField] protected ParticleSystem blobExplosion; [SerializeField] protected ParticleSystem teleportSplash; [SerializeField] protected ParticleSystem lavaSplash; [SerializeField] protected ParticleSystem particleTrail; public virtual void Init() { InitTemplate(particleEffect); InitTemplate(explosion); InitTemplate(rogueExplosion); InitTemplate(blobExplosion); InitTemplate(teleportSplash); InitTemplate(lavaSplash); InitTemplate(particleTrail); } protected virtual void InitTemplate(ParticleSystem template) { if (template == null) return; var main = template.main; main.startSize = particleSize; if (particleMaterial != null) { var renderer = template.GetComponent(); renderer.sharedMaterial = particleMaterial; } int childCount = template.transform.childCount; for (int i = 0; i < childCount; ++i) { var child = template.transform.GetChild(i); InitTemplate(child.GetComponent()); } } public virtual void RunParticleEffect(Vector3 position, Vector3 direction, Color colorMin, Color colorMax, int count, Layers layer) { var effect = InstantiateEffect(particleEffect, position, layer); if (effect == null) return; var main = effect.main; main.maxParticles = count; SetColorGradient(main, colorMin, colorMax); var velocity = effect.velocityOverLifetime; velocity.x = direction.x * 15f; velocity.y = direction.y * 15f; velocity.z = direction.z * 15f; } public virtual void CreateExplosion(Vector3 position, Layers layer) { InstantiateEffect(explosion, position, layer); } public virtual void CreateRogueExplosion(Vector3 position, Color colorMin, Color colorMax, Layers layer) { var effect = InstantiateEffect(rogueExplosion, position, layer); if (effect == null) return; SetColorGradient(effect.main, colorMin, colorMax); } public virtual void CreateBlobExplosion(Vector3 position, Layers layer) { InstantiateEffect(blobExplosion, position, layer); } public virtual void CreateTeleportSplash(Vector3 position, Layers layer) { InstantiateEffect(teleportSplash, position, layer); } public virtual void CreateLavaSplash(Vector3 position, Layers layer) { InstantiateEffect(lavaSplash, position, layer); } public virtual void CreateParticleTrail(ParticleTrail type, Vector3 start, Vector3 end, int interval, Layers layer) { var effect = InstantiateEffect(particleTrail, start, layer); if (effect == null) return; var controller = effect.gameObject.GetComponent(); if (controller == null) return; controller.Initialize(effect, type, end, interval); } protected virtual ParticleSystem InstantiateEffect(ParticleSystem template, Vector3 position, Layers layer) { if (template == null) return null; var effect = Object.Instantiate(template); SetLayer(effect.gameObject, layer); effect.transform.position = position; return effect; } protected static void SetLayer(GameObject go, Layers layer) { go.layer = (int)layer; int childCount = go.transform.childCount; for (int i = 0; i < childCount; ++i) { go.transform.GetChild(i).gameObject.layer = (int)layer; } } protected static void SetColorGradient(ParticleSystem.MainModule main, Color colorMin, Color colorMax) { var startColor = main.startColor; startColor.gradient.colorKeys = new[] { new GradientColorKey(colorMin, 0f), new GradientColorKey(colorMax, 1f), }; main.startColor = startColor; } }