Added Camera Path System prototype

This commit is contained in:
originalnicodr committed 2023-02-26 19:12:06 -03:00
1 parent bbfc2f55bb
commit e1e1f3d9ab
4 files changed
+494 -2

No files matched your search

+397
View File
@@ -7,6 +7,7 @@ using UnhollowerRuntimeLib;
using Il2CppInterop.Runtime.Injection;
#endif
#endif
using UnityExplorer.UI.Panels;
namespace UnityExplorer
{
@@ -70,6 +71,8 @@ namespace UnityExplorer
}
}
// Cinematic stuff
public class TimeScaleController : MonoBehaviour
{
internal static TimeScaleController Instance { get; private set; }
@@ -112,4 +115,398 @@ namespace UnityExplorer
settingTimeScale = false;
}
}
/*
Based of JPBotelho implementation
https://github.com/JPBotelho/Catmull-Rom-Splines
Catmull-Rom splines are Hermite curves with special tangent values.
Hermite curve formula:
(2t^3 - 3t^2 + 1) * p0 + (t^3 - 2t^2 + t) * m0 + (-2t^3 + 3t^2) * p1 + (t^3 - t^2) * m1
For points p0 and p1 passing through points m0 and m1 interpolated over t = [0, 1]
Tangent M[k] = (P[k+1] - P[k-1]) / 2
*/
public class CatmullRom
{
//Struct to keep position, normal and tangent of a spline point
[System.Serializable]
public struct CatmullRomPoint
{
public Vector3 position;
public Vector3 tangent;
public Vector3 normal;
public CatmullRomPoint(Vector3 position, Vector3 tangent, Vector3 normal)
{
this.position = position;
this.tangent = tangent;
this.normal = normal;
}
}
private int resolution; //Amount of points between control points. [Tesselation factor]
private bool closedLoop;
private CatmullRomPoint[] splinePoints; //Generated spline points
private Vector3[] controlPoints;
public bool playingPath;
int currentPoint = 0;
//Returns spline points. Count is contorolPoints * resolution + [resolution] points if closed loop.
public CatmullRomPoint[] GetPoints()
{
if(splinePoints == null)
{
throw new System.NullReferenceException("Spline not Initialized!");
}
return splinePoints;
}
public CatmullRom(Transform[] controlPoints, int resolution, bool closedLoop)
{
if(controlPoints == null || controlPoints.Length <= 2 || resolution < 2)
{
ExplorerCore.Log(controlPoints);
throw new ArgumentException("Catmull Rom Error: Too few control points or resolution too small");
}
this.controlPoints = new Vector3[controlPoints.Length];
for(int i = 0; i < controlPoints.Length; i++)
{
this.controlPoints[i] = controlPoints[i].position;
}
this.resolution = resolution;
this.closedLoop = closedLoop;
GenerateSplinePoints();
}
//Updates control points
public void Update(Transform[] controlPoints)
{
if(controlPoints.Length <= 0 || controlPoints == null)
{
throw new ArgumentException("Invalid control points");
}
this.controlPoints = new Vector3[controlPoints.Length];
for(int i = 0; i < controlPoints.Length; i++)
{
this.controlPoints[i] = controlPoints[i].position;
}
GenerateSplinePoints();
}
//Updates resolution and closed loop values
public void Update(int resolution, bool closedLoop)
{
if(resolution < 2)
{
throw new ArgumentException("Invalid Resolution. Make sure it's >= 1");
}
this.resolution = resolution;
this.closedLoop = closedLoop;
GenerateSplinePoints();
}
//Draws a line between every point and the next.
public void DrawSpline(Color color)
{
if(ValidatePoints())
{
//LineRenderer lineRenderer = new GameObject().GetComponent<LineRenderer>();
for(int i = 0; i < splinePoints.Length; i++)
{
if(i == splinePoints.Length - 1 && closedLoop)
{
//lineRenderer.SetPosition(i, splinePoints[0].position);
//lineRenderer.SetPosition(i + 1, splinePoints[i].position);
Debug.DrawLine(splinePoints[i].position, splinePoints[0].position, color);
}
else if(i < splinePoints.Length - 1)
{
//lineRenderer.SetPosition(i, splinePoints[i].position);
//lineRenderer.SetPosition(i + 1, splinePoints[i + 1].position);
Debug.DrawLine(splinePoints[i].position, splinePoints[i+1].position, color);
}
}
}
}
public void DrawNormals(float extrusion, Color color)
{
if(ValidatePoints())
{
for(int i = 0; i < splinePoints.Length; i++)
{
Debug.DrawLine(splinePoints[i].position, splinePoints[i].position + splinePoints[i].normal * extrusion, color);
}
}
}
public void DrawTangents(float extrusion, Color color)
{
if(ValidatePoints())
{
for(int i = 0; i < splinePoints.Length; i++)
{
Debug.DrawLine(splinePoints[i].position, splinePoints[i].position + splinePoints[i].tangent * extrusion, color);
}
}
}
//Validates if splinePoints have been set already. Throws nullref exception.
private bool ValidatePoints()
{
if(splinePoints == null)
{
throw new NullReferenceException("Spline not initialized!");
}
return splinePoints != null;
}
//Sets the length of the point array based on resolution/closed loop.
private void InitializeProperties()
{
int pointsToCreate;
if (closedLoop)
{
pointsToCreate = resolution * controlPoints.Length; //Loops back to the beggining, so no need to adjust for arrays starting at 0
}
else
{
pointsToCreate = resolution * (controlPoints.Length - 1);
}
splinePoints = new CatmullRomPoint[pointsToCreate];
}
//Math stuff to generate the spline points
private void GenerateSplinePoints()
{
InitializeProperties();
Vector3 p0, p1; //Start point, end point
Vector3 m0, m1; //Tangents
// First for loop goes through each individual control point and connects it to the next, so 0-1, 1-2, 2-3 and so on
int closedAdjustment = closedLoop ? 0 : 1;
for (int currentPoint = 0; currentPoint < controlPoints.Length - closedAdjustment; currentPoint++)
{
bool closedLoopFinalPoint = (closedLoop && currentPoint == controlPoints.Length - 1);
p0 = controlPoints[currentPoint];
if(closedLoopFinalPoint)
{
p1 = controlPoints[0];
}
else
{
p1 = controlPoints[currentPoint + 1];
}
// m0
if (currentPoint == 0) // Tangent M[k] = (P[k+1] - P[k-1]) / 2
{
if(closedLoop)
{
m0 = p1 - controlPoints[controlPoints.Length - 1];
}
else
{
m0 = p1 - p0;
}
}
else
{
m0 = p1 - controlPoints[currentPoint - 1];
}
// m1
if (closedLoop)
{
if (currentPoint == controlPoints.Length - 1) //Last point case
{
m1 = controlPoints[(currentPoint + 2) % controlPoints.Length] - p0;
}
else if (currentPoint == 0) //First point case
{
m1 = controlPoints[currentPoint + 2] - p0;
}
else
{
m1 = controlPoints[(currentPoint + 2) % controlPoints.Length] - p0;
}
}
else
{
if (currentPoint < controlPoints.Length - 2)
{
m1 = controlPoints[(currentPoint + 2) % controlPoints.Length] - p0;
}
else
{
m1 = p1 - p0;
}
}
m0 *= 0.5f; //Doing this here instead of in every single above statement
m1 *= 0.5f;
float pointStep = 1.0f / resolution;
if ((currentPoint == controlPoints.Length - 2 && !closedLoop) || closedLoopFinalPoint) //Final point
{
pointStep = 1.0f / (resolution - 1); // last point of last segment should reach p1
}
// Creates [resolution] points between this control point and the next
for (int tesselatedPoint = 0; tesselatedPoint < resolution; tesselatedPoint++)
{
float t = tesselatedPoint * pointStep;
CatmullRomPoint point = Evaluate(p0, p1, m0, m1, t);
splinePoints[currentPoint * resolution + tesselatedPoint] = point;
}
}
}
//Evaluates curve at t[0, 1]. Returns point/normal/tan struct. [0, 1] means clamped between 0 and 1.
public static CatmullRomPoint Evaluate(Vector3 start, Vector3 end, Vector3 tanPoint1, Vector3 tanPoint2, float t)
{
Vector3 position = CalculatePosition(start, end, tanPoint1, tanPoint2, t);
Vector3 tangent = CalculateTangent(start, end, tanPoint1, tanPoint2, t);
Vector3 normal = NormalFromTangent(tangent);
return new CatmullRomPoint(position, tangent, normal);
}
//Calculates curve position at t[0, 1]
public static Vector3 CalculatePosition(Vector3 start, Vector3 end, Vector3 tanPoint1, Vector3 tanPoint2, float t)
{
// Hermite curve formula:
// (2t^3 - 3t^2 + 1) * p0 + (t^3 - 2t^2 + t) * m0 + (-2t^3 + 3t^2) * p1 + (t^3 - t^2) * m1
Vector3 position = (2.0f * t * t * t - 3.0f * t * t + 1.0f) * start
+ (t * t * t - 2.0f * t * t + t) * tanPoint1
+ (-2.0f * t * t * t + 3.0f * t * t) * end
+ (t * t * t - t * t) * tanPoint2;
return position;
}
//Calculates tangent at t[0, 1]
public static Vector3 CalculateTangent(Vector3 start, Vector3 end, Vector3 tanPoint1, Vector3 tanPoint2, float t)
{
// Calculate tangents
// p'(t) = (6t² - 6t)p0 + (3t² - 4t + 1)m0 + (-6t² + 6t)p1 + (3t² - 2t)m1
Vector3 tangent = (6 * t * t - 6 * t) * start
+ (3 * t * t - 4 * t + 1) * tanPoint1
+ (-6 * t * t + 6 * t) * end
+ (3 * t * t - 2 * t) * tanPoint2;
return tangent.normalized;
}
//Calculates normal vector from tangent
public static Vector3 NormalFromTangent(Vector3 tangent)
{
return Vector3.Cross(tangent, Vector3.up).normalized / 2;
}
public void MaybeRunPath(){
if(playingPath){
if(ExplorerCore.CameraPathsManager != null && currentPoint < ExplorerCore.CameraPathsManager.GetPoints().Length){
FreeCamPanel.ourCamera.transform.position = ExplorerCore.CameraPathsManager.GetPoints()[currentPoint].position;
//FreeCamPanel.ourCamera.transform.rotation = ExplorerCore.CameraPathsManager.GetPoints()[currentPoint].rotation;
currentPoint++;
if(currentPoint >= ExplorerCore.CameraPathsManager.GetPoints().Length)
playingPath = false;
}
}
}
public void StartPath(){
playingPath = true;
}
}
/*
public struct LineDrawer
{
private LineRenderer lineRenderer;
private float lineSize;
public LineDrawer(float lineSize = 0.2f)
{
//GameObject lineObj = new GameObject("LineObj");
//lineRenderer = lineObj.AddComponent<UnityEngine.LineRenderer>();
//Particles/Additive
//lineRenderer.material = new Material(Shader.Find("Hidden/Internal-Colored"));
lineRenderer = new GameObject().GetComponent<UnityEngine.LineRenderer>();
this.lineSize = lineSize;
}
private void init(float lineSize = 0.2f)
{
if (lineRenderer == null)
{
//GameObject lineObj = new GameObject("LineObj");
//lineRenderer = lineObj.AddComponent<UnityEngine.LineRenderer>();
//Particles/Additive
//lineRenderer.material = new Material(Shader.Find("Hidden/Internal-Colored"));
lineRenderer = new GameObject().GetComponent<UnityEngine.LineRenderer>();
this.lineSize = lineSize;
}
}
//Draws lines through the provided vertices
public void DrawLineInGameView(Vector3 start, Vector3 end, Color color)
{
if (lineRenderer == null)
{
init(0.2f);
}
//Set color
lineRenderer.startColor = color;
lineRenderer.endColor = color;
//Set width
lineRenderer.startWidth = lineSize;
lineRenderer.endWidth = lineSize;
//Set line count which is 2
lineRenderer.positionCount = 2;
//Set the postion of both two lines
lineRenderer.SetPosition(0, start);
lineRenderer.SetPosition(1, end);
}
public void Destroy()
{
if (lineRenderer != null)
{
//UnityEngine.Object.Destroy(lineRenderer.gameObject);
}
}
}
*/
}
+5
View File
@@ -29,6 +29,8 @@ namespace UnityExplorer
public static HarmonyLib.Harmony Harmony { get; } = new HarmonyLib.Harmony(GUID);
public static CatmullRom CameraPathsManager = null;
/// <summary>
/// Initialize UnityExplorer with the provided Loader implementation.
/// </summary>
@@ -83,6 +85,9 @@ namespace UnityExplorer
{
UIManager.ShowMenu = !UIManager.ShowMenu;
}
if(CameraPathsManager != null)
CameraPathsManager.MaybeRunPath();
}
+88
View File
@@ -0,0 +1,88 @@
using UniverseLib.Input;
using UniverseLib.UI;
using UniverseLib.UI.Models;
#if UNHOLLOWER
using UnhollowerRuntimeLib;
#endif
#if INTEROP
using Il2CppInterop.Runtime.Injection;
#endif
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Collections;
namespace UnityExplorer.UI.Panels
{
public class CamPaths : UEPanel
{
public CamPaths(UIBase owner) : base(owner)
{
}
public override string Name => "CamPaths";
public override UIManager.Panels PanelType => UIManager.Panels.CamPaths;
public override int MinWidth => 400;
public override int MinHeight => 600;
public override Vector2 DefaultAnchorMin => new(0.4f, 0.4f);
public override Vector2 DefaultAnchorMax => new(0.6f, 0.6f);
public override bool NavButtonWanted => true;
public override bool ShouldSaveActiveState => true;
static ButtonRef startButton;
public List<Transform> controlPoints = new List<Transform>();
// ~~~~~~~~ UI construction / callbacks ~~~~~~~~
protected override void ConstructPanelContent()
{
startButton = UIFactory.CreateButton(ContentRoot, "Start", "Start CamPath");
UIFactory.SetLayoutElement(startButton.GameObject, minWidth: 150, minHeight: 25, flexibleWidth: 9999);
startButton.OnClick += StartButton_OnClick;
startButton = UIFactory.CreateButton(ContentRoot, "AddCamNode", "Add Cam node");
UIFactory.SetLayoutElement(startButton.GameObject, minWidth: 150, minHeight: 25, flexibleWidth: 9999);
startButton.OnClick += AddNode_OnClick;
}
void AddSpacer(int height)
{
GameObject obj = UIFactory.CreateUIObject("Spacer", ContentRoot);
UIFactory.SetLayoutElement(obj, minHeight: height, flexibleHeight: 0);
}
void StartButton_OnClick()
{
int resolution = 500;
bool closedLoop = false;
//float normalExtrusion = 0;
//float tangentExtrusion = 0;
if(ExplorerCore.CameraPathsManager == null)
ExplorerCore.CameraPathsManager = new CatmullRom(controlPoints.ToArray(), resolution, closedLoop);
else{
ExplorerCore.CameraPathsManager.Update(controlPoints.ToArray());
ExplorerCore.CameraPathsManager.Update(resolution, closedLoop);
}
ExplorerCore.Log("Iniciamos path.");
ExplorerCore.CameraPathsManager.StartPath();
ExplorerCore.Log("Clicked start path button.");
}
private Transform NewTransform(Transform original){
GameObject ob = new GameObject("CamPathNode");
ob.transform.position = original.position;
//ob.transform.rotation = original.rotation;
return ob.transform;
}
void AddNode_OnClick(){
Camera freeCam = FreeCamPanel.ourCamera;
controlPoints.Add(NewTransform(freeCam.transform));
}
}
}
+4 -2
View File
@@ -23,7 +23,8 @@ namespace UnityExplorer.UI
HookManager,
Clipboard,
Freecam,
LightsManager
LightsManager,
CamPaths
}
public enum VerticalAnchor
@@ -45,7 +46,7 @@ namespace UnityExplorer.UI
public static RectTransform NavBarRect;
public static GameObject NavbarTabButtonHolder;
private static readonly Vector2 NAVBAR_DIMENSIONS = new(1150f, 35f);
private static readonly Vector2 NAVBAR_DIMENSIONS = new(1250f, 35f);
private static ButtonRef closeBtn;
private static TimeScaleWidget timeScaleWidget;
@@ -92,6 +93,7 @@ namespace UnityExplorer.UI
UIPanels.Add(Panels.HookManager, new HookManagerPanel(UiBase));
UIPanels.Add(Panels.Freecam, new FreeCamPanel(UiBase));
UIPanels.Add(Panels.LightsManager, new LightsManager(UiBase));
UIPanels.Add(Panels.CamPaths, new CamPaths(UiBase));
UIPanels.Add(Panels.Clipboard, new ClipboardPanel(UiBase));
UIPanels.Add(Panels.ConsoleLog, new LogPanel(UiBase));
UIPanels.Add(Panels.Options, new OptionsPanel(UiBase));