// TODO: just a testing function with discs private void DrawCurveBrushSamplePoints(BrushSettings brushSettings, Vector3 position, Vector3 normal, Color innerColor, Color outerColor, AnimationCurve curveX, AnimationCurve curveZ) { // number of sample points in 1 direction, i. e. there will be n * n sample points int samplePointsPerRow = brushSettings.curveSamplePoints; // the sample point distance on a [0,1] range, i. e. for 10 the distance will be 0.1 float samplePointDistanceNormalized = 1f / samplePointsPerRow; for (var x = 0f; x <= 1f; x += samplePointDistanceNormalized) { for (var z = 0f; z <= 1f; z += samplePointDistanceNormalized) { float curvePointX = curveX.Evaluate(x); float curvePointZ = curveZ.Evaluate(z); // ensure value is [0,1] curvePointX = Mathf.Clamp01(curvePointX); curvePointZ = Mathf.Clamp01(curvePointZ); float discSize = brushSettings.brushSize * x; // is same as y Handles.color = new Color(innerColor.r, innerColor.g, innerColor.b, curvePointX * curvePointZ); float radius = brushSettings.brushSize * samplePointDistanceNormalized * 0.5f; // TODO: align depending on brush size float xPosition = position.x - brushSettings.brushSize * (x - 0.5f) - radius; float zPosition = position.z - brushSettings.brushSize * (z - 0.5f) - radius; // high enough offset for y, in case the terrain below the brush aligned in it's normal direction isn't flat // otherwise parts might be above terrain while others might be below it; another way would be to do an additional up raycast float yRaystOffset = 3000f; float yPosition = position.y + yRaystOffset; // individual disc position, but with y offset Vector3 discPosition = new Vector3(xPosition, yPosition, zPosition); // y via raycast down RaycastHit hit; // create layer mask without ignore raycast on which the preview gameobject is LayerMask layerMask = LayerUtils.GetPreviewLayerMask(brushSettings.layerMask); if (Physics.Raycast(discPosition, Vector3.down, out hit, Mathf.Infinity, layerMask)) { // set y position depending on the terrain discPosition.y = hit.point.y; // set the normal depending on the terrain normal = hit.normal; } // y via height sampling // discPosition.y = Terrain.activeTerrain.SampleHeight(discPosition); Handles.DrawSolidDisc(discPosition, normal, radius); } } }
// TODO: just a testing function with rectangles private void DrawCurveBrushSamplePointsAsGrid(BrushSettings brushSettings, Vector3 position, Vector3 normal, Color innerColor, Color outerColor, AnimationCurve curveX, AnimationCurve curveZ) { // number of sample points in 1 direction, i. e. there will be n * n sample points int samplePointsPerRow = brushSettings.curveSamplePoints; // the sample point distance on a [0,1] range, i. e. for 10 the distance will be 0.1 float samplePointDistanceNormalized = 1f / samplePointsPerRow; Vector3[,] v = new Vector3[samplePointsPerRow, samplePointsPerRow]; Color[,] c = new Color[samplePointsPerRow, samplePointsPerRow]; int i; int j; for (i = 0; i < samplePointsPerRow; i++) { for (j = 0; j < samplePointsPerRow; j++) { float x = i * samplePointDistanceNormalized; float z = j * samplePointDistanceNormalized; float curvePointX = curveX.Evaluate(x); float curvePointZ = curveZ.Evaluate(z); // ensure value is [0,1] curvePointX = Mathf.Clamp01(curvePointX); curvePointZ = Mathf.Clamp01(curvePointZ); float discSize = brushSettings.brushSize * x; // is same as y Handles.color = new Color(innerColor.r, innerColor.g, innerColor.b, curvePointX * curvePointZ); float radius = brushSettings.brushSize * samplePointDistanceNormalized * 0.5f; // TODO: align depending on brush size float xPosition = position.x - brushSettings.brushSize * (x - 0.5f) - radius; float zPosition = position.z - brushSettings.brushSize * (z - 0.5f) - radius; // high enough offset for y, in case the terrain below the brush aligned in it's normal direction isn't flat // otherwise parts might be above terrain while others might be below it; another way would be to do an additional up raycast float yRaystOffset = 3000f; float yPosition = position.y + yRaystOffset; // individual disc position, but with y offset Vector3 discPosition = new Vector3(xPosition, yPosition, zPosition); // rotate around y world axis float angle = brushSettings.brushRotation; discPosition -= position; // move to origin discPosition = Quaternion.Euler(0, angle, 0) * discPosition; // rotate around world axis discPosition += position; // move back to position // y via raycast down RaycastHit hit; // create layer mask without ignore raycast on which the preview gameobject is LayerMask layerMask = LayerUtils.GetPreviewLayerMask(brushSettings.layerMask); if (Physics.Raycast(discPosition, Vector3.down, out hit, Mathf.Infinity, layerMask)) { // set y position depending on the terrain discPosition.y = hit.point.y; // set the normal depending on the terrain normal = hit.normal; } // y via height sampling // discPosition.y = Terrain.activeTerrain.SampleHeight(discPosition); v[i, j] = discPosition; c[i, j] = Handles.color; // slope float slopeAngle = Vector3.Angle(normal.normalized, new Vector3(0, 1, 0)); //Handles.Label(discPosition, new GUIContent("angle: " + slopeAngle)); // if brush area isn't inside the slope range, make the color almost transparent if (slopeAngle < brushSettings.slopeMin || slopeAngle > brushSettings.slopeMax) { c[i, j].a = 0.05f; } } } for (i = 0; i < v.GetLength(0) - 1; i++) { for (j = 0; j < v.GetLength(1) - 1; j++) { Vector3[] verts = new Vector3[] { v[i, j], v[i, j + 1], v[i + 1, j + 1], v[i + 1, j], }; Handles.DrawSolidRectangleWithOutline(verts, c[i, j], new Color(0, 0, 0, c[i, j].a)); } } }
public bool DrawBrush(BrushSettings brushSettings, out BrushMode brushMode, out RaycastHit mouseHit) { brushMode = BrushMode.None; mouseHit = new RaycastHit(); float radius = brushSettings.brushSize / 2f; Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); // create layer mask without ignore raycast on which the preview gameobject is LayerMask layerMask = LayerUtils.GetPreviewLayerMask(brushSettings.layerMask); if (Physics.Raycast(ray.origin, ray.direction, out RaycastHit hit, Mathf.Infinity, layerMask)) { mouseHit = hit; /// /// process mouse events /// // brush size & rotation: control key pressed if (Event.current.control) { // mouse wheel up/down changes the radius if (Event.current.type == EventType.ScrollWheel) { // ctrl + shift + scroll = brush rotation if (Event.current.shift) { int rotationStepSize = 10; int rotationMin = 0; // TODO: find out of to get that from Range int rotationMax = 360; // TODO: find out of to get that from Range // scroll up if (Event.current.delta.y > 0) { brushSettings.brushRotation += rotationStepSize; if (brushSettings.brushRotation > rotationMax) { brushSettings.brushRotation = rotationMin + rotationStepSize; } Event.current.Use(); } // scroll down else if (Event.current.delta.y < 0) { brushSettings.brushRotation -= rotationStepSize; if (brushSettings.brushRotation < rotationMin) { brushSettings.brushRotation = rotationMax - rotationStepSize; } Event.current.Use(); } } // ctrl + scroll = brush size else { // scroll up if (Event.current.delta.y > 0) { brushSettings.brushSize++; Event.current.Use(); } // scroll down else if (Event.current.delta.y < 0) { brushSettings.brushSize--; // TODO: slider if (brushSettings.brushSize < 1) { brushSettings.brushSize = 1; } Event.current.Use(); } } } } // default: nothing pressed brushMode = BrushMode.None; // mouse pressed state: unity editor acts only on events, so we need to keep track of the click state if (Event.current.isMouse && Event.current.button == 0) { if (Event.current.type == EventType.MouseDown) { mousePressed = true; } else if (Event.current.type == EventType.MouseUp || Event.current.type == EventType.MouseLeaveWindow) { mousePressed = false; } } // keyboard only case if (Event.current.shift) { brushMode = BrushMode.ShiftKey; if (Event.current.control) { brushMode = BrushMode.ShiftCtrlKey; } } // check if mouse button is being pressed without dragging; if (mousePressed) { if (Event.current.shift) { brushMode = BrushMode.ShiftPressed; if (Event.current.control) { brushMode = BrushMode.ShiftCtrlPressed; } } } // keyboard + mouse case if (Event.current.isMouse) { // left button = 0; right = 1; middle = 2 if (Event.current.button == 0) { // drag case if (Event.current.type == EventType.MouseDown || Event.current.type == EventType.MouseDrag) { if (Event.current.shift) { brushMode = BrushMode.ShiftDrag; if (Event.current.control) { brushMode = BrushMode.ShiftCtrlDrag; } } } } } // draw brush gizmo DrawBrush(brushSettings, hit.point, hit.normal, radius, brushMode); } return(brushMode != BrushMode.None); }