public override void OnInspectorGUI() { VolumeGrass _target = (VolumeGrass)target; GUILayout.Space(10); GUILayout.Label("Mode", EditorStyles.boldLabel); int _state = GUILayout.Toolbar(_target.state, _target.stateStrings); MeshFilter filter = _target.gameObject.GetComponent(typeof(MeshFilter)) as MeshFilter; if ((_target.state == 0) && (_state == 1)) { // build mesh if (!_target.BuildMesh()) { EditorUtility.DisplayDialog("Error...", "Can't build mesh ", "Proceed", ""); return; // nie można zbudowac mesha (np. za mało wierzchołków) } } else if ((_target.state == 1) && (_state == 0)) { if (filter && filter.sharedMesh) { filter.sharedMesh = null; MeshFilter filter_sidewalls = null; Transform tr = _target.transform.Find("sidewalls"); if (tr != null) { filter_sidewalls = tr.gameObject.GetComponent(typeof(MeshFilter)) as MeshFilter; } if (filter_sidewalls && filter_sidewalls.sharedMesh) { filter_sidewalls.sharedMesh = null; } } _target.state = _state; EditorUtility.SetDirty(_target); return; } _target.state = _state; if (_target.state == 1) { GUILayout.Space(15); GUILayout.Label("Grass height/coverage modification", EditorStyles.boldLabel); GUILayout.BeginHorizontal(); bool prev_paint_height = _target.paint_height; GUILayout.Label("Modify volume height on vertices", EditorStyles.label); _target.paint_height = EditorGUILayout.Toggle(_target.paint_height); if (prev_paint_height != _target.paint_height) { EditorUtility.SetDirty(_target); } GUILayout.EndHorizontal(); if (_target.paint_height) { GUILayout.BeginHorizontal(); GUILayout.Label("Area size", EditorStyles.label); _target.paint_size = EditorGUILayout.Slider(_target.paint_size, 0.5f, 50); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Area smoothness", EditorStyles.label); _target.paint_smoothness = EditorGUILayout.Slider(_target.paint_smoothness, 0, 1); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Opacity", EditorStyles.label); _target.paint_opacity = EditorGUILayout.Slider(_target.paint_opacity, 0, 1); GUILayout.EndHorizontal(); } } GUILayout.Space(15); GUILayout.Label("General Settings", EditorStyles.boldLabel); GUILayout.BeginHorizontal(); GUILayout.Label("Show node numbers", EditorStyles.label); bool tmp_showNodeNumbers = EditorGUILayout.Toggle(_target.showNodeNumbers); if (_target.showNodeNumbers != tmp_showNodeNumbers) { _target.showNodeNumbers = tmp_showNodeNumbers; EditorUtility.SetDirty(_target); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Show tesselation points in build mode", EditorStyles.label); bool new_flag = EditorGUILayout.Toggle(_target.show_tesselation_points); if (_target.show_tesselation_points != new_flag) { _target.show_tesselation_points = new_flag; if ((!new_flag) && (_target.which_active == 3)) { _target.active_idx = -1; } EditorUtility.SetDirty(_target); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Default subnodes", EditorStyles.label); _target.bezier_subdivisions = EditorGUILayout.IntSlider(_target.bezier_subdivisions, 1, 50); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Snap to ground when dragging node", EditorStyles.label); _target.snap_on_move = EditorGUILayout.Toggle(_target.snap_on_move); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Snap to ground every node at once", EditorStyles.label); _target.snap_always = EditorGUILayout.Toggle(_target.snap_always); GUILayout.EndHorizontal(); GUILayout.Space(2); GUILayout.BeginHorizontal(); GUILayout.Label("Ground collision layer mask", EditorStyles.label); _target.ground_layerMask.value = EditorGUILayout.LayerField(_target.ground_layerMask.value); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Disable(hide) on incompatible hardware", EditorStyles.label); _target.hide_on_old_hardware = EditorGUILayout.Toggle(_target.hide_on_old_hardware); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Force ALT (slower) shader in editor", EditorStyles.label); bool prev_useOGL = _target.useOGL; _target.useOGL = EditorGUILayout.Toggle(_target.useOGL); if (_target.useOGL != prev_useOGL) { _target.checkOGL(); } GUILayout.EndHorizontal(); GUILayout.Space(15); GUILayout.Label("Build Settings", EditorStyles.boldLabel); GUILayout.BeginHorizontal(); GUILayout.Label("Slices on blades texture", EditorStyles.label); float prev_slices_num = _target.slices_num; _target.slices_num = EditorGUILayout.IntSlider(_target.slices_num, 1, 64); if (_target.slices_num != prev_slices_num) { _target.setupLODAndShader(_target.act_lod, true); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Slice planes per world unit", EditorStyles.label); float UV_ratio = 1.0f / (_target.mesh_height * _target.slices_num); float prev_plane_num = _target.plane_num; _target.plane_num = EditorGUILayout.Slider(_target.plane_num * UV_ratio, 0.3f, 80f) / UV_ratio; if (_target.plane_num != prev_plane_num) { _target.setupLODAndShader(_target.act_lod, true); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Grass height in world units", EditorStyles.label); _target.mesh_height = EditorGUILayout.Slider(_target.mesh_height, 0.02f, 3f); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Additional mesh height offset", EditorStyles.label); _target.add_height_offset = EditorGUILayout.Slider(_target.add_height_offset, -2f, 2f); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Subnode optimization threshold angle", EditorStyles.label); _target.colinear_treshold = EditorGUILayout.Slider(_target.colinear_treshold, 91f, 180f); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Adjust to ground", EditorStyles.label); _target.snap_on_build = EditorGUILayout.Toggle(_target.snap_on_build); GUILayout.EndHorizontal(); if (_target.snap_on_build) { GUILayout.BeginHorizontal(); GUILayout.Label("Max height error", EditorStyles.label); _target.max_y_error[_target.act_lod] = EditorGUILayout.Slider(_target.max_y_error[_target.act_lod], 0.01f, 1); GUILayout.EndHorizontal(); _target.min_edge_length[_target.act_lod] = EditorGUILayout.Slider("Min edge length", _target.min_edge_length[_target.act_lod], 0.01f, 5); GUILayout.BeginHorizontal(); GUILayout.Label("UV map grid resolution", EditorStyles.label); _target.mapping_grid_size = EditorGUILayout.IntSlider(_target.mapping_grid_size, 10, 300); GUILayout.EndHorizontal(); bool prev_custom_UV_bounds = _target.custom_UV_bounds; GUILayout.BeginHorizontal(); GUILayout.Label("Custom UV map grid bounds", EditorStyles.label); _target.custom_UV_bounds = EditorGUILayout.Toggle(_target.custom_UV_bounds); GUILayout.EndHorizontal(); if (prev_custom_UV_bounds != _target.custom_UV_bounds) { if (_target.custom_UV_bounds) { _target.getUVBounds(); _target.AdjustCustomUVBounds(); } EditorUtility.SetDirty(_target); } if (_target.custom_UV_bounds) { Vector4 old_bounds = new Vector4(_target.custom_minx, _target.custom_maxx, _target.custom_minz, _target.custom_maxz); Vector4 new_bounds = EditorGUILayout.Vector4Field("UV bounds rect (X=xMin, Y=xMax, Z=zMin, W=zMax)", old_bounds); if (Vector4.Distance(old_bounds, new_bounds) > 0) { if (new_bounds.x > _target.minx) { new_bounds.x = _target.minx; } if (new_bounds.y < _target.maxx) { new_bounds.y = _target.maxx; } if (new_bounds.z > _target.minz) { new_bounds.z = _target.minz; } if (new_bounds.w < _target.maxz) { new_bounds.w = _target.maxz; } _target.custom_minx = new_bounds.x; _target.custom_maxx = new_bounds.y; _target.custom_minz = new_bounds.z; _target.custom_maxz = new_bounds.w; _target.reinitUV = true; EditorUtility.SetDirty(_target); } } GUILayout.BeginHorizontal(); GUILayout.Label("UV2 range (lightmap 0..u, 0..v)", EditorStyles.label); _target.UV2range.x = EditorGUILayout.Slider(_target.UV2range.x, 0.1f, 1f); _target.UV2range.y = EditorGUILayout.Slider(_target.UV2range.y, 0.1f, 1f); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Border transitions on build", EditorStyles.label); _target.auto_border_transitions = EditorGUILayout.Toggle(_target.auto_border_transitions); GUILayout.EndHorizontal(); bool prev_fullBackGeometry = _target.fullBackGeometry; GUILayout.BeginHorizontal(); GUILayout.Label("Silhouettes backcut", EditorStyles.label); _target.fullBackGeometry = EditorGUILayout.Toggle(_target.fullBackGeometry); GUILayout.EndHorizontal(); if (!prev_fullBackGeometry && _target.fullBackGeometry) { EditorUtility.DisplayDialog("Info", "This feature can improve grass silhouette on _very_ wavy terrain, but requires full grass geometry (instead of sidewalls only) to be rendered into depth buffer. Takes effect on play.", "Proceed", ""); } } GUILayout.Space(12); if ((filter == null) || (filter.sharedMesh == null)) { GUILayout.Label(" Current LOD mesh ", EditorStyles.miniLabel); } else { GUILayout.Label(" Current LOD mesh (" + filter.sharedMesh.vertices.Length + " verts, " + (filter.sharedMesh.triangles.Length / 3) + " tris)", EditorStyles.miniLabel); } if (_target.state == 1) { GUILayout.BeginHorizontal(); GUILayout.Label(" Used when distance < than ", EditorStyles.miniLabel); _target.LOD_distances[_target.act_lod] = EditorGUILayout.IntField(_target.LOD_distances[_target.act_lod]); GUILayout.EndHorizontal(); } for (int i = 0; i < 4; i++) { if (_target.LODs[i] == null) { _target.lodStrings[i] = _target.lodStrings_empty[i]; } else { _target.lodStrings[i] = _target.lodStrings_occupied[i]; } } int act_lod = GUILayout.Toolbar(_target.act_lod, _target.lodStrings); if (_target.act_lod != act_lod) { if (_target.state == 1) { _target.setupLODAndShader(act_lod, true); } _target.act_lod = act_lod; } if (_target.state == 1) { GUILayout.BeginHorizontal(); string lb; if (_target.LODs[_target.act_lod] != null) { lb = "Rebuild LOD"; } else { lb = "Build LOD"; } if (GUILayout.Button(lb, GUILayout.Width(85), GUILayout.Height(20))) { if (!_target.BuildMesh()) { EditorUtility.DisplayDialog("Error...", "Can't build mesh ", "Proceed", ""); } } if (_target.LODs[_target.act_lod] != null) { if (GUILayout.Button("Delete LOD", GUILayout.Width(85), GUILayout.Height(20))) { if ((filter != null) && (filter.sharedMesh != null)) { filter.sharedMesh = null; DestroyImmediate(_target.LODs[_target.act_lod]); _target.LODs[_target.act_lod] = null; if (_target.LODs_sidewalls[_target.act_lod] != null) { Transform tr = _target.transform.Find("sidewalls"); if (tr != null) { tr.parent = null; DestroyImmediate(tr); } DestroyImmediate(_target.LODs_sidewalls[_target.act_lod]); _target.LODs_sidewalls[_target.act_lod] = null; } } } } if (_target.snap_on_build && (_target.LODs[_target.act_lod] != null)) { if (GUILayout.Button("Refine UV at slopes", GUILayout.Height(20))) { if ((filter != null) && (filter.sharedMesh != null)) { Mesh msh = filter.sharedMesh; Vector3[] vertices = msh.vertices; Vector2[] uvs = msh.uv; Vector4[] tangents = msh.tangents; Color[] colors = msh.colors; _target.iterateUVs(_target.mapping_grid_size >> 2, vertices, uvs, tangents, colors); msh.uv = uvs; msh.tangents = tangents; } } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); bool prev_checkerMatFlag = _target.checkerMatFlag; GUILayout.Label("Show units checker (useful when refining)", EditorStyles.label); _target.checkerMatFlag = EditorGUILayout.Toggle(_target.checkerMatFlag); if (_target.checkerMatFlag && (!prev_checkerMatFlag)) { _target.showChecker(); } else if ((!_target.checkerMatFlag) && prev_checkerMatFlag) { _target.hideChecker(); } GUILayout.EndHorizontal(); } if (_target.active_idx >= 0) { GUILayout.Space(15); if (_target.which_active == 3) { // tesselation points GUILayout.Label("Tesselation Point Properties", EditorStyles.boldLabel); Vector3 vec; if (_target.localGlobalState == 0) { // local vec = EditorGUILayout.Vector3Field("Position", _target.tesselation_points[_target.active_idx]); } else { // global vec = _target.T_wl(EditorGUILayout.Vector3Field("Position", _target.T_lw(_target.tesselation_points[_target.active_idx]))); } if (_target.ConstrainPoints(vec)) { EditorUtility.SetDirty(_target); } GUILayout.BeginHorizontal(); _target.localGlobalState = GUILayout.Toolbar(_target.localGlobalState, _target.localGlobalStrings); if (GUILayout.Button("Delete Point", GUILayout.Width(100), GUILayout.Height(20))) { Undo.RegisterUndo(new Object[2] { _target, _target.transform }, "grass edit"); _target.DeleteActiveControlPoint(); EditorUtility.SetDirty(_target); } GUILayout.EndHorizontal(); } else { // nodes GUILayout.Label("Node Properties", EditorStyles.boldLabel); if (_target.state == 0) { Vector3 vec; if (_target.localGlobalState == 0) { // local if (_target.which_active == 0) { vec = EditorGUILayout.Vector3Field("Position", _target.control_points[_target.active_idx]); } else if (_target.which_active == 1) { vec = EditorGUILayout.Vector3Field("Position", _target.bezier_pointsA[_target.active_idx]); } else { vec = EditorGUILayout.Vector3Field("Position", _target.bezier_pointsB[_target.active_idx]); } } else { // global if (_target.which_active == 0) { vec = _target.T_wl(EditorGUILayout.Vector3Field("Position", _target.T_lw(_target.control_points[_target.active_idx]))); } else if (_target.which_active == 1) { vec = _target.T_wl(EditorGUILayout.Vector3Field("Position", _target.T_lw(_target.bezier_pointsA[_target.active_idx]))); } else { vec = _target.T_wl(EditorGUILayout.Vector3Field("Position", _target.T_lw(_target.bezier_pointsB[_target.active_idx]))); } } if (_target.ConstrainPoints(vec)) { EditorUtility.SetDirty(_target); } GUILayout.BeginHorizontal(); _target.localGlobalState = GUILayout.Toolbar(_target.localGlobalState, _target.localGlobalStrings); if (GUILayout.Button("Delete Node", GUILayout.Width(100), GUILayout.Height(20))) { Undo.RegisterUndo(new Object[2] { _target, _target.transform }, "grass edit"); _target.DeleteActiveControlPoint(); EditorUtility.SetDirty(_target); } GUILayout.EndHorizontal(); } int subs = EditorGUILayout.IntSlider("Subnodes count", (int)_target.subdivisions[_target.active_idx], 1, 50); if (subs != _target.subdivisions[_target.active_idx]) { _target.subdivisions[_target.active_idx] = subs; EditorUtility.SetDirty(_target); } GUILayout.BeginHorizontal(); GUILayout.Label("Optimize collinear subnodes", EditorStyles.label); _target.optimize_subnodes[_target.active_idx] = EditorGUILayout.Toggle(_target.optimize_subnodes[_target.active_idx]); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Build sidewall", EditorStyles.label); _target.side_walls[_target.active_idx] = EditorGUILayout.Toggle(_target.side_walls[_target.active_idx]); GUILayout.EndHorizontal(); } } GUILayout.Space(15); }