mirror of
https://github.com/originalnicodr/CinematicUnityExplorer.git
synced 2026-10-10 21:13:15 +08:00
27851a88ea
* Added support for new input system. First implementation. * Deleted unnecessary prefix calls, typed most "var" variables, added support for wasReleasedThisFrame, used GetButtonWasPressed on IInputManager::GetKeyUp, refactored GetMouseButton to use the cached buttons, and split the postfix functions into multiple ones. * Fixed builds for other .net versions. * Added a class to handle gamepad input to use with the freecam. * Fixed some logging stuff, and added a dropdown on the freecam panel to select the controller that you want to use to control the freecam. Also refactored the freecam panel a bit UI wise. * Made the gamepad interceptor class compatible with the INewInputSystem by reusing its patch functions for the gamepad buttons. Also did some cleaning regarding key normalization. * Fixed runtime warnings and added gamepad controls to the readme. Also made changing the FOV faster. * Fixed dpad on some versions of the engine/library. Also refactored the gamepad input code on FreeCamBehaviour::ProcessInput(), inverted dpad up and down functionalities, modified the fov change speed, added a minimum value for the fov, and added a reset tilt button to the gamepad. * Update README.md * Fixed freecam default mode on forced, and hid the controller dropdown on the freecam panel when the game uses the legacy input system. * Added more controller hotkeys, * Added slower camera rotation based on fov. * Added an ExplorerCore log call warning when trying to play a path with fewer than 3 nodes.
707 lines
31 KiB
C#
707 lines
31 KiB
C#
using UnityEngine.SceneManagement;
|
|
using UnityExplorer.Serializers;
|
|
using UniverseLib.Input;
|
|
using UniverseLib.UI;
|
|
using UniverseLib.UI.Models;
|
|
#if UNHOLLOWER
|
|
using UnhollowerRuntimeLib;
|
|
#endif
|
|
#if INTEROP
|
|
using Il2CppInterop.Runtime.Injection;
|
|
#endif
|
|
using UniverseLib.UI.Widgets.ScrollView;
|
|
|
|
namespace UnityExplorer.UI.Panels
|
|
{
|
|
public class CamPaths : UEPanel, ICellPoolDataSource<CamPathNodeCell>
|
|
{
|
|
public CamPaths(UIBase owner) : base(owner)
|
|
{
|
|
controlPoints = new List<CatmullRom.CatmullRomPoint>();
|
|
followObject = null;
|
|
pathVisualizer = null;
|
|
time = 10;
|
|
|
|
// Timer setup
|
|
startTimer = new System.Timers.Timer(3000);
|
|
startTimer.Elapsed += (source, e) => StartPath();
|
|
startTimer.AutoReset = false;
|
|
|
|
// CatmullRom Constants
|
|
alphaCatmullRomSlider = new Slider();
|
|
tensionCatmullRomSlider = new Slider();
|
|
}
|
|
|
|
public override string Name => "Cam Paths";
|
|
public override UIManager.Panels PanelType => UIManager.Panels.CamPaths;
|
|
public override int MinWidth => 725;
|
|
public override int MinHeight => 300;
|
|
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;
|
|
public List<CatmullRom.CatmullRomPoint> controlPoints = new List<CatmullRom.CatmullRomPoint>();
|
|
bool closedLoop;
|
|
float time = 10;
|
|
public int currentSelectedNodeIndex = -1;
|
|
|
|
public GameObject followObject;
|
|
|
|
Toggle visualizePathToggle;
|
|
public GameObject pathVisualizer;
|
|
Toggle closedLoopToggle;
|
|
InputFieldRef TimeInput;
|
|
|
|
bool unpauseOnPlay;
|
|
bool waitBeforePlay;
|
|
private System.Timers.Timer startTimer;
|
|
public bool pauseOnFinish;
|
|
|
|
InputFieldRef alphaCatmullRomInput;
|
|
Slider alphaCatmullRomSlider;
|
|
float alphaCatmullRomValue = 0.5f;
|
|
|
|
InputFieldRef tensionCatmullRomInput;
|
|
Slider tensionCatmullRomSlider;
|
|
float tensionCatmullRomValue = 0;
|
|
|
|
private InputFieldRef saveLoadInputField;
|
|
private Toggle loadPathOnCamToggle;
|
|
|
|
public ScrollPool<CamPathNodeCell> nodesScrollPool;
|
|
public int ItemCount => controlPoints.Count;
|
|
private static bool DoneScrollPoolInit;
|
|
|
|
CamPointsUpdater camPointsUpdater;
|
|
|
|
public override void SetActive(bool active)
|
|
{
|
|
base.SetActive(active);
|
|
if (active && !DoneScrollPoolInit)
|
|
{
|
|
LayoutRebuilder.ForceRebuildLayoutImmediate(this.Rect);
|
|
nodesScrollPool.Initialize(this);
|
|
DoneScrollPoolInit = true;
|
|
}
|
|
|
|
nodesScrollPool.Refresh(true, false);
|
|
}
|
|
|
|
public void OnCellBorrowed(CamPathNodeCell cell) { }
|
|
|
|
public void SetCell(CamPathNodeCell cell, int index){
|
|
if (index >= controlPoints.Count)
|
|
{
|
|
cell.Disable();
|
|
return;
|
|
}
|
|
|
|
CatmullRom.CatmullRomPoint point = controlPoints[index];
|
|
cell.point = point;
|
|
cell.index = index;
|
|
cell.indexLabel.text = $"{index}";
|
|
|
|
cell.indexLabel.color = (index == currentSelectedNodeIndex) ? Color.green : Color.white;
|
|
}
|
|
|
|
// ~~~~~~~~ UI construction / callbacks ~~~~~~~~
|
|
|
|
protected override void ConstructPanelContent()
|
|
{
|
|
GameObject horiGroup = UIFactory.CreateHorizontalGroup(ContentRoot, "MainOptions", false, false, true, true, 3,
|
|
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
|
|
UIFactory.SetLayoutElement(horiGroup, minHeight: 25, flexibleWidth: 9999);
|
|
|
|
ButtonRef startButton = UIFactory.CreateButton(horiGroup, "Start", "►", new Color(0.2f, 0.4f, 0.2f));
|
|
UIFactory.SetLayoutElement(startButton.GameObject, minWidth: 50, minHeight: 25);
|
|
startButton.OnClick += StartButton_OnClick;
|
|
|
|
ButtonRef pauseContinueButton = UIFactory.CreateButton(horiGroup, "Pause/Continue", "❚❚/►");
|
|
UIFactory.SetLayoutElement(pauseContinueButton.GameObject, minWidth: 50, minHeight: 25);
|
|
pauseContinueButton.OnClick += TogglePause_OnClick;
|
|
|
|
ButtonRef stopButton = UIFactory.CreateButton(horiGroup, "Stop", "■", new Color(0.4f, 0.2f, 0.2f));
|
|
UIFactory.SetLayoutElement(stopButton.GameObject, minWidth: 50, minHeight: 25);
|
|
stopButton.ButtonText.fontSize = 20;
|
|
stopButton.OnClick += Stop_OnClick;
|
|
|
|
ButtonRef AddNodeButton = UIFactory.CreateButton(horiGroup, "AddCamNode", "+");
|
|
UIFactory.SetLayoutElement(AddNodeButton.GameObject, minWidth: 50, minHeight: 25);
|
|
AddNodeButton.ButtonText.fontSize = 20;
|
|
AddNodeButton.OnClick += AddNode;
|
|
|
|
ButtonRef DeletePath = UIFactory.CreateButton(horiGroup, "DeletePath", "Clear");
|
|
UIFactory.SetLayoutElement(DeletePath.GameObject, minWidth: 70, minHeight: 25);
|
|
DeletePath.OnClick += () => {
|
|
controlPoints.Clear();
|
|
nodesScrollPool.Refresh(true, false);
|
|
MaybeRedrawPath();
|
|
};
|
|
|
|
closedLoopToggle = new Toggle();
|
|
GameObject toggleClosedLoopObj = UIFactory.CreateToggle(horiGroup, "Close path in a loop", out closedLoopToggle, out Text toggleClosedLoopText);
|
|
UIFactory.SetLayoutElement(toggleClosedLoopObj, minHeight: 25, flexibleWidth: 9999);
|
|
closedLoopToggle.isOn = false;
|
|
closedLoopToggle.onValueChanged.AddListener((isClosedLoop) => {closedLoop = isClosedLoop; MaybeRedrawPath(); EventSystemHelper.SetSelectedGameObject(null);});
|
|
toggleClosedLoopText.text = "Close path in a loop";
|
|
|
|
AddInputField("Time", "Path time (in seconds at 60fps):", $"Default: {time}", out TimeInput, Time_OnEndEdit, 50, horiGroup);
|
|
TimeInput.Text = time.ToString();
|
|
|
|
|
|
GameObject secondRow = UIFactory.CreateHorizontalGroup(ContentRoot, "ExtraOptions", false, false, true, true, 3,
|
|
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
|
|
UIFactory.SetLayoutElement(secondRow, minHeight: 25, flexibleWidth: 9999);
|
|
|
|
GameObject visualizePathObj = UIFactory.CreateToggle(secondRow, "Visualize Path", out visualizePathToggle, out Text visualizePathText);
|
|
UIFactory.SetLayoutElement(visualizePathObj, minHeight: 25, flexibleWidth: 9999);
|
|
visualizePathToggle.isOn = false;
|
|
visualizePathToggle.onValueChanged.AddListener(ToggleVisualizePath);
|
|
visualizePathText.text = "Visualize path";
|
|
|
|
GameObject unpauseOnPlayObj = UIFactory.CreateToggle(secondRow, "Unpause on play", out Toggle unpauseOnPlayToggle, out Text unpauseOnPlayText);
|
|
UIFactory.SetLayoutElement(unpauseOnPlayObj, minHeight: 25, flexibleWidth: 9999);
|
|
unpauseOnPlayToggle.isOn = false;
|
|
unpauseOnPlayToggle.onValueChanged.AddListener((value) => unpauseOnPlay = value);
|
|
unpauseOnPlayText.text = "Unpause on play";
|
|
|
|
GameObject pauseOnFinishObj = UIFactory.CreateToggle(secondRow, "Pause on finish", out Toggle pauseOnFinishToggle, out Text pauseOnFinishText);
|
|
UIFactory.SetLayoutElement(pauseOnFinishObj, minHeight: 25, flexibleWidth: 9999);
|
|
pauseOnFinishToggle.isOn = false;
|
|
pauseOnFinishToggle.onValueChanged.AddListener((value) => pauseOnFinish = value);
|
|
pauseOnFinishText.text = "Pause on finish";
|
|
|
|
GameObject waitBeforePlayObj = UIFactory.CreateToggle(secondRow, "Wait before play", out Toggle waitBeforePlayToggle, out Text waitBeforePlayText);
|
|
UIFactory.SetLayoutElement(waitBeforePlayObj, minHeight: 25, flexibleWidth: 9999);
|
|
waitBeforePlayToggle.isOn = false;
|
|
waitBeforePlayToggle.onValueChanged.AddListener((value) => waitBeforePlay = value);
|
|
waitBeforePlayText.text = "Wait 3 seconds before start";
|
|
|
|
|
|
// CatmullRom alpha value
|
|
GameObject thridRow = AddInputField("alphaCatmullRom", "Alpha:", "0.5", out alphaCatmullRomInput, AlphaCatmullRom_OnEndEdit, 50);
|
|
alphaCatmullRomInput.Text = alphaCatmullRomValue.ToString();
|
|
|
|
GameObject alphaCatmullRomObj = UIFactory.CreateSlider(thridRow, "Alpha CatmullRom Slider", out alphaCatmullRomSlider);
|
|
UIFactory.SetLayoutElement(alphaCatmullRomObj, minHeight: 25, minWidth: 100, flexibleWidth: 0);
|
|
alphaCatmullRomSlider.m_FillImage.color = Color.clear;
|
|
alphaCatmullRomSlider.minValue = 0;
|
|
alphaCatmullRomSlider.maxValue = 1;
|
|
alphaCatmullRomSlider.value = alphaCatmullRomValue;
|
|
alphaCatmullRomSlider.onValueChanged.AddListener((newAlpha) => {
|
|
alphaCatmullRomValue = newAlpha;
|
|
alphaCatmullRomInput.Text = alphaCatmullRomValue.ToString("0.00");
|
|
|
|
MaybeRedrawPath();
|
|
});
|
|
|
|
// CatmullRom tension value
|
|
AddInputField("tensionCatmullRomO", "Tension:", "0", out tensionCatmullRomInput, TensionCatmullRom_OnEndEdit, 50, thridRow);
|
|
tensionCatmullRomInput.Text = tensionCatmullRomValue.ToString();
|
|
|
|
GameObject tensionCatmullRomObj = UIFactory.CreateSlider(thridRow, "Tension CatmullRom Slider", out tensionCatmullRomSlider);
|
|
UIFactory.SetLayoutElement(tensionCatmullRomObj, minHeight: 25, minWidth: 100, flexibleWidth: 0);
|
|
tensionCatmullRomSlider.m_FillImage.color = Color.clear;
|
|
tensionCatmullRomSlider.minValue = 0;
|
|
tensionCatmullRomSlider.maxValue = 1;
|
|
tensionCatmullRomSlider.value = tensionCatmullRomValue;
|
|
tensionCatmullRomSlider.onValueChanged.AddListener((newTension) => {
|
|
tensionCatmullRomValue = newTension;
|
|
tensionCatmullRomInput.Text = tensionCatmullRomValue.ToString("0.00");
|
|
|
|
MaybeRedrawPath();
|
|
});
|
|
|
|
GameObject fourthRow = UIFactory.CreateHorizontalGroup(ContentRoot, "SerializationOptions", false, false, true, true, 3,
|
|
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
|
|
UIFactory.SetLayoutElement(fourthRow, minHeight: 25, flexibleWidth: 9999);
|
|
|
|
saveLoadInputField = UIFactory.CreateInputField(fourthRow, "PathName", "File name");
|
|
UIFactory.SetLayoutElement(saveLoadInputField.GameObject, minWidth: 320, minHeight: 25);
|
|
|
|
GameObject spacer1 = UIFactory.CreateUIObject("Spacer", fourthRow);
|
|
LayoutElement spaceLayout1 = UIFactory.SetLayoutElement(spacer1, minWidth: 20, flexibleWidth: 0);
|
|
|
|
ButtonRef savePath = UIFactory.CreateButton(fourthRow, "SavePathButton", "Save path");
|
|
UIFactory.SetLayoutElement(savePath.GameObject, minWidth: 100, minHeight: 25);
|
|
savePath.OnClick += SavePath;
|
|
|
|
ButtonRef loadPath = UIFactory.CreateButton(fourthRow, "LoadPathButton", "Load path");
|
|
UIFactory.SetLayoutElement(loadPath.GameObject, minWidth: 100, minHeight: 25);
|
|
loadPath.OnClick += LoadPath;
|
|
|
|
GameObject loadPathOnCamObj = UIFactory.CreateToggle(fourthRow, "Load path on cam", out loadPathOnCamToggle, out Text loadPathOnCamText);
|
|
UIFactory.SetLayoutElement(loadPathOnCamObj, minHeight: 25, flexibleWidth: 9999);
|
|
loadPathOnCamToggle.isOn = false;
|
|
loadPathOnCamText.text = "Load path starting on current camera state";
|
|
|
|
nodesScrollPool = UIFactory.CreateScrollPool<CamPathNodeCell>(ContentRoot, "NodeList", out GameObject scrollObj,
|
|
out GameObject scrollContent, new Color(0.03f, 0.03f, 0.03f));
|
|
UIFactory.SetLayoutElement(scrollObj, flexibleWidth: 9999, flexibleHeight: 9999);
|
|
}
|
|
|
|
void AlphaCatmullRom_OnEndEdit(string input)
|
|
{
|
|
if (!ParseUtility.TryParse(input, out float parsed, out Exception parseEx))
|
|
{
|
|
ExplorerCore.LogWarning($"Could not parse value: {parseEx.ReflectionExToString()}");
|
|
alphaCatmullRomInput.Text = alphaCatmullRomValue.ToString();
|
|
return;
|
|
}
|
|
|
|
alphaCatmullRomValue = parsed;
|
|
alphaCatmullRomSlider.value = alphaCatmullRomValue;
|
|
|
|
MaybeRedrawPath();
|
|
EventSystemHelper.SetSelectedGameObject(null);
|
|
}
|
|
|
|
void TensionCatmullRom_OnEndEdit(string input)
|
|
{
|
|
if (!ParseUtility.TryParse(input, out float parsed, out Exception parseEx))
|
|
{
|
|
ExplorerCore.LogWarning($"Could not parse value: {parseEx.ReflectionExToString()}");
|
|
tensionCatmullRomInput.Text = tensionCatmullRomValue.ToString();
|
|
return;
|
|
}
|
|
|
|
tensionCatmullRomValue = parsed;
|
|
tensionCatmullRomSlider.value = tensionCatmullRomValue;
|
|
|
|
MaybeRedrawPath();
|
|
EventSystemHelper.SetSelectedGameObject(null);
|
|
}
|
|
|
|
private void ToggleVisualizePath(bool enable){
|
|
// Had to include this check because the pathVisualizer was null for some reason
|
|
if (enable){
|
|
if (controlPoints.Count > 2){
|
|
CreateAndMovePathVisualizer();
|
|
|
|
UpdateCatmullRomMoverData();
|
|
List<CatmullRom.CatmullRomPoint> lookaheadPoints = GetCameraPathsManager().GetLookaheadPoints();
|
|
int skip_points = 5; // How many points do we have to skip before drawing another arrow (otherwise they look very cluttered)
|
|
int n = 0;
|
|
for (int i=0; i < lookaheadPoints.Count; i++){
|
|
// We also want to draw an arrow on the last point in case its skipped.
|
|
if (n < skip_points && i != lookaheadPoints.Count - 1){
|
|
n++;
|
|
continue;
|
|
}
|
|
|
|
Vector3 arrowPos = lookaheadPoints[i].position;
|
|
if (followObject != null){
|
|
if (!FreeCamPanel.followRotationToggle.isOn){
|
|
arrowPos = Quaternion.Inverse(FreeCamPanel.followObject.transform.rotation) * arrowPos;
|
|
}
|
|
// TODO: Avoid using TransformPoint
|
|
arrowPos = followObject.transform.TransformPoint(arrowPos);
|
|
}
|
|
Quaternion arrowRot = lookaheadPoints[i].rotation;
|
|
if (followObject != null && FreeCamPanel.followRotationToggle.isOn) arrowRot = followObject.transform.rotation * arrowRot;
|
|
|
|
// We could expose the color of the arrow to a setting
|
|
GameObject arrow = ArrowGenerator.CreateArrow(arrowPos, arrowRot, Color.green);
|
|
arrow.transform.SetParent(pathVisualizer.transform, true);
|
|
n = 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (pathVisualizer) {
|
|
UnityEngine.Object.Destroy(pathVisualizer);
|
|
pathVisualizer = null;
|
|
CreateAndMovePathVisualizer();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void MaybeRedrawPath(){
|
|
if (visualizePathToggle.isOn){
|
|
ToggleVisualizePath(false);
|
|
ToggleVisualizePath(true);
|
|
}
|
|
}
|
|
|
|
GameObject AddInputField(string name, string labelText, string placeHolder, out InputFieldRef inputField, Action<string> onInputEndEdit, int inputMinWidth = 50, GameObject existingRow = null)
|
|
{
|
|
GameObject row = existingRow != null ? existingRow : UIFactory.CreateHorizontalGroup(ContentRoot, "Editor Field",
|
|
false, false, true, true, 5, default, new Color(1, 1, 1, 0));
|
|
|
|
Text posLabel = UIFactory.CreateLabel(row, $"{name}_Label", labelText);
|
|
UIFactory.SetLayoutElement(posLabel.gameObject, minWidth: 40, minHeight: 25);
|
|
|
|
inputField = UIFactory.CreateInputField(row, $"{name}_Input", placeHolder);
|
|
UIFactory.SetLayoutElement(inputField.GameObject, minWidth: inputMinWidth, minHeight: 25);
|
|
inputField.Component.GetOnEndEdit().AddListener(onInputEndEdit);
|
|
|
|
return row;
|
|
}
|
|
|
|
void StartButton_OnClick()
|
|
{
|
|
StartPath();
|
|
}
|
|
|
|
public void StartPath(){
|
|
if (waitBeforePlay) {
|
|
if (startTimer.Enabled){
|
|
startTimer.Stop();
|
|
}
|
|
|
|
startTimer.Enabled = true;
|
|
return;
|
|
}
|
|
|
|
EventSystemHelper.SetSelectedGameObject(null);
|
|
|
|
|
|
if (GetCameraPathsManager()){
|
|
UpdateCatmullRomMoverData();
|
|
GetCameraPathsManager().StartPath();
|
|
UIManager.ShowMenu = false;
|
|
visualizePathToggle.isOn = false;
|
|
}
|
|
|
|
if (unpauseOnPlay && UIManager.GetTimeScaleWidget().IsPaused()){
|
|
UIManager.GetTimeScaleWidget().PauseToggle();
|
|
}
|
|
}
|
|
|
|
public void TogglePause(){
|
|
if(GetCameraPathsManager()){
|
|
GetCameraPathsManager().TogglePause();
|
|
if (visualizePathToggle.isOn && GetCameraPathsManager().IsPaused()) visualizePathToggle.isOn = false;
|
|
}
|
|
|
|
EventSystemHelper.SetSelectedGameObject(null);
|
|
}
|
|
|
|
void TogglePause_OnClick(){
|
|
TogglePause();
|
|
}
|
|
|
|
public void StopPath(){
|
|
if (GetCameraPathsManager()){
|
|
GetCameraPathsManager().Stop();
|
|
}
|
|
|
|
EventSystemHelper.SetSelectedGameObject(null);
|
|
}
|
|
|
|
void Stop_OnClick(){
|
|
StopPath();
|
|
}
|
|
|
|
public void AddNode(){
|
|
EventSystemHelper.SetSelectedGameObject(null);
|
|
|
|
CatmullRom.CatmullRomPoint point = new CatmullRom.CatmullRomPoint(
|
|
FreeCamPanel.GetCameraPosition(),
|
|
FreeCamPanel.GetCameraRotation(),
|
|
FreeCamPanel.GetFreecam().fieldOfView
|
|
);
|
|
|
|
controlPoints.Add(point);
|
|
currentSelectedNodeIndex = controlPoints.Count - 1;
|
|
nodesScrollPool.Refresh(true, false);
|
|
MaybeRedrawPath();
|
|
}
|
|
|
|
void Time_OnEndEdit(string input)
|
|
{
|
|
EventSystemHelper.SetSelectedGameObject(null);
|
|
|
|
if (!ParseUtility.TryParse(input, out float parsed, out Exception parseEx))
|
|
{
|
|
ExplorerCore.LogWarning($"Could not parse value: {parseEx.ReflectionExToString()}");
|
|
return;
|
|
}
|
|
|
|
time = parsed;
|
|
if(GetCameraPathsManager()){
|
|
GetCameraPathsManager().setTime(time);
|
|
}
|
|
}
|
|
|
|
private CatmullRom.CatmullRomMover GetCameraPathsManager(){
|
|
return FreeCamPanel.cameraPathMover;
|
|
}
|
|
|
|
public void UpdatedFollowObject(GameObject obj){
|
|
// Had to include this check because the pathVisualizer was null for some reason
|
|
if (followObject != null){
|
|
// destroy the obhject from the parent
|
|
GameObject.Destroy(camPointsUpdater);
|
|
camPointsUpdater = null;
|
|
}
|
|
followObject = obj;
|
|
if (obj != null){
|
|
// create the component on the object itself
|
|
camPointsUpdater = RuntimeHelper.AddComponent<CamPointsUpdater>(obj, typeof(CamPointsUpdater));
|
|
|
|
camPointsUpdater.SetFollowObject(obj);
|
|
}
|
|
//nodesScrollPool.Refresh(true, false);
|
|
}
|
|
|
|
public void CreateAndMovePathVisualizer(){
|
|
if (pathVisualizer == null)
|
|
pathVisualizer = new GameObject("PathVisualizer");
|
|
|
|
if (followObject != null){
|
|
pathVisualizer.transform.position = followObject.transform.position;
|
|
if (FreeCamPanel.followRotationToggle.isOn){
|
|
Vector3 offset = pathVisualizer.transform.position - followObject.transform.position;
|
|
pathVisualizer.transform.position = pathVisualizer.transform.position - offset + followObject.transform.rotation * offset;
|
|
pathVisualizer.transform.rotation = followObject.transform.rotation;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void TranslatePointsToGlobal(bool isFollowingRotation){
|
|
if (isFollowingRotation) TranslatePointsRotationToGlobal();
|
|
TranslatePointsPositionToGlobal();
|
|
}
|
|
|
|
public void TranslatePointsToLocal(bool isFollowingRotation){
|
|
TranslatePointsPositionToLocal();
|
|
if (isFollowingRotation) TranslatePointsRotationToLocal();
|
|
}
|
|
|
|
private void TranslatePointsPositionToGlobal(){
|
|
List<CatmullRom.CatmullRomPoint> newControlPoints = new List<CatmullRom.CatmullRomPoint>();
|
|
foreach(CatmullRom.CatmullRomPoint point in controlPoints){
|
|
Vector3 newPos = point.position + followObject.transform.position;
|
|
|
|
CatmullRom.CatmullRomPoint newPoint = new CatmullRom.CatmullRomPoint(newPos, point.rotation, point.fov);
|
|
newControlPoints.Add(newPoint);
|
|
}
|
|
|
|
controlPoints = newControlPoints;
|
|
}
|
|
|
|
// Also changes position based on rotation
|
|
public void TranslatePointsRotationToGlobal(){
|
|
List<CatmullRom.CatmullRomPoint> newControlPoints = new List<CatmullRom.CatmullRomPoint>();
|
|
foreach(CatmullRom.CatmullRomPoint point in controlPoints){
|
|
Quaternion newRot = followObject.transform.rotation * point.rotation;
|
|
Vector3 newPos = followObject.transform.rotation * point.position;
|
|
|
|
CatmullRom.CatmullRomPoint newPoint = new CatmullRom.CatmullRomPoint(newPos, newRot, point.fov);
|
|
newControlPoints.Add(newPoint);
|
|
}
|
|
|
|
controlPoints = newControlPoints;
|
|
}
|
|
|
|
private void TranslatePointsPositionToLocal(){
|
|
List<CatmullRom.CatmullRomPoint> newControlPoints = new List<CatmullRom.CatmullRomPoint>();
|
|
foreach(CatmullRom.CatmullRomPoint point in controlPoints){
|
|
Vector3 newPos = point.position - followObject.transform.position;
|
|
CatmullRom.CatmullRomPoint newPoint = new CatmullRom.CatmullRomPoint(newPos, point.rotation, point.fov);
|
|
|
|
newControlPoints.Add(newPoint);
|
|
}
|
|
|
|
controlPoints = newControlPoints;
|
|
}
|
|
|
|
// Also changes position based on rotation
|
|
public void TranslatePointsRotationToLocal(){
|
|
List<CatmullRom.CatmullRomPoint> newControlPoints = new List<CatmullRom.CatmullRomPoint>();
|
|
foreach(CatmullRom.CatmullRomPoint point in controlPoints){
|
|
Quaternion newRot = Quaternion.Inverse(followObject.transform.rotation) * point.rotation;
|
|
Vector3 newPos = Quaternion.Inverse(followObject.transform.rotation) * point.position;
|
|
|
|
CatmullRom.CatmullRomPoint newPoint = new CatmullRom.CatmullRomPoint(newPos, newRot, point.fov);
|
|
newControlPoints.Add(newPoint);
|
|
}
|
|
|
|
controlPoints = newControlPoints;
|
|
}
|
|
|
|
void UpdateCatmullRomMoverData() {
|
|
GetCameraPathsManager().setClosedLoop(closedLoop);
|
|
GetCameraPathsManager().setSplinePoints(controlPoints.ToArray());
|
|
GetCameraPathsManager().setTime(time);
|
|
GetCameraPathsManager().setCatmullRomVariables(alphaCatmullRomValue, tensionCatmullRomValue);
|
|
|
|
GetCameraPathsManager().CalculateLookahead();
|
|
}
|
|
|
|
private void SavePath(){
|
|
string filename = saveLoadInputField.Component.text;
|
|
if (filename.EndsWith(".cuepath") || filename.EndsWith(".CUEPATH")) filename = filename.Substring(filename.Length-7);
|
|
if (string.IsNullOrEmpty(filename)) filename = $"{DateTime.Now.ToString("yyyy-M-d HH-mm-ss")}";
|
|
string camPathsFolderPath = Path.Combine(ExplorerCore.ExplorerFolder, "CamPaths");
|
|
System.IO.Directory.CreateDirectory(camPathsFolderPath);
|
|
|
|
// Serialize
|
|
string serializedData = CamPathSerializer.Serialize(controlPoints, time, alphaCatmullRomValue, tensionCatmullRomValue, closedLoop, SceneManager.GetActiveScene().name);
|
|
File.WriteAllText($"{camPathsFolderPath}\\{filename}.cuepath", serializedData);
|
|
}
|
|
|
|
void NavigateToNode(int nodeIndex)
|
|
{
|
|
if (nodeIndex < 0 || nodeIndex >= controlPoints.Count)
|
|
return;
|
|
|
|
currentSelectedNodeIndex = nodeIndex;
|
|
CatmullRom.CatmullRomPoint point = controlPoints[nodeIndex];
|
|
FreeCamPanel.SetCameraPosition(point.position);
|
|
FreeCamPanel.SetCameraRotation(point.rotation);
|
|
FreeCamPanel.SetFOV(point.fov);
|
|
nodesScrollPool.Refresh(true, false);
|
|
}
|
|
|
|
public void NavigateToNextNode()
|
|
{
|
|
if (controlPoints.Count == 0)
|
|
return;
|
|
|
|
int nextIndex = (currentSelectedNodeIndex + 1) % controlPoints.Count;
|
|
NavigateToNode(nextIndex);
|
|
}
|
|
|
|
public void NavigateToPreviousNode()
|
|
{
|
|
if (controlPoints.Count == 0)
|
|
return;
|
|
|
|
int prevIndex = currentSelectedNodeIndex - 1;
|
|
if (prevIndex < 0)
|
|
prevIndex = controlPoints.Count - 1;
|
|
NavigateToNode(prevIndex);
|
|
}
|
|
|
|
public void OnNodeDeleted(int deletedIndex)
|
|
{
|
|
// If the current selected index is out of bounds, adjust it
|
|
if (currentSelectedNodeIndex >= controlPoints.Count)
|
|
{
|
|
currentSelectedNodeIndex = controlPoints.Count - 1;
|
|
}
|
|
// If we deleted the selected node or a node before it, adjust the index
|
|
else if (deletedIndex <= currentSelectedNodeIndex && currentSelectedNodeIndex > 0)
|
|
{
|
|
currentSelectedNodeIndex--;
|
|
}
|
|
|
|
if (controlPoints.Count == 0)
|
|
currentSelectedNodeIndex = -1;
|
|
}
|
|
|
|
private void LoadPath(){
|
|
string filename = saveLoadInputField.Component.text;
|
|
if (filename.EndsWith(".cuepath") || filename.EndsWith(".CUEPATH")) filename = filename.Substring(filename.Length-7);
|
|
if (string.IsNullOrEmpty(filename)){
|
|
ExplorerCore.LogWarning("Empty file name. Please write the name of the file to load.");
|
|
return;
|
|
}
|
|
|
|
string camPathsFolderPath = Path.Combine(ExplorerCore.ExplorerFolder, "CamPaths");
|
|
string pathFile;
|
|
try {
|
|
pathFile = File.ReadAllText($"{camPathsFolderPath}\\{filename}.cuepath");
|
|
}
|
|
catch (Exception ex) {
|
|
ExplorerCore.LogWarning(ex);
|
|
return;
|
|
}
|
|
CamPathSerializeObject deserializedObj;
|
|
try {
|
|
deserializedObj = CamPathSerializer.Deserialize(pathFile);
|
|
}
|
|
catch (Exception ex) {
|
|
ExplorerCore.LogWarning(ex);
|
|
return;
|
|
}
|
|
|
|
if (deserializedObj.sceneName != SceneManager.GetActiveScene().name && !loadPathOnCamToggle.isOn) {
|
|
loadPathOnCamToggle.isOn = true;
|
|
ExplorerCore.LogWarning("Loaded a path on a different scene than the one it was saved on. Spawning it starting from the current camera state.");
|
|
}
|
|
|
|
if (loadPathOnCamToggle.isOn) {
|
|
|
|
// We enable the freecam so we can use it to spawn the camera path relative to it
|
|
if (!FreeCamPanel.inFreeCamMode) {
|
|
FreeCamPanel.ToggleFreecam();
|
|
}
|
|
|
|
controlPoints.Clear();
|
|
// The first point will be the camera position, and we adapt the following points from there
|
|
AddNode();
|
|
|
|
CatmullRom.CatmullRomPoint startingPoint = controlPoints.ElementAt(0);
|
|
CatmullRom.CatmullRomPoint originalStartingPoint = deserializedObj.points.ElementAt(0);
|
|
// We only want to use the camera pos and rotation, not the fov
|
|
startingPoint.fov = originalStartingPoint.fov;
|
|
controlPoints[0] = startingPoint;
|
|
|
|
foreach (CatmullRom.CatmullRomPoint point in deserializedObj.points.Skip(1)) {
|
|
CatmullRom.CatmullRomPoint newPoint = point;
|
|
|
|
Quaternion offsetRot = startingPoint.rotation * Quaternion.Inverse(originalStartingPoint.rotation);
|
|
newPoint.position = offsetRot * (newPoint.position - originalStartingPoint.position) + startingPoint.position;
|
|
newPoint.rotation = offsetRot * newPoint.rotation;
|
|
|
|
controlPoints.Add(newPoint);
|
|
}
|
|
} else {
|
|
controlPoints = deserializedObj.points;
|
|
}
|
|
|
|
TimeInput.Text = deserializedObj.time.ToString("0.00");
|
|
Time_OnEndEdit(TimeInput.Text);
|
|
alphaCatmullRomInput.Text = deserializedObj.alpha.ToString("0.00");
|
|
AlphaCatmullRom_OnEndEdit(alphaCatmullRomInput.Text);
|
|
tensionCatmullRomInput.Text = deserializedObj.tension.ToString("0.00");
|
|
TensionCatmullRom_OnEndEdit(tensionCatmullRomInput.Text);
|
|
closedLoopToggle.isOn = deserializedObj.closePath;
|
|
|
|
// Update nodes
|
|
nodesScrollPool.Refresh(true, false);
|
|
}
|
|
|
|
}
|
|
|
|
public class CamPointsUpdater : MonoBehaviour
|
|
{
|
|
GameObject followObject;
|
|
|
|
#if CPP
|
|
static CamPointsUpdater()
|
|
{
|
|
ClassInjector.RegisterTypeInIl2Cpp<CamPointsUpdater>();
|
|
}
|
|
|
|
public CamPointsUpdater(IntPtr ptr) : base(ptr) { }
|
|
#endif
|
|
|
|
internal void SetFollowObject(GameObject obj){
|
|
followObject = obj;
|
|
}
|
|
|
|
internal void Update()
|
|
{
|
|
if (followObject == null)
|
|
return;
|
|
|
|
//CamPathsPanel.MaybeRedrawPath();
|
|
|
|
CamPaths CamPathsPanel = UIManager.GetPanel<CamPaths>(UIManager.Panels.CamPaths);
|
|
|
|
if (CamPathsPanel.pathVisualizer){
|
|
CamPathsPanel.pathVisualizer.transform.position = followObject.transform.position;
|
|
if (FreeCamPanel.followRotationToggle.isOn){
|
|
Vector3 offset = CamPathsPanel.pathVisualizer.transform.position - FreeCamPanel.followObject.transform.position;
|
|
CamPathsPanel.pathVisualizer.transform.position = CamPathsPanel.pathVisualizer.transform.position - offset + FreeCamPanel.followObject.transform.rotation * offset;
|
|
CamPathsPanel.pathVisualizer.transform.rotation = followObject.transform.rotation;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|