mirror of
https://github.com/originalnicodr/CinematicUnityExplorer.git
synced 2026-10-10 21:13:15 +08:00
Added Camera Path System prototype
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user