public void OptimseMeshes() { #if UNITY_EDITOR for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = _points[i]; for (int m = 0; m < curve.dynamicTrackMesh.meshCount; m++) { MeshUtility.Optimize(curve.dynamicTrackMesh[m].mesh); } for (int m = 0; m < curve.dynamicOffroadMesh.meshCount; m++) { MeshUtility.Optimize(curve.dynamicOffroadMesh[m].mesh); } for (int m = 0; m < curve.dynamicBumperMesh.meshCount; m++) { MeshUtility.Optimize(curve.dynamicBumperMesh[m].mesh); } for (int m = 0; m < curve.dynamicBoundaryMesh.meshCount; m++) { MeshUtility.Optimize(curve.dynamicBoundaryMesh[m].mesh); } for (int m = 0; m < curve.dynamicBottomMesh.meshCount; m++) { MeshUtility.Optimize(curve.dynamicBottomMesh[m].mesh); } } _optimised = true; #endif }
// /// <summary> // /// Mark the pit lane as dirty so it will be recalculated/rebuilt // /// </summary> // public void SetPitDirty() // { // for (int i = 0; i < pitlane.numberOfPoints; i++) // { // pitlane[i].isDirty = true; // } // } // // /// <summary> // /// Set pit lane point data to rerender // /// </summary> // public void ReRenderPit() // { // for (int i = 0; i < pitlane.numberOfPoints; i++) // { // pitlane[i].shouldReRender = true; // } // } public void GenerateSecondaryUVSet() { #if UNITY_EDITOR for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = _points[i]; List <Mesh> unwrapList = new List <Mesh>(); unwrapList.AddRange(curve.dynamicTrackMesh.meshes); unwrapList.AddRange(curve.dynamicOffroadMesh.meshes); unwrapList.AddRange(curve.dynamicBumperMesh.meshes); unwrapList.AddRange(curve.dynamicBoundaryMesh.meshes); unwrapList.AddRange(curve.dynamicBottomMesh.meshes); int count = unwrapList.Count; for (int m = 0; m < count; m++) { if (unwrapList[m].vertexCount == 0) { continue; } Unwrapping.GenerateSecondaryUVSet(unwrapList[m]); } } _lightmapGenerated = true; #endif }
public void GenerateSecondaryUVSet() { #if UNITY_EDITOR for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = _points[i]; for (int m = 0; m < curve.dynamicTrackMesh.meshCount; m++) { Unwrapping.GenerateSecondaryUVSet(curve.dynamicTrackMesh[m].mesh); } for (int m = 0; m < curve.dynamicOffroadMesh.meshCount; m++) { Unwrapping.GenerateSecondaryUVSet(curve.dynamicOffroadMesh[m].mesh); } for (int m = 0; m < curve.dynamicBumperMesh.meshCount; m++) { Unwrapping.GenerateSecondaryUVSet(curve.dynamicBumperMesh[m].mesh); } for (int m = 0; m < curve.dynamicBoundaryMesh.meshCount; m++) { Unwrapping.GenerateSecondaryUVSet(curve.dynamicBoundaryMesh[m].mesh); } for (int m = 0; m < curve.dynamicBottomMesh.meshCount; m++) { Unwrapping.GenerateSecondaryUVSet(curve.dynamicBottomMesh[m].mesh); } } _lightmapGenerated = true; #endif }
public static void ConformTrack(TrackBuildRTrack track, Terrain terrain) { TerrainData terrainData = terrain.terrainData; int terrainWidth = terrainData.heightmapWidth; int terrainHeight = terrainData.heightmapHeight; float terrainHeightmapY = terrain.terrainData.heightmapScale.y; float terrainY = terrain.transform.position.y / terrainHeightmapY; float conformAccuracy = track.conformAccuracy; float[,] originalData = terrainData.GetHeights(0, 0, terrainWidth, terrainHeight); Bounds trackBounds = new Bounds(); int numberOfCurves = track.numberOfCurves; for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = track[i]; if (curve.holder == null) { continue; } Renderer[] rends = curve.holder.GetComponentsInChildren <Renderer>(); foreach (Renderer rend in rends) { trackBounds.Encapsulate(rend.bounds); } } Vector3 trackOffset = track.transform.position - terrain.transform.position; Vector3 trackScale = new Vector3(trackBounds.size.x / terrainData.size.x, 1.0f / terrain.terrainData.size.y, trackBounds.size.z / terrainData.size.z); int realNumberOfPoints = track.realNumberOfPoints; for (int i = 0; i < realNumberOfPoints; i++) { TrackBuildRPoint point = track[i]; Vector3 trackPointPosition = point.position; int pointX = Mathf.RoundToInt(((trackPointPosition.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int pointY = Mathf.RoundToInt(((trackPointPosition.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); pointX = Mathf.Clamp(pointX, 0, terrainWidth - 1); pointY = Mathf.Clamp(pointY, 0, terrainHeight - 1); trackPointPosition.y = originalData[pointY, pointX] * terrain.terrainData.size.y - terrainY + conformAccuracy; point.position = trackPointPosition; Vector3 controlPoint = point.forwardControlPoint; pointX = Mathf.RoundToInt(((controlPoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); pointY = Mathf.RoundToInt(((controlPoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); pointX = Mathf.Clamp(pointX, 0, terrainWidth - 1); pointY = Mathf.Clamp(pointY, 0, terrainHeight - 1); controlPoint.y = originalData[pointY, pointX] * terrain.terrainData.size.y - terrainY + conformAccuracy; point.forwardControlPoint = controlPoint; point.isDirty = true; } track.RecalculateCurves(); }
public void Init() { track = gameObject.AddComponent <TrackBuildRTrack>(); track.InitTextures(); track.baseTransform = transform; TrackBuildRPoint p0 = gameObject.AddComponent <TrackBuildRPoint>(); // ScriptableObject.CreateInstance<TrackBuildRPoint>(); TrackBuildRPoint p1 = gameObject.AddComponent <TrackBuildRPoint>(); //ScriptableObject.CreateInstance<TrackBuildRPoint>(); TrackBuildRPoint p2 = gameObject.AddComponent <TrackBuildRPoint>(); //ScriptableObject.CreateInstance<TrackBuildRPoint>(); TrackBuildRPoint p3 = gameObject.AddComponent <TrackBuildRPoint>(); //ScriptableObject.CreateInstance<TrackBuildRPoint>(); p0.baseTransform = transform; p1.baseTransform = transform; p2.baseTransform = transform; p3.baseTransform = transform; p0.position = new Vector3(-20, 0, -20); p1.position = new Vector3(20, 0, -20); p2.position = new Vector3(20, 0, 20); p3.position = new Vector3(-20, 0, 20); p0.forwardControlPoint = new Vector3(0, 0, -20); p1.forwardControlPoint = new Vector3(40, 0, -20); p2.forwardControlPoint = new Vector3(0, 0, 20); p3.forwardControlPoint = new Vector3(-40, 0, 20); p0.leftForwardControlPoint = new Vector3(-15, 0, -20); p1.leftForwardControlPoint = new Vector3(25, 0, -20); p2.leftForwardControlPoint = new Vector3(5, 0, 20); p3.leftForwardControlPoint = new Vector3(-35, 0, 20); p0.rightForwardControlPoint = new Vector3(15, 0, -20); p1.rightForwardControlPoint = new Vector3(55, 0, -20); p2.rightForwardControlPoint = new Vector3(-5, 0, 20); p3.rightForwardControlPoint = new Vector3(-45, 0, 20); track.AddPoint(p0); track.AddPoint(p1); track.AddPoint(p2); track.AddPoint(p3); generator = gameObject.AddComponent <TrackBuildRGenerator>(); ForceFullRecalculation(); track.diagramMesh = new Mesh(); track.diagramMesh.vertices = new [] { new Vector3(-1, 0, -1), new Vector3(1, 0, -1), new Vector3(-1, 0, 1), new Vector3(1, 0, 1) }; track.diagramMesh.uv = new [] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, 1), new Vector2(1, 1) }; track.diagramMesh.triangles = new [] { 1, 0, 2, 1, 2, 3 }; track.diagramGO = new GameObject("Diagram"); track.diagramGO.transform.parent = transform; track.diagramGO.transform.localPosition = Vector3.zero; track.diagramGO.AddComponent <MeshFilter>().mesh = track.diagramMesh; track.diagramMaterial = new Material(Shader.Find("Unlit/Texture")); track.diagramGO.AddComponent <MeshRenderer>().material = track.diagramMaterial; track.diagramGO.AddComponent <MeshCollider>().sharedMesh = track.diagramMesh; }
//Sample nextNormIndex position on the entire curve based on time (0-1) public Vector3 GetRightBoundaryPosition(float t) { float curveT = 1.0f / numberOfCurves; int point = Mathf.FloorToInt(t / curveT); float ct = Mathf.Clamp01((t - point * curveT) * numberOfCurves); TrackBuildRPoint pointA = GetPoint(point); TrackBuildRPoint pointB = GetPoint(point + 1); return(SplineMaths.CalculateBezierPoint(ct, pointA.rightTrackBoundary, pointA.rightForwardControlPoint, pointB.rightBackwardControlPoint, pointB.rightTrackBoundary)); }
public float GetTrackCrownAngle(float t) { float curveT = 1.0f / numberOfCurves; int point = Mathf.FloorToInt(t / curveT); float ct = Mathf.Clamp01((t - point * curveT) * numberOfCurves); float hermite = SplineMaths.CalculateHermite(ct); TrackBuildRPoint pointA = GetPoint(point); TrackBuildRPoint pointB = GetPoint(point + 1); return(Mathf.LerpAngle(pointA.crownAngle, pointB.crownAngle, hermite)); }
public Vector3 GetTrackCross(float t) { float curveT = 1.0f / numberOfCurves; int point = Mathf.FloorToInt(t / curveT); float ct = Mathf.Clamp01((t - point * curveT) * numberOfCurves); float hermite = SplineMaths.CalculateHermite(ct); TrackBuildRPoint pointA = GetPoint(point); TrackBuildRPoint pointB = GetPoint(point + 1); return(Vector3.Slerp(pointA.trackCross, pointB.trackCross, hermite)); }
/// <summary> /// unfinished! /// </summary> /// <param name="track"></param> /// <param name="selectedPoint"></param> public static void AddTwist(TrackBuildRTrack track, int selectedPoint) { TrackBuildRPoint atPoint = track[selectedPoint]; TrackBuildRPoint lastPoint = track.GetPoint(selectedPoint - 1); float twistDistance = Mathf.Min((lastPoint.arcLength + atPoint.arcLength) * 0.333f, track.maxJumpLength); Vector3 twistDirection = atPoint.trackDirection; Vector3 twistMiddle = atPoint.worldPosition; Vector3 twistUp = atPoint.trackUp; Vector3 twistAxis = Vector3.Cross(twistDirection, twistUp); float twistRadius = track.twistRadius; Vector3 twistStartPosition = -twistDirection * (twistDistance * 0.33f); Vector3 twistEndPosition = twistDirection * (twistDistance * 0.33f); Vector3 twistCentreHeight = twistUp * twistRadius; Quaternion twistAngle = Quaternion.LookRotation(twistDirection, twistUp); Vector3 twistCenter = atPoint.worldPosition + Vector3.up * twistRadius; float controlPointLength = twistRadius / (Mathf.PI); int numberOfPoints = track.twistPoints; float arcPercent = 1.0f / numberOfPoints; TrackBuildRPoint[] loopPoints = new TrackBuildRPoint[numberOfPoints + 1]; for (int i = 0; i < numberOfPoints; i++) { float pointArcPercent = arcPercent * i; float radA = Mathf.PI * 2 * (pointArcPercent + 0.5f); Vector3 pointLoopPosition = twistAngle * ((new Vector3(Mathf.Sin(radA), Mathf.Cos(radA), 0)) * twistRadius); float smoothI = pointArcPercent * pointArcPercent * (3.0f - 2.0f * pointArcPercent); Vector3 lateral = Vector3.Lerp(twistStartPosition, twistEndPosition, pointArcPercent + (pointArcPercent - smoothI)); Vector3 pointPosition = (pointLoopPosition) + lateral; Vector3 pointDirection = Vector3.Cross(-pointLoopPosition, twistAxis).normalized; TrackBuildRPoint newTrackPoint = track.InsertPoint(selectedPoint + 1 + i); newTrackPoint.worldPosition = twistCenter + pointPosition; newTrackPoint.trackUpQ = Quaternion.LookRotation(-pointLoopPosition, pointDirection); newTrackPoint.forwardControlPoint = newTrackPoint.worldPosition + (pointDirection * controlPointLength); loopPoints[i] = newTrackPoint; } atPoint.worldPosition += twistStartPosition; atPoint.trackUpQ = Quaternion.LookRotation(Vector3.up, atPoint.trackDirection); atPoint.forwardControlPoint = atPoint.worldPosition + (twistDirection * controlPointLength) - twistAxis; loopPoints[6] = atPoint; // _trackBuildR.pointMode = TrackBuildR.pointModes.transform; for (int i = 0; i < numberOfPoints + 1; i++) { loopPoints[i].extrudeTrack = true; loopPoints[i].extrudeTrackBottom = true; loopPoints[i].extrudeLength = 0.5f; loopPoints[i].RecalculateStoredValues(); } }
// /// <summary> // /// Mark the pit lane as dirty so it will be recalculated/rebuilt // /// </summary> // public void SetPitDirty() // { // for (int i = 0; i < pitlane.numberOfPoints; i++) // { // pitlane[i].isDirty = true; // } // } // // /// <summary> // /// Set pit lane point data to rerender // /// </summary> // public void ReRenderPit() // { // for (int i = 0; i < pitlane.numberOfPoints; i++) // { // pitlane[i].shouldReRender = true; // } // } public void SolveTangents() { for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = _points[i]; curve.dynamicTrackMesh.SolveTangents(); curve.dynamicOffroadMesh.SolveTangents(); curve.dynamicBumperMesh.SolveTangents(); curve.dynamicBoundaryMesh.SolveTangents(); curve.dynamicBottomMesh.SolveTangents(); } _tangentsGenerated = true; }
public Vector3 GetTrackDirection(float t) { float curveT = 1.0f / numberOfCurves; int point = Mathf.FloorToInt(t / curveT); float ct = Mathf.Clamp01((t - point * curveT) * numberOfCurves); float hermite = SplineMaths.CalculateHermite(ct); TrackBuildRPoint pointA = GetPoint(point); TrackBuildRPoint pointB = GetPoint(point + 1); Quaternion trackDirectionA = Quaternion.LookRotation(pointA.trackDirection); Quaternion trackDirectionB = Quaternion.LookRotation(pointB.trackDirection); Quaternion trackDirectionQ = Quaternion.Slerp(trackDirectionA, trackDirectionB, hermite); return(trackDirectionQ * Vector3.forward); }
public TrackBuildRPoint AddPoint(Vector3 position) { #if UNITY_EDITOR TrackBuildRPoint point = Undo.AddComponent <TrackBuildRPoint>(gameObject); //ScriptableObject.CreateInstance<TrackBuildRPoint>(); #else TrackBuildRPoint point = gameObject.AddComponent <TrackBuildRPoint>(); //ScriptableObject.CreateInstance<TrackBuildRPoint>(); #endif point.baseTransform = baseTransform; point.position = position; point.isDirty = true; _points.Add(point); RecalculateCurves(); return(point); }
public static void AddLoop(TrackBuildRTrack track, int selectedPointIndex) { TrackBuildRPoint atPoint = track[selectedPointIndex]; int atPointIndex = selectedPointIndex; float loopRadius = track.loopRadius; float trackWidth = atPoint.width; Vector3 trackDirection = atPoint.trackDirection.normalized; Vector3 trackUp = atPoint.trackUpQ * Vector3.forward; Vector3 trackCross = atPoint.trackCross; Vector3 entryPosition = atPoint.worldPosition + (trackCross * trackWidth * 0.6f); Vector3 loopAxis = Vector3.Cross(trackDirection, trackUp); Vector3 loopCenter = atPoint.worldPosition + Vector3.up * loopRadius; Quaternion loopAngle = Quaternion.FromToRotation(Vector3.right, trackDirection); float controlPointLength = loopRadius / (Mathf.PI); Vector3 controlPointLateral = -loopAxis; int numberOfPoints = 6; float arcPercent = 1.0f / numberOfPoints; TrackBuildRPoint[] loopPoints = new TrackBuildRPoint[7]; for (int i = 0; i < numberOfPoints; i++) { float pointArcPercent = arcPercent * i; TrackBuildRPoint newTrackPoint = track.InsertPoint(atPointIndex + 1); float rad = Mathf.PI * 2 * (pointArcPercent + 0.5f); Vector3 pointLoopPosition = loopAngle * ((new Vector3(Mathf.Sin(rad), Mathf.Cos(rad), 0)) * loopRadius); Vector3 lateral = Vector3.Lerp((trackCross * trackWidth * -0.6f), (trackCross * trackWidth * 0.6f), pointArcPercent); Vector3 pointPosition = (pointLoopPosition) + lateral; Vector3 pointDirection = Vector3.Cross(-pointLoopPosition, loopAxis).normalized; newTrackPoint.worldPosition = loopCenter + pointPosition; newTrackPoint.trackUpQ = Quaternion.LookRotation(-pointLoopPosition, pointDirection); newTrackPoint.forwardControlPoint = newTrackPoint.worldPosition + (pointDirection * controlPointLength) - controlPointLateral; loopPoints[i] = newTrackPoint; } atPoint.worldPosition = entryPosition; atPoint.trackUpQ = Quaternion.LookRotation(Vector3.up, atPoint.trackDirection); atPoint.forwardControlPoint = atPoint.worldPosition + (trackDirection * controlPointLength) + controlPointLateral; loopPoints[6] = atPoint; // _trackBuildR.pointMode = TrackBuildR.pointModes.transform; for (int i = 0; i < numberOfPoints + 1; i++) { loopPoints[i].extrudeTrack = true; loopPoints[i].extrudeTrackBottom = true; loopPoints[i].extrudeLength = 0.5f; loopPoints[i].RecalculateStoredValues(); } }
//Sample nextNormIndex position on the entire curve based on time (0-1) public Vector3 GetTrackPosition(float t) { if (realNumberOfPoints < 2) { Debug.LogError("Not enough points to define a curve"); return(Vector3.zero); } float curveT = 1.0f / numberOfCurves; int point = Mathf.FloorToInt(t / curveT); float ct = Mathf.Clamp01((t - point * curveT) * numberOfCurves); TrackBuildRPoint pointA = GetPoint(point); TrackBuildRPoint pointB = GetPoint(point + 1); return(SplineMaths.CalculateBezierPoint(ct, pointA.position, pointA.forwardControlPoint, pointB.backwardControlPoint, pointB.position)); }
public static void AddJumpTwist(TrackBuildRTrack track, int selectedPoint) { TrackBuildRPoint atPoint = track[selectedPoint]; TrackBuildRPoint lastPoint = track.GetPoint(selectedPoint - 1); TrackBuildRPoint nextPoint = track.GetPoint(selectedPoint + 1); float trackPartDistance = lastPoint.arcLength + atPoint.arcLength; float jumpDistance = Mathf.Min(trackPartDistance * 0.333f, track.maxJumpLength); float trackWidth = atPoint.width * 0.5f; Vector3 startCross = atPoint.trackCross; Vector3 jumpDirection = atPoint.trackDirection; Vector3 jumpMiddle = atPoint.worldPosition; Quaternion atPointUpQ = atPoint.trackUpQ; Quaternion trackUpJump = Quaternion.AngleAxis(track.twistAngle, -jumpDirection); Quaternion trackCrossExit = trackUpJump * (atPointUpQ); Quaternion trackCrossEntry = Quaternion.Inverse(trackUpJump) * (atPointUpQ); Vector3 jumpLateral = startCross * track.twistAngle / 33.3f; Vector3 jumpHeight = atPointUpQ * (Vector3.forward * track.jumpHeight); Vector3 jumpStartPosition = jumpMiddle - jumpDirection * (jumpDistance * 0.33f) + jumpHeight - jumpLateral; Vector3 jumpEndPosition = jumpMiddle + jumpDirection * (jumpDistance * 0.33f) + jumpHeight + jumpLateral; lastPoint.extrudeTrack = true; lastPoint.extrudeLength = track.jumpHeight; lastPoint.extrudeCurveEnd = true; lastPoint.extrudeTrackBottom = true; atPoint.Reset(); atPoint.worldPosition = jumpStartPosition; atPoint.forwardControlPoint = jumpDirection * (jumpDistance * 0.5f) + jumpStartPosition + jumpHeight; atPoint.trackUpQ = trackCrossExit; atPoint.render = false; TrackBuildRPoint jumpEnd = track.InsertPoint(selectedPoint + 1); jumpEnd.worldPosition = jumpEndPosition; jumpEnd.forwardControlPoint = jumpDirection * (jumpDistance * 0.5f) + jumpEndPosition - jumpHeight; jumpEnd.trackUpQ = trackCrossEntry; jumpEnd.extrudeTrack = true; jumpEnd.extrudeLength = track.jumpHeight; jumpEnd.extrudeCurveEnd = true; jumpEnd.extrudeTrackBottom = true; atPoint.RecalculateStoredValues(); jumpEnd.RecalculateStoredValues(); }
public TrackBuildRPoint InsertPoint(int index) { #if UNITY_EDITOR Undo.IncrementCurrentGroup(); TrackBuildRPoint point = Undo.AddComponent <TrackBuildRPoint>(gameObject); //ScriptableObject.CreateInstance<TrackBuildRPoint>(); #else TrackBuildRPoint point = gameObject.AddComponent <TrackBuildRPoint>(); //ScriptableObject.CreateInstance<TrackBuildRPoint>(); #endif point.baseTransform = baseTransform; #if UNITY_EDITOR Undo.RecordObject(this, "Remove Point"); #endif _points.Insert(index, point); #if UNITY_EDITOR Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); #endif RecalculateCurves(); return(point); }
public void RemovePoint(TrackBuildRPoint point) { if (_points.Count < 3) { Debug.Log("We can't see any point in allowing you to delete any more points so we're not going to do it."); return; } #if UNITY_EDITOR Undo.IncrementCurrentGroup(); _points.Remove(point); Undo.DestroyObjectImmediate(point.holder); Undo.DestroyObjectImmediate(point); Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); #else _points.Remove(point); #endif RecalculateCurves(); }
public void RemoveTexture(TrackBuildRTexture texture) { int textureIndex = _textures.IndexOf(texture); #if UNITY_EDITOR Undo.IncrementCurrentGroup(); Undo.RecordObject(this, "Remove Texture"); _textures.Remove(texture); Undo.DestroyObjectImmediate(texture); Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); #else _textures.Remove(texture); #endif //ensure that curves are not referenceing textures that no longer exist for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = _points[i]; if (curve.trackTextureStyleIndex > textureIndex) { curve.trackTextureStyleIndex--; } if (curve.offroadTextureStyleIndex > textureIndex) { curve.offroadTextureStyleIndex--; } if (curve.bumperTextureStyleIndex > textureIndex) { curve.bumperTextureStyleIndex--; } if (curve.boundaryTextureStyleIndex > textureIndex) { curve.boundaryTextureStyleIndex--; } if (curve.bottomTextureStyleIndex > textureIndex) { curve.bottomTextureStyleIndex--; } } }
public static void AddJump(TrackBuildRTrack track, int selectedPoint) { TrackBuildRPoint atPoint = track[selectedPoint]; TrackBuildRPoint lastPoint = track.GetPoint(selectedPoint - 1); TrackBuildRPoint nextPoint = track.GetPoint(selectedPoint + 1); float trackPartDistance = lastPoint.arcLength + atPoint.arcLength; float jumpDistance = Mathf.Min(trackPartDistance * 0.333f, track.maxJumpLength); Vector3 jumpDirection = atPoint.trackDirection; Vector3 jumpMiddle = atPoint.worldPosition; Vector3 startCross = atPoint.trackCross; float trackWidth = atPoint.width * 0.5f; Quaternion trackUp = atPoint.trackUpQ; Vector3 jumpHeight = trackUp * (Vector3.forward * track.jumpHeight); Vector3 jumpStartPosition = jumpMiddle - jumpDirection * (jumpDistance * 0.33f); Vector3 jumpEndPosition = jumpMiddle + jumpDirection * (jumpDistance * 0.33f); lastPoint.extrudeTrack = true; lastPoint.extrudeLength = track.jumpHeight; lastPoint.extrudeCurveEnd = true; atPoint.Reset(); atPoint.worldPosition = jumpStartPosition + jumpHeight; atPoint.forwardControlPoint = jumpDirection * (jumpDistance * 0.5f) + jumpStartPosition + jumpHeight * 2; atPoint.trackUpQ = trackUp; atPoint.render = false; TrackBuildRPoint jumpEnd = track.InsertPoint(selectedPoint + 1); jumpEnd.worldPosition = jumpEndPosition + jumpHeight; jumpEnd.forwardControlPoint = jumpDirection * (jumpDistance * 0.5f) + jumpEndPosition; jumpEnd.trackUpQ = trackUp; jumpEnd.extrudeTrack = true; jumpEnd.extrudeLength = track.jumpHeight; jumpEnd.extrudeCurveEnd = true; atPoint.RecalculateStoredValues(); jumpEnd.RecalculateStoredValues(); }
private static void ExportModel(TrackBuildR track) { GameObject baseObject = new GameObject(track.exportFilename); baseObject.transform.position = CURRENT_TRANSFORM.position; baseObject.transform.rotation = CURRENT_TRANSFORM.rotation; EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.0f); track.ForceFullRecalculation(); EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.1f); try { TrackBuildRTrack trackData = track.track; //check overwrites... string newDirectory = ROOT_FOLDER + track.exportFilename; if (!CreateFolder(newDirectory)) { EditorUtility.ClearProgressBar(); return; } EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.15f); int numberOfCurves = trackData.numberOfCurves; int numberOfDynMeshes = 9; float exportProgress = 0.75f / (numberOfCurves * numberOfDynMeshes); ExportMaterial[] exportMaterials = new ExportMaterial[1]; ExportMaterial exportTexture = new ExportMaterial(); TrackBuildRTexture[] textures = trackData.GetTexturesArray(); int textureCount = textures.Length; Material[] materials = new Material[textureCount]; for (int t = 0; t < textureCount; t++) { TrackBuildRTexture texture = textures[t]; if (!texture.isSubstance && !texture.isUSer) { string materialPath = string.Format("{0}{1}/{2}.mat", ROOT_FOLDER, track.exportFilename, texture.customName); if (File.Exists(materialPath)) { Material mat = (Material)AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material)); EditorUtility.CopySerialized(texture.material, mat); AssetDatabase.SaveAssets(); materials[t] = mat; continue; } Material tempMat = Object.Instantiate(texture.material); AssetDatabase.CreateAsset(tempMat, materialPath); materials[t] = (Material)AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material)); } else { materials[t] = texture.isUSer ? texture.userMaterial : texture.proceduralMaterial; } } string[] dynNames = { "track", "bumper", "boundary", "bottom", "offroad", "trackCollider" }; string[] colliderNames = { "track collider", "wall collider", "offroad collider", "bumper collider" }; for (int c = 0; c < numberOfCurves; c++) { TrackBuildRPoint curve = trackData[c]; DynamicMesh[] dynMeshes = new DynamicMesh[numberOfDynMeshes]; dynMeshes[0] = curve.dynamicTrackMesh; dynMeshes[1] = curve.dynamicBumperMesh; dynMeshes[2] = curve.dynamicBoundaryMesh; dynMeshes[3] = curve.dynamicBottomMesh; dynMeshes[4] = curve.dynamicOffroadMesh; dynMeshes[5] = curve.dynamicColliderMesh1; //track surface dynMeshes[6] = curve.dynamicColliderMesh2; //walls and roof dynMeshes[7] = curve.dynamicColliderMesh3; //track bottom and offroad dynMeshes[8] = curve.dynamicColliderMesh4; //bumpers int[] textureIndeices = { curve.trackTextureStyleIndex, curve.bumperTextureStyleIndex, curve.boundaryTextureStyleIndex, curve.bottomTextureStyleIndex, curve.offroadTextureStyleIndex, 0 }; PhysicMaterial[] physicMaterials = { trackData.Texture(curve.trackTextureStyleIndex).physicMaterial, trackData.Texture(curve.boundaryTextureStyleIndex).physicMaterial, trackData.Texture(curve.offroadTextureStyleIndex).physicMaterial, trackData.Texture(curve.bumperTextureStyleIndex).physicMaterial }; for (int d = 0; d < numberOfDynMeshes; d++) { int textureIndex = Mathf.Clamp(d, 0, 5); if (EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "Exporting Track Curve " + c + " " + dynNames[textureIndex], 0.15f + exportProgress * (c * numberOfDynMeshes + d))) { EditorUtility.ClearProgressBar(); return; } DynamicMesh exportDynMesh = dynMeshes[d]; // if(track.includeTangents || exportDynMesh.isEmpty) // exportDynMesh.Build();//rebuild with tangents TrackBuildRTexture texture = trackData.Texture(textureIndeices[textureIndex]); exportTexture.name = texture.customName; exportTexture.material = texture.material; exportTexture.generated = false; exportTexture.filepath = texture.filePath; exportMaterials[0] = exportTexture; Material mat = materials[textureIndeices[textureIndex]]; int meshCount = exportDynMesh.meshCount; Mesh[] meshes = exportDynMesh.meshes; for (int i = 0; i < meshCount; i++) { Mesh exportMesh = meshes[i]; MeshUtility.Optimize(exportMesh); string filenameSuffix = trackModelName(dynNames[textureIndex], c, (meshCount > 1) ? i : -1); // "trackCurve" + c + ((meshCount > 1) ? "_" + i.ToString() : ""); if (d > 4) //colliders { filenameSuffix = string.Format("{0}_{1}", filenameSuffix, (d % 5)); } string filename = track.exportFilename + filenameSuffix; Export(filename, ROOT_FOLDER + track.exportFilename + "/", track, exportMesh, exportMaterials); if (track.createPrefabOnExport) { AssetDatabase.Refresh();//ensure the database is up to date... string modelFilePath = ROOT_FOLDER + track.exportFilename + "/" + filename + FILE_EXTENTION; if (d < numberOfDynMeshes - 4) { GameObject newModel = (GameObject)PrefabUtility.InstantiatePrefab(AssetDatabase.LoadMainAssetAtPath(modelFilePath)); newModel.name = filename; newModel.transform.parent = baseObject.transform; newModel.transform.localPosition = Vector3.zero; newModel.transform.localRotation = Quaternion.identity; MeshRenderer[] renders = newModel.GetComponentsInChildren <MeshRenderer>(); foreach (MeshRenderer rend in renders) { rend.material = mat; } } else { int colliderIndex = d - (numberOfDynMeshes - 4); GameObject colliderObject = new GameObject(colliderNames[colliderIndex]); MeshCollider collider = colliderObject.AddComponent <MeshCollider>(); collider.sharedMesh = (Mesh)AssetDatabase.LoadAssetAtPath(modelFilePath, typeof(Mesh)); collider.material = physicMaterials[colliderIndex]; colliderObject.transform.parent = baseObject.transform; colliderObject.transform.localPosition = Vector3.zero; colliderObject.transform.localRotation = Quaternion.identity; } } } } } if (track.createPrefabOnExport) { string prefabPath = ROOT_FOLDER + track.exportFilename + "/" + track.exportFilename + ".prefab"; Object prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)); if (prefab == null) { prefab = PrefabUtility.CreateEmptyPrefab(prefabPath); } PrefabUtility.ReplacePrefab(baseObject, prefab, ReplacePrefabOptions.ConnectToPrefab); } EditorUtility.DisplayCancelableProgressBar(PROGRESSBAR_TEXT, "", 0.70f); AssetDatabase.Refresh();//ensure the database is up to date... } catch (System.Exception e) { Debug.LogError("Track BuildR Export Error: " + e); EditorUtility.ClearProgressBar(); AssetDatabase.Refresh();//ensure the database is up to date... } Object.DestroyImmediate(baseObject); EditorUtility.ClearProgressBar(); EditorUtility.UnloadUnusedAssetsImmediate(); AssetDatabase.Refresh(); }
public float GetTrackPercentage(TrackBuildRPoint point) { int index = _points.IndexOf(point); return(index / (float)numberOfPoints); }
public void AddPoint(TrackBuildRPoint point) { point.baseTransform = baseTransform; _points.Add(point); RecalculateCurves(); }
public void InsertPoint(TrackBuildRPoint point, int index) { point.baseTransform = baseTransform; _points.Insert(index, point); RecalculateCurves(); }
public void FromKML(string coordinates) { Clear(); string[] coorArray = coordinates.Split(new [] { " " }, StringSplitOptions.RemoveEmptyEntries); List <Vector3> kmlpoints = new List <Vector3>(); Vector3 xyCenter = new Vector3(); Vector3 lastCoord = Vector3.zero; int numberOfCoords = coorArray.Length; for (int i = 0; i < numberOfCoords; i++) { string coord = coorArray[i]; if (coord == "") { continue; } string[] coordEntry = coord.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); if (coordEntry.Length < 3) { continue; } float KMLPointX = float.Parse(coordEntry[0]); float KMLPointY = float.Parse(coordEntry[2]); float KMLPointZ = float.Parse(coordEntry[1]); Vector3 newKMLPoint = new Vector3(KMLPointX, KMLPointY, KMLPointZ); if (Vector3.Distance(newKMLPoint, lastCoord) < 1.0f && lastCoord != Vector3.zero) { continue;//skip duplicate point } kmlpoints.Add(newKMLPoint); xyCenter.x += newKMLPoint.x; xyCenter.z += newKMLPoint.z; lastCoord = newKMLPoint; } int numberOfPoints = kmlpoints.Count; xyCenter.x *= 1.0f / numberOfPoints; xyCenter.z *= 1.0f / numberOfPoints; if (Vector3.Distance(kmlpoints[numberOfPoints - 1], kmlpoints[0]) < 1.0f) { kmlpoints.RemoveAt(numberOfPoints - 1); } numberOfPoints = kmlpoints.Count; List <Vector3> eulerpoints = new List <Vector3>(); float dataTrackLength = 0; Vector3 lastPoint = Vector3.zero; for (int i = 0; i < numberOfPoints; i++) { Vector3 KMLPoint = kmlpoints[i]; Vector3 newEulerPoint = GPSMaths.Simple(xyCenter, KMLPoint); newEulerPoint.y = KMLPoint.y; eulerpoints.Add(newEulerPoint); if (i > 0) { dataTrackLength += Vector3.Distance(lastPoint, newEulerPoint); } lastPoint = newEulerPoint; } if (dataTrackLength > 20000) { if (!EditorUtility.DisplayDialog("Really Long Track!", "The Data is creating a track length around " + (dataTrackLength / 1000).ToString("F1") + "km long", "Continue", "Cancel")) { return; } } if (dataTrackLength < 1) { if (EditorUtility.DisplayDialog("No Track!", "The Data not producing a viable track", "Ok")) { return; } } for (int i = 0; i < numberOfPoints; i++) { TrackBuildRPoint point = gameObject.AddComponent <TrackBuildRPoint>(); point.baseTransform = baseTransform; point.position = eulerpoints[i]; point.isDirty = true; _points.Add(point); } for (int i = 0; i < numberOfPoints; i++) { TrackBuildRPoint point = _points[i]; Vector3 thisPosition = point.worldPosition; Vector3 lastPosition = GetPoint(i - 1).worldPosition; Vector3 nextPosition = GetPoint(i + 1).worldPosition; Vector3 backwardTrack = thisPosition - lastPosition; Vector3 forwardTrack = nextPosition - thisPosition; Vector3 controlDirection = (backwardTrack + forwardTrack).normalized; float forwardMag = forwardTrack.magnitude; float backwardMag = backwardTrack.magnitude; if (forwardMag == 0) { forwardMag = backwardMag; } if (backwardMag == 0) { backwardMag = forwardMag; } float controlMagnitude = Mathf.Min(forwardMag, backwardMag) * 0.1666f; point.forwardControlPoint = (controlDirection * controlMagnitude) + thisPosition; } InitTextures(); RecalculateCurves(); }
/// <summary> /// unfinished! /// </summary> /// <param name="track"></param> /// <param name="selectedPoint"></param> public static void AddTwist(TrackBuildRTrack track, int selectedPoint) { TrackBuildRPoint atPoint = track[selectedPoint]; TrackBuildRPoint lastPoint = track.GetPoint(selectedPoint - 1); float twistDistance = Mathf.Min((lastPoint.arcLength + atPoint.arcLength) * 0.333f, track.maxJumpLength); Vector3 twistDirection = atPoint.trackDirection; // Vector3 twistMiddle = atPoint.worldPosition; Vector3 twistUp = atPoint.trackUp; Vector3 twistAxis = Vector3.Cross(twistDirection, twistUp); float twistRadius = track.twistRadius; Vector3 twistStartPosition = -twistDirection * (twistDistance * 0.33f); Vector3 twistEndPosition = twistDirection * (twistDistance * 0.33f); // Vector3 twistCentreHeight = twistUp * twistRadius; Quaternion twistAngle = Quaternion.LookRotation(twistDirection, twistUp); Vector3 twistCenter = atPoint.worldPosition + Vector3.up * twistRadius; float controlPointLength = twistRadius / (Mathf.PI); int numberOfPoints = track.twistPoints; float arcPercent = 1.0f / numberOfPoints; TrackBuildRPoint[] loopPoints = new TrackBuildRPoint[numberOfPoints+1]; for (int i = 0; i < numberOfPoints; i++) { float pointArcPercent = arcPercent * i; float radA = Mathf.PI * 2 * (pointArcPercent + 0.5f); Vector3 pointLoopPosition = twistAngle * ((new Vector3(Mathf.Sin(radA), Mathf.Cos(radA), 0)) * twistRadius); float smoothI = pointArcPercent * pointArcPercent * (3.0f - 2.0f * pointArcPercent); Vector3 lateral = Vector3.Lerp(twistStartPosition, twistEndPosition, pointArcPercent + (pointArcPercent - smoothI)); Vector3 pointPosition = (pointLoopPosition) + lateral; Vector3 pointDirection = Vector3.Cross(-pointLoopPosition, twistAxis).normalized; TrackBuildRPoint newTrackPoint = track.InsertPoint(selectedPoint + 1 + i); newTrackPoint.worldPosition = twistCenter + pointPosition; newTrackPoint.trackUpQ = Quaternion.LookRotation(-pointLoopPosition, pointDirection); newTrackPoint.forwardControlPoint = newTrackPoint.worldPosition + (pointDirection * controlPointLength); loopPoints[i] = newTrackPoint; } atPoint.worldPosition += twistStartPosition; atPoint.trackUpQ = Quaternion.LookRotation(Vector3.up, atPoint.trackDirection); atPoint.forwardControlPoint = atPoint.worldPosition + (twistDirection * controlPointLength) - twistAxis; loopPoints[6] = atPoint; // _trackBuildR.pointMode = TrackBuildR.pointModes.transform; for (int i = 0; i < numberOfPoints + 1; i++) { loopPoints[i].extrudeTrack = true; loopPoints[i].extrudeTrackBottom = true; loopPoints[i].extrudeLength = 0.5f; loopPoints[i].RecalculateStoredValues(); } }
/// <summary> /// Warning - not working correctly - yet... /// </summary> /// <param name="track"></param> /// <param name="terrain"></param> /// <param name="curve"></param> public static void MergeTerrain(TrackBuildRTrack track, Terrain terrain, TrackBuildRPoint curve) { // ResetTerrain(track, terrain); TerrainData terrainData = terrain.terrainData; int terrainWidth = terrainData.heightmapWidth; int terrainHeight = terrainData.heightmapHeight; float terrainHeightmapY = terrain.terrainData.heightmapScale.y; float terrainY = terrain.transform.position.y / terrainHeightmapY; Vector3 meshScale = terrainData.heightmapScale; float terrainAccuracy = track.terrainAccuracy; float terrainMergeMargin = track.terrainMergeMargin; float[,] originalData = terrainData.GetHeights(0, 0, terrainWidth, terrainHeight); float[,] mergeData = new float[terrainWidth,terrainHeight]; float[,] modifiedData = new float[terrainWidth,terrainHeight]; int[,] modifiedPointLock = new int[terrainWidth,terrainHeight]; Bounds trackBounds = new Bounds(); if(curve.holder != null) { Renderer[] rends = curve.holder.GetComponentsInChildren<Renderer>(); foreach(Renderer rend in rends) trackBounds.Encapsulate(rend.bounds); } Vector3 trackOffset = track.transform.position - terrain.transform.position; Vector3 trackScale = new Vector3(trackBounds.size.x / terrainData.size.x, 1.0f / terrain.terrainData.size.y, trackBounds.size.z / terrainData.size.z); float mergeWidth = track.terrainMergeWidth; AnimationCurve mergeCurve = track.mergeCurve; float minScaleUnit = Mathf.Min(meshScale.x, meshScale.z); int storedPointSize = curve.storedPointSize; for(int p = 0; p < storedPointSize - 1; p++) { Vector3 pointA = curve.sampledPoints[p]; Vector3 pointB = curve.sampledPoints[p + 1]; Vector3 crossA = curve.sampledTrackCrosses[p]; Vector3 crossB = curve.sampledTrackCrosses[p + 1]; float widthA = curve.sampledWidths[p] * terrainMergeMargin; float widthB = curve.sampledWidths[p + 1] * terrainMergeMargin; // float heightA = (pointA.y - terrainAccuracy) * trackScale.y - terrainY; // float heightB = (pointB.y - terrainAccuracy) * trackScale.y - terrainY; Vector3 lpointA = (pointA - crossA * (widthA + mergeWidth)); Vector3 rpointA = (pointA + crossA * (widthA + mergeWidth)); Vector3 lpointB = (pointB - crossB * (widthB + mergeWidth)); Vector3 rpointB = (pointB + crossB * (widthB + mergeWidth)); float crownA = curve.sampledCrowns[p] - terrainAccuracy; float crownB = curve.sampledCrowns[p + 1] - terrainAccuracy; float pointDistanceLeft = Vector3.Distance(lpointA, lpointB) * 1.8f; float pointDistanceRight = Vector3.Distance(rpointA, rpointB) * 1.8f; float pointDistance = Mathf.Max(pointDistanceLeft, pointDistanceRight); float pointFillResolution = minScaleUnit / pointDistance * 0.125f; float heightLA = (lpointA.y + track.transform.position.y - terrainAccuracy) * trackScale.y - terrainY; float heightRA = (rpointA.y + track.transform.position.y - terrainAccuracy) * trackScale.y - terrainY; float heightLB = (lpointB.y + track.transform.position.y - terrainAccuracy) * trackScale.y - terrainY; float heightRB = (rpointB.y + track.transform.position.y - terrainAccuracy) * trackScale.y - terrainY; for(float pf = 0; pf < 1; pf += pointFillResolution)//point a to point b { //Track Filler Vector3 fillPoint = Vector3.Lerp(pointA, pointB, pf); Vector3 fillCross = Vector3.Lerp(crossA, crossB, pf); float fillWidth = Mathf.Lerp(widthA, widthB, pf);// *1.2f; float fillCrown = Mathf.Lerp(crownA, crownB, pf);// *1.2f; float fillTrackHeightL = Mathf.Lerp(heightLA, heightLB, pf); float fillTrackHeightR = Mathf.Lerp(heightRA, heightRB, pf); Vector3 leftTrackPoint = fillPoint - fillCross * fillWidth; Vector3 rightTrackPoint = fillPoint + fillCross * fillWidth; int leftX = Mathf.RoundToInt(((leftTrackPoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int leftY = Mathf.RoundToInt(((leftTrackPoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); int rightX = Mathf.RoundToInt(((rightTrackPoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int rightY = Mathf.RoundToInt(((rightTrackPoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); int diffX = leftX - rightX; int diffY = leftY - rightY; int trackCrossFillAmount = Mathf.Max(Mathf.Abs(diffX), 1) * Mathf.Max(Mathf.Abs(diffY), 1); for(int f = 0; f < trackCrossFillAmount; f++)//left to right { float move = f / (float)trackCrossFillAmount; int fillX = Mathf.RoundToInt(Mathf.Lerp(leftX, rightX, move)); int fillY = Mathf.RoundToInt(Mathf.Lerp(leftY, rightY, move)); if(fillX < 0 || fillY < 0 || fillX > terrainWidth || fillY > terrainHeight) continue; float crownHeight = Mathf.Sin(move * Mathf.PI) * fillCrown / terrainHeightmapY; float fillTrackHeight = Mathf.Lerp(fillTrackHeightL, fillTrackHeightR, move) + crownHeight; int currentPointLock = modifiedPointLock[fillY, fillX]; if(currentPointLock == 0 || mergeData[fillY, fillX] > fillTrackHeight) mergeData[fillY, fillX] = fillTrackHeight; modifiedPointLock[fillY, fillX] = 1;//point lock } //Merge Vector3 leftMergePoint = leftTrackPoint - fillCross * mergeWidth; int leftMergeLeftX = Mathf.RoundToInt(((leftMergePoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int leftMergeLeftY = Mathf.RoundToInt(((leftMergePoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); int leftMergeRightX = Mathf.RoundToInt(((leftTrackPoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int leftMergeRightY = Mathf.RoundToInt(((leftTrackPoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); // int leftMergeDiffX = leftMergeLeftX - leftMergeRightX; int leftMergeDiffY = leftMergeLeftY - leftMergeRightY; int leftMergeFillAmount = Mathf.Max(Mathf.Abs(leftMergeDiffX), 1) * Mathf.Max(Mathf.Abs(leftMergeDiffY), 1);// Mathf.Max(Mathf.Abs(leftMergeDiffX), Mathf.Abs(leftMergeDiffY)); for(int f = 0; f < leftMergeFillAmount; f++) { float move = f / (float)leftMergeFillAmount; int fillX = Mathf.RoundToInt(Mathf.Lerp(leftMergeLeftX, leftMergeRightX, move)); int fillY = Mathf.RoundToInt(Mathf.Lerp(leftMergeLeftY, leftMergeRightY, move)); if(fillX < 0 || fillY < 0 || fillX > terrainWidth || fillY > terrainHeight) continue; float curveStrength = mergeCurve.Evaluate(move); float fillTrackHeight = fillTrackHeightL; float mergeHeight = Mathf.Lerp(originalData[fillY, fillX], fillTrackHeight, curveStrength); int pointLock = modifiedPointLock[fillY, fillX]; float currentMergeHeight = mergeData[fillY, fillX]; float currentDifference = Mathf.Abs(currentMergeHeight - fillTrackHeight); float newDifference = Mathf.Abs(mergeHeight - fillTrackHeight); if(newDifference < currentDifference && pointLock == 0) { mergeData[fillY, fillX] = mergeHeight; } } Vector3 rightMergePoint = rightTrackPoint + fillCross * mergeWidth; int rightMergeLeftX = Mathf.RoundToInt(((rightMergePoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int rightMergeLeftY = Mathf.RoundToInt(((rightMergePoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); int rightMergeRightX = Mathf.RoundToInt(((rightTrackPoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int rightMergeRightY = Mathf.RoundToInt(((rightTrackPoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); // int rightMergeDiffX = rightMergeLeftX - rightMergeRightX; int rightMergeDiffY = rightMergeLeftY - rightMergeRightY; int rightMergeFillAmount = Mathf.Max(Mathf.Abs(rightMergeDiffX), 1) * Mathf.Max(Mathf.Abs(rightMergeDiffY), 1);// Mathf.Max(Mathf.Abs(leftMergeDiffX), Mathf.Abs(leftMergeDiffY)); for(int f = 0; f < rightMergeFillAmount; f++) { float move = f / (float)rightMergeFillAmount; int fillX = Mathf.RoundToInt(Mathf.Lerp(rightMergeLeftX, rightMergeRightX, move)); int fillY = Mathf.RoundToInt(Mathf.Lerp(rightMergeLeftY, rightMergeRightY, move)); if(fillX < 0 || fillY < 0 || fillX > terrainWidth || fillY > terrainHeight) continue; float curveStrength = mergeCurve.Evaluate(move); float fillTrackHeight = fillTrackHeightR; float mergeHeight = Mathf.Lerp(originalData[fillY, fillX], fillTrackHeight, curveStrength); int pointLock = modifiedPointLock[fillY, fillX]; float currentMergeHeight = mergeData[fillY, fillX]; float currentDifference = Mathf.Abs(currentMergeHeight - fillTrackHeight); float newDifference = Mathf.Abs(mergeHeight - fillTrackHeight); if(newDifference < currentDifference && pointLock == 0) { mergeData[fillY, fillX] = mergeHeight; } } } } for(int x = 0; x < terrainWidth; x++) { for(int y = 0; y < terrainHeight; y++) { bool isNotEdge = x > 0 && x < terrainWidth - 1 && y > 0 && y < terrainHeight - 1; if(mergeData[x, y] == 0) { int mergeNeighbours = 0; if(isNotEdge) { mergeNeighbours += (mergeData[x + 1, y] != 0) ? 1 : 0; mergeNeighbours += (mergeData[x - 1, y] != 0) ? 1 : 0; mergeNeighbours += (mergeData[x, y + 1] != 0) ? 1 : 0; mergeNeighbours += (mergeData[x, y - 1] != 0) ? 1 : 0; } if(mergeNeighbours > 1)//if a hole is surounded by rasied terrain in two or more neighbouring places { float mergeHeight = 0; mergeHeight += (mergeData[x + 1, y] != 0) ? mergeData[x + 1, y] : 0; mergeHeight += (mergeData[x - 1, y] != 0) ? mergeData[x - 1, y] : 0; mergeHeight += (mergeData[x, y + 1] != 0) ? mergeData[x, y + 1] : 0; mergeHeight += (mergeData[x, y - 1] != 0) ? mergeData[x, y - 1] : 0; modifiedData[x, y] = mergeHeight / mergeNeighbours;//clean up holes } else { modifiedData[x, y] = originalData[x, y];//use original } } else { modifiedData[x, y] = mergeData[x, y]; } terrainData.SetHeights(0, 0, modifiedData); terrain.terrainData = terrainData; } } }
public void RemovePoint(TrackBuildRPoint point) { if(_points.Count < 3) { Debug.Log("We can't see any point in allowing you to delete any more points so we're not going to do it."); return; } #if UNITY_EDITOR Undo.IncrementCurrentGroup(); _points.Remove(point); Undo.DestroyObjectImmediate(point.holder); Undo.DestroyObjectImmediate(point); Undo.CollapseUndoOperations(Undo.GetCurrentGroup()); #else _points.Remove(point); #endif RecalculateCurves(); }
public float GetTrackPercentage(TrackBuildRPoint point) { int index = _points.IndexOf(point); return index / (float)numberOfPoints; }
public void RecalculateCurves() { if (_points.Count < 2)//there is no track with only one point :o) { return; } if (isDirty) { _tangentsGenerated = false; _lightmapGenerated = false; _optimised = false; } //calculate approx bezier arc length per curve for (int i = 0; i < realNumberOfPoints; i++) { TrackBuildRPoint pointA = GetPoint(i); TrackBuildRPoint pointB = GetPoint(i + 1); float thisArcLength = 0; thisArcLength += Vector3.Distance(pointA.position, pointA.forwardControlPoint); thisArcLength += Vector3.Distance(pointA.forwardControlPoint, pointB.backwardControlPoint); thisArcLength += Vector3.Distance(pointB.backwardControlPoint, pointB.position); _points[i].arcLength = thisArcLength; if (thisArcLength == 0) { DestroyImmediate(pointA); i--; } } for (int i = 0; i < realNumberOfPoints; i++) { TrackBuildRPoint pointA = GetPoint(i); TrackBuildRPoint pointB = GetPoint(i + 1); TrackBuildRPoint pointC = GetPoint(i - 1); pointA.nextPoint = pointB; pointA.lastPoint = pointC; pointA.pointName = "point " + i; } foreach (TrackBuildRPoint curve in _points) { if (!curve.curveIsDirty)//only recalculate modified points { continue; } if (curve.arcLength > 0) { TrackBuildRPoint pointA = curve; TrackBuildRPoint pointB = curve.nextPoint; //Build accurate arc length data into curve curve.center = Vector3.zero; int arcLengthResolution = Mathf.Max(Mathf.RoundToInt(curve.arcLength * 10), 1); float alTime = 1.0f / arcLengthResolution; float calculatedTotalArcLength = 0; curve.storedArcLengthsFull = new float[arcLengthResolution]; curve.storedArcLengthsFull[0] = 0.0f; Vector3 pA = curve.position; for (int i = 0; i < arcLengthResolution - 1; i++) { curve.center += pA; float altB = alTime * (i + 1) + alTime; Vector3 pB = SplineMaths.CalculateBezierPoint(altB, curve.position, curve.forwardControlPoint, pointB.backwardControlPoint, pointB.position); float arcLength = Vector3.Distance(pA, pB); calculatedTotalArcLength += arcLength; curve.storedArcLengthsFull[i + 1] = calculatedTotalArcLength; pA = pB;//switch over values so we only calculate the bezier once } curve.arcLength = calculatedTotalArcLength; curve.center /= arcLengthResolution; int storedPointSize = Mathf.RoundToInt(calculatedTotalArcLength / meshResolution); curve.storedPointSize = storedPointSize; curve.normalisedT = new float[storedPointSize]; curve.targetSize = new float[storedPointSize]; curve.midPointPerc = new float[storedPointSize]; curve.prevNormIndex = new int[storedPointSize]; curve.nextNormIndex = new int[storedPointSize]; curve.clipArrayLeft = new bool[storedPointSize]; curve.clipArrayRight = new bool[storedPointSize]; //calculate normalised spline data int normalisedIndex = 0; curve.normalisedT[0] = 0; for (int p = 1; p < storedPointSize; p++) { float t = p / (float)(storedPointSize - 1); float targetLength = t * calculatedTotalArcLength; curve.targetSize[p] = targetLength; int it = 1000; while (targetLength > curve.storedArcLengthsFull[normalisedIndex]) { normalisedIndex++; it--; if (it < 0) { curve.normalisedT[p] = 0; break; } } normalisedIndex = Mathf.Min(normalisedIndex, arcLengthResolution);//ensure we've not exceeded the length int prevNormIndex = Mathf.Max((normalisedIndex - 1), 0); int nextNormIndex = normalisedIndex; float lengthBefore = curve.storedArcLengthsFull[prevNormIndex]; float lengthAfter = curve.storedArcLengthsFull[nextNormIndex]; float midPointPercentage = (targetLength - lengthBefore) / (lengthAfter - lengthBefore); curve.midPointPerc[p] = midPointPercentage; curve.prevNormIndex[p] = prevNormIndex; curve.nextNormIndex[p] = nextNormIndex; float normalisedT = (normalisedIndex + midPointPercentage) / arcLengthResolution;//lerp between the values to get the exact normal T curve.normalisedT[p] = normalisedT; } curve.sampledPoints = new Vector3[storedPointSize]; curve.sampledLeftBoundaryPoints = new Vector3[storedPointSize]; curve.sampledRightBoundaryPoints = new Vector3[storedPointSize]; curve.sampledWidths = new float[storedPointSize]; curve.sampledCrowns = new float[storedPointSize]; curve.sampledTrackDirections = new Vector3[storedPointSize]; curve.sampledTrackUps = new Vector3[storedPointSize]; curve.sampledTrackCrosses = new Vector3[storedPointSize]; curve.sampledAngles = new float[storedPointSize]; for (int p = 0; p < storedPointSize; p++) { float tN = curve.normalisedT[p]; float tH = SplineMaths.CalculateHermite(tN); curve.sampledPoints[p] = SplineMaths.CalculateBezierPoint(tN, pointA.position, pointA.forwardControlPoint, pointB.backwardControlPoint, pointB.position); curve.sampledLeftBoundaryPoints[p] = SplineMaths.CalculateBezierPoint(tN, pointA.leftTrackBoundary, pointA.leftForwardControlPoint, pointB.leftBackwardControlPoint, pointB.leftTrackBoundary); curve.sampledRightBoundaryPoints[p] = SplineMaths.CalculateBezierPoint(tN, pointA.rightTrackBoundary, pointA.rightForwardControlPoint, pointB.rightBackwardControlPoint, pointB.rightTrackBoundary); curve.sampledWidths[p] = Mathf.LerpAngle(pointA.width, pointB.width, tH); curve.sampledCrowns[p] = Mathf.LerpAngle(pointA.crownAngle, pointB.crownAngle, tH); curve.sampledTrackUps[p] = Quaternion.Slerp(pointA.trackUpQ, pointB.trackUpQ, tH) * Vector3.forward; curve.clipArrayLeft[p] = true; curve.clipArrayRight[p] = true; } } } for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = _points[i]; if (!curve.curveIsDirty)//only recalculate modified points { continue; } if (curve.arcLength > 0) { int lastCurveIndex = (i > 0) ? i - 1 : (_looped) ? numberOfCurves - 1 : 0; int nextCurveIndex = (i < numberOfCurves - 1) ? i + 1 : (_looped) ? 0 : numberOfCurves - 1; TrackBuildRPoint lastcurve = _points[lastCurveIndex]; TrackBuildRPoint nextcurve = _points[nextCurveIndex]; int storedPointSize = curve.storedPointSize; for (int p = 0; p < storedPointSize; p++) { int pA = p - 1; int pB = p; int pC = p + 1; if (pA < 0) { pA = 0; } if (pC >= storedPointSize) { pC = storedPointSize - 1; } Vector3 sampledPointA = curve.sampledPoints[pA]; Vector3 sampledPointB = curve.sampledPoints[pB]; Vector3 sampledPointC = curve.sampledPoints[pC]; if (p == 0 && lastcurve != null && lastcurve.sampledPoints.Length > 1) { sampledPointA = lastcurve.sampledPoints[lastcurve.storedPointSize - 2];//retrieve the penultimate point from the last curve } if (p == storedPointSize - 1 && nextcurve != null && nextcurve.sampledPoints.Length > 1) { sampledPointC = nextcurve.sampledPoints[1];//retrieve the second point from the next curve } Vector3 sampledTrackDirectionA = (sampledPointB - sampledPointA); Vector3 sampledTrackDirectionB = (sampledPointC - sampledPointB); Vector3 sampledTrackDirection = (sampledTrackDirectionA + sampledTrackDirectionB).normalized; curve.sampledTrackDirections[pB] = sampledTrackDirection; curve.sampledTrackCrosses[pB] = Vector3.Cross(curve.sampledTrackUps[pB], sampledTrackDirection); curve.sampledAngles[pB] = Vector3.Angle(sampledTrackDirectionA, sampledTrackDirectionB) * -Mathf.Sign(Vector3.Dot((sampledTrackDirectionB - sampledTrackDirectionA), curve.sampledTrackCrosses[pB])); curve.clipArrayLeft[pB] = true; curve.clipArrayRight[pB] = true; } } } bool dirtyTextures = false; foreach (TrackBuildRTexture texture in _textures) { if (texture.isDirty) { dirtyTextures = true;//if nextNormIndex point was dirty, ensure it's rerendered } texture.isDirty = false; } foreach (TrackBuildRPoint point in _points) { if (point.curveIsDirty || dirtyTextures) { point.shouldReRender = true;//if nextNormIndex point was dirty, ensure it's rerendered } } //clean points foreach (TrackBuildRPoint point in _points) { point.isDirty = false;//reset all points - data is no longer considered dirty } //recalculate track length _trackLength = 0; foreach (TrackBuildRPoint curve in _points) { _trackLength += curve.arcLength; } }
void OnSceneGUI() { if (_track.drawMode) { DrawTrack(); return; } if (SceneView.focusedWindow != null) { SceneView.focusedWindow.wantsMouseMove = false; } Vector3 position = _trackBuildR.transform.position; Camera sceneCamera = Camera.current; _handleSize = HandleUtility.GetHandleSize(_trackBuildR.transform.position) * 0.1f; int realNumberOfPoints = _track.realNumberOfPoints; Ray mouseRay = Camera.current.ScreenPointToRay(new Vector3(Event.current.mousePosition.x, Screen.height - Event.current.mousePosition.y - 30, 0)); Quaternion mouseLookDirection = Quaternion.LookRotation(-mouseRay.direction); int numberOfCurves = _track.numberOfCurves; switch (_trackBuildR.mode) { case TrackBuildR.modes.track: Handles.color = TrackBuildRColours.GREEN; switch (_trackBuildR.pointMode) { case TrackBuildR.pointModes.add: if (SceneView.focusedWindow != null) { SceneView.focusedWindow.wantsMouseMove = true; } if (Event.current.type == EventType.MouseMove) { Repaint(); } Handles.color = TrackBuildRColours.GREY; for (int i = 0; i < _track.realNumberOfPoints; i++) { Vector3 pointPos = _track[i].worldPosition; float handleSize = HandleUtility.GetHandleSize(pointPos); Handles.DotCap(0, pointPos, Quaternion.identity, handleSize * 0.05f); } Handles.color = TrackBuildRColours.GREEN; float mousePercentage = NearestmMousePercentage(); // _track.GetNearestPoint(mousePlanePoint); Vector3 mouseTrackPoint = _track.GetTrackPosition(mousePercentage) + position; Handles.Label(mouseTrackPoint, "Add New Track Point"); float newPointHandleSize = HandleUtility.GetHandleSize(mouseTrackPoint) * HANDLE_SCALE; if (Handles.Button(mouseTrackPoint, mouseLookDirection, newPointHandleSize, newPointHandleSize, Handles.DotCap)) { int newPointIndex = _track.GetLastPointIndex(mousePercentage); TrackBuildRPoint newPoint = _track.InsertPoint(newPointIndex + 1); newPoint.worldPosition = mouseTrackPoint; newPoint.width = _track.GetTrackWidth(mousePercentage); newPoint.crownAngle = _track.GetTrackCrownAngle(mousePercentage); selectedPoint = newPointIndex + 1; GUI.changed = true; _trackBuildR.pointMode = TrackBuildR.pointModes.transform; } break; case TrackBuildR.pointModes.remove: if (SceneView.focusedWindow != null) { SceneView.focusedWindow.wantsMouseMove = true; } Handles.color = TrackBuildRColours.RED; for (int i = 0; i < realNumberOfPoints; i++) { TrackBuildRPoint point = _track[i]; float pointHandleSize = HandleUtility.GetHandleSize(point.worldPosition) * HANDLE_SCALE; Handles.Label(point.worldPosition, "Remove Track Point"); if (Handles.Button(point.worldPosition, mouseLookDirection, pointHandleSize, pointHandleSize, Handles.DotCap)) { _track.RemovePoint(point); GUI.changed = true; _trackBuildR.pointMode = TrackBuildR.pointModes.transform; } } break; default: SceneGUIPointBased(); break; } //draw track outline for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = _track[i]; if (curve == null) { continue; } int curvePoints = curve.storedPointSize; float dotPA = Vector3.Dot(sceneCamera.transform.forward, curve.worldPosition - sceneCamera.transform.position); float dotPB = Vector3.Dot(sceneCamera.transform.forward, curve.nextPoint.worldPosition - sceneCamera.transform.position); if (dotPA < 0 && dotPB < 0) { continue; } float curveDistance = Vector3.Distance(sceneCamera.transform.position, curve.center); int pointJump = Mathf.Max((int)(curveDistance / 20.0f), 1); Color trackOutline = curve.render ? TrackBuildRColours.GREEN : TrackBuildRColours.RED; Color trackOutlineA = trackOutline; trackOutlineA.a = 0.5f; for (int p = pointJump; p < curvePoints; p += pointJump) { int indexA = p - pointJump; int indexB = p; if (p + pointJump > curvePoints - 1) { indexB = curvePoints - 1; } Handles.color = trackOutlineA; Handles.DrawLine(curve.sampledPoints[indexA] + position, curve.sampledPoints[indexB] + position); Handles.color = trackOutline; Vector3 trackCrossWidth = curve.sampledTrackCrosses[indexA] * (curve.sampledWidths[indexA] * 0.5f); Handles.DrawLine(curve.sampledPoints[indexA] + trackCrossWidth + position, curve.sampledPoints[indexB] + trackCrossWidth + position); Handles.DrawLine(curve.sampledPoints[indexA] - trackCrossWidth + position, curve.sampledPoints[indexB] - trackCrossWidth + position); } } break; case TrackBuildR.modes.boundary: //draw boundary outline for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = _track[i]; int curvePoints = curve.storedPointSize; float dotPA = Vector3.Dot(sceneCamera.transform.forward, curve.worldPosition - sceneCamera.transform.position); float dotPB = Vector3.Dot(sceneCamera.transform.forward, curve.nextPoint.worldPosition - sceneCamera.transform.position); if (dotPA < 0 && dotPB < 0) { continue; } float curveDistance = Vector3.Distance(sceneCamera.transform.position, curve.center); int pointJump = Mathf.Max((int)(curveDistance / 20.0f), 1); for (int p = pointJump; p < curvePoints; p += pointJump) { int indexA = p - pointJump; int indexB = p; if (p + pointJump > curvePoints - 1) { indexB = curvePoints - 1; } if (_track.disconnectBoundary) { Handles.color = TrackBuildRColours.BLUE; Handles.DrawLine(curve.sampledLeftBoundaryPoints[indexA] + position, curve.sampledLeftBoundaryPoints[indexB] + position); Handles.color = TrackBuildRColours.RED; Handles.DrawLine(curve.sampledRightBoundaryPoints[indexA] + position, curve.sampledRightBoundaryPoints[indexB] + position); } else { Vector3 trackCrossWidth = curve.sampledTrackCrosses[indexA] * (curve.sampledWidths[indexA] * 0.5f); Handles.color = TrackBuildRColours.BLUE; Handles.DrawLine(curve.sampledPoints[indexA] + trackCrossWidth + position, curve.sampledPoints[indexB] + trackCrossWidth + position); Handles.color = TrackBuildRColours.RED; Handles.DrawLine(curve.sampledPoints[indexA] - trackCrossWidth + position, curve.sampledPoints[indexB] - trackCrossWidth + position); } } } SceneGUIPointBased(); break; case TrackBuildR.modes.textures: for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint thisCurve = _track[i]; float pointHandleSize = HandleUtility.GetHandleSize(thisCurve.center) * HANDLE_SCALE; Handles.color = (i == selectedCurveIndex) ? TrackBuildRColours.RED : TrackBuildRColours.BLUE; if (Handles.Button(thisCurve.center, Quaternion.identity, pointHandleSize, pointHandleSize, Handles.DotCap)) { selectedCurveIndex = i; GUIUtility.hotControl = 0; GUIUtility.keyboardControl = 0; GUI.changed = true; } } Handles.color = TrackBuildRColours.RED; TrackBuildRPoint selectedCurve = _track[selectedCurveIndex]; int numberOfSelectedCurvePoints = selectedCurve.storedPointSize; for (int i = 0; i < numberOfSelectedCurvePoints - 1; i++) { Vector3 leftPointA = selectedCurve.sampledLeftBoundaryPoints[i]; Vector3 leftPointB = selectedCurve.sampledLeftBoundaryPoints[i + 1]; Vector3 rightPointA = selectedCurve.sampledRightBoundaryPoints[i]; Vector3 rightPointB = selectedCurve.sampledRightBoundaryPoints[i + 1]; Handles.DrawLine(leftPointA, leftPointB); Handles.DrawLine(rightPointA, rightPointB); if (i == 0) { Handles.DrawLine(leftPointA, rightPointA); } if (i == numberOfSelectedCurvePoints - 2) { Handles.DrawLine(leftPointB, rightPointB); } } break; case TrackBuildR.modes.terrain: //nothing break; case TrackBuildR.modes.stunt: SceneGUIPointBased(); TrackBuildRPoint atPoint = _track[selectedPoint]; TrackBuildRPoint lastPoint = _track.GetPoint(selectedPoint - 1); TrackBuildRPoint nextPoint = _track.GetPoint(selectedPoint + 1); float trackWidth; Vector3 startCross; Vector3 p0, p1, p2, p3, p4, p5, p6, p7; switch (_trackBuildR.stuntMode) { case TrackBuildR.stuntModes.loop: atPoint = _track[selectedPoint]; trackWidth = atPoint.width; float loopRadius = _track.loopRadius; Vector3 loopPosition = atPoint.worldPosition; Vector3 trackDirection = atPoint.trackDirection.normalized; Vector3 trackup = atPoint.trackUpQ * Vector3.forward; Vector3 trackCross = atPoint.trackCross; Vector3 loopCentreHeight = loopRadius * trackup; Quaternion loopAngle = Quaternion.FromToRotation(Vector3.right, trackDirection); for (float i = 0; i < 0.99f; i += 0.01f) { float radA = Mathf.PI * 2 * (i + 0.5f); float radB = Mathf.PI * 2 * (i + 0.51f); Vector3 pointLoopPositionA = loopAngle * ((new Vector3(Mathf.Sin(radA), Mathf.Cos(radA), 0)) * loopRadius); Vector3 pointLoopPositionB = loopAngle * ((new Vector3(Mathf.Sin(radB), Mathf.Cos(radB), 0)) * loopRadius); Vector3 lateral = Vector3.Lerp((trackCross * trackWidth * -0.6f), (trackCross * trackWidth * 0.6f), i); Vector3 pointPositionA = (pointLoopPositionA) + lateral + loopPosition + loopCentreHeight; Vector3 pointPositionB = (pointLoopPositionB) + lateral + loopPosition + loopCentreHeight; Handles.DrawLine(pointPositionA, pointPositionB); } break; case TrackBuildR.stuntModes.jump: atPoint = _track[selectedPoint]; lastPoint = _track.GetPoint(selectedPoint - 1); nextPoint = _track.GetPoint(selectedPoint + 1); float trackPartDistance = lastPoint.arcLength + atPoint.arcLength; float jumpDistance = Mathf.Min(trackPartDistance * 0.333f, _track.maxJumpLength); Vector3 jumpDirection = atPoint.trackDirection; Vector3 jumpMiddle = atPoint.worldPosition; startCross = atPoint.trackCross; trackWidth = atPoint.width * 0.5f; Quaternion trackUp = atPoint.trackUpQ; Vector3 jumpHeight = trackUp * (Vector3.forward * _track.jumpHeight); Vector3 jumpStartPosition = jumpMiddle - jumpDirection * (jumpDistance * 0.33f); Vector3 jumpEndPosition = jumpMiddle + jumpDirection * (jumpDistance * 0.33f); p0 = lastPoint.worldPosition + trackWidth * startCross; p1 = lastPoint.worldPosition - trackWidth * startCross; p2 = jumpStartPosition + trackWidth * startCross + jumpHeight; p3 = jumpStartPosition - trackWidth * startCross + jumpHeight; p4 = jumpEndPosition + trackWidth * startCross + jumpHeight; p5 = jumpEndPosition - trackWidth * startCross + jumpHeight; p6 = nextPoint.worldPosition + trackWidth * startCross; p7 = nextPoint.worldPosition - trackWidth * startCross; Handles.DrawLine(p0, p2); Handles.DrawLine(p1, p3); Handles.DrawLine(p0, p1); Handles.DrawLine(p2, p3); Handles.DrawLine(p2, p2 - jumpHeight); Handles.DrawLine(p3, p3 - jumpHeight); Handles.DrawLine(p0, p2 - jumpHeight); Handles.DrawLine(p1, p3 - jumpHeight); Handles.DrawLine(p4, p6); Handles.DrawLine(p5, p7); Handles.DrawLine(p4, p5); Handles.DrawLine(p6, p7); Handles.DrawLine(p4, p4 - jumpHeight); Handles.DrawLine(p5, p5 - jumpHeight); Handles.DrawLine(p6, p4 - jumpHeight); Handles.DrawLine(p7, p5 - jumpHeight); break; // case TrackBuildR.stuntModes.twist: // // atPoint = _track[selectedPoint]; // lastPoint = _track.GetPoint(selectedPoint - 1); // // float twistDistance = Mathf.Min((lastPoint.arcLength + atPoint.arcLength) * 0.333f, _track.maxJumpLength); // // Vector3 twistDirection = atPoint.trackDirection; // Vector3 twistMiddle = atPoint.worldPosition; // Vector3 twistUp = atPoint.trackUp; // float twistRadius = _track.twistRadius; // Vector3 twistStartPosition = twistMiddle - twistDirection * (twistDistance * 0.33f); // Vector3 twistEndPosition = twistMiddle + twistDirection * (twistDistance * 0.33f); // Vector3 twistCentreHeight = twistUp * twistRadius; // Quaternion twistAngle = Quaternion.LookRotation(twistDirection, twistUp); // for(float i = 0; i < 0.99f; i += 0.01f ) // { // float radA = Mathf.PI * 2 * (i+0.5f); // float radB = Mathf.PI * 2 * (i+0.51f); // Vector3 pointLoopPositionA = twistAngle * ((new Vector3(Mathf.Sin(radA), Mathf.Cos(radA), 0)) * twistRadius); // Vector3 pointLoopPositionB = twistAngle * ((new Vector3(Mathf.Sin(radB), Mathf.Cos(radB), 0)) * twistRadius); // float smoothI = i * i * (3.0f - 2.0f * i); // Vector3 lateral = Vector3.Lerp(twistStartPosition, twistEndPosition, i + (i-smoothI)); // Vector3 pointPositionA = (pointLoopPositionA) + lateral + twistCentreHeight; // Vector3 pointPositionB = (pointLoopPositionB) + lateral + twistCentreHeight; // Handles.DrawLine(pointPositionA, pointPositionB); // } // // break; case TrackBuildR.stuntModes.jumptwist: atPoint = _track[selectedPoint]; lastPoint = _track.GetPoint(selectedPoint - 1); nextPoint = _track.GetPoint(selectedPoint + 1); float trackTPartDistance = lastPoint.arcLength + atPoint.arcLength; float jumpTDistance = Mathf.Min(trackTPartDistance * 0.333f, _track.maxJumpLength); trackWidth = atPoint.width * 0.5f; startCross = atPoint.trackCross; Vector3 jumpTDirection = atPoint.trackDirection; Vector3 jumpTMiddle = atPoint.worldPosition; Quaternion atPointUpQ = atPoint.trackUpQ; Quaternion trackUpJump = Quaternion.AngleAxis(_track.twistAngle, -jumpTDirection); Vector3 trackCrossExit = trackUpJump * startCross; Vector3 trackCrossEntry = Quaternion.Inverse(trackUpJump) * startCross; Vector3 jumpLateral = startCross * _track.twistAngle / 33.3f; Vector3 jumpTHeight = atPointUpQ * (Vector3.forward * _track.jumpHeight); Vector3 jumpTStartPosition = jumpTMiddle - jumpTDirection * (jumpTDistance * 0.33f) + jumpTHeight - jumpLateral; Vector3 jumpTEndPosition = jumpTMiddle + jumpTDirection * (jumpTDistance * 0.33f) + jumpTHeight + jumpLateral; p0 = lastPoint.worldPosition + trackWidth * startCross; p1 = lastPoint.worldPosition - trackWidth * startCross; p2 = jumpTStartPosition + trackWidth * trackCrossExit; p3 = jumpTStartPosition - trackWidth * trackCrossExit; p4 = jumpTEndPosition + trackWidth * trackCrossEntry; p5 = jumpTEndPosition - trackWidth * trackCrossEntry; p6 = nextPoint.worldPosition + trackWidth * startCross; p7 = nextPoint.worldPosition - trackWidth * startCross; Handles.DrawLine(p0, p2); Handles.DrawLine(p1, p3); Handles.DrawLine(p0, p1); Handles.DrawLine(p2, p3); // Handles.DrawLine(p2, p2 - jumpTHeight); // Handles.DrawLine(p3, p3 - jumpTHeight); // Handles.DrawLine(p0, p2 - jumpTHeight); // Handles.DrawLine(p1, p3 - jumpTHeight); Handles.DrawLine(p4, p6); Handles.DrawLine(p5, p7); Handles.DrawLine(p4, p5); Handles.DrawLine(p6, p7); // Handles.DrawLine(p4, p4 - jumpTHeight); // Handles.DrawLine(p5, p5 - jumpTHeight); // Handles.DrawLine(p6, p4 - jumpTHeight); // Handles.DrawLine(p7, p5 - jumpTHeight); break; } break; case TrackBuildR.modes.diagram: if (SceneView.focusedWindow != null) { SceneView.focusedWindow.wantsMouseMove = true; } if (!_track.showDiagram) { break; } Plane diagramPlane = new Plane(Vector3.up, position); float diagramDistance; float crossSize = _handleSize * 10; switch (_trackBuildR.track.assignedPoints) { case 0: //display the diagram scale points Vector3 diagramPointA = _track.scalePointA; Vector3 diagramPointB = _track.scalePointB; if (diagramPointA != Vector3.zero || diagramPointB != Vector3.zero) { Handles.color = TrackBuildRColours.BLUE; Handles.DrawLine(diagramPointA, diagramPointA + Vector3.left * crossSize); Handles.DrawLine(diagramPointA, diagramPointA + Vector3.right * crossSize); Handles.DrawLine(diagramPointA, diagramPointA + Vector3.forward * crossSize); Handles.DrawLine(diagramPointA, diagramPointA + Vector3.back * crossSize); Handles.color = TrackBuildRColours.GREEN; Handles.DrawLine(diagramPointB, diagramPointB + Vector3.left * crossSize); Handles.DrawLine(diagramPointB, diagramPointB + Vector3.right * crossSize); Handles.DrawLine(diagramPointB, diagramPointB + Vector3.forward * crossSize); Handles.DrawLine(diagramPointB, diagramPointB + Vector3.back * crossSize); Handles.color = TrackBuildRColours.RED; Handles.DrawLine(diagramPointA, diagramPointB); } break; case 1: //place the first of two scale points to define the diagram scale size Ray diagramRay = Camera.current.ScreenPointToRay(new Vector3(Event.current.mousePosition.x, Screen.height - Event.current.mousePosition.y - 30, 0)); if (diagramPlane.Raycast(diagramRay, out diagramDistance)) { Vector3 diagramPlanePoint = diagramRay.GetPoint(diagramDistance); Handles.color = TrackBuildRColours.BLUE; Handles.DrawLine(diagramPlanePoint, diagramPlanePoint + Vector3.left * crossSize); Handles.DrawLine(diagramPlanePoint, diagramPlanePoint + Vector3.right * crossSize); Handles.DrawLine(diagramPlanePoint, diagramPlanePoint + Vector3.forward * crossSize); Handles.DrawLine(diagramPlanePoint, diagramPlanePoint + Vector3.back * crossSize); Handles.color = new Color(0, 0, 0, 0); if (Handles.Button(diagramPlanePoint, Quaternion.identity, crossSize, crossSize, Handles.DotCap)) { _track.scalePointA = diagramPlanePoint; _track.assignedPoints = 2; } } break; case 2: //place the second of two scale points to define the diagram scale Vector3 diagramPoint1 = _track.scalePointA; Handles.color = TrackBuildRColours.BLUE; Handles.DrawLine(diagramPoint1, diagramPoint1 + Vector3.left * crossSize); Handles.DrawLine(diagramPoint1, diagramPoint1 + Vector3.right * crossSize); Handles.DrawLine(diagramPoint1, diagramPoint1 + Vector3.forward * crossSize); Handles.DrawLine(diagramPoint1, diagramPoint1 + Vector3.back * crossSize); Ray diagramRayB = Camera.current.ScreenPointToRay(new Vector3(Event.current.mousePosition.x, Screen.height - Event.current.mousePosition.y - 30, 0)); if (diagramPlane.Raycast(diagramRayB, out diagramDistance)) { Vector3 diagramPlanePoint = diagramRayB.GetPoint(diagramDistance); Handles.color = TrackBuildRColours.RED; Handles.DrawLine(diagramPlanePoint, diagramPoint1); Handles.color = TrackBuildRColours.GREEN; Handles.DrawLine(diagramPlanePoint, diagramPlanePoint + Vector3.left * crossSize); Handles.DrawLine(diagramPlanePoint, diagramPlanePoint + Vector3.right * crossSize); Handles.DrawLine(diagramPlanePoint, diagramPlanePoint + Vector3.forward * crossSize); Handles.DrawLine(diagramPlanePoint, diagramPlanePoint + Vector3.back * crossSize); Handles.color = new Color(0, 0, 0, 0); if (Handles.Button(diagramPlanePoint, Quaternion.identity, crossSize, crossSize, Handles.DotCap)) { _track.scalePointB = diagramPlanePoint; _track.assignedPoints = 0; //wUpdateDiagram(); } } break; } break; } if (Event.current.type == EventType.ValidateCommand) { switch (Event.current.commandName) { case "UndoRedoPerformed": // Debug.Log("UndoRedoPerformed"); _trackBuildR.ForceFullRecalculation(); GUI.changed = true; return; } } if (GUI.changed) { UpdateGui(); } }
public void UpdateRender() { if (track.numberOfCurves == 0) { return; } track.RecalculateCurves(); float trackMeshRes = track.meshResolution; float bumperDistanceA = 0; float bumperDistanceB = 0; int numberOfCurves = track.numberOfCurves; bool renderTrack = track.render; float UVOffset = 0; int polyCount = 0; for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = track[i]; DynamicMeshGenericMultiMaterialMesh dynamicTrackMesh = curve.dynamicTrackMesh; DynamicMeshGenericMultiMaterialMesh dynamicBoundaryMesh = curve.dynamicBoundaryMesh; DynamicMeshGenericMultiMaterialMesh dynamicOffroadMesh = curve.dynamicOffroadMesh; DynamicMeshGenericMultiMaterialMesh dynamicBumperMesh = curve.dynamicBumperMesh; DynamicMeshGenericMultiMaterialMesh dynamicColliderMesh = curve.dynamicColliderMesh; DynamicMeshGenericMultiMaterialMesh dynamicBottomMesh = curve.dynamicBottomMesh; if (!curve.render || !renderTrack) { dynamicTrackMesh.Clear(); dynamicBoundaryMesh.Clear(); dynamicColliderMesh.Clear(); dynamicOffroadMesh.Clear(); dynamicBumperMesh.Clear(); dynamicBottomMesh.Clear(); } if (curve.shouldReRender && curve.render && renderTrack) { dynamicTrackMesh.Clear(); dynamicTrackMesh.subMeshCount = 1; dynamicBoundaryMesh.Clear(); dynamicBoundaryMesh.subMeshCount = 1; dynamicColliderMesh.Clear(); dynamicColliderMesh.subMeshCount = 1; dynamicOffroadMesh.Clear(); dynamicOffroadMesh.subMeshCount = 1; dynamicBumperMesh.Clear(); dynamicBumperMesh.subMeshCount = 1; dynamicBottomMesh.Clear(); dynamicBottomMesh.subMeshCount = 1; dynamicTrackMesh.name = "curve " + i + " track mesh"; dynamicBoundaryMesh.name = "curve " + i + " boundary mesh"; dynamicColliderMesh.name = "curve " + i + " trackCollider mesh"; dynamicOffroadMesh.name = "curve " + i + " offroad mesh"; dynamicBumperMesh.name = "curve " + i + " bumper mesh"; dynamicBottomMesh.name = "curve " + i + " bottom mesh"; bool trackTextureFlip = (track.numberOfTextures > 0) ? track.Texture(curve.trackTextureStyleIndex).flipped : false; bool boundaryTextureFlip = (track.numberOfTextures > 0) ? track.Texture(curve.boundaryTextureStyleIndex).flipped : false; bool bumperTextureFlip = (track.numberOfTextures > 0) ? track.Texture(curve.bumperTextureStyleIndex).flipped : false; bool bottomTextureFlip = (track.numberOfTextures > 0) ? track.Texture(curve.bottomTextureStyleIndex).flipped : false; int storedPointSize = curve.storedPointSize; float curveLength = curve.arcLength; //Store these points so we can use previous values when Bezier clips itself Vector3 leftPointA = curve.sampledLeftBoundaryPoints[0]; Vector3 rightPointA = curve.sampledRightBoundaryPoints[0]; Vector3 leftPointB = curve.sampledLeftBoundaryPoints[0]; Vector3 rightPointB = curve.sampledRightBoundaryPoints[0]; for (int p = 0; p < storedPointSize - 1; p++) { float tA = curve.normalisedT[p]; float tB = curve.normalisedT[p + 1]; int sampleIndexA = p; int sampleIndexB = sampleIndexA + 1; Vector3 pointA = curve.sampledPoints[sampleIndexA]; Vector3 pointB = curve.sampledPoints[sampleIndexB]; float trackWidthA = curve.sampledWidths[sampleIndexA] * 0.5f; float trackWidthB = curve.sampledWidths[sampleIndexB] * 0.5f; float trackCrownA = curve.sampledCrowns[sampleIndexA]; float trackCrownB = curve.sampledCrowns[sampleIndexB]; Vector3 trackUpA = curve.sampledTrackUps[sampleIndexA]; Vector3 trackUpB = curve.sampledTrackUps[sampleIndexB]; Vector3 trackCrossA = curve.sampledTrackCrosses[sampleIndexA]; Vector3 trackCrossB = curve.sampledTrackCrosses[sampleIndexB]; float trackAngle = curve.sampledAngles[sampleIndexA]; if (trackUpA == Vector3.zero || trackUpB == Vector3.zero) { return; } //TrackBuildRTexture texture = track.Texture(curve.trackTextureStyleIndex) ;// track.trackTexture; int pointANumber = Mathf.Max(Mathf.CeilToInt(trackWidthA / trackMeshRes / 2) * 2, 2); //number of verts along line A int pointBNumber = Mathf.Max(Mathf.CeilToInt(trackWidthB / trackMeshRes / 2) * 2, 2); //number of verts along line B int numberOfNewVerts = pointANumber + pointBNumber; Vector3[] uncrownedVerts = new Vector3[numberOfNewVerts]; if (curve.clipArrayLeft[sampleIndexA]) { leftPointA = (pointA + (trackCrossA * -trackWidthA)); } if (curve.clipArrayRight[sampleIndexA]) { rightPointA = (pointA + (trackCrossA * trackWidthA)); } float curveLengthA = (curveLength * tA) / trackWidthA + UVOffset; float curveLengthB = (curveLength * tB) / trackWidthB + UVOffset; float lerpASize = 1.0f / (pointANumber - 1); //track vertex/uv data for point nextNormIndex Vector3[] newAPoints = new Vector3[pointANumber]; Vector3[] newTrackPoints = new Vector3[pointANumber + pointBNumber]; Vector2[] newTrackUVs = new Vector2[pointANumber + pointBNumber]; for (int pa = 0; pa < pointANumber; pa++) { float lerpValue = lerpASize * pa; Vector3 crownVector = Quaternion.LookRotation(trackUpA) * new Vector3(0, 0, Mathf.Sin(lerpValue * Mathf.PI) * trackCrownA); Vector3 uncrownedVert = Vector3.Lerp(leftPointA, rightPointA, lerpValue); uncrownedVerts[pa] = uncrownedVert; Vector3 newVert = uncrownedVert + crownVector; newAPoints[pa] = newVert; newTrackPoints[pa] = newVert; Vector2 newUV = (!trackTextureFlip) ? new Vector2(lerpValue, curveLengthA) : new Vector2(curveLengthA, lerpValue); newTrackUVs[pa] = newUV; } //track vertex/uv data for point prevNormIndex if (curve.clipArrayLeft[sampleIndexB]) { leftPointB = (pointB + (trackCrossB * -trackWidthB)); } if (curve.clipArrayRight[sampleIndexB]) { rightPointB = (pointB + (trackCrossB * trackWidthB)); } float lerpBSize = 1.0f / (pointBNumber - 1); Vector3[] newBPoints = new Vector3[pointBNumber]; for (int pb = 0; pb < pointBNumber; pb++) { float lerpValue = lerpBSize * pb; Vector3 crownVector = Quaternion.LookRotation(trackUpB) * new Vector3(0, 0, Mathf.Sin(lerpValue * Mathf.PI) * trackCrownB); Vector3 uncrownedVert = Vector3.Lerp(leftPointB, rightPointB, lerpValue); uncrownedVerts[pb + pointANumber] = uncrownedVert; Vector3 newVert = uncrownedVert + crownVector; newBPoints[pb] = newVert; newTrackPoints[pb + pointANumber] = newVert; Vector2 newUV = (!trackTextureFlip) ? new Vector2(lerpValue, curveLengthB) : new Vector2(curveLengthB, lerpValue); newTrackUVs[pb + pointANumber] = newUV; } int baseTriPointA = 0; int baseTriPointB = pointANumber; int triPointA = baseTriPointA; int triPointB = baseTriPointB; int newTriPointCountA = 1; int newTriPointCountB = 1; int[] newTrackTris = new int[(numberOfNewVerts - 2) * 3]; for (int v = 0; v < numberOfNewVerts - 2; v++) { int newTriPointA = baseTriPointA + newTriPointCountA; int newTriPointB = baseTriPointB + newTriPointCountB; float newTriPointADist, newTriPointBDist; if (newTriPointA >= baseTriPointA + pointANumber) { newTriPointADist = float.PositiveInfinity; } else { newTriPointADist = Vector3.SqrMagnitude(uncrownedVerts[newTriPointA] - uncrownedVerts[baseTriPointA]); } if (newTriPointB >= baseTriPointB + pointBNumber) { newTriPointBDist = float.PositiveInfinity; } else { newTriPointBDist = Vector3.SqrMagnitude(uncrownedVerts[newTriPointB] - uncrownedVerts[baseTriPointB]); } if (newTriPointADist < newTriPointBDist) { newTrackTris[v * 3] = triPointA; newTrackTris[v * 3 + 1] = triPointB; newTrackTris[v * 3 + 2] = newTriPointA; triPointA = newTriPointA; newTriPointCountA++; } else { newTrackTris[v * 3] = triPointA; newTrackTris[v * 3 + 1] = triPointB; newTrackTris[v * 3 + 2] = newTriPointB; triPointB = newTriPointB; newTriPointCountB++; } } dynamicTrackMesh.AddData(newTrackPoints, newTrackUVs, newTrackTris, 0); dynamicColliderMesh.AddData(newTrackPoints, newTrackUVs, newTrackTris, 0); //Boundary float trackBoundaryWallHeight = curve.boundaryHeight;// track.boundaryHeight; Vector3 leftBoundaryPointA, leftBoundaryPointB, rightBoundaryPointA, rightBoundaryPointB; if (track.disconnectBoundary) { leftBoundaryPointA = curve.sampledLeftBoundaryPoints[sampleIndexA]; leftBoundaryPointB = curve.sampledLeftBoundaryPoints[sampleIndexB]; rightBoundaryPointA = curve.sampledRightBoundaryPoints[sampleIndexA]; rightBoundaryPointB = curve.sampledRightBoundaryPoints[sampleIndexB]; } else { leftBoundaryPointA = leftPointA; leftBoundaryPointB = leftPointB; rightBoundaryPointA = rightPointA; rightBoundaryPointB = rightPointB; } Vector3[] newWallVerts; Vector2[] newWallUVs; int[] newWallTris; //Boundary Render Mesh if (curve.renderBounds) { //LEFT newWallVerts = new[] { leftBoundaryPointA, leftBoundaryPointB, leftBoundaryPointA + trackUpA * trackBoundaryWallHeight, leftBoundaryPointB + trackUpB * trackBoundaryWallHeight }; if (!boundaryTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 1, 0, 2, 1, 2, 3 }; // newWallTris = (boundaryTextureFlip) ? (new[] { 1, 0, 2, 1, 2, 3 }) : (new[] { 0,2,1,2,3,1 }); // newWallTris = (!track.renderBoundaryWallReverse) ? new[] { 1, 0, 2, 1, 2, 3 } : new[] { 1, 0, 2, 1, 2, 3, 0, 1, 2, 2, 1, 3 }; dynamicBoundaryMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (track.renderBoundaryWallReverse) { newWallTris = new[] { 0, 1, 2, 2, 1, 3 }; // newWallTris = (boundaryTextureFlip) ? (new[] { 0, 1, 2, 2, 1, 3 }) : (new[] { 0, 2, 1, 2, 3, 1 }); dynamicBoundaryMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } //RIGHT newWallVerts = (new[] { rightBoundaryPointA, rightBoundaryPointB, rightBoundaryPointA + trackUpA * trackBoundaryWallHeight, rightBoundaryPointB + trackUpB * trackBoundaryWallHeight }); //newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), }; if (!boundaryTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 0, 1, 2, 2, 1, 3 }; //newWallTris = (!track.renderBoundaryWallReverse) ? new[] { 0, 1, 2, 2, 1, 3 } : new[] { 1, 0, 2, 1, 2, 3, 0, 1, 2, 2, 1, 3 }; dynamicBoundaryMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (track.renderBoundaryWallReverse) { newWallTris = new[] { 1, 0, 2, 1, 2, 3 }; dynamicBoundaryMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } } if (curve.trackCollider) { //COLLIDER walls for on track border float trackColliderWallHeight = track.trackColliderWallHeight; if (curve.colliderSides) { newWallVerts = (new[] { leftBoundaryPointA, leftBoundaryPointB, leftBoundaryPointA + trackUpA * trackColliderWallHeight, leftBoundaryPointB + trackUpB * trackColliderWallHeight }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); newWallVerts = (new[] { rightBoundaryPointA, rightBoundaryPointB, rightBoundaryPointA + trackUpA * trackColliderWallHeight, rightBoundaryPointB + trackUpB * trackColliderWallHeight }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 0, 1, 2, 2, 1, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } //offroad bits if (track.disconnectBoundary) { Vector2 offroadTextureSize = Vector2.one; if (track.numberOfTextures > 0) { offroadTextureSize = track.Texture(curve.offroadTextureStyleIndex).textureUnitSize;// track.offroadTexture.textureUnitSize; } newWallVerts = (new[] { leftPointA, leftPointB, leftBoundaryPointA, leftBoundaryPointB }); newWallUVs = (new[] { new Vector2(leftPointA.x / offroadTextureSize.x, leftPointA.z / offroadTextureSize.y), new Vector2(leftPointB.x / offroadTextureSize.x, leftPointB.z / offroadTextureSize.y), new Vector2(leftBoundaryPointA.x / offroadTextureSize.x, leftBoundaryPointA.z / offroadTextureSize.y), new Vector2(leftBoundaryPointB.x / offroadTextureSize.x, leftBoundaryPointB.z / offroadTextureSize.y) }); newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicOffroadMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); newWallVerts = (new[] { rightPointA, rightPointB, rightBoundaryPointA, rightBoundaryPointB }); newWallUVs = (new[] { new Vector2(rightPointA.x / offroadTextureSize.x, rightPointA.z / offroadTextureSize.y), new Vector2(rightPointB.x / offroadTextureSize.x, rightPointB.z / offroadTextureSize.y), new Vector2(rightBoundaryPointA.x / offroadTextureSize.x, rightBoundaryPointA.z / offroadTextureSize.y), new Vector2(rightBoundaryPointB.x / offroadTextureSize.x, rightBoundaryPointB.z / offroadTextureSize.y) }); newWallTris = (new[] { 0, 1, 2, 2, 1, 3 }); dynamicOffroadMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); newWallVerts = (new[] { leftPointA, leftPointB, leftBoundaryPointA, leftBoundaryPointB }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); newWallVerts = (new[] { rightPointA, rightPointB, rightBoundaryPointA, rightBoundaryPointB }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 0, 1, 2, 2, 1, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } if (track.includeColliderRoof) { newWallVerts = (new[] { leftBoundaryPointA + trackUpA * trackColliderWallHeight, leftBoundaryPointB + trackUpB * trackColliderWallHeight, rightBoundaryPointA + trackUpA * trackColliderWallHeight, rightBoundaryPointB + trackUpB * trackColliderWallHeight }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } } if ((track.trackBumpers && curve.generateBumpers) || curve.generateBumpers) { float bumperWidth = track.bumperWidth; float bumperHeight = track.bumperHeight; Vector3 bumperRaisedA = trackUpA * bumperHeight; Vector3 bumperRaisedB = trackUpB * bumperHeight; float trackAngleThreashold = track.bumperAngleThresold; //left bumpers if (trackAngle >= trackAngleThreashold) { Vector3 offroadEdgeDirectionA = (leftBoundaryPointA - leftPointA).normalized; Vector3 trackEdgeDirectionA = (newAPoints[1] - newAPoints[0]).normalized; Vector3 bumperDirectionA = (Vector3.Project(offroadEdgeDirectionA, trackUpA) - trackEdgeDirectionA).normalized; Vector3 offroadEdgeDirectionB = (leftBoundaryPointB - leftPointB).normalized; Vector3 trackEdgeDirectionB = (newBPoints[1] - newBPoints[0]).normalized; Vector3 bumperDirectionB = (Vector3.Project(offroadEdgeDirectionB, trackUpB) - trackEdgeDirectionB).normalized; float trackEdgeA = Vector3.Distance(pointA, leftPointA); float offroadEdgeA = Vector3.Distance(pointA, leftBoundaryPointA); bool offroadBumper = (trackEdgeA < (offroadEdgeA - bumperWidth)); Vector3 bumperLeft0 = (offroadBumper ? leftPointA + bumperDirectionA * bumperWidth : leftBoundaryPointA) + bumperRaisedA; Vector3 bumperLeft1 = (offroadBumper ? leftPointA : bumperLeft0 - (bumperDirectionA * bumperWidth) - bumperRaisedB);//bumperLeft0 + (trackEdgeDirectionA * bumperWidth)) - bumperRaisedB; Vector3 bumperLeft2 = (offroadBumper ? leftPointB + bumperDirectionB * bumperWidth : leftBoundaryPointB) + bumperRaisedB; Vector3 bumperLeft3 = (offroadBumper ? leftPointB : bumperLeft2 - (bumperDirectionB * bumperWidth) - bumperRaisedB); float bumperSegmentDistanceA = Vector3.Distance(bumperLeft0, bumperLeft2); float uvStartA, uvEndA; if (track.numberOfTextures > 0) { uvStartA = bumperDistanceA / track.Texture(curve.bumperTextureStyleIndex).textureUnitSize.y; // track.bumperTexture.textureUnitSize.y; uvEndA = (bumperDistanceA + bumperSegmentDistanceA) / track.Texture(curve.bumperTextureStyleIndex).textureUnitSize.y; // track.bumperTexture.textureUnitSize.y; } else { uvStartA = bumperDistanceA; // track.bumperTexture.textureUnitSize.y; uvEndA = (bumperDistanceA + bumperSegmentDistanceA); // track.bumperTexture.textureUnitSize.y; } newWallVerts = (new[] { bumperLeft0, bumperLeft1, bumperLeft2, bumperLeft3 }); if (!bumperTextureFlip) { newWallUVs = (new[] { new Vector2(uvStartA, 1), new Vector2(uvStartA, 0), new Vector2(uvEndA, 1), new Vector2(uvEndA, 0) }); } else { newWallUVs = (new[] { new Vector2(1, uvStartA), new Vector2(0, uvStartA), new Vector2(1, uvEndA), new Vector2(0, uvEndA) }); } newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicBumperMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); bumperDistanceA += bumperSegmentDistanceA; newWallVerts = (new[] { bumperLeft0, bumperLeft1, bumperLeft2, bumperLeft3 }); newWallUVs = (new[] { Vector2.zero, Vector2.zero, Vector2.zero, Vector2.zero }); newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } //Right bumpers if (trackAngle < -trackAngleThreashold) { Vector3 trackEdgeDirectionA = (newAPoints[pointANumber - 2] - newAPoints[pointANumber - 1]).normalized; Vector3 trackEdgeDirectionB = (newBPoints[pointBNumber - 2] - newBPoints[pointBNumber - 1]).normalized; Vector3 bumperRight0 = ((Vector3.Distance(pointA, rightPointA) < (Vector3.Distance(pointA, rightBoundaryPointA) - bumperWidth)) ? rightPointA : rightBoundaryPointA) + bumperRaisedA; Vector3 bumperRight1 = bumperRight0 + (trackEdgeDirectionA * bumperWidth); Vector3 bumperRight2 = ((Vector3.Distance(pointB, rightPointB) < (Vector3.Distance(pointB, rightBoundaryPointB) - bumperWidth)) ? rightPointB : rightBoundaryPointB) + bumperRaisedB; Vector3 bumperRight3 = bumperRight2 + (trackEdgeDirectionB * bumperWidth); float bumperSegmentDistanceB = Vector3.Distance(bumperRight0, bumperRight2); //float bumperSegmentDistanceA = Vector3.Distance(bumperLeft0, bumperLeft2); float uvStartB, uvEndB; if (track.numberOfTextures > 0) { uvStartB = bumperDistanceB / track.Texture(curve.bumperTextureStyleIndex).textureUnitSize.y; // track.bumperTexture.textureUnitSize.y; uvEndB = (bumperDistanceB + bumperSegmentDistanceB) / track.Texture(curve.bumperTextureStyleIndex).textureUnitSize.y; // track.bumperTexture.textureUnitSize.y; } else { uvStartB = bumperDistanceB; uvEndB = (bumperDistanceB + bumperSegmentDistanceB); } newWallVerts = (new[] { bumperRight0, bumperRight1, bumperRight2, bumperRight3 }); if (!bumperTextureFlip) { newWallUVs = (new[] { new Vector2(uvStartB, 1), new Vector2(uvStartB, 0), new Vector2(uvEndB, 1), new Vector2(uvEndB, 0) }); } else { newWallUVs = (new[] { new Vector2(1, uvStartB), new Vector2(0, uvStartB), new Vector2(1, uvEndB), new Vector2(0, uvEndB) }); } // newWallTris = (new[] { 1, 0, 2, 1, 2, 3 }); newWallTris = (new[] { 0, 1, 2, 1, 3, 2 }); dynamicBumperMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); bumperDistanceB += bumperSegmentDistanceB; } } //Track Bottom Mesh if (curve.extrudeTrack || curve.extrudeTrackBottom) { float extrusionLength = curve.extrudeLength; Vector3 extrusionA = -trackUpA * extrusionLength; Vector3 extrusionB = -trackUpB * extrusionLength; Vector3 pl0 = leftBoundaryPointA; Vector3 pl1 = leftBoundaryPointB; Vector3 pl2 = leftBoundaryPointA + extrusionA; Vector3 pl3 = leftBoundaryPointB + extrusionB; Vector3 pr0 = rightBoundaryPointA; Vector3 pr1 = rightBoundaryPointB; Vector3 pr2 = rightBoundaryPointA + extrusionA; Vector3 pr3 = rightBoundaryPointB + extrusionB; float bevelLerp = 0.5f - curve.extrudeBevel * 0.3333f; Vector3 bevelOutA = trackCrossA.normalized * (trackWidthA * 0.5f); Vector3 bevelOutB = trackCrossB.normalized * (trackWidthB * 0.5f); Vector3 pl2b = Vector3.Lerp(pl2 - bevelOutA, pr2 + bevelOutA, bevelLerp); Vector3 pl3b = Vector3.Lerp(pl3 - bevelOutB, pr3 + bevelOutB, bevelLerp); Vector3 pr2b = Vector3.Lerp(pr2 + bevelOutA, pl2 - bevelOutA, bevelLerp); Vector3 pr3b = Vector3.Lerp(pr3 + bevelOutB, pl3 - bevelOutB, bevelLerp); if (curve.extrudeTrack) { //LEFT newWallVerts = new[] { pl0, pl1, pl2b, pl3b }; if (!bottomTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 1, 0, 2, 1, 2, 3 }; dynamicBottomMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (curve.trackCollider) { dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } //RIGHT newWallVerts = (new[] { pr0, pr1, pr2b, pr3b }); if (!bottomTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 0, 1, 2, 2, 1, 3 }; dynamicBottomMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (curve.trackCollider) { dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } if (curve.extrudeCurveEnd) { //Ends if (p == 0) { newWallVerts = (new[] { pl0, pl2b, pr0, pr2b }); if (!bottomTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 1, 0, 2, 1, 2, 3 }; dynamicBottomMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (curve.trackCollider) { dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } } if (p == storedPointSize - 2) { newWallVerts = (new[] { pl1, pl3b, pr1, pr3b }); if (!bottomTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 0, 1, 2, 2, 1, 3 }; dynamicBottomMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (curve.trackCollider) { dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } } } } if (curve.extrudeTrackBottom) { if (!curve.extrudeTrack) { newWallVerts = new[] { pl0, pl1, pr0, pr1 } } ; else { newWallVerts = new[] { pl2b, pl3b, pr2b, pr3b } }; if (!bottomTextureFlip) { newWallUVs = new[] { new Vector2(curveLengthA, 0), new Vector2(curveLengthB, 0), new Vector2(curveLengthA, 1), new Vector2(curveLengthB, 1), } } ; else { newWallUVs = new[] { new Vector2(1, curveLengthA), new Vector2(1, curveLengthB), new Vector2(0, curveLengthA), new Vector2(0, curveLengthB), } }; newWallTris = new[] { 1, 0, 2, 1, 2, 3 }; dynamicBottomMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); if (curve.trackCollider) { dynamicColliderMesh.AddData(newWallVerts, newWallUVs, newWallTris, 0); } } } if (p == storedPointSize - 2) { UVOffset = curveLengthB; } } if (curve.holder != null) { DestroyImmediate(curve.holder); } GameObject newCurveMeshHolder = new GameObject("curve " + (i + 1)); newCurveMeshHolder.transform.parent = transform; newCurveMeshHolder.transform.localPosition = Vector3.zero; curve.holder = newCurveMeshHolder; int numberOfMeshes; if (!dynamicTrackMesh.isEmpty) { dynamicTrackMesh.name = "Curve " + i + " Track Mesh"; dynamicTrackMesh.Build(); numberOfMeshes = dynamicTrackMesh.meshCount; for (int m = 0; m < numberOfMeshes; m++) { GameObject newMeshHolder = new GameObject("model " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshFilter>().sharedMesh = dynamicTrackMesh[m].mesh; if (track.numberOfTextures > 0) { newMeshHolder.AddComponent <MeshRenderer>().material = track.Texture(curve.trackTextureStyleIndex).GetMaterial();// track.trackTexture.material; } #if UNITY_EDITOR EditorUtility.SetSelectedWireframeHidden(newMeshHolder.renderer, !track.showWireframe); #endif } } if (!dynamicBoundaryMesh.isEmpty) { dynamicBoundaryMesh.Build(); numberOfMeshes = dynamicBoundaryMesh.meshCount; for (int m = 0; m < numberOfMeshes; m++) { GameObject newMeshHolder = new GameObject("boundary " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshFilter>().sharedMesh = dynamicBoundaryMesh[m].mesh; if (track.numberOfTextures > 0) { newMeshHolder.AddComponent <MeshRenderer>().material = track.Texture(curve.boundaryTextureStyleIndex).GetMaterial();// track.trackTexture.material; } #if UNITY_EDITOR EditorUtility.SetSelectedWireframeHidden(newMeshHolder.renderer, !track.showWireframe); #endif } } if (track.disconnectBoundary && !dynamicOffroadMesh.isEmpty) { dynamicOffroadMesh.Build(); numberOfMeshes = dynamicOffroadMesh.meshCount; for (int m = 0; m < numberOfMeshes; m++) { GameObject newMeshHolder = new GameObject("offroad " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshFilter>().sharedMesh = dynamicOffroadMesh[m].mesh; if (track.numberOfTextures > 0) { newMeshHolder.AddComponent <MeshRenderer>().material = track.Texture(curve.offroadTextureStyleIndex).GetMaterial();// track.offroadTexture.material; } #if UNITY_EDITOR EditorUtility.SetSelectedWireframeHidden(newMeshHolder.renderer, !track.showWireframe); #endif } } if (track.includeCollider && curve.trackCollider && !dynamicColliderMesh.isEmpty) { dynamicColliderMesh.Build(); int numberOfColliderMeshes = dynamicColliderMesh.meshCount; for (int m = 0; m < numberOfColliderMeshes; m++) { GameObject newMeshHolder = new GameObject("trackCollider " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshCollider>().sharedMesh = dynamicColliderMesh[m].mesh; } } if (track.trackBumpers && !dynamicBumperMesh.isEmpty) { dynamicBumperMesh.Build(); numberOfMeshes = dynamicBumperMesh.meshCount; for (int m = 0; m < numberOfMeshes; m++) { GameObject newMeshHolder = new GameObject("bumper " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshFilter>().sharedMesh = dynamicBumperMesh[m].mesh; if (track.numberOfTextures > 0) { newMeshHolder.AddComponent <MeshRenderer>().material = track.Texture(curve.bumperTextureStyleIndex).GetMaterial();// track.bumperTexture.material; } #if UNITY_EDITOR EditorUtility.SetSelectedWireframeHidden(newMeshHolder.renderer, !track.showWireframe); #endif } } if (!dynamicBottomMesh.isEmpty) { dynamicBottomMesh.Build(); numberOfMeshes = dynamicBottomMesh.meshCount; for (int m = 0; m < numberOfMeshes; m++) { GameObject newMeshHolder = new GameObject("bottom " + (m + 1)); newMeshHolder.transform.parent = curve.holder.transform; newMeshHolder.transform.localPosition = Vector3.zero; newMeshHolder.AddComponent <MeshFilter>().sharedMesh = dynamicBottomMesh[m].mesh; if (track.numberOfTextures > 0) { newMeshHolder.AddComponent <MeshRenderer>().material = track.Texture(curve.bottomTextureStyleIndex).GetMaterial();// track.trackTexture.material; } #if UNITY_EDITOR EditorUtility.SetSelectedWireframeHidden(newMeshHolder.renderer, !track.showWireframe); #endif } } } else { if (curve.holder != null && (!curve.render || !renderTrack)) { DestroyImmediate(curve.holder); } } polyCount += dynamicBottomMesh.triangleCount / 3; polyCount += dynamicBoundaryMesh.triangleCount / 3; polyCount += dynamicBumperMesh.triangleCount / 3; polyCount += dynamicOffroadMesh.triangleCount / 3; polyCount += dynamicTrackMesh.triangleCount / 3; } track.TrackRendered(); track.lastPolycount = polyCount; #if UNITY_EDITOR EditorUtility.UnloadUnusedAssets(); #endif }
private void SceneGUIPointBased() { Vector3 position = _trackBuildR.transform.position; Camera sceneCamera = Camera.current; _handleSize = HandleUtility.GetHandleSize(_trackBuildR.transform.position) * 0.1f; int realNumberOfPoints = _track.realNumberOfPoints; Ray mouseRay = Camera.current.ScreenPointToRay(new Vector3(Event.current.mousePosition.x, Screen.height - Event.current.mousePosition.y - 30, 0)); for (int i = 0; i < realNumberOfPoints; i++) { TrackBuildRPoint point = _track[i]; if (Vector3.Dot(sceneCamera.transform.forward, point.worldPosition - sceneCamera.transform.position) < 0) { continue; } Handles.Label(point.worldPosition, "point " + (i + 1)); float pointHandleSize = HandleUtility.GetHandleSize(point.worldPosition) * HANDLE_SCALE; Handles.color = (i == selectedPoint) ? TrackBuildRColours.RED : TrackBuildRColours.GREEN; if (Handles.Button(point.worldPosition, Quaternion.identity, pointHandleSize, pointHandleSize, Handles.DotCap)) { selectedPoint = i; GUIUtility.hotControl = 0; GUIUtility.keyboardControl = 0; GUI.changed = true; point.isDirty = true; } if (i == selectedPoint) { switch (_trackBuildR.mode) { case TrackBuildR.modes.track: switch (_trackBuildR.pointMode) { case TrackBuildR.pointModes.transform: Vector3 currentPosition = point.worldPosition; currentPosition = Handles.DoPositionHandle(currentPosition, Quaternion.identity); if (currentPosition != point.worldPosition) { Undo.RecordObject(point, "Point Changed"); point.isDirty = true; point.worldPosition = currentPosition; } //greyed out control points for user ease Handles.color = TrackBuildRColours.DARK_GREY; Handles.DrawLine(point.worldPosition, point.forwardControlPoint + position); Handles.DrawLine(point.worldPosition, point.backwardControlPoint + position); if (Handles.Button(point.backwardControlPoint + position, Quaternion.identity, pointHandleSize, pointHandleSize, Handles.DotCap)) { _trackBuildR.pointMode = TrackBuildR.pointModes.controlpoint; } if (Handles.Button(point.forwardControlPoint + position, Quaternion.identity, pointHandleSize, pointHandleSize, Handles.DotCap)) { _trackBuildR.pointMode = TrackBuildR.pointModes.controlpoint; } break; case TrackBuildR.pointModes.controlpoint: //render reverse first so forward renders on top Handles.DrawLine(point.worldPosition, point.backwardControlPoint + position); point.backwardControlPoint = Handles.DoPositionHandle(point.backwardControlPoint + position, Quaternion.identity) - position; if (Vector3.Dot(mouseRay.direction, point.worldPosition - mouseRay.origin) > 0) { Handles.Label(point.backwardControlPoint, "point " + (i + 1) + " reverse control point"); } if (Vector3.Dot(mouseRay.direction, point.worldPosition - mouseRay.origin) > 0) { Handles.Label(point.forwardControlPoint, "point " + (i + 1) + " control point"); } Handles.color = TrackBuildRColours.RED; Handles.DrawLine(point.worldPosition, point.forwardControlPoint + position); point.forwardControlPoint = Handles.DoPositionHandle(point.forwardControlPoint + position, Quaternion.identity) - position; break; case TrackBuildR.pointModes.trackup: Undo.RecordObject(point, "Point Changed"); point.trackUpQ = Handles.RotationHandle(point.trackUpQ, point.worldPosition); Handles.color = TrackBuildRColours.BLUE; Handles.ArrowCap(0, point.worldPosition, point.trackUpQ, pointHandleSize * 10); Handles.Label(point.worldPosition + point.trackUpQ * Vector3.forward * pointHandleSize * 15, "Up Vector"); Handles.color = TrackBuildRColours.GREEN; Handles.ArrowCap(0, point.worldPosition, Quaternion.LookRotation(point.trackDirection), pointHandleSize * 10); Handles.Label(point.worldPosition + point.trackDirection * pointHandleSize * 15, "Direction Vector"); Handles.color = TrackBuildRColours.RED; Quaternion quatForward = Quaternion.LookRotation(point.trackUpQ * Vector3.up); Handles.ArrowCap(0, point.worldPosition, quatForward, pointHandleSize * 10); Handles.Label(point.worldPosition + Quaternion.LookRotation(point.trackUpQ * Vector3.up) * Vector3.forward * pointHandleSize * 15, "Up Forward Vector"); Handles.color = TrackBuildRColours.PURPLE; Quaternion quatCross = Quaternion.LookRotation(point.trackCross); Handles.ArrowCap(0, point.worldPosition, quatCross, pointHandleSize * 10); Handles.Label(point.worldPosition + point.trackCross * pointHandleSize * 15, "Cross Vector"); break; case TrackBuildR.pointModes.trackpoint: //Track Width Handles.color = TrackBuildRColours.RED; float pointWidth = point.width / 2; Vector3 sliderPos = Handles.Slider(point.worldPosition + point.trackCross * pointWidth, point.trackCross); float pointwidth = Vector3.Distance(sliderPos, point.worldPosition) * 2; if (pointwidth != point.width) { Undo.RecordObject(point, "Point Changed"); point.isDirty = true; point.width = pointwidth; } //Crown Handles.color = TrackBuildRColours.GREEN; Vector3 crownPosition = point.worldPosition + point.trackUp * point.crownAngle; Vector3 newCrownPosition = Handles.Slider(crownPosition, point.trackUp); Vector3 crownDifference = newCrownPosition - point.worldPosition; if (crownDifference.sqrMagnitude != 0) //Crown Modified { Undo.RecordObject(point, "Point Changed"); point.isDirty = true; point.crownAngle = Vector3.Project(crownDifference, point.trackUp).magnitude *Mathf.Sign(Vector3.Dot(crownDifference, point.trackUp)); } break; } break; case TrackBuildR.modes.boundary: if (_track.disconnectBoundary) { if (Vector3.Dot(mouseRay.direction, point.worldPosition - mouseRay.origin) > 0) { Handles.color = TrackBuildRColours.RED; Handles.Label(point.leftTrackBoundaryWorld, "point " + (i + 1) + " left track boundary"); Handles.Label(point.rightTrackBoundaryWorld, "point " + (i + 1) + " right track boundary"); Handles.DrawLine(point.worldPosition, point.leftTrackBoundaryWorld); Handles.DrawLine(point.worldPosition, point.rightTrackBoundaryWorld); } switch (_trackBuildR.boundaryMode) { case TrackBuildR.boundaryModes.transform: Undo.RecordObject(point, "Point Changed"); point.leftTrackBoundaryWorld = Handles.DoPositionHandle(point.leftTrackBoundaryWorld, Quaternion.identity); point.rightTrackBoundaryWorld = Handles.DoPositionHandle(point.rightTrackBoundaryWorld, Quaternion.identity); break; case TrackBuildR.boundaryModes.controlpoint: Undo.RecordObject(point, "Point Changed"); Handles.color = TrackBuildRColours.RED; Handles.DrawLine(point.leftTrackBoundaryWorld, point.leftForwardControlPoint + position); point.leftForwardControlPoint = Handles.DoPositionHandle(point.leftForwardControlPoint + position, Quaternion.identity) - position; Handles.DrawLine(point.leftTrackBoundaryWorld, point.leftBackwardControlPoint + position); point.leftBackwardControlPoint = Handles.DoPositionHandle(point.leftBackwardControlPoint + position, Quaternion.identity) - position; Handles.DrawLine(point.rightTrackBoundaryWorld, point.rightForwardControlPoint + position); point.rightForwardControlPoint = Handles.DoPositionHandle(point.rightForwardControlPoint + position, Quaternion.identity) - position; Handles.DrawLine(point.rightTrackBoundaryWorld, point.rightBackwardControlPoint + position); point.rightBackwardControlPoint = Handles.DoPositionHandle(point.rightBackwardControlPoint + position, Quaternion.identity) - position; break; } } break; case TrackBuildR.modes.stunt: break; } } } }
public static void MergeTerrain(TrackBuildRTrack track, Terrain terrain) { TerrainData terrainData = terrain.terrainData; int terrainWidth = terrainData.heightmapWidth; int terrainHeight = terrainData.heightmapHeight; float terrainHeightmapY = terrain.terrainData.heightmapScale.y; float terrainY = terrain.transform.position.y / terrainHeightmapY; Vector3 meshScale = terrainData.heightmapScale; float terrainAccuracy = track.terrainAccuracy; float terrainMergeMargin = track.terrainMergeMargin; float[,] originalData = terrainData.GetHeights(0, 0, terrainWidth, terrainHeight); float[,] mergeData = new float[terrainWidth, terrainHeight]; float[,] modifiedData = new float[terrainWidth, terrainHeight]; int[,] modifiedPointLock = new int[terrainWidth, terrainHeight]; Bounds trackBounds = new Bounds(); int numberOfCurves = track.numberOfCurves; for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = track[i]; if (curve.holder == null) { continue; } Renderer[] rends = curve.holder.GetComponentsInChildren <Renderer>(); foreach (Renderer rend in rends) { trackBounds.Encapsulate(rend.bounds); } } Vector3 trackOffset = track.transform.position - terrain.transform.position; Vector3 trackScale = new Vector3(trackBounds.size.x / terrainData.size.x, 1.0f / terrain.terrainData.size.y, trackBounds.size.z / terrainData.size.z); float mergeWidth = track.terrainMergeWidth; AnimationCurve mergeCurve = track.mergeCurve; float minScaleUnit = Mathf.Min(meshScale.x, meshScale.z); for (int i = 0; i < numberOfCurves; i++) { TrackBuildRPoint curve = track[i]; int storedPointSize = curve.storedPointSize; for (int p = 0; p < storedPointSize - 1; p++) { Vector3 pointA = curve.sampledPoints[p]; Vector3 pointB = curve.sampledPoints[p + 1]; Vector3 crossA = curve.sampledTrackCrosses[p]; Vector3 crossB = curve.sampledTrackCrosses[p + 1]; float widthA = curve.sampledWidths[p] * terrainMergeMargin; float widthB = curve.sampledWidths[p + 1] * terrainMergeMargin; // float heightA = (pointA.y - terrainAccuracy) * trackScale.y - terrainY; // float heightB = (pointB.y - terrainAccuracy) * trackScale.y - terrainY; Vector3 lpointA = (pointA - crossA * (widthA + mergeWidth)); Vector3 rpointA = (pointA + crossA * (widthA + mergeWidth)); Vector3 lpointB = (pointB - crossB * (widthB + mergeWidth)); Vector3 rpointB = (pointB + crossB * (widthB + mergeWidth)); float crownA = curve.sampledCrowns[p] - terrainAccuracy; float crownB = curve.sampledCrowns[p + 1] - terrainAccuracy; float pointDistanceLeft = Vector3.Distance(lpointA, lpointB) * 1.8f; float pointDistanceRight = Vector3.Distance(rpointA, rpointB) * 1.8f; float pointDistance = Mathf.Max(pointDistanceLeft, pointDistanceRight); float pointFillResolution = minScaleUnit / pointDistance * 0.125f; float heightLA = (lpointA.y - terrainAccuracy) * trackScale.y - terrainY; float heightRA = (rpointA.y - terrainAccuracy) * trackScale.y - terrainY; float heightLB = (lpointB.y - terrainAccuracy) * trackScale.y - terrainY; float heightRB = (rpointB.y - terrainAccuracy) * trackScale.y - terrainY; for (float pf = 0; pf < 1; pf += pointFillResolution)//point a to point b { //Track Filler Vector3 fillPoint = Vector3.Lerp(pointA, pointB, pf); Vector3 fillCross = Vector3.Lerp(crossA, crossB, pf); float fillWidth = Mathf.Lerp(widthA, widthB, pf); // *1.2f; float fillCrown = Mathf.Lerp(crownA, crownB, pf); // *1.2f; float fillTrackHeightL = Mathf.Lerp(heightLA, heightLB, pf); float fillTrackHeightR = Mathf.Lerp(heightRA, heightRB, pf); Vector3 leftTrackPoint = fillPoint - fillCross * fillWidth; Vector3 rightTrackPoint = fillPoint + fillCross * fillWidth; int leftX = Mathf.RoundToInt(((leftTrackPoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int leftY = Mathf.RoundToInt(((leftTrackPoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); int rightX = Mathf.RoundToInt(((rightTrackPoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int rightY = Mathf.RoundToInt(((rightTrackPoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); int diffX = leftX - rightX; int diffY = leftY - rightY; int trackCrossFillAmount = Mathf.Max(Mathf.Abs(diffX), 1) * Mathf.Max(Mathf.Abs(diffY), 1); for (int f = 0; f < trackCrossFillAmount; f++)//left to right { float move = f / (float)trackCrossFillAmount; int fillX = Mathf.RoundToInt(Mathf.Lerp(leftX, rightX, move)); int fillY = Mathf.RoundToInt(Mathf.Lerp(leftY, rightY, move)); if (fillX < 0 || fillY < 0 || fillX >= terrainWidth || fillY >= terrainHeight) { continue; } float crownHeight = Mathf.Sin(move * Mathf.PI) * fillCrown / terrainHeightmapY; float fillTrackHeight = Mathf.Lerp(fillTrackHeightL, fillTrackHeightR, move) + crownHeight; int currentPointLock = modifiedPointLock[fillY, fillX]; if (currentPointLock == 0 || mergeData[fillY, fillX] > fillTrackHeight) { mergeData[fillY, fillX] = fillTrackHeight; } modifiedPointLock[fillY, fillX] = 1;//point lock } //Merge Vector3 leftMergePoint = leftTrackPoint - fillCross * mergeWidth; int leftMergeLeftX = Mathf.RoundToInt(((leftMergePoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int leftMergeLeftY = Mathf.RoundToInt(((leftMergePoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); int leftMergeRightX = Mathf.RoundToInt(((leftTrackPoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int leftMergeRightY = Mathf.RoundToInt(((leftTrackPoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); // int leftMergeDiffX = leftMergeLeftX - leftMergeRightX; int leftMergeDiffY = leftMergeLeftY - leftMergeRightY; int leftMergeFillAmount = Mathf.Max(Mathf.Abs(leftMergeDiffX), 1) * Mathf.Max(Mathf.Abs(leftMergeDiffY), 1);// Mathf.Max(Mathf.Abs(leftMergeDiffX), Mathf.Abs(leftMergeDiffY)); for (int f = 0; f < leftMergeFillAmount; f++) { float move = f / (float)leftMergeFillAmount; int fillX = Mathf.RoundToInt(Mathf.Lerp(leftMergeLeftX, leftMergeRightX, move)); int fillY = Mathf.RoundToInt(Mathf.Lerp(leftMergeLeftY, leftMergeRightY, move)); if (fillX < 0 || fillY < 0 || fillX >= terrainWidth || fillY >= terrainHeight) { continue; } float curveStrength = mergeCurve.Evaluate(move); float fillTrackHeight = fillTrackHeightL; float mergeHeight = Mathf.Lerp(originalData[fillY, fillX], fillTrackHeight, curveStrength); int pointLock = modifiedPointLock[fillY, fillX]; float currentMergeHeight = mergeData[fillY, fillX]; float currentDifference = Mathf.Abs(currentMergeHeight - fillTrackHeight); float newDifference = Mathf.Abs(mergeHeight - fillTrackHeight); if (newDifference < currentDifference && pointLock == 0) { mergeData[fillY, fillX] = mergeHeight; } } Vector3 rightMergePoint = rightTrackPoint + fillCross * mergeWidth; int rightMergeLeftX = Mathf.RoundToInt(((rightMergePoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int rightMergeLeftY = Mathf.RoundToInt(((rightMergePoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); int rightMergeRightX = Mathf.RoundToInt(((rightTrackPoint.x + trackOffset.x) / trackBounds.size.x * trackScale.x) * terrainData.heightmapWidth); int rightMergeRightY = Mathf.RoundToInt(((rightTrackPoint.z + trackOffset.z) / trackBounds.size.z * trackScale.z) * terrainData.heightmapHeight); // int rightMergeDiffX = rightMergeLeftX - rightMergeRightX; int rightMergeDiffY = rightMergeLeftY - rightMergeRightY; int rightMergeFillAmount = Mathf.Max(Mathf.Abs(rightMergeDiffX), 1) * Mathf.Max(Mathf.Abs(rightMergeDiffY), 1);// Mathf.Max(Mathf.Abs(leftMergeDiffX), Mathf.Abs(leftMergeDiffY)); for (int f = 0; f < rightMergeFillAmount; f++) { float move = f / (float)rightMergeFillAmount; int fillX = Mathf.RoundToInt(Mathf.Lerp(rightMergeLeftX, rightMergeRightX, move)); int fillY = Mathf.RoundToInt(Mathf.Lerp(rightMergeLeftY, rightMergeRightY, move)); if (fillX < 0 || fillY < 0 || fillX >= terrainWidth || fillY >= terrainHeight) { continue; } float curveStrength = mergeCurve.Evaluate(move); float fillTrackHeight = fillTrackHeightR; float mergeHeight = Mathf.Lerp(originalData[fillY, fillX], fillTrackHeight, curveStrength); int pointLock = modifiedPointLock[fillY, fillX]; float currentMergeHeight = mergeData[fillY, fillX]; float currentDifference = Mathf.Abs(currentMergeHeight - fillTrackHeight); float newDifference = Mathf.Abs(mergeHeight - fillTrackHeight); if (newDifference < currentDifference && pointLock == 0) { mergeData[fillY, fillX] = mergeHeight; } } } } } for (int x = 0; x < terrainWidth; x++) { for (int y = 0; y < terrainHeight; y++) { bool isNotEdge = x > 0 && x < terrainWidth - 1 && y > 0 && y < terrainHeight - 1; if (mergeData[x, y] == 0) { int mergeNeighbours = 0; if (isNotEdge) { mergeNeighbours += (mergeData[x + 1, y] != 0) ? 1 : 0; mergeNeighbours += (mergeData[x - 1, y] != 0) ? 1 : 0; mergeNeighbours += (mergeData[x, y + 1] != 0) ? 1 : 0; mergeNeighbours += (mergeData[x, y - 1] != 0) ? 1 : 0; } if (mergeNeighbours > 1)//if a hole is surounded by rasied terrain in two or more neighbouring places { float mergeHeight = 0; mergeHeight += (mergeData[x + 1, y] != 0) ? mergeData[x + 1, y] : 0; mergeHeight += (mergeData[x - 1, y] != 0) ? mergeData[x - 1, y] : 0; mergeHeight += (mergeData[x, y + 1] != 0) ? mergeData[x, y + 1] : 0; mergeHeight += (mergeData[x, y - 1] != 0) ? mergeData[x, y - 1] : 0; modifiedData[x, y] = mergeHeight / mergeNeighbours;//clean up holes } else { modifiedData[x, y] = originalData[x, y];//use original } } else { modifiedData[x, y] = mergeData[x, y]; } } } terrainData.SetHeights(0, 0, modifiedData); terrain.terrainData = terrainData; }
public virtual void FromXML(XmlNode node) { XmlNodeList textureNodes = node.SelectNodes("textures/texture"); foreach (XmlNode textureNode in textureNodes) { TrackBuildRTexture newTexture = gameObject.AddComponent <TrackBuildRTexture>(); newTexture.FromXML(textureNode); _textures.Add(newTexture); } XmlNodeList trackPointNodes = node.SelectNodes("trackpoints/trackpoint"); foreach (XmlNode trackPointNode in trackPointNodes) { TrackBuildRPoint point = gameObject.AddComponent <TrackBuildRPoint>(); point.FromXML(trackPointNode); point.baseTransform = baseTransform; point.isDirty = true; _points.Add(point); } if (node["meshResolution"] != null) { _meshResolution = float.Parse(node["meshResolution"].FirstChild.Value); } if (node["includeCollider"] != null) { includeCollider = bool.Parse(node["includeCollider"].FirstChild.Value); } if (node["includeColliderRoof"] != null) { includeColliderRoof = bool.Parse(node["includeColliderRoof"].FirstChild.Value); } if (node["trackColliderWallHeight"] != null) { trackColliderWallHeight = float.Parse(node["trackColliderWallHeight"].FirstChild.Value); } if (node["trackBumpers"] != null) { trackBumpers = bool.Parse(node["trackBumpers"].FirstChild.Value); } if (node["bumperHeight"] != null) { bumperHeight = float.Parse(node["bumperHeight"].FirstChild.Value); } if (node["bumperWidth"] != null) { bumperWidth = float.Parse(node["bumperWidth"].FirstChild.Value); } if (node["bumperAngleThresold"] != null) { bumperAngleThresold = float.Parse(node["bumperAngleThresold"].FirstChild.Value); } if (node["disconnectBoundary"] != null) { _disconnectBoundary = bool.Parse(node["disconnectBoundary"].FirstChild.Value); } if (node["renderBoundaryWallReverse"] != null) { _renderBoundaryWallReverse = bool.Parse(node["renderBoundaryWallReverse"].FirstChild.Value); } RecalculateCurves(); }