diff options
Diffstat (limited to 'Scripts/InstanceGrass.cs')
| -rwxr-xr-x | Scripts/InstanceGrass.cs | 377 |
1 files changed, 0 insertions, 377 deletions
diff --git a/Scripts/InstanceGrass.cs b/Scripts/InstanceGrass.cs deleted file mode 100755 index d6a1eab..0000000 --- a/Scripts/InstanceGrass.cs +++ /dev/null @@ -1,377 +0,0 @@ -using UdonSharp; -using UnityEngine; -using VRC.SDKBase; - -#if UNITY_EDITOR && !COMPILER_UDONSHARP -using UnityEditor; -using System.IO; -#endif - -public class InstanceGrass : UdonSharpBehaviour -{ - [SerializeField] public GameObject prefab_; - // The density of instances, in instances per meter. - [SerializeField] public float density_; - // The extent along each cardinal axis where instances will be rendered, in - // meters. I.e. render inside a cube with edges this long. - [SerializeField] public Vector3 extent_meters_; - [SerializeField] public float min_distance_; - [SerializeField] public Vector3 angle_randomization_; - [SerializeField] public float scale_randomization_; - - // GPU mode settings - [SerializeField] public Vector3Int max_grid_size_ = new Vector3Int(512, 1, 512); // Maximum grid dimensions for GPU mode - [SerializeField] public Material blit_material_; // Auto-generated in editor, set by custom editor - - private Mesh mesh_; - private Material instance_material_; // Extracted from prefab - private Transform base_transform_; - private Vector3 cell_dim_; - private Vector3 inv_cell_dim_; - private Vector3Int count_; - private int total_instances_; - private bool initialized_; - - // Track fields to detect runtime changes. - private float density_live_; - private Vector3 extent_meters_live_; - private float min_distance_live_; - private Vector3 angle_randomization_live_; - private float scale_randomization_live_; - - // GPU-specific resources - private RenderTexture instance_data_tex_; - private Matrix4x4[] identity_transforms_; // All identity matrices - private float[] instance_ids_; - private MaterialPropertyBlock instance_properties_; - private int tex_width_; - private int tex_height_; - - private void Init() { - // Extract components from prefab. - if (prefab_ != null) { - MeshFilter mesh_filter = prefab_.GetComponent<MeshFilter>(); - if (mesh_filter == null && prefab_.transform.childCount > 0) { - mesh_filter = prefab_.GetComponentInChildren<MeshFilter>(); - } - if (mesh_filter != null) { - mesh_ = mesh_filter.sharedMesh; - if (mesh_ == null) { - mesh_ = mesh_filter.mesh; - } - } else { - Debug.LogError("[Grass::Debug] Could not find MeshFilter on prefab or children."); - } - - MeshRenderer mesh_renderer = prefab_.GetComponent<MeshRenderer>(); - if (mesh_renderer == null && prefab_.transform.childCount > 0) { - mesh_renderer = prefab_.GetComponentInChildren<MeshRenderer>(); - } - if (mesh_renderer != null) { - Material[] materials = mesh_renderer.sharedMaterials; - if (materials != null && materials.Length > 0) { - instance_material_ = materials[0]; - instance_material_.enableInstancing = true; - } else { - Debug.LogError("[Grass::Debug] MeshRenderer has no materials."); - } - } else { - Debug.LogError("[Grass::Debug] Could not find MeshRenderer on prefab."); - } - - base_transform_ = prefab_.transform; - Debug.Log($"[Grass::Debug] Prefab transform - pos:{base_transform_.position}, rot:{base_transform_.rotation.eulerAngles}, scale:{base_transform_.localScale}"); - } else { - Debug.LogError("[Grass::Debug] prefab is null in Init()."); - } - - density_ = Mathf.Max(1e-6f, density_); - extent_meters_ = Vector3.Max(Vector3.one * 1e-6f, extent_meters_); - angle_randomization_ = new Vector3( - Mathf.Clamp(angle_randomization_.x, 0f, 180f), - Mathf.Clamp(angle_randomization_.y, 0f, 180f), - Mathf.Clamp(angle_randomization_.z, 0f, 180f)); - - // Use max_grid_size_ directly for GPU mode - count_ = Vector3Int.Min(max_grid_size_, new Vector3Int( - Mathf.Max(1, Mathf.RoundToInt(density_ * extent_meters_.x)), - Mathf.Max(1, Mathf.RoundToInt(density_ * extent_meters_.y)), - Mathf.Max(1, Mathf.RoundToInt(density_ * extent_meters_.z)))); - - cell_dim_ = new Vector3( - extent_meters_.x / count_.x, - extent_meters_.y / count_.y, - extent_meters_.z / count_.z); - inv_cell_dim_ = new Vector3( - 1f / cell_dim_.x, - 1f / cell_dim_.y, - 1f / cell_dim_.z); - - total_instances_ = count_.x * count_.y * count_.z; - - // Calculate texture dimensions (power of 2, minimum 256x256) - int min_tex_size = Mathf.CeilToInt(Mathf.Sqrt(total_instances_)); - tex_width_ = Mathf.Max(256, Mathf.NextPowerOfTwo(min_tex_size)); - tex_height_ = tex_width_; - - // Create instance data texture - if (instance_data_tex_ != null) { - instance_data_tex_.Release(); - } - instance_data_tex_ = new RenderTexture(tex_width_, tex_height_, 0, RenderTextureFormat.ARGBFloat); - instance_data_tex_.filterMode = FilterMode.Point; - instance_data_tex_.Create(); - - // Create transform array and per-instance IDs. - if (identity_transforms_ == null || identity_transforms_.Length != total_instances_) { - identity_transforms_ = new Matrix4x4[total_instances_]; - for (int i = 0; i < total_instances_; i++) { - identity_transforms_[i] = Matrix4x4.identity; - } - } - - if (instance_ids_ == null || instance_ids_.Length != total_instances_) { - instance_ids_ = new float[total_instances_]; - for (int i = 0; i < total_instances_; i++) { - instance_ids_[i] = i; - } - } - - if (mesh_ != null) { - // Prevent frustum culling from clipping instanced draws that move in-shader. - mesh_.bounds = new Bounds(Vector3.zero, Vector3.one * 100000f); - } - - density_live_ = density_; - extent_meters_live_ = extent_meters_; - min_distance_live_ = min_distance_; - angle_randomization_live_ = angle_randomization_; - scale_randomization_live_ = scale_randomization_; - - Debug.Log($"[Grass::Debug] Init: density={density_}, extent={extent_meters_}, count={count_}, cell_dim={cell_dim_}, instances={total_instances_}, tex={tex_width_}x{tex_height_}, scale={base_transform_.localScale}, rot={base_transform_.localRotation.eulerAngles}"); - Debug.Log($"[Grass::Debug] Init details: count_.x={count_.x}, count_.y={count_.y}, count_.z={count_.z}, extent.x={extent_meters_.x}, extent.z={extent_meters_.z}"); - } - - private bool Valid() { - if (prefab_ == null) { - Debug.LogError("[Grass::Debug] prefab is null."); - return false; - } - if (mesh_ == null) { - Debug.LogError("[Grass::Debug] mesh is null."); - return false; - } - if (instance_material_ == null) { - Debug.LogError("[Grass::Debug] instance_material is null."); - return false; - } - if (blit_material_ == null) { - Debug.LogError("[Grass::Debug] blit_material is null (failed to generate)."); - return false; - } - if (instance_data_tex_ == null) { - Debug.LogError("[Grass::Debug] instance_data_tex is null."); - return false; - } - return true; - } - - void Start() { - Init(); - if (!Valid()) { - return; - } - - initialized_ = true; - Debug.Log($"[Grass::Debug] GPU mode initialized"); - } - - void Update() { - // Reinitialize if any config changed at runtime. - if (density_ != density_live_ || - extent_meters_ != extent_meters_live_ || - min_distance_ != min_distance_live_ || - angle_randomization_ != angle_randomization_live_ || - scale_randomization_ != scale_randomization_live_) { - Init(); - initialized_ = false; - } - - if (!Valid()) { - return; - } - - VRCPlayerApi lcl_player = Networking.LocalPlayer; - Vector3 player_pos = lcl_player.GetPosition(); - - // Only update grid position on axes with extent >= 1cm - // For axes with smaller extent, keep grid at 0 (don't move with player) - int grid_x = extent_meters_.x >= 0.01f ? Mathf.FloorToInt(player_pos.x * inv_cell_dim_.x) : 0; - int grid_y = extent_meters_.y >= 0.01f ? Mathf.FloorToInt(player_pos.y * inv_cell_dim_.y) : 0; - int grid_z = extent_meters_.z >= 0.01f ? Mathf.FloorToInt(player_pos.z * inv_cell_dim_.z) : 0; - - int half_x = count_.x / 2; - int half_y = count_.y / 2; - int half_z = count_.z / 2; - - // Update blit material properties - blit_material_.SetVector("_PlayerGridPos", new Vector3(grid_x, grid_y, grid_z)); - blit_material_.SetVector("_GridCount", new Vector3(count_.x, count_.y, count_.z)); - blit_material_.SetVector("_GridHalf", new Vector3(half_x, half_y, half_z)); - blit_material_.SetVector("_TexDimensions", new Vector2(tex_width_, tex_height_)); - - // Blit to generate instance data texture - VRCGraphics.Blit(null, instance_data_tex_, blit_material_); - - // Update instance material properties - instance_material_.SetTexture("_Instance_Texture_Offset_Data_Tex", instance_data_tex_); - instance_material_.SetVector("_Instance_Texture_Offset_Cell_Dimensions", cell_dim_); - instance_material_.SetVector("_Instance_Texture_Offset_Angle_Randomization", angle_randomization_); - instance_material_.SetFloat("_Instance_Texture_Offset_Scale_Randomization", scale_randomization_); - instance_material_.SetVector("_Instance_Texture_Offset_Base_Scale", base_transform_.localScale); - - // Pass rotation as quaternion (x, y, z, w) - Quaternion rot = base_transform_.localRotation; - instance_material_.SetVector("_Instance_Texture_Offset_Base_Rotation", new Vector4(rot.x, rot.y, rot.z, rot.w)); - - // Distance culling parameters - instance_material_.SetFloat("_Instance_Distance_Culling_Min_Distance", min_distance_); - float max_distance = Mathf.Max(extent_meters_.x, Mathf.Max(extent_meters_.y, extent_meters_.z)); - instance_material_.SetFloat("_Instance_Distance_Culling_Max_Distance", max_distance * 0.5f); - - if (Time.frameCount % 300 == 0) { - Debug.Log($"[Grass::Debug] Drawing {total_instances_} GPU instances"); - Debug.Log($"[Grass::Debug] GridCount={count_}, TexDim={tex_width_}x{tex_height_}, CellDim={cell_dim_}"); - } - - // Draw instances with identity transforms - GPU handles everything - VRCGraphics.DrawMeshInstanced( - mesh_, - 0, - instance_material_, - identity_transforms_, - total_instances_, - GetInstanceProperties(), - UnityEngine.Rendering.ShadowCastingMode.Off, - true, - 0, - null, - UnityEngine.Rendering.LightProbeUsage.Off, - null); - - initialized_ = true; - } - - private MaterialPropertyBlock GetInstanceProperties() { - if (instance_properties_ == null) { - instance_properties_ = new MaterialPropertyBlock(); - } - - instance_properties_.SetFloatArray("_Instance_ID", instance_ids_); - return instance_properties_; - } - - void OnDestroy() { - if (instance_data_tex_ != null) { - instance_data_tex_.Release(); - } - } -} - -#if UNITY_EDITOR && !COMPILER_UDONSHARP -[CustomEditor(typeof(InstanceGrass))] -public class InstanceGrassEditor : Editor -{ - public override void OnInspectorGUI() - { - DrawDefaultInspector(); - - InstanceGrass script = (InstanceGrass)target; - - // Auto-generate blit material if missing - if (script.blit_material_ == null) - { - if (GUILayout.Button("Generate Blit Material")) - { - GenerateBlitMaterial(script); - } - - EditorGUILayout.HelpBox("Blit material is missing. Click the button above to auto-generate it.", MessageType.Warning); - } - else - { - EditorGUILayout.HelpBox("Blit material is set. GPU instancing ready.", MessageType.Info); - } - } - - private void GenerateBlitMaterial(InstanceGrass script) - { - Shader blitShader = Shader.Find("yum_food/GrassGridBlit"); - if (blitShader == null) - { - EditorUtility.DisplayDialog("Error", "Could not find shader 'yum_food/GrassGridBlit'. Make sure GrassGridBlit.shader is imported.", "OK"); - return; - } - - // Get hierarchy path - Transform current = script.transform; - string hierarchyPath = current.name; - while (current.parent != null) - { - current = current.parent; - hierarchyPath = current.name + "/" + hierarchyPath; - } - - string assetPath = $"Assets/yum_food/3ner/Grass_Generated/{hierarchyPath}/Grass_generated.mat"; - - // Create or load existing material - Material blitMat = AssetDatabase.LoadAssetAtPath<Material>(assetPath); - if (blitMat == null) - { - // Create new material - blitMat = new Material(blitShader); - blitMat.name = "Grass_generated"; - - // Ensure directory exists - string directory = Path.GetDirectoryName(assetPath); - if (!Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } - - AssetDatabase.CreateAsset(blitMat, assetPath); - Debug.Log($"[Grass::Editor] Created blit material at {assetPath}"); - } - else - { - // Update existing material shader - blitMat.shader = blitShader; - EditorUtility.SetDirty(blitMat); - Debug.Log($"[Grass::Editor] Updated existing blit material at {assetPath}"); - } - - script.blit_material_ = blitMat; - EditorUtility.SetDirty(script); - AssetDatabase.SaveAssets(); - } - - [UnityEditor.Callbacks.DidReloadScripts] - private static void OnScriptsReloaded() - { - // Auto-generate blit material for all InstanceGrass components when scripts reload - InstanceGrass[] allGrass = FindObjectsOfType<InstanceGrass>(); - foreach (var grass in allGrass) - { - if (grass.blit_material_ == null) - { - var editor = CreateEditor(grass) as InstanceGrassEditor; - if (editor != null) - { - editor.GenerateBlitMaterial(grass); - DestroyImmediate(editor); - } - } - } - } -} -#endif |
