mirror of
https://github.com/originalnicodr/CinematicUnityExplorer.git
synced 2025-07-19 01:57:56 +08:00
Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
e3584654a6 | |||
388c9fbc29 | |||
2f35b9d2c7 | |||
b82cc62186 | |||
9429b84cfa | |||
2719c69753 | |||
9144f89e32 | |||
3bc1268444 | |||
4c100daf83 | |||
e7d591176e | |||
ca46607c49 | |||
7fb67b4e28 | |||
c0f42ad983 | |||
6111c1743b | |||
b03d81052b | |||
173eeb993a | |||
07971489c4 | |||
2604dd8286 | |||
ca81bcc5dc | |||
b95178775c | |||
3928cdc4fe | |||
81d15270fe | |||
2b557a8c23 | |||
462201534d | |||
f3b154658c | |||
7ab907d7b0 | |||
736ebdfa1b | |||
c9aa93a6a4 | |||
78bf2fc634 | |||
6c493aaf7d | |||
b6d459b664 | |||
7a580d12ed | |||
8f2ec19660 | |||
f855f6d430 | |||
a6446c50e4 | |||
f3b48bf069 | |||
396e653495 | |||
68c482df22 | |||
a868cc74ac | |||
343ccb9474 | |||
48fec535fd | |||
5e9abd33b4 | |||
6ebf19dfa0 | |||
cc34a50e21 | |||
954b6f4f1c | |||
b785f76ba3 | |||
d2d22de66c | |||
af2ad057fb | |||
ca726e0ae7 | |||
a02460be33 | |||
30b9e6e387 | |||
58f477ed0d | |||
fd672615be | |||
f8886700d9 | |||
dae6930b43 | |||
d9ebe5819c | |||
171ee3f0d8 | |||
448ab66f5b | |||
b090f68732 | |||
13dcef668f |
34
.github/workflows/dotnet.yml
vendored
34
.github/workflows/dotnet.yml
vendored
@ -29,79 +29,79 @@ jobs:
|
|||||||
|
|
||||||
# Upload artifacts
|
# Upload artifacts
|
||||||
- name: Upload BepInEx.IL2CPP
|
- name: Upload BepInEx.IL2CPP
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.BepInEx.IL2CPP.zip
|
name: CinematicUnityExplorer.BepInEx.IL2CPP.zip
|
||||||
path: ./Release/CinematicUnityExplorer.BepInEx.IL2CPP/
|
path: ./Release/CinematicUnityExplorer.BepInEx.IL2CPP/
|
||||||
|
|
||||||
- name: Upload BepInEx.IL2CPP.CoreCLR
|
- name: Upload BepInEx.IL2CPP.CoreCLR
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.BepInEx.IL2CPP.CoreCLR.zip
|
name: CinematicUnityExplorer.BepInEx.IL2CPP.CoreCLR.zip
|
||||||
path: ./Release/CinematicUnityExplorer.BepInEx.IL2CPP.CoreCLR/
|
path: ./Release/CinematicUnityExplorer.BepInEx.IL2CPP.CoreCLR/
|
||||||
|
|
||||||
- name: Upload BepInEx.Unity.IL2CPP.CoreCLR
|
- name: Upload BepInEx.Unity.IL2CPP.CoreCLR
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.BepInEx.Unity.IL2CPP.CoreCLR.zip
|
name: CinematicUnityExplorer.BepInEx.Unity.IL2CPP.CoreCLR.zip
|
||||||
path: ./Release/CinematicUnityExplorer.BepInEx.Unity.IL2CPP.CoreCLR/
|
path: ./Release/CinematicUnityExplorer.BepInEx.Unity.IL2CPP.CoreCLR/
|
||||||
|
|
||||||
- name: Upload BepInEx5.Mono
|
- name: Upload BepInEx5.Mono
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.BepInEx5.Mono.zip
|
name: CinematicUnityExplorer.BepInEx5.Mono.zip
|
||||||
path: ./Release/CinematicUnityExplorer.BepInEx5.Mono/
|
path: ./Release/CinematicUnityExplorer.BepInEx5.Mono/
|
||||||
|
|
||||||
- name: Upload BepInEx6.Mono
|
- name: Upload BepInEx6.Mono
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.BepInEx6.Mono.zip
|
name: CinematicUnityExplorer.BepInEx6.Mono.zip
|
||||||
path: ./Release/CinematicUnityExplorer.BepInEx6.Mono/
|
path: ./Release/CinematicUnityExplorer.BepInEx6.Mono/
|
||||||
|
|
||||||
- name: Upload BepInEx6.Unity.Mono
|
- name: Upload BepInEx6.Unity.Mono
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.BepInEx6.Unity.Mono.zip
|
name: CinematicUnityExplorer.BepInEx6.Unity.Mono.zip
|
||||||
path: ./Release/CinematicUnityExplorer.BepInEx6.Unity.Mono/
|
path: ./Release/CinematicUnityExplorer.BepInEx6.Unity.Mono/
|
||||||
|
|
||||||
- name: Upload MelonLoader.IL2CPP
|
- name: Upload MelonLoader.IL2CPP
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.MelonLoader.IL2CPP.zip
|
name: CinematicUnityExplorer.MelonLoader.IL2CPP.zip
|
||||||
path: ./Release/CinematicUnityExplorer.MelonLoader.IL2CPP/
|
path: ./Release/CinematicUnityExplorer.MelonLoader.IL2CPP/
|
||||||
|
|
||||||
- name: Upload MelonLoader.IL2CPP.net6preview
|
- name: Upload MelonLoader.IL2CPP.net6preview
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.MelonLoader.IL2CPP.net6preview.zip
|
name: CinematicUnityExplorer.MelonLoader.IL2CPP.net6preview.zip
|
||||||
path: ./Release/CinematicUnityExplorer.MelonLoader.IL2CPP.net6preview/
|
path: ./Release/CinematicUnityExplorer.MelonLoader.IL2CPP.net6preview/
|
||||||
|
|
||||||
- name: Upload MelonLoader.IL2CPP.CoreCLR
|
- name: Upload MelonLoader.IL2CPP.CoreCLR
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.MelonLoader.IL2CPP.CoreCLR.zip
|
name: CinematicUnityExplorer.MelonLoader.IL2CPP.CoreCLR.zip
|
||||||
path: ./Release/CinematicUnityExplorer.MelonLoader.IL2CPP.CoreCLR/
|
path: ./Release/CinematicUnityExplorer.MelonLoader.IL2CPP.CoreCLR/
|
||||||
|
|
||||||
- name: Upload MelonLoader.Mono
|
- name: Upload MelonLoader.Mono
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.MelonLoader.Mono.zip
|
name: CinematicUnityExplorer.MelonLoader.Mono.zip
|
||||||
path: ./Release/CinematicUnityExplorer.MelonLoader.Mono/
|
path: ./Release/CinematicUnityExplorer.MelonLoader.Mono/
|
||||||
|
|
||||||
- name: Upload Standalone.IL2CPP
|
- name: Upload Standalone.IL2CPP
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.Standalone.IL2CPP.zip
|
name: CinematicUnityExplorer.Standalone.IL2CPP.zip
|
||||||
path: ./Release/CinematicUnityExplorer.Standalone.IL2CPP/
|
path: ./Release/CinematicUnityExplorer.Standalone.IL2CPP/
|
||||||
|
|
||||||
- name: Upload Standalone.Mono
|
- name: Upload Standalone.Mono
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.Standalone.Mono.zip
|
name: CinematicUnityExplorer.Standalone.Mono.zip
|
||||||
path: ./Release/CinematicUnityExplorer.Standalone.Mono/
|
path: ./Release/CinematicUnityExplorer.Standalone.Mono/
|
||||||
|
|
||||||
- name: Upload Editor
|
- name: Upload Editor
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CinematicUnityExplorer.Editor.zip
|
name: CinematicUnityExplorer.Editor.zip
|
||||||
path: ./UnityEditorPackage/
|
path: ./UnityEditorPackage/
|
||||||
@ -127,7 +127,13 @@ jobs:
|
|||||||
run: ./build_connector.ps1
|
run: ./build_connector.ps1
|
||||||
|
|
||||||
- name: Upload Unity IGCS Connector
|
- name: Upload Unity IGCS Connector
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: UnityIGCSConnector.dll
|
name: UnityIGCSConnector.dll
|
||||||
path: ./Release/UnityIGCSConnector.dll
|
path: ./Release/UnityIGCSConnector.dll
|
||||||
|
|
||||||
|
- name: Upload Unity IGCS Connector 32bit
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: UnityIGCSConnector.32.dll
|
||||||
|
path: ./Release/UnityIGCSConnector.32.dll
|
||||||
|
14
README.md
14
README.md
@ -136,6 +136,7 @@ The original Unity Explorer had a Freecam feature, but even if it was useful at
|
|||||||
- Added tilt support.
|
- Added tilt support.
|
||||||
- Added FOV control support.
|
- Added FOV control support.
|
||||||
- Added near and far clip sliders on the Freecam panel, to cut out objects near the camera out of view, or include more scene objects on sight.
|
- Added near and far clip sliders on the Freecam panel, to cut out objects near the camera out of view, or include more scene objects on sight.
|
||||||
|
- Added new freecam methods to increase compatibility across games.
|
||||||
- Automatically disable the Cinemachine component if present when using the gameplay camera, to avoid the game from trying to take control.
|
- Automatically disable the Cinemachine component if present when using the gameplay camera, to avoid the game from trying to take control.
|
||||||
- Added a toggle to block the freecam from moving with user input.
|
- Added a toggle to block the freecam from moving with user input.
|
||||||
- Unlocked the mouse on freecam even when the mod UI is hidden.
|
- Unlocked the mouse on freecam even when the mod UI is hidden.
|
||||||
@ -147,6 +148,10 @@ You can click on the "Follow object" button on the panel and select the object y
|
|||||||
|
|
||||||
By default the camera only follows the object's position, but you can also make it follow its rotation as if the camera was physically bound to the object by checking the "Follow Object Rotation" toggle. Should be useful for mimicking a car camera, a character POV, or creating motion blur.
|
By default the camera only follows the object's position, but you can also make it follow its rotation as if the camera was physically bound to the object by checking the "Follow Object Rotation" toggle. Should be useful for mimicking a car camera, a character POV, or creating motion blur.
|
||||||
|
|
||||||
|
### Look At Object
|
||||||
|
|
||||||
|
As a side note, you can also "look at" a specific object in the world to force the camera to look directly toward it no matter the camera movement.
|
||||||
|
|
||||||
### Game input block for Unity's legacy system
|
### Game input block for Unity's legacy system
|
||||||
Added game input block for Unity's legacy system. You can now block (or unblock) the game's input when using the freecam, as long as the game is using the Unity Legacy Input system. If the game uses a custom solution or the latest Unity system then this won't work. Implementing this for Unity's new system is in the backlog, so if you find a game using it (should say "Initialized new InputSystem support." on the logs) then please let me know so I can implement it using that game!
|
Added game input block for Unity's legacy system. You can now block (or unblock) the game's input when using the freecam, as long as the game is using the Unity Legacy Input system. If the game uses a custom solution or the latest Unity system then this won't work. Implementing this for Unity's new system is in the backlog, so if you find a game using it (should say "Initialized new InputSystem support." on the logs) then please let me know so I can implement it using that game!
|
||||||
|
|
||||||
@ -188,6 +193,7 @@ It allows you to create nodes to build camera paths for videos and cinematics. F
|
|||||||
- Wait 3 seconds before start toggler.
|
- Wait 3 seconds before start toggler.
|
||||||
- Move nodes up or down on the list.
|
- Move nodes up or down on the list.
|
||||||
- Control over the tension and alpha values of the path, which are curve constants that change the resulting path created from the nodes. Their effect can be clearly seen when visualizing the path, as explained below.
|
- Control over the tension and alpha values of the path, which are curve constants that change the resulting path created from the nodes. Their effect can be clearly seen when visualizing the path, as explained below.
|
||||||
|
- Saving and loading camera paths to reuse them in different sessions.
|
||||||
|
|
||||||
As a side note, the mod UI will be disabled once the path starts, to ease video recording.
|
As a side note, the mod UI will be disabled once the path starts, to ease video recording.
|
||||||
|
|
||||||
@ -204,10 +210,16 @@ Allows you to manually play characters and NPC animations in a scene. This shoul
|
|||||||
|
|
||||||
Favorite animations so they appear first on the dropdown list by clicking on the star button with the animation selected.
|
Favorite animations so they appear first on the dropdown list by clicking on the star button with the animation selected.
|
||||||
|
|
||||||
The Animator Panel also allows you to freeze all characters in a scene all at once, alongside giving you control over which characters should ignore the master toggler. That way you can make the playable character avoid getting frozen, or avoid un-freezing NPCs or enemies that already have the animations you want them to have.
|
The Animator Panel also allows you to freeze all characters in a scene all at once, alongside giving you control over which characters should ignore the master toggler. Each animator also comes with a toggle to be able to hide a character meshes from a scene.
|
||||||
|
|
||||||
Alongside all of this, you can also open each character game object by clicking on their names, so you can move, rotate, scale them around, disable them, or further edit their properties and child objects.
|
Alongside all of this, you can also open each character game object by clicking on their names, so you can move, rotate, scale them around, disable them, or further edit their properties and child objects.
|
||||||
|
|
||||||
|
### Bones Panel
|
||||||
|
|
||||||
|
For each animator, you can also spawn a Bones Panel. This panel will list all of the character's bones and meshes, and provide easy-to-access toggles to disable them and sliders to move them around, allowing you to pose a character to your liking.
|
||||||
|
|
||||||
|
You can also save and load poses across different characters (if they have the same skeleton) and sessions.
|
||||||
|
|
||||||
## Misc Panel
|
## Misc Panel
|
||||||
- HUD toggle.
|
- HUD toggle.
|
||||||
- Force high LODs toggle. This means that the highest models possible will be forced on all meshes.
|
- Force high LODs toggle. This means that the highest models possible will be forced on all meshes.
|
||||||
|
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "com.originalnicodr.cinematicunityexplorer",
|
"name": "com.originalnicodr.cinematicunityexplorer",
|
||||||
"version": "1.1.0",
|
"version": "1.3.0",
|
||||||
"displayName": "CinematicUnityExplorer",
|
"displayName": "CinematicUnityExplorer",
|
||||||
"description": "UnityExplorer fork focused on providing tools for creating marketing material for Unity games.",
|
"description": "UnityExplorer fork focused on providing tools for creating marketing material for Unity games.",
|
||||||
"unity": "2017.1",
|
"unity": "2017.1",
|
||||||
|
@ -76,13 +76,19 @@
|
|||||||
<PostBuildEventUseInBuild>false</PostBuildEventUseInBuild>
|
<PostBuildEventUseInBuild>false</PostBuildEventUseInBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<TargetName>UnityIGCSConnector</TargetName>
|
<TargetName>UnityIGCSConnector.32</TargetName>
|
||||||
|
<OutDir>$(SolutionDir)..\$(Configuration)\</OutDir>
|
||||||
|
<PostBuildEventUseInBuild>false</PostBuildEventUseInBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<TargetName>UnityIGCSConnector</TargetName>
|
<TargetName>UnityIGCSConnector</TargetName>
|
||||||
|
<OutDir>$(SolutionDir)..\$(Configuration)\</OutDir>
|
||||||
|
<PostBuildEventUseInBuild>false</PostBuildEventUseInBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
<TargetName>UnityIGCSConnector</TargetName>
|
<TargetName>UnityIGCSConnector.32</TargetName>
|
||||||
|
<OutDir>$(SolutionDir)..\$(Configuration)\</OutDir>
|
||||||
|
<PostBuildEventUseInBuild>false</PostBuildEventUseInBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
@ -90,7 +96,7 @@
|
|||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;UNITYIGCSCONNECTOR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;UNITYIGCSCONNECTOR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@ -107,7 +113,7 @@
|
|||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;UNITYIGCSCONNECTOR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;NDEBUG;UNITYIGCSCONNECTOR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@ -164,4 +170,4 @@
|
|||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -19,7 +19,7 @@ SessionCallback GlobalEndSession = NULL;
|
|||||||
// There are things that only needs to be run once.
|
// There are things that only needs to be run once.
|
||||||
static int first_initialization = 1;
|
static int first_initialization = 1;
|
||||||
|
|
||||||
EXPOSE int IGCS_StartScreenshotSession(uint8_t _ignore) {
|
EXPOSE int __cdecl IGCS_StartScreenshotSession(uint8_t _ignore) {
|
||||||
if (GlobalStartSession) {
|
if (GlobalStartSession) {
|
||||||
GlobalStartSession();
|
GlobalStartSession();
|
||||||
printf("Called StartSession\n");
|
printf("Called StartSession\n");
|
||||||
@ -27,12 +27,12 @@ EXPOSE int IGCS_StartScreenshotSession(uint8_t _ignore) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPOSE void IGCS_EndScreenshotSession() {
|
EXPOSE void __cdecl IGCS_EndScreenshotSession() {
|
||||||
GlobalEndSession();
|
GlobalEndSession();
|
||||||
printf("Called EndSession\n");
|
printf("Called EndSession\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPOSE uint8_t* U_IGCS_Initialize(MoveCameraCallback cb, SessionCallback start_cb, SessionCallback end_cb) {
|
EXPOSE uint8_t* __cdecl U_IGCS_Initialize(MoveCameraCallback cb, SessionCallback start_cb, SessionCallback end_cb) {
|
||||||
AllocConsole();
|
AllocConsole();
|
||||||
printf("Initializing callback\n");
|
printf("Initializing callback\n");
|
||||||
GlobalCallback = cb;
|
GlobalCallback = cb;
|
||||||
@ -40,7 +40,11 @@ EXPOSE uint8_t* U_IGCS_Initialize(MoveCameraCallback cb, SessionCallback start_c
|
|||||||
GlobalEndSession = end_cb;
|
GlobalEndSession = end_cb;
|
||||||
|
|
||||||
// Load IGCS
|
// Load IGCS
|
||||||
|
#ifdef _M_IX86
|
||||||
|
HMODULE igcs = LoadLibraryA("IgcsConnector.addon32");
|
||||||
|
#else
|
||||||
HMODULE igcs = LoadLibraryA("IgcsConnector.addon64");
|
HMODULE igcs = LoadLibraryA("IgcsConnector.addon64");
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!igcs) {
|
if (!igcs) {
|
||||||
MessageBoxA(
|
MessageBoxA(
|
||||||
@ -69,9 +73,9 @@ EXPOSE uint8_t* U_IGCS_Initialize(MoveCameraCallback cb, SessionCallback start_c
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPOSE void IGCS_MoveCameraPanorama() {}
|
EXPOSE void __cdecl IGCS_MoveCameraPanorama() {}
|
||||||
|
|
||||||
EXPOSE void IGCS_MoveCameraMultishot(float step_left, float step_up, float fov, int from_start) {
|
EXPOSE void __cdecl IGCS_MoveCameraMultishot(float step_left, float step_up, float fov, int from_start) {
|
||||||
GlobalCallback(step_left, step_up, fov, from_start);
|
GlobalCallback(step_left, step_up, fov, from_start);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
# ----------- Build UnityIGCSConnector ------------
|
# ----------- Build UnityIGCSConnector ------------
|
||||||
msbuild.exe UnityIGCSConnector/UnityIGCSConnector.sln -p:Configuration=Release
|
msbuild.exe UnityIGCSConnector/UnityIGCSConnector.sln -p:Configuration=Release -p:Platform=x64
|
||||||
|
|
||||||
|
msbuild.exe UnityIGCSConnector/UnityIGCSConnector.sln -p:Configuration=Release -p:Platform=x86
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityExplorer.Config;
|
||||||
#if UNHOLLOWER
|
#if UNHOLLOWER
|
||||||
using IL2CPPUtils = UnhollowerBaseLib.UnhollowerUtils;
|
using IL2CPPUtils = UnhollowerBaseLib.UnhollowerUtils;
|
||||||
#endif
|
#endif
|
||||||
@ -12,8 +13,12 @@ namespace UnityExplorer
|
|||||||
{
|
{
|
||||||
public class ArrowGenerator
|
public class ArrowGenerator
|
||||||
{
|
{
|
||||||
public static GameObject CreateArrow(Vector3 arrowPosition, Quaternion arrowRotation, Color color){
|
public static GameObject CreateArrow(Vector3 arrowPosition, Quaternion arrowRotation, Color color)
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
|
float arrowSizeValue = ConfigManager.Arrow_Size.Value;
|
||||||
|
Vector3 arrowSize = new Vector3(Math.Max(arrowSizeValue, 0.1f), Math.Max(arrowSizeValue, 0.1f), Math.Max(arrowSizeValue, 0.1f));
|
||||||
|
|
||||||
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
||||||
cylinder.GetComponent<Collider>().enabled = false;
|
cylinder.GetComponent<Collider>().enabled = false;
|
||||||
cylinder.GetComponent<MeshFilter>().mesh = CreateCylinderMesh(0.01f, 20, 2);
|
cylinder.GetComponent<MeshFilter>().mesh = CreateCylinderMesh(0.01f, 20, 2);
|
||||||
@ -34,6 +39,7 @@ namespace UnityExplorer
|
|||||||
|
|
||||||
GameObject arrow = new GameObject("CUE-Arrow");
|
GameObject arrow = new GameObject("CUE-Arrow");
|
||||||
cylinder.transform.SetParent(arrow.transform, true);
|
cylinder.transform.SetParent(arrow.transform, true);
|
||||||
|
arrow.transform.localScale = arrowSize;
|
||||||
arrow.transform.position = arrowPosition;
|
arrow.transform.position = arrowPosition;
|
||||||
arrow.transform.rotation = arrowRotation;
|
arrow.transform.rotation = arrowRotation;
|
||||||
arrow.transform.position += 0.5f * arrow.transform.forward; // Move the arrow forward so the cylinder starts on the wanted position
|
arrow.transform.position += 0.5f * arrow.transform.forward; // Move the arrow forward so the cylinder starts on the wanted position
|
||||||
|
@ -20,14 +20,17 @@ namespace UnityExplorer.UI.Panels
|
|||||||
public AnimatorPlayer animatorPlayer;
|
public AnimatorPlayer animatorPlayer;
|
||||||
|
|
||||||
public Toggle IgnoreMasterToggle;
|
public Toggle IgnoreMasterToggle;
|
||||||
public Toggle AnimatorToggle;
|
public AnimatorPausePlayButton animatorToggler;
|
||||||
|
public Toggle MeshToggle;
|
||||||
public ButtonRef inspectButton;
|
public ButtonRef inspectButton;
|
||||||
ButtonRef playButton;
|
|
||||||
public Dropdown animatorDropdown;
|
public Dropdown animatorDropdown;
|
||||||
ButtonRef favAnimation;
|
ButtonRef favAnimation;
|
||||||
|
Slider animationTimeline;
|
||||||
|
ButtonRef playButton;
|
||||||
|
ButtonRef openBonesPanelButton;
|
||||||
|
|
||||||
// ICell
|
// ICell
|
||||||
public float DefaultHeight => 25f;
|
public float DefaultHeight => 30f;
|
||||||
public GameObject UIRoot { get; set; }
|
public GameObject UIRoot { get; set; }
|
||||||
public RectTransform Rect { get; set; }
|
public RectTransform Rect { get; set; }
|
||||||
|
|
||||||
@ -39,7 +42,8 @@ namespace UnityExplorer.UI.Panels
|
|||||||
this.animatorPlayer = animatorPlayer;
|
this.animatorPlayer = animatorPlayer;
|
||||||
inspectButton.ButtonText.text = animatorPlayer.animator.name;
|
inspectButton.ButtonText.text = animatorPlayer.animator.name;
|
||||||
IgnoreMasterToggle.isOn = animatorPlayer.shouldIgnoreMasterToggle;
|
IgnoreMasterToggle.isOn = animatorPlayer.shouldIgnoreMasterToggle;
|
||||||
AnimatorToggle.isOn = animatorPlayer.animator.speed != 0;
|
animatorToggler.isOn = animatorPlayer.animator.speed != 0;
|
||||||
|
MeshToggle.isOn = animatorPlayer.IsMeshHidden();
|
||||||
|
|
||||||
UpdateDropdownOptions();
|
UpdateDropdownOptions();
|
||||||
}
|
}
|
||||||
@ -64,7 +68,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void PlayButton_OnClick(){
|
private void PlayButton_OnClick(){
|
||||||
animatorPlayer.PlayOverridingAnimation();
|
animatorPlayer.PlayOverridingAnimation(0);
|
||||||
// Needed for the new animation to play for some reason
|
// Needed for the new animation to play for some reason
|
||||||
EnableAnimation(false);
|
EnableAnimation(false);
|
||||||
EnableAnimation(true);
|
EnableAnimation(true);
|
||||||
@ -72,31 +76,34 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
public virtual GameObject CreateContent(GameObject parent)
|
public virtual GameObject CreateContent(GameObject parent)
|
||||||
{
|
{
|
||||||
GameObject AnimatorToggleObj = UIFactory.CreateToggle(parent, $"AnimatorToggle", out AnimatorToggle, out Text animatorToggleText);
|
UIRoot = UIFactory.CreateUIObject("AnimatorCell", parent);
|
||||||
UIFactory.SetLayoutElement(AnimatorToggleObj, minHeight: 25);
|
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, false, false, true, true, 4, childAlignment: TextAnchor.MiddleLeft);
|
||||||
AnimatorToggle.isOn = animatorPlayer != null && animatorPlayer.animator.speed == 1;
|
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleWidth: 9999);
|
||||||
AnimatorToggle.onValueChanged.AddListener(EnableAnimation);
|
|
||||||
|
|
||||||
UIRoot = AnimatorToggleObj;
|
GameObject MeshToggleObj = UIFactory.CreateToggle(UIRoot, "MeshToggle", out MeshToggle, out Text MeshToggleText);
|
||||||
UIRoot.SetActive(false);
|
UIFactory.SetLayoutElement(MeshToggleObj, minHeight: 30);
|
||||||
|
MeshToggle.onValueChanged.AddListener(EnableMesh);
|
||||||
|
|
||||||
Rect = UIRoot.GetComponent<RectTransform>();
|
Rect = UIRoot.GetComponent<RectTransform>();
|
||||||
Rect.anchorMin = new Vector2(0, 1);
|
Rect.anchorMin = new Vector2(0, 1);
|
||||||
Rect.anchorMax = new Vector2(0, 1);
|
Rect.anchorMax = new Vector2(0, 1);
|
||||||
Rect.pivot = new Vector2(0.5f, 1);
|
Rect.pivot = new Vector2(0.5f, 1);
|
||||||
Rect.sizeDelta = new Vector2(25, 25);
|
Rect.sizeDelta = new Vector2(30, 30);
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
|
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 0);
|
||||||
|
|
||||||
inspectButton = UIFactory.CreateButton(UIRoot, "InspectButton", "");
|
inspectButton = UIFactory.CreateButton(UIRoot, "InspectButton", "");
|
||||||
UIFactory.SetLayoutElement(inspectButton.GameObject, minWidth: 200, minHeight: 25);
|
UIFactory.SetLayoutElement(inspectButton.GameObject, minWidth: 200, minHeight: 25);
|
||||||
inspectButton.OnClick += () => InspectorManager.Inspect(animatorPlayer.animator.gameObject);
|
inspectButton.OnClick += () => InspectorManager.Inspect(animatorPlayer.animator.gameObject);
|
||||||
|
|
||||||
|
animatorToggler = new AnimatorPausePlayButton(UIRoot, animatorPlayer != null && animatorPlayer.animator.speed == 1);
|
||||||
|
animatorToggler.OnClick += ButtonEnableAnimation;
|
||||||
|
|
||||||
ButtonRef resetAnimation = UIFactory.CreateButton(UIRoot, "Reset Animation", "Reset");
|
ButtonRef resetAnimation = UIFactory.CreateButton(UIRoot, "Reset Animation", "Reset");
|
||||||
UIFactory.SetLayoutElement(resetAnimation.GameObject, minWidth: 50, minHeight: 25);
|
UIFactory.SetLayoutElement(resetAnimation.GameObject, minWidth: 50, minHeight: 25);
|
||||||
resetAnimation.OnClick += ResetAnimation;
|
resetAnimation.OnClick += ResetAnimation;
|
||||||
|
|
||||||
GameObject ignoresMasterTogglerObj = UIFactory.CreateToggle(UIRoot, $"AnimatorIgnoreMasterToggle", out IgnoreMasterToggle, out Text ignoreMasterToggleText);
|
GameObject ignoresMasterTogglerObj = UIFactory.CreateToggle(UIRoot, "AnimatorIgnoreMasterToggle", out IgnoreMasterToggle, out Text ignoreMasterToggleText);
|
||||||
UIFactory.SetLayoutElement(ignoresMasterTogglerObj, minHeight: 25, minWidth: 155);
|
UIFactory.SetLayoutElement(ignoresMasterTogglerObj, minHeight: 25, minWidth: 155);
|
||||||
IgnoreMasterToggle.isOn = false;
|
IgnoreMasterToggle.isOn = false;
|
||||||
IgnoreMasterToggle.onValueChanged.AddListener(IgnoreMasterToggle_Clicked);
|
IgnoreMasterToggle.onValueChanged.AddListener(IgnoreMasterToggle_Clicked);
|
||||||
@ -112,7 +119,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
favAnimation.ButtonText.text = animatorPlayer.IsAnimationFaved(animatorPlayer.overridingAnimation) ? "★" : "☆";
|
favAnimation.ButtonText.text = animatorPlayer.IsAnimationFaved(animatorPlayer.overridingAnimation) ? "★" : "☆";
|
||||||
};
|
};
|
||||||
|
|
||||||
GameObject overridingAnimationObj = UIFactory.CreateDropdown(UIRoot, $"Animations_Dropdown", out animatorDropdown, null, 14, (idx) => {
|
GameObject overridingAnimationObj = UIFactory.CreateDropdown(UIRoot, "Animations_Dropdown", out animatorDropdown, null, 14, (idx) => {
|
||||||
if (animatorPlayer.animator.wrappedObject == null)
|
if (animatorPlayer.animator.wrappedObject == null)
|
||||||
return;
|
return;
|
||||||
animatorPlayer.overridingAnimation = idx < animatorDropdown.options.Count ? animatorPlayer.animations.Find(a => a.name == animatorDropdown.options[idx].text) : animatorPlayer.overridingAnimation;
|
animatorPlayer.overridingAnimation = idx < animatorDropdown.options.Count ? animatorPlayer.animations.Find(a => a.name == animatorDropdown.options[idx].text) : animatorPlayer.overridingAnimation;
|
||||||
@ -151,10 +158,24 @@ namespace UnityExplorer.UI.Panels
|
|||||||
//UpdateDropdownOptions();
|
//UpdateDropdownOptions();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
GameObject animationTimelineObj = UIFactory.CreateSlider(UIRoot, "AnimationTimelineSlider", out animationTimeline);
|
||||||
|
UIFactory.SetLayoutElement(animationTimelineObj, minHeight: 25, minWidth: 200, flexibleHeight: 0);
|
||||||
|
animationTimeline.minValue = 0;
|
||||||
|
animationTimeline.maxValue = 1;
|
||||||
|
animationTimeline.onValueChanged.AddListener((float val) => {
|
||||||
|
animatorPlayer.PlayOverridingAnimation(val);
|
||||||
|
animatorToggler.isOn = false;
|
||||||
|
});
|
||||||
|
|
||||||
playButton = UIFactory.CreateButton(UIRoot, "PlayButton", "Play", new Color(0.2f, 0.26f, 0.2f));
|
playButton = UIFactory.CreateButton(UIRoot, "PlayButton", "Play", new Color(0.2f, 0.26f, 0.2f));
|
||||||
UIFactory.SetLayoutElement(playButton.Component.gameObject, minHeight: 25, minWidth: 90);
|
UIFactory.SetLayoutElement(playButton.Component.gameObject, minHeight: 25, minWidth: 90);
|
||||||
playButton.OnClick += PlayButton_OnClick;
|
playButton.OnClick += PlayButton_OnClick;
|
||||||
|
|
||||||
|
openBonesPanelButton = UIFactory.CreateButton(UIRoot, "OpenBonesPanelButton", "Open Bones Panel");
|
||||||
|
UIFactory.SetLayoutElement(openBonesPanelButton.Component.gameObject, minWidth: 150, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
||||||
|
|
||||||
|
openBonesPanelButton.OnClick += () => { animatorPlayer.OpenBonesPanel(); };
|
||||||
|
|
||||||
return UIRoot;
|
return UIRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,9 +187,17 @@ namespace UnityExplorer.UI.Panels
|
|||||||
animatorPlayer.shouldIgnoreMasterToggle = value;
|
animatorPlayer.shouldIgnoreMasterToggle = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void ButtonEnableAnimation(){
|
||||||
|
EnableAnimation(animatorToggler.isOn);
|
||||||
|
}
|
||||||
|
|
||||||
internal void EnableAnimation(bool value){
|
internal void EnableAnimation(bool value){
|
||||||
if (animatorPlayer.animator.wrappedObject != null)
|
if (animatorPlayer.animator.wrappedObject != null)
|
||||||
animatorPlayer.animator.speed = value ? 1 : 0;
|
animatorPlayer.animator.speed = value ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void EnableMesh(bool value){
|
||||||
|
animatorPlayer.SetMeshesEnabled(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,11 @@ namespace UnityExplorer.UI.Panels
|
|||||||
public List<IAnimationClip> animations = new List<IAnimationClip>();
|
public List<IAnimationClip> animations = new List<IAnimationClip>();
|
||||||
public bool shouldIgnoreMasterToggle = false;
|
public bool shouldIgnoreMasterToggle = false;
|
||||||
|
|
||||||
|
BonesManager bonesManager;
|
||||||
|
private List<Transform> bones = new List<Transform>();
|
||||||
|
private List<SkinnedMeshRenderer> skinnedMeshes = new();
|
||||||
|
private List<MeshRenderer> extraMeshes = new();
|
||||||
|
|
||||||
public IAnimationClip overridingAnimation;
|
public IAnimationClip overridingAnimation;
|
||||||
private IAnimationClip lastCurrentAnimation;
|
private IAnimationClip lastCurrentAnimation;
|
||||||
private IRuntimeAnimatorController originalAnimatorController;
|
private IRuntimeAnimatorController originalAnimatorController;
|
||||||
@ -42,6 +47,13 @@ namespace UnityExplorer.UI.Panels
|
|||||||
this.overridingAnimation = lastCurrentAnimation != null ? lastCurrentAnimation : (animations.Count > 0 ? animations[0] : null);
|
this.overridingAnimation = lastCurrentAnimation != null ? lastCurrentAnimation : (animations.Count > 0 ? animations[0] : null);
|
||||||
|
|
||||||
this.favAnimations = new List<IAnimationClip>();
|
this.favAnimations = new List<IAnimationClip>();
|
||||||
|
|
||||||
|
SearchMeshes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SearchMeshes(){
|
||||||
|
skinnedMeshes = new List<SkinnedMeshRenderer>(animator.wrappedObject.gameObject.GetComponentsInChildren<SkinnedMeshRenderer>(false));
|
||||||
|
extraMeshes = new List<MeshRenderer>(animator.wrappedObject.gameObject.GetComponentsInChildren<MeshRenderer>(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include the animations being played in other layers
|
// Include the animations being played in other layers
|
||||||
@ -54,6 +66,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void ResetAnimation(){
|
public void ResetAnimation(){
|
||||||
|
if (bonesManager != null) bonesManager.turnOffAnimatorToggle.isOn = true;
|
||||||
// Let the game change animations again
|
// Let the game change animations again
|
||||||
animator.StopPlayback();
|
animator.StopPlayback();
|
||||||
if (originalAnimatorController != null && animator.wrappedObject != null){
|
if (originalAnimatorController != null && animator.wrappedObject != null){
|
||||||
@ -65,7 +78,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PlayOverridingAnimation(){
|
public void PlayOverridingAnimation(float normalizedTime){
|
||||||
ResetAnimation();
|
ResetAnimation();
|
||||||
if (animatorOverrideController == null){
|
if (animatorOverrideController == null){
|
||||||
animatorOverrideController = new IAnimatorOverrideController();
|
animatorOverrideController = new IAnimatorOverrideController();
|
||||||
@ -84,7 +97,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
IAnimationClip currentAnimation = animator.GetCurrentAnimatorClipInfo(0)[0].clip;
|
IAnimationClip currentAnimation = animator.GetCurrentAnimatorClipInfo(0)[0].clip;
|
||||||
animatorOverrideController[currentAnimation] = overridingAnimation;
|
animatorOverrideController[currentAnimation] = overridingAnimation;
|
||||||
animator.Play(currentAnimation.name);
|
animator.Play(currentAnimation.name, normalizedTime);
|
||||||
|
|
||||||
lastCurrentAnimation = currentAnimation;
|
lastCurrentAnimation = currentAnimation;
|
||||||
}
|
}
|
||||||
@ -115,6 +128,45 @@ namespace UnityExplorer.UI.Panels
|
|||||||
animator.enabled = value;
|
animator.enabled = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Transform> GetMeshesTransforms(){
|
||||||
|
List<Transform> meshes = new List<Transform>();
|
||||||
|
|
||||||
|
foreach (SkinnedMeshRenderer skinnedMesh in skinnedMeshes) {
|
||||||
|
meshes.AddRange(skinnedMesh.bones);
|
||||||
|
}
|
||||||
|
meshes.AddRange(extraMeshes.Select(m => m.transform));
|
||||||
|
meshes.RemoveAll(item => item == null);
|
||||||
|
return meshes.GroupBy(b => b.name).Select(b => b.First()).ToList().OrderBy(b => b.name).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenBonesPanel(){
|
||||||
|
if (skinnedMeshes.Count == 0 && extraMeshes.Count == 0) return;
|
||||||
|
if (bonesManager == null){
|
||||||
|
bonesManager = new BonesManager(UIManager.GetPanel<UnityExplorer.UI.Panels.AnimatorPanel>(UIManager.Panels.AnimatorPanel).Owner, GetMeshesTransforms(), animator);
|
||||||
|
}
|
||||||
|
bonesManager.SetActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MaybeResetBonesPanel(){
|
||||||
|
if (bonesManager == null) return;
|
||||||
|
bonesManager.RefreshBones(GetMeshesTransforms());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMeshesEnabled(bool value){
|
||||||
|
foreach (SkinnedMeshRenderer skinnedMesh in skinnedMeshes) {
|
||||||
|
skinnedMesh.TryCast<Renderer>().enabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (MeshRenderer meshRenderer in extraMeshes) {
|
||||||
|
meshRenderer.gameObject.SetActive(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMeshHidden(){
|
||||||
|
// Could maybe save a variable to set on SetMeshesEnabled instead
|
||||||
|
return skinnedMeshes.Any( m => m.TryCast<Renderer>().enabled) || extraMeshes.Any( m => m.gameObject.activeSelf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IAnimator
|
public class IAnimator
|
||||||
@ -209,9 +261,9 @@ namespace UnityExplorer.UI.Panels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Play(string animatorClip){
|
public void Play(string animatorClip, float normalizedTime){
|
||||||
MethodInfo play = realType.GetMethod("Play", new Type[] {typeof(string), typeof(int), typeof(float)});
|
MethodInfo play = realType.GetMethod("Play", new Type[] {typeof(string), typeof(int), typeof(float)});
|
||||||
play.Invoke(_animator.TryCast(), new object[] {animatorClip, -1, 0f});
|
play.Invoke(_animator.TryCast(), new object[] {animatorClip, -1, normalizedTime});
|
||||||
}
|
}
|
||||||
|
|
||||||
public float speed
|
public float speed
|
||||||
|
411
src/Cinematic/Cells/BonesCell.cs
Normal file
411
src/Cinematic/Cells/BonesCell.cs
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
using UnityExplorer.UI.Widgets;
|
||||||
|
using UniverseLib.Input;
|
||||||
|
using UniverseLib.UI;
|
||||||
|
using UniverseLib.UI.Models;
|
||||||
|
using UniverseLib.UI.Widgets.ScrollView;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Panels
|
||||||
|
{
|
||||||
|
public class BonesCell : ICell
|
||||||
|
{
|
||||||
|
public BoneTree boneTree;
|
||||||
|
public TransformControls transformControls;
|
||||||
|
|
||||||
|
ComponentControl positionControl;
|
||||||
|
ComponentControl rotationControl;
|
||||||
|
ComponentControl scaleControl;
|
||||||
|
public AxisComponentControl CurrentSlidingAxisControl { get; set; }
|
||||||
|
public BonesManager Owner;
|
||||||
|
private Text boneName;
|
||||||
|
|
||||||
|
private GameObject expandBonesRow;
|
||||||
|
private ButtonRef expandBonesButton;
|
||||||
|
private Text expandBonesText;
|
||||||
|
private LayoutElement spaceLayout;
|
||||||
|
static private int TREE_LEVEL_IDENTATION = 20;
|
||||||
|
|
||||||
|
// ICell
|
||||||
|
public float DefaultHeight => 25f;
|
||||||
|
public GameObject UIRoot { get; set; }
|
||||||
|
public RectTransform Rect { get; set; }
|
||||||
|
|
||||||
|
public bool Enabled => UIRoot.activeSelf;
|
||||||
|
public void Enable() => UIRoot.SetActive(true);
|
||||||
|
public void Disable() => UIRoot.SetActive(false);
|
||||||
|
|
||||||
|
public void SetBoneTree(BoneTree boneTree, BonesManager bonesManager){
|
||||||
|
this.boneTree = boneTree;
|
||||||
|
boneName.text = boneTree.obj.name;
|
||||||
|
Owner = bonesManager;
|
||||||
|
|
||||||
|
if (boneTree.childTrees.Count == 0){
|
||||||
|
expandBonesRow.SetActive(false);
|
||||||
|
} else {
|
||||||
|
expandBonesRow.SetActive(true);
|
||||||
|
bool isTreeExpanded = IsTreeExpanded();
|
||||||
|
expandBonesButton.ButtonText.text = isTreeExpanded ? "▼" : "▶";
|
||||||
|
expandBonesText.text = isTreeExpanded ? "Collapse bones" : "Expand bones";
|
||||||
|
}
|
||||||
|
|
||||||
|
spaceLayout.minWidth = TREE_LEVEL_IDENTATION * boneTree.level;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExpandOrCollapseBoneTree(){
|
||||||
|
if (IsTreeExpanded()){
|
||||||
|
// Collapse
|
||||||
|
List<BoneTree> treesToRemove = boneTree.childTrees.Select(t => t.flatten()).SelectMany(l => l).ToList();
|
||||||
|
Owner.boneTrees = Owner.boneTrees.Except(treesToRemove).ToList();
|
||||||
|
expandBonesButton.ButtonText.text = "▶";
|
||||||
|
expandBonesText.text = "Expand bones";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Expand
|
||||||
|
int index = Owner.boneTrees.FindIndex(t => t == boneTree);
|
||||||
|
if (index == -1) return;
|
||||||
|
|
||||||
|
Owner.boneTrees.InsertRange(index + 1, boneTree.childTrees);
|
||||||
|
expandBonesButton.ButtonText.text = "▼";
|
||||||
|
expandBonesText.text = "Collapse bones";
|
||||||
|
}
|
||||||
|
|
||||||
|
Owner.boneScrollPool.Refresh(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsTreeExpanded(){
|
||||||
|
if (boneTree == null) return true;
|
||||||
|
return boneTree.childTrees.Any(t1 => Owner.boneTrees.Any(t2 => t2 == t1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual GameObject CreateContent(GameObject parent)
|
||||||
|
{
|
||||||
|
UIRoot = UIFactory.CreateUIObject("CellRoot", parent, new Vector2(25, 25));
|
||||||
|
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, false, false, true, true, 4, childAlignment: TextAnchor.MiddleRight);
|
||||||
|
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 800);
|
||||||
|
|
||||||
|
Rect = UIRoot.GetComponent<RectTransform>();
|
||||||
|
Rect.anchorMin = new Vector2(0, 1);
|
||||||
|
Rect.anchorMax = new Vector2(0, 1);
|
||||||
|
Rect.pivot = new Vector2(0.5f, 1);
|
||||||
|
Rect.sizeDelta = new Vector2(30, 30);
|
||||||
|
|
||||||
|
GameObject spacer = UIFactory.CreateUIObject("Spacer", UIRoot);
|
||||||
|
spaceLayout = UIFactory.SetLayoutElement(spacer, minWidth: 0, flexibleWidth: 0);
|
||||||
|
|
||||||
|
GameObject baseCell = UIFactory.CreateUIObject("BaseCell", UIRoot);
|
||||||
|
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(baseCell, false, false, true, true, 3);
|
||||||
|
UIFactory.SetLayoutElement(baseCell, minHeight: 25, minWidth: 50, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
GameObject header = UIFactory.CreateUIObject("BoneHeader", baseCell);
|
||||||
|
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(header, false, false, true, true, 4, childAlignment: TextAnchor.MiddleLeft);
|
||||||
|
UIFactory.SetLayoutElement(header, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 800);
|
||||||
|
|
||||||
|
GameObject MeshToggleObj = UIFactory.CreateToggle(header, "MeshToggle", out Toggle MeshToggle, out Text MeshToggleText);
|
||||||
|
UIFactory.SetLayoutElement(MeshToggleObj, minHeight: 30);
|
||||||
|
MeshToggle.onValueChanged.AddListener(SetBoneEnabled);
|
||||||
|
|
||||||
|
boneName = UIFactory.CreateLabel(header, $"BoneName", "", fontSize: 18);
|
||||||
|
UIFactory.SetLayoutElement(boneName.gameObject, minWidth: 100, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
GameObject headerButtons = UIFactory.CreateUIObject("BoneHeader", header);
|
||||||
|
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(headerButtons, false, false, true, true, 4, childAlignment: TextAnchor.MiddleRight);
|
||||||
|
UIFactory.SetLayoutElement(headerButtons, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 800);
|
||||||
|
|
||||||
|
ButtonRef inspectButton = UIFactory.CreateButton(headerButtons, "InspectButton", "Inspect");
|
||||||
|
UIFactory.SetLayoutElement(inspectButton.GameObject, minWidth: 75, minHeight: 25);
|
||||||
|
inspectButton.OnClick += () => InspectorManager.Inspect(boneTree.obj);
|
||||||
|
|
||||||
|
ButtonRef restoreBoneStateButton = UIFactory.CreateButton(headerButtons, "RestoreBoneState", "Restore State");
|
||||||
|
UIFactory.SetLayoutElement(restoreBoneStateButton.GameObject, minWidth: 125, minHeight: 25);
|
||||||
|
restoreBoneStateButton.OnClick += RestoreBoneState;
|
||||||
|
|
||||||
|
positionControl = ComponentControl.Create(this, baseCell, "Local Position", TransformType.LocalPosition, 0.01f);
|
||||||
|
rotationControl = ComponentControl.Create(this, baseCell, "Rotation", TransformType.Rotation, 10f);
|
||||||
|
scaleControl = ComponentControl.Create(this, baseCell, "Scale", TransformType.Scale, 0.1f);
|
||||||
|
|
||||||
|
expandBonesRow = UIFactory.CreateUIObject("ExpandBonesRow", baseCell);
|
||||||
|
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(expandBonesRow, false, false, true, true, 4, childAlignment: TextAnchor.MiddleLeft);
|
||||||
|
UIFactory.SetLayoutElement(expandBonesRow, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 800);
|
||||||
|
|
||||||
|
expandBonesButton = UIFactory.CreateButton(expandBonesRow, "ExpandBonesButton", IsTreeExpanded() ? "⯆" : "▶", new Color(0.05f, 0.05f, 0.05f));
|
||||||
|
UIFactory.SetLayoutElement(expandBonesButton.Component.gameObject, minHeight: 25, minWidth: 25);
|
||||||
|
expandBonesButton.OnClick += ExpandOrCollapseBoneTree;
|
||||||
|
|
||||||
|
expandBonesText = UIFactory.CreateLabel(expandBonesRow, $"ExpandBonesText", IsTreeExpanded() ? "Collapse bones" : "Expand bones");
|
||||||
|
UIFactory.SetLayoutElement(expandBonesText.gameObject, minWidth: 100, minHeight: 25);
|
||||||
|
|
||||||
|
return UIRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RestoreBoneState(){
|
||||||
|
Owner.RestoreBoneState(boneTree.obj.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetBoneEnabled(bool value){
|
||||||
|
boneTree.obj.SetActive(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransformControls-like functions
|
||||||
|
public void UpdateTransformControlValues(bool force){
|
||||||
|
positionControl.Update(force);
|
||||||
|
rotationControl.Update(force);
|
||||||
|
scaleControl.Update(force);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateVectorSlider()
|
||||||
|
{
|
||||||
|
AxisComponentControl control = CurrentSlidingAxisControl;
|
||||||
|
|
||||||
|
if (control == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!IInputManager.GetMouseButton(0))
|
||||||
|
{
|
||||||
|
control.slider.value = 0f;
|
||||||
|
control = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AxisControlOperation(control.slider.value, control.parent, control.axis);
|
||||||
|
|
||||||
|
if (Owner.turnOffAnimatorToggle.isOn) Owner.turnOffAnimatorToggle.isOn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AxisControlOperation(float value, ComponentControl parent, int axis)
|
||||||
|
{
|
||||||
|
Transform transform = boneTree.obj.transform;
|
||||||
|
|
||||||
|
Vector3 vector = parent.Type switch
|
||||||
|
{
|
||||||
|
TransformType.Position => transform.position,
|
||||||
|
TransformType.LocalPosition => transform.localPosition,
|
||||||
|
TransformType.Rotation => transform.localEulerAngles,
|
||||||
|
TransformType.Scale => transform.localScale,
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
|
||||||
|
// apply vector value change
|
||||||
|
switch (axis)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
vector.x += value; break;
|
||||||
|
case 1:
|
||||||
|
vector.y += value; break;
|
||||||
|
case 2:
|
||||||
|
vector.z += value; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set vector back to transform
|
||||||
|
switch (parent.Type)
|
||||||
|
{
|
||||||
|
case TransformType.Position:
|
||||||
|
transform.position = vector; break;
|
||||||
|
case TransformType.LocalPosition:
|
||||||
|
transform.localPosition = vector; break;
|
||||||
|
case TransformType.Rotation:
|
||||||
|
transform.localEulerAngles = vector; break;
|
||||||
|
case TransformType.Scale:
|
||||||
|
transform.localScale = vector; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateTransformControlValues(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplication of Vector3Control class
|
||||||
|
public class ComponentControl
|
||||||
|
{
|
||||||
|
public BonesCell Owner { get; }
|
||||||
|
public Transform Transform => Owner.boneTree.obj.transform;
|
||||||
|
public TransformType Type { get; }
|
||||||
|
|
||||||
|
public InputFieldRef MainInput { get; }
|
||||||
|
|
||||||
|
public AxisComponentControl[] axisComponentControl { get; } = new AxisComponentControl[3];
|
||||||
|
|
||||||
|
public InputFieldRef IncrementInput { get; set; }
|
||||||
|
public float Increment { get; set; } = 0.1f;
|
||||||
|
|
||||||
|
Vector3 lastValue;
|
||||||
|
|
||||||
|
Vector3 CurrentValue => Type switch
|
||||||
|
{
|
||||||
|
TransformType.Position => Transform.position,
|
||||||
|
TransformType.LocalPosition => Transform.localPosition,
|
||||||
|
TransformType.Rotation => Transform.localEulerAngles,
|
||||||
|
TransformType.Scale => Transform.localScale,
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
|
||||||
|
public ComponentControl(BonesCell cell, TransformType type, InputFieldRef input)
|
||||||
|
{
|
||||||
|
this.Owner = cell;
|
||||||
|
this.Type = type;
|
||||||
|
this.MainInput = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(bool force)
|
||||||
|
{
|
||||||
|
// Probably not needed
|
||||||
|
if (Transform == null) return;
|
||||||
|
|
||||||
|
Vector3 currValue = CurrentValue;
|
||||||
|
if (force || (!MainInput.Component.isFocused && !lastValue.Equals(currValue)))
|
||||||
|
{
|
||||||
|
MainInput.Text = ParseUtility.ToStringForInput<Vector3>(currValue);
|
||||||
|
lastValue = currValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTransformInputEndEdit(TransformType type, string input)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case TransformType.Position:
|
||||||
|
{
|
||||||
|
if (ParseUtility.TryParse(input, out Vector3 val, out _))
|
||||||
|
Transform.position = val;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TransformType.LocalPosition:
|
||||||
|
{
|
||||||
|
if (ParseUtility.TryParse(input, out Vector3 val, out _))
|
||||||
|
Transform.localPosition = val;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TransformType.Rotation:
|
||||||
|
{
|
||||||
|
if (ParseUtility.TryParse(input, out Vector3 val, out _))
|
||||||
|
Transform.localEulerAngles = val;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TransformType.Scale:
|
||||||
|
{
|
||||||
|
if (ParseUtility.TryParse(input, out Vector3 val, out _))
|
||||||
|
Transform.localScale = val;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Owner.UpdateTransformControlValues(true);
|
||||||
|
if (Owner.Owner.turnOffAnimatorToggle.isOn) Owner.Owner.turnOffAnimatorToggle.isOn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IncrementInput_OnEndEdit(string value)
|
||||||
|
{
|
||||||
|
if (!ParseUtility.TryParse(value, out float increment, out _))
|
||||||
|
IncrementInput.Text = ParseUtility.ToStringForInput<float>(Increment);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Increment = increment;
|
||||||
|
foreach (AxisComponentControl slider in axisComponentControl)
|
||||||
|
{
|
||||||
|
slider.slider.minValue = -increment;
|
||||||
|
slider.slider.maxValue = increment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ComponentControl Create(BonesCell cell, GameObject transformGroup, string title, TransformType type, float increment)
|
||||||
|
{
|
||||||
|
GameObject rowObj = UIFactory.CreateUIObject($"Row_{title}", transformGroup);
|
||||||
|
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(rowObj, false, false, true, true, 5, 0, 0, 0, 0, default);
|
||||||
|
UIFactory.SetLayoutElement(rowObj, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
Text titleLabel = UIFactory.CreateLabel(rowObj, "PositionLabel", title, TextAnchor.MiddleRight, Color.grey);
|
||||||
|
UIFactory.SetLayoutElement(titleLabel.gameObject, minHeight: 25, minWidth: 110);
|
||||||
|
|
||||||
|
InputFieldRef inputField = UIFactory.CreateInputField(rowObj, "InputField", "...");
|
||||||
|
UIFactory.SetLayoutElement(inputField.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 999);
|
||||||
|
|
||||||
|
ComponentControl control = new(cell, type, inputField);
|
||||||
|
|
||||||
|
inputField.Component.GetOnEndEdit().AddListener((string value) => { control.OnTransformInputEndEdit(type, value); });
|
||||||
|
control.Increment = increment;
|
||||||
|
|
||||||
|
control.axisComponentControl[0] = AxisComponentControl.Create(rowObj, "X", 0, control);
|
||||||
|
control.axisComponentControl[1] = AxisComponentControl.Create(rowObj, "Y", 1, control);
|
||||||
|
control.axisComponentControl[2] = AxisComponentControl.Create(rowObj, "Z", 2, control);
|
||||||
|
|
||||||
|
control.IncrementInput = UIFactory.CreateInputField(rowObj, "IncrementInput", "...");
|
||||||
|
control.IncrementInput.Text = increment.ToString();
|
||||||
|
UIFactory.SetLayoutElement(control.IncrementInput.GameObject, minWidth: 30, flexibleWidth: 0, minHeight: 25);
|
||||||
|
control.IncrementInput.Component.GetOnEndEdit().AddListener(control.IncrementInput_OnEndEdit);
|
||||||
|
|
||||||
|
if (type == TransformType.Scale){
|
||||||
|
GameObject extraRowObj = UIFactory.CreateUIObject("Row_UniformScale", transformGroup);
|
||||||
|
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(extraRowObj, false, false, true, true, 5, 0, 0, 0, 0, default);
|
||||||
|
UIFactory.SetLayoutElement(extraRowObj, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
Text uniformScaleTitleLabel = UIFactory.CreateLabel(extraRowObj, "UniformScaleLabel", "Uniform Scale:", TextAnchor.MiddleRight, Color.grey);
|
||||||
|
UIFactory.SetLayoutElement(uniformScaleTitleLabel.gameObject, minHeight: 25, minWidth: 110);
|
||||||
|
|
||||||
|
GameObject uniformScaleControlObj = UIFactory.CreateSlider(extraRowObj, "UniformScaleSlider", out Slider uniformScaleControl);
|
||||||
|
UIFactory.SetLayoutElement(uniformScaleControlObj, minHeight: 25, minWidth: 200, flexibleHeight: 0);
|
||||||
|
uniformScaleControl.minValue = 0.0001f;
|
||||||
|
uniformScaleControl.maxValue = 10;
|
||||||
|
uniformScaleControl.value = 1;
|
||||||
|
uniformScaleControl.onValueChanged.AddListener((float val) => { cell.boneTree.obj.transform.localScale = new Vector3(val, val, val); });
|
||||||
|
}
|
||||||
|
|
||||||
|
return control;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Duplication of AxisControl class
|
||||||
|
public class AxisComponentControl
|
||||||
|
{
|
||||||
|
public readonly ComponentControl parent;
|
||||||
|
|
||||||
|
public readonly int axis;
|
||||||
|
public readonly Slider slider;
|
||||||
|
|
||||||
|
public AxisComponentControl(int axis, Slider slider, ComponentControl parentControl)
|
||||||
|
{
|
||||||
|
this.parent = parentControl;
|
||||||
|
this.axis = axis;
|
||||||
|
this.slider = slider;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnVectorSliderChanged(float value)
|
||||||
|
{
|
||||||
|
parent.Owner.CurrentSlidingAxisControl = value == 0f ? null : this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnVectorMinusClicked()
|
||||||
|
{
|
||||||
|
parent.Owner.AxisControlOperation(-this.parent.Increment, this.parent, this.axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnVectorPlusClicked()
|
||||||
|
{
|
||||||
|
parent.Owner.AxisControlOperation(this.parent.Increment, this.parent, this.axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AxisComponentControl Create(GameObject parent, string title, int axis, ComponentControl owner)
|
||||||
|
{
|
||||||
|
Text label = UIFactory.CreateLabel(parent, $"Label_{title}", $"{title}:", TextAnchor.MiddleRight, Color.grey);
|
||||||
|
UIFactory.SetLayoutElement(label.gameObject, minHeight: 25, minWidth: 30);
|
||||||
|
|
||||||
|
GameObject sliderObj = UIFactory.CreateSlider(parent, $"Slider_{title}", out Slider slider);
|
||||||
|
UIFactory.SetLayoutElement(sliderObj, minHeight: 25, minWidth: 75, flexibleWidth: 0);
|
||||||
|
slider.m_FillImage.color = Color.clear;
|
||||||
|
|
||||||
|
slider.minValue = -owner.Increment;
|
||||||
|
slider.maxValue = owner.Increment;
|
||||||
|
|
||||||
|
AxisComponentControl sliderControl = new(axis, slider, owner);
|
||||||
|
|
||||||
|
slider.onValueChanged.AddListener(sliderControl.OnVectorSliderChanged);
|
||||||
|
|
||||||
|
ButtonRef minusButton = UIFactory.CreateButton(parent, "MinusIncrementButton", "-");
|
||||||
|
UIFactory.SetLayoutElement(minusButton.GameObject, minWidth: 20, flexibleWidth: 0, minHeight: 25);
|
||||||
|
minusButton.OnClick += sliderControl.OnVectorMinusClicked;
|
||||||
|
|
||||||
|
ButtonRef plusButton = UIFactory.CreateButton(parent, "PlusIncrementButton", "+");
|
||||||
|
UIFactory.SetLayoutElement(plusButton.GameObject, minWidth: 20, flexibleWidth: 0, minHeight: 25);
|
||||||
|
plusButton.OnClick += sliderControl.OnVectorPlusClicked;
|
||||||
|
|
||||||
|
return sliderControl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,8 +43,11 @@ namespace UnityExplorer.UI.Panels
|
|||||||
ButtonRef copyFovButton = UIFactory.CreateButton(UIRoot, "Copy Camera FoV", "Copy Camera FoV");
|
ButtonRef copyFovButton = UIFactory.CreateButton(UIRoot, "Copy Camera FoV", "Copy Camera FoV");
|
||||||
UIFactory.SetLayoutElement(copyFovButton.GameObject, minWidth: 100, minHeight: 25, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(copyFovButton.GameObject, minWidth: 100, minHeight: 25, flexibleWidth: 9999);
|
||||||
copyFovButton.OnClick += () => {
|
copyFovButton.OnClick += () => {
|
||||||
point.fov = FreeCamPanel.ourCamera.fieldOfView;
|
Camera freecam = FreeCamPanel.GetFreecam();
|
||||||
GetCamPathsPanel().controlPoints[index] = point;
|
if (freecam != null) {
|
||||||
|
point.fov = freecam.fieldOfView;
|
||||||
|
GetCamPathsPanel().controlPoints[index] = point;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ButtonRef moveToPointButton = UIFactory.CreateButton(UIRoot, "Move Cam to Node", "Move Cam to Node");
|
ButtonRef moveToPointButton = UIFactory.CreateButton(UIRoot, "Move Cam to Node", "Move Cam to Node");
|
||||||
@ -52,7 +55,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
moveToPointButton.OnClick += () => {
|
moveToPointButton.OnClick += () => {
|
||||||
FreeCamPanel.SetCameraRotation(point.rotation);
|
FreeCamPanel.SetCameraRotation(point.rotation);
|
||||||
FreeCamPanel.SetCameraPosition(point.position);
|
FreeCamPanel.SetCameraPosition(point.position);
|
||||||
FreeCamPanel.ourCamera.fieldOfView = point.fov;
|
FreeCamPanel.SetFOV(point.fov);
|
||||||
};
|
};
|
||||||
|
|
||||||
ButtonRef moveUpButton = UIFactory.CreateButton(UIRoot, "MoveUp", "▲");
|
ButtonRef moveUpButton = UIFactory.CreateButton(UIRoot, "MoveUp", "▲");
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityExplorer.Config;
|
||||||
using UniverseLib.UI;
|
using UniverseLib.UI;
|
||||||
using UniverseLib.UI.Models;
|
using UniverseLib.UI.Models;
|
||||||
using UniverseLib.UI.Widgets.ScrollView;
|
using UniverseLib.UI.Widgets.ScrollView;
|
||||||
@ -29,7 +30,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
//Active toggle
|
//Active toggle
|
||||||
Toggle toggleLight;
|
Toggle toggleLight;
|
||||||
GameObject toggleObj = UIFactory.CreateToggle(UIRoot, "UseGameCameraToggle", out toggleLight, out label);
|
GameObject toggleObj = UIFactory.CreateToggle(UIRoot, "ActivateLightToggle", out toggleLight, out label);
|
||||||
UIFactory.SetLayoutElement(toggleObj, minHeight: 25, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(toggleObj, minHeight: 25, flexibleWidth: 9999);
|
||||||
toggleLight.onValueChanged.AddListener(value => { light.SetActive(value); });
|
toggleLight.onValueChanged.AddListener(value => { light.SetActive(value); });
|
||||||
toggleLight.isOn = true;
|
toggleLight.isOn = true;
|
||||||
@ -69,6 +70,9 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
private void ToggleVisualizer(){
|
private void ToggleVisualizer(){
|
||||||
GameObject visualizer = light.transform.GetChild(0).gameObject;
|
GameObject visualizer = light.transform.GetChild(0).gameObject;
|
||||||
|
float arrowSize = ConfigManager.Arrow_Size.Value;
|
||||||
|
Vector3 arrowSizeVec = new Vector3(Math.Max(arrowSize, 0.1f), Math.Max(arrowSize, 0.1f), Math.Max(arrowSize, 0.1f));
|
||||||
|
light.transform.GetChild(0).localScale = arrowSizeVec;
|
||||||
visualizer.SetActive(!visualizer.activeSelf);
|
visualizer.SetActive(!visualizer.activeSelf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,7 +94,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void CopyFreeCamTransform(GameObject obj){
|
public static void CopyFreeCamTransform(GameObject obj){
|
||||||
Camera freeCam = FreeCamPanel.ourCamera;
|
Camera freeCam = FreeCamPanel.GetFreecam();
|
||||||
|
|
||||||
if (freeCam != null) {
|
if (freeCam != null) {
|
||||||
obj.transform.position = freeCam.transform.position;
|
obj.transform.position = freeCam.transform.position;
|
||||||
|
89
src/Cinematic/Serializers/BonesSerializer.cs
Normal file
89
src/Cinematic/Serializers/BonesSerializer.cs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Serializers
|
||||||
|
{
|
||||||
|
public class BonesSerializer
|
||||||
|
{
|
||||||
|
public static string Serialize(Dictionary<string, List<CachedBonesTransform>> dict)
|
||||||
|
{
|
||||||
|
SerializableDictionary serializableDict = new SerializableDictionary(dict);
|
||||||
|
|
||||||
|
var serializer = new XmlSerializer(typeof(SerializableDictionary));
|
||||||
|
using (var writer = new StringWriter())
|
||||||
|
{
|
||||||
|
serializer.Serialize(writer, serializableDict);
|
||||||
|
return writer.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, List<CachedBonesTransform>> Deserialize(string xml)
|
||||||
|
{
|
||||||
|
var serializer = new XmlSerializer(typeof(SerializableDictionary));
|
||||||
|
using (var reader = new StringReader(xml))
|
||||||
|
{
|
||||||
|
return ((SerializableDictionary)serializer.Deserialize(reader)).ToDictionary();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old versions of .net can't natively serialize dictionaries, therefore we make a couple of classes to do it ourselves
|
||||||
|
[XmlRoot("Dictionary")]
|
||||||
|
public class SerializableDictionary
|
||||||
|
{
|
||||||
|
[XmlElement("Item")]
|
||||||
|
public List<DictionaryItem> Items { get; set; } = new List<DictionaryItem>();
|
||||||
|
|
||||||
|
public SerializableDictionary() { }
|
||||||
|
|
||||||
|
public SerializableDictionary(Dictionary<string, List<CachedBonesTransform>> dictionary)
|
||||||
|
{
|
||||||
|
foreach (var kvp in dictionary)
|
||||||
|
{
|
||||||
|
Items.Add(new DictionaryItem { Key = kvp.Key, Value = kvp.Value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, List<CachedBonesTransform>> ToDictionary()
|
||||||
|
{
|
||||||
|
var dictionary = new Dictionary<string, List<CachedBonesTransform>>();
|
||||||
|
foreach (var item in Items)
|
||||||
|
{
|
||||||
|
dictionary[item.Key] = item.Value;
|
||||||
|
}
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DictionaryItem
|
||||||
|
{
|
||||||
|
[XmlAttribute("Key")]
|
||||||
|
public string Key { get; set; }
|
||||||
|
|
||||||
|
[XmlElement("Value")]
|
||||||
|
public List<CachedBonesTransform> Value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CachedBonesTransform
|
||||||
|
{
|
||||||
|
public CachedBonesTransform(Vector3 position, Vector3 angles, Vector3 scale)
|
||||||
|
{
|
||||||
|
this.position = position;
|
||||||
|
this.angles = angles;
|
||||||
|
this.scale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// They cant be read-only because it causes problems with XML serialization in older versions of .NET
|
||||||
|
public Vector3 position { get; set; }
|
||||||
|
public Vector3 angles { get; set; }
|
||||||
|
public Vector3 scale { get; set; }
|
||||||
|
|
||||||
|
public void CopyToTransform(Transform transform){
|
||||||
|
transform.localPosition = position;
|
||||||
|
transform.localEulerAngles = angles;
|
||||||
|
transform.localScale = scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
src/Cinematic/Serializers/CamPathSerializer.cs
Normal file
50
src/Cinematic/Serializers/CamPathSerializer.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Serializers
|
||||||
|
{
|
||||||
|
public class CamPathSerializer
|
||||||
|
{
|
||||||
|
public static string Serialize(List<CatmullRom.CatmullRomPoint> points, float time, float alpha, float tension, bool closePath, string sceneName)
|
||||||
|
{
|
||||||
|
CamPathSerializeObject serializeObject = new CamPathSerializeObject(points, time, alpha, tension, closePath, sceneName);
|
||||||
|
var serializer = new XmlSerializer(typeof(CamPathSerializeObject));
|
||||||
|
using (var writer = new StringWriter())
|
||||||
|
{
|
||||||
|
serializer.Serialize(writer, serializeObject);
|
||||||
|
return writer.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CamPathSerializeObject Deserialize(string xml)
|
||||||
|
{
|
||||||
|
var serializer = new XmlSerializer(typeof(CamPathSerializeObject));
|
||||||
|
using (var reader = new StringReader(xml))
|
||||||
|
{
|
||||||
|
return ((CamPathSerializeObject)serializer.Deserialize(reader));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CamPathSerializeObject
|
||||||
|
{
|
||||||
|
public CamPathSerializeObject(List<CatmullRom.CatmullRomPoint> points, float time, float alpha, float tension, bool closePath, string sceneName)
|
||||||
|
{
|
||||||
|
this.points = points;
|
||||||
|
this.time = time;
|
||||||
|
this.tension = tension;
|
||||||
|
this.alpha = alpha;
|
||||||
|
this.closePath = closePath;
|
||||||
|
this.sceneName = sceneName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly List<CatmullRom.CatmullRomPoint> points;
|
||||||
|
public readonly float time;
|
||||||
|
public readonly float alpha;
|
||||||
|
public readonly float tension;
|
||||||
|
public readonly bool closePath;
|
||||||
|
public readonly string sceneName;
|
||||||
|
}
|
||||||
|
}
|
@ -22,13 +22,21 @@ namespace CinematicUnityExplorer.Cinematic
|
|||||||
public class UnityIGCSConnector
|
public class UnityIGCSConnector
|
||||||
{
|
{
|
||||||
// UnityIGCSConnector.dll definitions.
|
// UnityIGCSConnector.dll definitions.
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
private delegate void MoveCameraCallback(float step_left, float step_up, float fov, int from_start);
|
private delegate void MoveCameraCallback(float step_left, float step_up, float fov, int from_start);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
private delegate void SessionCallback();
|
private delegate void SessionCallback();
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
private delegate IntPtr U_IGCS_Initialize(MoveCameraCallback callback, SessionCallback start_cb, SessionCallback end_cb);
|
private delegate IntPtr U_IGCS_Initialize(MoveCameraCallback callback, SessionCallback start_cb, SessionCallback end_cb);
|
||||||
|
|
||||||
// Store the initial position when a session start in IGCSDof.
|
// Store the initial position when a session start in IGCSDof.
|
||||||
Mono.CSharp.Tuple<Vector3, Quaternion> position = null;
|
Mono.CSharp.Tuple<Vector3, Quaternion> position = null;
|
||||||
|
|
||||||
|
// When we end a session, we need to make sure to go back to the position when the session ends.
|
||||||
|
private Mono.CSharp.Tuple<Vector3, Quaternion> endSessionPosition = null;
|
||||||
|
|
||||||
private readonly bool isValid = false;
|
private readonly bool isValid = false;
|
||||||
private bool _isActive = false;
|
private bool _isActive = false;
|
||||||
public bool IsActive => isValid && _isActive;
|
public bool IsActive => isValid && _isActive;
|
||||||
@ -42,21 +50,21 @@ namespace CinematicUnityExplorer.Cinematic
|
|||||||
private readonly Queue<StepCommand> commands = new();
|
private readonly Queue<StepCommand> commands = new();
|
||||||
|
|
||||||
private IntPtr CameraStatus = IntPtr.Zero;
|
private IntPtr CameraStatus = IntPtr.Zero;
|
||||||
// In order to avoid allocations on every Update call, we create this buffer to allocate once
|
|
||||||
// and copy from here the CameraStatus (because Marshal.Copy requires a buffer, urgh).
|
|
||||||
private readonly byte[] CameraStatusBuffer = new byte[] { 0x0 };
|
|
||||||
|
|
||||||
public void UpdateFreecamStatus(bool enabled)
|
public void UpdateFreecamStatus(bool enabled)
|
||||||
{
|
{
|
||||||
if (CameraStatus == IntPtr.Zero) return;
|
if (CameraStatus == IntPtr.Zero) return;
|
||||||
|
|
||||||
CameraStatusBuffer[0] = enabled ? (byte)0x1 : (byte)0x0;
|
Marshal.WriteByte(CameraStatus, enabled ? (byte)0x1 : (byte)0x0);
|
||||||
Marshal.Copy(CameraStatusBuffer, 0, CameraStatus, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExecuteCameraCommand(Camera cam)
|
public void ExecuteCameraCommand(Camera cam)
|
||||||
{
|
{
|
||||||
var transform = cam.transform;
|
var transform = cam.transform;
|
||||||
|
|
||||||
|
// Check whether we should go back to the original position despite being active or not
|
||||||
|
this.ShouldMoveToOriginalPosition(transform);
|
||||||
|
|
||||||
if (!_isActive || position == null)
|
if (!_isActive || position == null)
|
||||||
{
|
{
|
||||||
position = new(transform.position, transform.rotation);
|
position = new(transform.position, transform.rotation);
|
||||||
@ -89,8 +97,23 @@ namespace CinematicUnityExplorer.Cinematic
|
|||||||
_isActive = true;
|
_isActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// At the EndSession, since we have a queue system, we have to have a special check when the session ends and
|
||||||
|
// then move the camera back to the original position, because the queue gets cleaned as soon as the session
|
||||||
|
// ends.
|
||||||
|
public void ShouldMoveToOriginalPosition(Transform transform)
|
||||||
|
{
|
||||||
|
if (!isValid) return;
|
||||||
|
if (endSessionPosition == null) return;
|
||||||
|
|
||||||
|
transform.position = endSessionPosition.Item1;
|
||||||
|
transform.rotation = endSessionPosition.Item2;
|
||||||
|
|
||||||
|
endSessionPosition = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void EndSession()
|
private void EndSession()
|
||||||
{
|
{
|
||||||
|
endSessionPosition = position;
|
||||||
position = null;
|
position = null;
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
|
|
||||||
@ -100,10 +123,11 @@ namespace CinematicUnityExplorer.Cinematic
|
|||||||
|
|
||||||
public UnityIGCSConnector()
|
public UnityIGCSConnector()
|
||||||
{
|
{
|
||||||
var lib = NativeMethods.LoadLibrary(@"UnityIGCSConnector.dll");
|
var libraryName = IntPtr.Size == 8 ? @"UnityIGCSConnector.dll" : @"UnityIGCSConnector.32.dll";
|
||||||
|
var lib = NativeMethods.LoadLibrary(libraryName);
|
||||||
if (lib == IntPtr.Zero)
|
if (lib == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
ExplorerCore.LogWarning("UnityIGCSConnector.dll was not found so IGCSDof will not be available");
|
ExplorerCore.LogWarning($"{libraryName} was not found so IGCSDof will not be available");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +145,8 @@ namespace CinematicUnityExplorer.Cinematic
|
|||||||
|
|
||||||
CameraStatus = initFunc((MoveCameraCallback)delegates[0], (SessionCallback)delegates[1], (SessionCallback)delegates[2]);
|
CameraStatus = initFunc((MoveCameraCallback)delegates[0], (SessionCallback)delegates[1], (SessionCallback)delegates[2]);
|
||||||
if (CameraStatus == IntPtr.Zero){
|
if (CameraStatus == IntPtr.Zero){
|
||||||
throw new InvalidDataException("IGCSDof returned an invalid pointer which means something went wrong");
|
// This is actually a InvalidDataException, but some games dont allow you to throw that.
|
||||||
|
throw new Exception("IGCSDof returned an invalid pointer which means something went wrong");
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid = true;
|
isValid = true;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using UnityExplorer.UI;
|
using UnityExplorer.UI;
|
||||||
|
using UnityExplorer.UI.Panels;
|
||||||
|
|
||||||
namespace UnityExplorer.Config
|
namespace UnityExplorer.Config
|
||||||
{
|
{
|
||||||
@ -29,8 +30,11 @@ namespace UnityExplorer.Config
|
|||||||
public static ConfigElement<string> CSConsole_Assembly_Blacklist;
|
public static ConfigElement<string> CSConsole_Assembly_Blacklist;
|
||||||
public static ConfigElement<string> Reflection_Signature_Blacklist;
|
public static ConfigElement<string> Reflection_Signature_Blacklist;
|
||||||
public static ConfigElement<bool> Reflection_Hide_NativeInfoPtrs;
|
public static ConfigElement<bool> Reflection_Hide_NativeInfoPtrs;
|
||||||
|
public static ConfigElement<bool> Auto_Scale_UI;
|
||||||
|
public static ConfigElement<bool> Reset_Camera_Transform;
|
||||||
|
public static ConfigElement<float> Arrow_Size;
|
||||||
|
public static ConfigElement<bool> Advanced_Freecam_Selection;
|
||||||
|
|
||||||
public static ConfigElement<bool> Default_Gameplay_Freecam;
|
|
||||||
public static ConfigElement<KeyCode> Pause;
|
public static ConfigElement<KeyCode> Pause;
|
||||||
public static ConfigElement<KeyCode> Frameskip;
|
public static ConfigElement<KeyCode> Frameskip;
|
||||||
public static ConfigElement<KeyCode> Screenshot;
|
public static ConfigElement<KeyCode> Screenshot;
|
||||||
@ -58,6 +62,10 @@ namespace UnityExplorer.Config
|
|||||||
public static ConfigElement<KeyCode> Reset_FOV;
|
public static ConfigElement<KeyCode> Reset_FOV;
|
||||||
public static ConfigElement<KeyCode> Toggle_Animations;
|
public static ConfigElement<KeyCode> Toggle_Animations;
|
||||||
|
|
||||||
|
public static ConfigElement<FreeCamPanel.FreeCameraType> Default_Freecam;
|
||||||
|
public static ConfigElement<string> Custom_Components_To_Disable;
|
||||||
|
public static ConfigElement<string> Preferred_Target_Camera;
|
||||||
|
|
||||||
// internal configs
|
// internal configs
|
||||||
internal static InternalConfigHandler InternalHandler { get; private set; }
|
internal static InternalConfigHandler InternalHandler { get; private set; }
|
||||||
internal static readonly Dictionary<UIManager.Panels, ConfigElement<string>> PanelSaveData = new();
|
internal static readonly Dictionary<UIManager.Panels, ConfigElement<string>> PanelSaveData = new();
|
||||||
@ -163,7 +171,7 @@ namespace UnityExplorer.Config
|
|||||||
"Optional keybind to begin a UI-mode Mouse Inspect.",
|
"Optional keybind to begin a UI-mode Mouse Inspect.",
|
||||||
KeyCode.None);
|
KeyCode.None);
|
||||||
|
|
||||||
CSConsole_Assembly_Blacklist = new("CSharp Console Assembly Blacklist",
|
CSConsole_Assembly_Blacklist = new("CSharp Console Assembly Blacklist",
|
||||||
"Use this to blacklist Assembly names from being referenced by the C# Console. Requires a Reset of the C# Console.\n" +
|
"Use this to blacklist Assembly names from being referenced by the C# Console. Requires a Reset of the C# Console.\n" +
|
||||||
"Separate each Assembly with a semicolon ';'." +
|
"Separate each Assembly with a semicolon ';'." +
|
||||||
"For example, to blacklist Assembly-CSharp, you would add 'Assembly-CSharp;'",
|
"For example, to blacklist Assembly-CSharp, you would add 'Assembly-CSharp;'",
|
||||||
@ -174,20 +182,32 @@ namespace UnityExplorer.Config
|
|||||||
"Seperate signatures with a semicolon ';'.\r\n" +
|
"Seperate signatures with a semicolon ';'.\r\n" +
|
||||||
"For example, to blacklist Camera.main, you would add 'UnityEngine.Camera.main;'",
|
"For example, to blacklist Camera.main, you would add 'UnityEngine.Camera.main;'",
|
||||||
"");
|
"");
|
||||||
|
|
||||||
Reflection_Hide_NativeInfoPtrs = new("Hide NativeMethodInfoPtr_s and NativeFieldInfoPtr_s",
|
Reflection_Hide_NativeInfoPtrs = new("Hide NativeMethodInfoPtr_s and NativeFieldInfoPtr_s",
|
||||||
"Use this to blacklist NativeMethodPtr_s and NativeFieldInfoPtrs_s from the class inspector, mainly to reduce clutter.\r\n" +
|
"Use this to blacklist NativeMethodPtr_s and NativeFieldInfoPtrs_s from the class inspector, mainly to reduce clutter.\r\n" +
|
||||||
"For example, this will hide 'Class.NativeFieldInfoPtr_value' for the field 'Class.value'.",
|
"For example, this will hide 'Class.NativeFieldInfoPtr_value' for the field 'Class.value'.",
|
||||||
false);
|
false);
|
||||||
|
|
||||||
Default_Gameplay_Freecam = new("Default Gameplay Freecam",
|
Auto_Scale_UI = new("Make the mod UI automatically scale with resolution",
|
||||||
"Turn this on if you want the default gameplay freecam toggle on the Freecam panel to be on on startup.",
|
"Especially useful when running games in high resolutions and you are having a hard time reading the mods menu (requires restart).",
|
||||||
|
true);
|
||||||
|
|
||||||
|
Reset_Camera_Transform = new("Reset Camera transform on freecam disable",
|
||||||
|
"Reset the camera position and rotation between freecam sessions, so the freecam always starts from the gameplay position and rotation.",
|
||||||
|
true);
|
||||||
|
|
||||||
|
Arrow_Size = new("Visualizers arrows size",
|
||||||
|
"Cam Paths nodes and Lights Manager lights visualizers' arrow size (must be positive) (needs visualizer toggled to reflect changes).",
|
||||||
|
1f);
|
||||||
|
|
||||||
|
Advanced_Freecam_Selection = new("Advanced Freecam Selection",
|
||||||
|
"Enables certain advanced settings on the Freecam panel, in case the user can't get the freecam to work properly (requires game reset).",
|
||||||
false);
|
false);
|
||||||
|
|
||||||
Pause = new("Pause",
|
Pause = new("Pause",
|
||||||
"Toggle the pause of the game.",
|
"Toggle the pause of the game.",
|
||||||
KeyCode.PageUp);
|
KeyCode.PageUp);
|
||||||
|
|
||||||
Frameskip = new("Frameskip",
|
Frameskip = new("Frameskip",
|
||||||
"Skip a frame when the game is paused.",
|
"Skip a frame when the game is paused.",
|
||||||
KeyCode.PageDown);
|
KeyCode.PageDown);
|
||||||
@ -241,7 +261,7 @@ namespace UnityExplorer.Config
|
|||||||
Left_1 = new("Left 1",
|
Left_1 = new("Left 1",
|
||||||
"Move the freecam to the left.",
|
"Move the freecam to the left.",
|
||||||
KeyCode.A);
|
KeyCode.A);
|
||||||
|
|
||||||
Left_2 = new("Left 2",
|
Left_2 = new("Left 2",
|
||||||
"Move the freecam to the left, alt key.",
|
"Move the freecam to the left, alt key.",
|
||||||
KeyCode.LeftArrow);
|
KeyCode.LeftArrow);
|
||||||
@ -289,6 +309,19 @@ namespace UnityExplorer.Config
|
|||||||
Toggle_Animations = new("Toggle NPC animations",
|
Toggle_Animations = new("Toggle NPC animations",
|
||||||
"Toggle NPC animations as selected in the Animator panel.",
|
"Toggle NPC animations as selected in the Animator panel.",
|
||||||
KeyCode.Keypad0);
|
KeyCode.Keypad0);
|
||||||
|
|
||||||
|
Default_Freecam = new("Default Freecam mode",
|
||||||
|
"Default type of freecam selected on startup (gets automatically updated with the last type of camera used).",
|
||||||
|
FreeCamPanel.FreeCameraType.New);
|
||||||
|
|
||||||
|
Custom_Components_To_Disable = new("Custom components to disable",
|
||||||
|
"List of custom components to disable when enabling the freecam (gets automatically updated when editing it from the freecam panel).",
|
||||||
|
"");
|
||||||
|
|
||||||
|
Preferred_Target_Camera = new("Preferred Target Camera",
|
||||||
|
"The camera that will be targeted by the freecam methods.\n" +
|
||||||
|
"Only used when Advanced Freecam Selection is enabled.",
|
||||||
|
"\\");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ namespace UnityExplorer
|
|||||||
public static class ExplorerCore
|
public static class ExplorerCore
|
||||||
{
|
{
|
||||||
public const string NAME = "CinematicUnityExplorer";
|
public const string NAME = "CinematicUnityExplorer";
|
||||||
public const string VERSION = "1.1.0";
|
public const string VERSION = "1.3.0";
|
||||||
public const string AUTHOR = "originalnicodr, Sinai, yukieiji";
|
public const string AUTHOR = "originalnicodr, Sinai, yukieiji";
|
||||||
public const string GUID = "com.originalnicodr.cinematicunityexplorer";
|
public const string GUID = "com.originalnicodr.cinematicunityexplorer";
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ namespace UnityExplorer
|
|||||||
|
|
||||||
KeypressListener.Setup();
|
KeypressListener.Setup();
|
||||||
|
|
||||||
MakeUEUIScale();
|
if (ConfigManager.Auto_Scale_UI.Value) MakeUEUIScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Update()
|
internal static void Update()
|
||||||
@ -205,6 +205,8 @@ namespace UnityExplorer
|
|||||||
foreach (CanvasScaler scaler in canvasScalers)
|
foreach (CanvasScaler scaler in canvasScalers)
|
||||||
{
|
{
|
||||||
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||||
|
scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
|
||||||
|
scaler.matchWidthOrHeight = 0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,6 +100,9 @@ namespace UnityExplorer
|
|||||||
|
|
||||||
public static void InspectWithFilters(object target, string filterInputField, UnityExplorer.Inspectors.MemberFilter memberFilterFlags = UnityExplorer.Inspectors.MemberFilter.All, bool staticReflection = false, CacheObjectBase parent = null)
|
public static void InspectWithFilters(object target, string filterInputField, UnityExplorer.Inspectors.MemberFilter memberFilterFlags = UnityExplorer.Inspectors.MemberFilter.All, bool staticReflection = false, CacheObjectBase parent = null)
|
||||||
{
|
{
|
||||||
|
if (TryFocusActiveInspector(target))
|
||||||
|
return;
|
||||||
|
|
||||||
ReflectionInspector inspector = CreateInspector<ReflectionInspector>(target, staticReflection, parent);
|
ReflectionInspector inspector = CreateInspector<ReflectionInspector>(target, staticReflection, parent);
|
||||||
inspector.filterInputField.Text = filterInputField;
|
inspector.filterInputField.Text = filterInputField;
|
||||||
//TODO: Update the member flags visually on the inspector.
|
//TODO: Update the member flags visually on the inspector.
|
||||||
|
@ -46,9 +46,10 @@ namespace UnityExplorer.Inspectors
|
|||||||
public override bool CanDragAndResize => false;
|
public override bool CanDragAndResize => false;
|
||||||
private Action<GameObject> inspectorAction = null;
|
private Action<GameObject> inspectorAction = null;
|
||||||
|
|
||||||
internal Text objNameLabel;
|
private Text inspectorLabelTitle;
|
||||||
internal Text objPathLabel;
|
private Text objNameLabel;
|
||||||
internal Text mousePosLabel;
|
private Text objPathLabel;
|
||||||
|
private Text mousePosLabel;
|
||||||
|
|
||||||
public MouseInspector(UIBase owner) : base(owner)
|
public MouseInspector(UIBase owner) : base(owner)
|
||||||
{
|
{
|
||||||
@ -125,6 +126,43 @@ namespace UnityExplorer.Inspectors
|
|||||||
return Inspecting;
|
return Inspecting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the title text in the inspector UI, if the inspector title label is assigned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The new title text to display in the inspector.</param>
|
||||||
|
internal void UpdateInspectorTitle(string title)
|
||||||
|
{
|
||||||
|
// Unity null check - if inspectorLabelTitle is assigned, update its text.
|
||||||
|
if (inspectorLabelTitle)
|
||||||
|
{
|
||||||
|
inspectorLabelTitle.text = title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the object name label in the inspector UI, if the label is assigned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The new object name to display.</param>
|
||||||
|
internal void UpdateObjectNameLabel(string name)
|
||||||
|
{
|
||||||
|
// Unity null check - if objNameLabel is assigned, update its text.
|
||||||
|
if (objNameLabel)
|
||||||
|
{
|
||||||
|
objNameLabel.text = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the object path label in the inspector UI, if the label is assigned.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The new object path to display.</param>
|
||||||
|
internal void UpdateObjectPathLabel(string path)
|
||||||
|
{
|
||||||
|
// Unity null check - if objPathLabel is assigned, update its text.
|
||||||
|
if (objPathLabel)
|
||||||
|
{
|
||||||
|
objPathLabel.text = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateInspect()
|
public void UpdateInspect()
|
||||||
{
|
{
|
||||||
if (IInputManager.GetKeyDown(KeyCode.Escape))
|
if (IInputManager.GetKeyDown(KeyCode.Escape))
|
||||||
@ -156,7 +194,7 @@ namespace UnityExplorer.Inspectors
|
|||||||
lastMousePos = mousePos;
|
lastMousePos = mousePos;
|
||||||
|
|
||||||
// use the raw mouse pos for the label
|
// use the raw mouse pos for the label
|
||||||
mousePosLabel.text = $"<color=grey>Mouse Position:</color> {mousePos.ToString()}";
|
mousePosLabel.text = $"<color=grey>Mouse Position:</color> {mousePos.x}, {mousePos.y}";
|
||||||
|
|
||||||
// constrain the mouse pos we use within certain bounds
|
// constrain the mouse pos we use within certain bounds
|
||||||
if (mousePos.x < 350)
|
if (mousePos.x < 350)
|
||||||
@ -196,11 +234,11 @@ namespace UnityExplorer.Inspectors
|
|||||||
|
|
||||||
// Title text
|
// Title text
|
||||||
|
|
||||||
Text title = UIFactory.CreateLabel(inspectContent,
|
inspectorLabelTitle = UIFactory.CreateLabel(inspectContent,
|
||||||
"InspectLabel",
|
"InspectLabel",
|
||||||
"<b>Mouse Inspector</b> (press <b>ESC</b> to cancel)",
|
"",
|
||||||
TextAnchor.MiddleCenter);
|
TextAnchor.MiddleCenter);
|
||||||
UIFactory.SetLayoutElement(title.gameObject, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(inspectorLabelTitle.gameObject, flexibleWidth: 9999);
|
||||||
|
|
||||||
mousePosLabel = UIFactory.CreateLabel(inspectContent, "MousePosLabel", "Mouse Position:", TextAnchor.MiddleCenter);
|
mousePosLabel = UIFactory.CreateLabel(inspectContent, "MousePosLabel", "Mouse Position:", TextAnchor.MiddleCenter);
|
||||||
|
|
||||||
|
@ -17,10 +17,13 @@ namespace UnityExplorer.Inspectors.MouseInspectors
|
|||||||
private static readonly List<CanvasGroup> wasDisabledCanvasGroups = new();
|
private static readonly List<CanvasGroup> wasDisabledCanvasGroups = new();
|
||||||
private static readonly List<GameObject> objectsAddedCastersTo = new();
|
private static readonly List<GameObject> objectsAddedCastersTo = new();
|
||||||
|
|
||||||
|
private const string DEFAULT_INSPECTOR_TITLE = "<b>UI Inspector</b> (press <b>ESC</b> to cancel)";
|
||||||
|
|
||||||
public override void OnBeginMouseInspect()
|
public override void OnBeginMouseInspect()
|
||||||
{
|
{
|
||||||
SetupUIRaycast();
|
SetupUIRaycast();
|
||||||
MouseInspector.Instance.objPathLabel.text = "";
|
MouseInspector.Instance.UpdateInspectorTitle(DEFAULT_INSPECTOR_TITLE);
|
||||||
|
MouseInspector.Instance.UpdateObjectPathLabel("");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ClearHitData()
|
public override void ClearHitData()
|
||||||
@ -71,9 +74,9 @@ namespace UnityExplorer.Inspectors.MouseInspectors
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currentHitObjects.Any())
|
if (currentHitObjects.Any())
|
||||||
MouseInspector.Instance.objNameLabel.text = $"Click to view UI Objects under mouse: {currentHitObjects.Count}";
|
MouseInspector.Instance.UpdateObjectNameLabel($"Click to view UI Objects under mouse: {currentHitObjects.Count}");
|
||||||
else
|
else
|
||||||
MouseInspector.Instance.objNameLabel.text = $"No UI objects under mouse.";
|
MouseInspector.Instance.UpdateObjectNameLabel( $"No UI objects under mouse.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetupUIRaycast()
|
private static void SetupUIRaycast()
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
namespace UnityExplorer.Inspectors.MouseInspectors
|
using UnityExplorer.UI.Panels;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Inspectors.MouseInspectors
|
||||||
{
|
{
|
||||||
public class WorldInspector : MouseInspectorBase
|
public class WorldInspector : MouseInspectorBase
|
||||||
{
|
{
|
||||||
@ -7,15 +9,26 @@
|
|||||||
|
|
||||||
public override void OnBeginMouseInspect()
|
public override void OnBeginMouseInspect()
|
||||||
{
|
{
|
||||||
MainCamera = Camera.main;
|
|
||||||
|
|
||||||
if (!MainCamera)
|
if (!EnsureMainCamera())
|
||||||
{
|
{
|
||||||
ExplorerCore.LogWarning("No MainCamera found! Cannot inspect world!");
|
ExplorerCore.LogWarning("No MainCamera found! Cannot inspect world!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assigns it as the MainCamera and updates the inspector title.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cam">The camera to assign.</param>
|
||||||
|
private static void AssignCamAndUpdateTitle(Camera cam)
|
||||||
|
{
|
||||||
|
MainCamera = cam;
|
||||||
|
MouseInspector.Instance.UpdateInspectorTitle(
|
||||||
|
$"<b>World Inspector ({MainCamera.name})</b> (press <b>ESC</b> to cancel)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public override void ClearHitData()
|
public override void ClearHitData()
|
||||||
{
|
{
|
||||||
lastHitObject = null;
|
lastHitObject = null;
|
||||||
@ -26,11 +39,52 @@
|
|||||||
inspectorAction(lastHitObject);
|
inspectorAction(lastHitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to ensure that MainCamera is assigned. If not then attempts to find it.
|
||||||
|
/// If no cameras are available, logs a warning and returns null.
|
||||||
|
/// </summary>
|
||||||
|
private static Camera EnsureMainCamera()
|
||||||
|
{
|
||||||
|
if (MainCamera){
|
||||||
|
// We still call this in case the last title was from the UIInspector
|
||||||
|
AssignCamAndUpdateTitle(MainCamera);
|
||||||
|
return MainCamera;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Camera.main){
|
||||||
|
AssignCamAndUpdateTitle(Camera.main);
|
||||||
|
return MainCamera;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExplorerCore.LogWarning("No Camera.main found, trying to find a camera named 'Main Camera' or 'MainCamera'...");
|
||||||
|
Camera namedCam = Camera.allCameras.FirstOrDefault(c => c.name is "Main Camera" or "MainCamera");
|
||||||
|
if (namedCam) {
|
||||||
|
AssignCamAndUpdateTitle(namedCam);
|
||||||
|
return MainCamera;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FreeCamPanel.inFreeCamMode && FreeCamPanel.GetFreecam()){
|
||||||
|
AssignCamAndUpdateTitle(FreeCamPanel.GetFreecam());
|
||||||
|
return MainCamera;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExplorerCore.LogWarning("No camera named found, using the first camera created...");
|
||||||
|
var fallbackCam = Camera.allCameras.FirstOrDefault();
|
||||||
|
if (fallbackCam) {
|
||||||
|
AssignCamAndUpdateTitle(fallbackCam);
|
||||||
|
return MainCamera;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach here, no cameras were found at all.
|
||||||
|
ExplorerCore.LogWarning("No valid cameras found!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public override void UpdateMouseInspect(Vector2 mousePos)
|
public override void UpdateMouseInspect(Vector2 mousePos)
|
||||||
{
|
{
|
||||||
if (!MainCamera)
|
// Attempt to ensure camera each time UpdateMouseInspect is called
|
||||||
MainCamera = Camera.main;
|
// in case something changed or wasn't set initially.
|
||||||
if (!MainCamera)
|
if (!EnsureMainCamera())
|
||||||
{
|
{
|
||||||
ExplorerCore.LogWarning("No Main Camera was found, unable to inspect world!");
|
ExplorerCore.LogWarning("No Main Camera was found, unable to inspect world!");
|
||||||
MouseInspector.Instance.StopInspect();
|
MouseInspector.Instance.StopInspect();
|
||||||
@ -51,8 +105,8 @@
|
|||||||
if (obj != lastHitObject)
|
if (obj != lastHitObject)
|
||||||
{
|
{
|
||||||
lastHitObject = obj;
|
lastHitObject = obj;
|
||||||
MouseInspector.Instance.objNameLabel.text = $"<b>Click to Inspect:</b> <color=cyan>{obj.name}</color>";
|
MouseInspector.Instance.UpdateObjectNameLabel($"<b>Click to Inspect:</b> <color=cyan>{obj.name}</color>");
|
||||||
MouseInspector.Instance.objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}";
|
MouseInspector.Instance.UpdateObjectPathLabel($"Path: {obj.transform.GetTransformPath(true)}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ using System.Text;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityExplorer.Config;
|
using UnityExplorer.Config;
|
||||||
using UnityExplorer.UI;
|
using UnityExplorer.UI;
|
||||||
|
using UnityExplorer.UI.Panels;
|
||||||
using UniverseLib;
|
using UniverseLib;
|
||||||
|
|
||||||
namespace UnityExplorer.Loader.Standalone
|
namespace UnityExplorer.Loader.Standalone
|
||||||
@ -26,6 +27,13 @@ namespace UnityExplorer.Loader.Standalone
|
|||||||
public bool Force_Unlock_Mouse = true;
|
public bool Force_Unlock_Mouse = true;
|
||||||
public KeyCode Force_Unlock_Toggle;
|
public KeyCode Force_Unlock_Toggle;
|
||||||
public bool Disable_EventSystem_Override;
|
public bool Disable_EventSystem_Override;
|
||||||
|
public bool Auto_Scale_UI;
|
||||||
|
public bool Reset_Camera_Transform;
|
||||||
|
public FreeCamPanel.FreeCameraType Default_Freecam;
|
||||||
|
public string Custom_Components_To_Disable;
|
||||||
|
public string Preferred_Target_Camera;
|
||||||
|
public float Arrow_Size = 1f;
|
||||||
|
public bool Advanced_Freecam_Selection;
|
||||||
|
|
||||||
public KeyCode Pause;
|
public KeyCode Pause;
|
||||||
public KeyCode Frameskip;
|
public KeyCode Frameskip;
|
||||||
@ -80,6 +88,14 @@ namespace UnityExplorer.Loader.Standalone
|
|||||||
ConfigManager.Force_Unlock_Mouse.Value = this.Force_Unlock_Mouse;
|
ConfigManager.Force_Unlock_Mouse.Value = this.Force_Unlock_Mouse;
|
||||||
ConfigManager.Force_Unlock_Toggle.Value = this.Force_Unlock_Toggle;
|
ConfigManager.Force_Unlock_Toggle.Value = this.Force_Unlock_Toggle;
|
||||||
ConfigManager.Disable_EventSystem_Override.Value = this.Disable_EventSystem_Override;
|
ConfigManager.Disable_EventSystem_Override.Value = this.Disable_EventSystem_Override;
|
||||||
|
ConfigManager.Auto_Scale_UI.Value = this.Auto_Scale_UI;
|
||||||
|
ConfigManager.Reset_Camera_Transform.Value = this.Reset_Camera_Transform;
|
||||||
|
ConfigManager.Default_Freecam.Value = this.Default_Freecam;
|
||||||
|
ConfigManager.Custom_Components_To_Disable.Value = this.Custom_Components_To_Disable;
|
||||||
|
ConfigManager.Preferred_Target_Camera.Value = this.Preferred_Target_Camera;
|
||||||
|
|
||||||
|
ConfigManager.Arrow_Size.Value = this.Arrow_Size;
|
||||||
|
ConfigManager.Advanced_Freecam_Selection.Value = this.Advanced_Freecam_Selection;
|
||||||
|
|
||||||
ConfigManager.Pause.Value = this.Pause;
|
ConfigManager.Pause.Value = this.Pause;
|
||||||
ConfigManager.Frameskip.Value = this.Frameskip;
|
ConfigManager.Frameskip.Value = this.Frameskip;
|
||||||
|
@ -163,7 +163,7 @@ namespace UnityExplorer.ObjectExplorer
|
|||||||
if (!string.IsNullOrEmpty(input))
|
if (!string.IsNullOrEmpty(input))
|
||||||
nameFilter = input;
|
nameFilter = input;
|
||||||
|
|
||||||
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
|
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy;
|
||||||
|
|
||||||
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
|
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
{
|
{
|
||||||
@ -181,7 +181,7 @@ namespace UnityExplorer.ObjectExplorer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return instances;
|
return instances.Distinct().ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,14 +23,15 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
public override string Name => "Animator";
|
public override string Name => "Animator";
|
||||||
public override UIManager.Panels PanelType => UIManager.Panels.AnimatorPanel;
|
public override UIManager.Panels PanelType => UIManager.Panels.AnimatorPanel;
|
||||||
public override int MinWidth => 900;
|
public override int MinWidth => 1300;
|
||||||
public override int MinHeight => 200;
|
public override int MinHeight => 200;
|
||||||
public override Vector2 DefaultAnchorMin => new(0.4f, 0.4f);
|
public override Vector2 DefaultAnchorMin => new(0.4f, 0.4f);
|
||||||
public override Vector2 DefaultAnchorMax => new(0.6f, 0.6f);
|
public override Vector2 DefaultAnchorMax => new(0.6f, 0.6f);
|
||||||
public override bool NavButtonWanted => true;
|
public override bool NavButtonWanted => true;
|
||||||
public override bool ShouldSaveActiveState => true;
|
public override bool ShouldSaveActiveState => true;
|
||||||
|
|
||||||
Toggle masterAnimatorToggle = new Toggle();
|
AnimatorPausePlayButton masterAnimatorPlayer;
|
||||||
|
Toggle masterMeshToggle;
|
||||||
|
|
||||||
private static ScrollPool<AnimatorCell> animatorScrollPool;
|
private static ScrollPool<AnimatorCell> animatorScrollPool;
|
||||||
internal List<AnimatorPlayer> animators = new List<AnimatorPlayer>();
|
internal List<AnimatorPlayer> animators = new List<AnimatorPlayer>();
|
||||||
@ -47,13 +48,11 @@ namespace UnityExplorer.UI.Panels
|
|||||||
animatorScrollPool.Initialize(this);
|
animatorScrollPool.Initialize(this);
|
||||||
DoneScrollPoolInit = true;
|
DoneScrollPoolInit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
animatorScrollPool.Refresh(true, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FindAllAnimators(){
|
private void FindAllAnimators(){
|
||||||
// Enable all animators on refresh
|
// Enable all animators on refresh
|
||||||
masterAnimatorToggle.isOn = true; // Will also trigger "MasterToggleAnimators(true)"
|
masterAnimatorPlayer.isOn = true; // Will also trigger "MasterToggleAnimators(true)"
|
||||||
|
|
||||||
Type searchType = ReflectionUtility.GetTypeByName("UnityEngine.Animator");
|
Type searchType = ReflectionUtility.GetTypeByName("UnityEngine.Animator");
|
||||||
searchType = searchType is Type type ? type : searchType.GetActualType();
|
searchType = searchType is Type type ? type : searchType.GetActualType();
|
||||||
@ -68,8 +67,20 @@ namespace UnityExplorer.UI.Panels
|
|||||||
{
|
{
|
||||||
if (animators[i].animator.wrappedObject != null){
|
if (animators[i].animator.wrappedObject != null){
|
||||||
int newAnimatorsIndex = newAnimators.FindIndex(a => a.animator.wrappedObject == animators[i].animator.wrappedObject);
|
int newAnimatorsIndex = newAnimators.FindIndex(a => a.animator.wrappedObject == animators[i].animator.wrappedObject);
|
||||||
if (newAnimatorsIndex != -1)
|
if (newAnimatorsIndex != -1) {
|
||||||
|
// If refreshing the animator gives us new animations, add them to the already existing ones.
|
||||||
|
// Might break stuff.
|
||||||
|
foreach (IAnimationClip animationClip in newAnimators[newAnimatorsIndex].animations) {
|
||||||
|
// TODO: Refactor AnimatorPlayer.animations from List<IAnimationClip> to HashSet<IAnimationClip> to avoid checking this
|
||||||
|
if (!animators[i].animations.Contains(animationClip))
|
||||||
|
animators[i].animations.Add(animationClip);
|
||||||
|
}
|
||||||
newAnimators[newAnimatorsIndex] = animators[i];
|
newAnimators[newAnimatorsIndex] = animators[i];
|
||||||
|
|
||||||
|
// Reset meshes
|
||||||
|
newAnimators[newAnimatorsIndex].SearchMeshes();
|
||||||
|
newAnimators[newAnimatorsIndex].MaybeResetBonesPanel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
animators = newAnimators;
|
animators = newAnimators;
|
||||||
@ -85,7 +96,9 @@ namespace UnityExplorer.UI.Panels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MasterToggleAnimators(bool enable){
|
public void MasterToggleAnimators(){
|
||||||
|
bool enable = masterAnimatorPlayer.isOn;
|
||||||
|
|
||||||
// Load animators for the first time if there are not any
|
// Load animators for the first time if there are not any
|
||||||
if (animators.Count == 0) FindAllAnimators();
|
if (animators.Count == 0) FindAllAnimators();
|
||||||
|
|
||||||
@ -99,8 +112,21 @@ namespace UnityExplorer.UI.Panels
|
|||||||
animatorScrollPool.Refresh(true, false);
|
animatorScrollPool.Refresh(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void MasterToggleMeshes(bool enable){
|
||||||
|
// Load animators for the first time if there are not any
|
||||||
|
if (animators.Count == 0) FindAllAnimators();
|
||||||
|
|
||||||
|
foreach (AnimatorPlayer animatorPlayer in animators){
|
||||||
|
if (!animatorPlayer.shouldIgnoreMasterToggle){
|
||||||
|
animatorPlayer.SetMeshesEnabled(enable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animatorScrollPool.Refresh(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
public void HotkeyToggleAnimators(){
|
public void HotkeyToggleAnimators(){
|
||||||
masterAnimatorToggle.isOn = !masterAnimatorToggle.isOn;
|
masterAnimatorPlayer.isOn = !masterAnimatorPlayer.isOn;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ~~~~~~~~ UI construction / callbacks ~~~~~~~~
|
// ~~~~~~~~ UI construction / callbacks ~~~~~~~~
|
||||||
@ -110,25 +136,38 @@ namespace UnityExplorer.UI.Panels
|
|||||||
GameObject firstGroup = UIFactory.CreateHorizontalGroup(ContentRoot, "MainOptions", false, false, true, true, 3,
|
GameObject firstGroup = UIFactory.CreateHorizontalGroup(ContentRoot, "MainOptions", false, false, true, true, 3,
|
||||||
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
|
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
|
||||||
UIFactory.SetLayoutElement(firstGroup, minHeight: 25, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(firstGroup, minHeight: 25, flexibleWidth: 9999);
|
||||||
//UIElements.Add(horiGroup);
|
|
||||||
|
|
||||||
ButtonRef updateAnimators = UIFactory.CreateButton(firstGroup, "RefreshAnimators", "Refresh Animators");
|
GameObject headerSpace1 = UIFactory.CreateUIObject("HeaderSpace1", firstGroup);
|
||||||
UIFactory.SetLayoutElement(updateAnimators.GameObject, minWidth: 150, minHeight: 25);
|
UIFactory.SetLayoutElement(headerSpace1, minWidth: 0, flexibleWidth: 0);
|
||||||
updateAnimators.OnClick += FindAllAnimators;
|
|
||||||
|
GameObject meshObj = UIFactory.CreateToggle(firstGroup, "Master Mesh Toggle", out masterMeshToggle, out Text masterMeshText);
|
||||||
|
UIFactory.SetLayoutElement(meshObj, minHeight: 25, minWidth: 230);
|
||||||
|
masterMeshToggle.onValueChanged.AddListener(value => MasterToggleMeshes(value));
|
||||||
|
masterMeshText.text = "Master Mesh Toggler";
|
||||||
|
|
||||||
|
masterAnimatorPlayer = new AnimatorPausePlayButton(firstGroup);
|
||||||
|
masterAnimatorPlayer.OnClick += MasterToggleAnimators;
|
||||||
|
|
||||||
|
Text masterAnimatorPlayerText = UIFactory.CreateLabel(firstGroup, "MasterAnimatorToggleLabel", "Master Animator Toggler", TextAnchor.MiddleRight);
|
||||||
|
UIFactory.SetLayoutElement(masterAnimatorPlayerText.gameObject, flexibleWidth: 0, minHeight: 25);
|
||||||
|
|
||||||
|
GameObject headerSpace2 = UIFactory.CreateUIObject("HeaderSpace2", firstGroup);
|
||||||
|
UIFactory.SetLayoutElement(headerSpace2, minWidth: 10, flexibleWidth: 0);
|
||||||
|
|
||||||
ButtonRef resetAnimators = UIFactory.CreateButton(firstGroup, "ResetAnimators", "Reset Animators");
|
ButtonRef resetAnimators = UIFactory.CreateButton(firstGroup, "ResetAnimators", "Reset Animators");
|
||||||
UIFactory.SetLayoutElement(resetAnimators.GameObject, minWidth: 150, minHeight: 25);
|
UIFactory.SetLayoutElement(resetAnimators.GameObject, minWidth: 150, minHeight: 25);
|
||||||
resetAnimators.OnClick += ResetAllAnimators;
|
resetAnimators.OnClick += ResetAllAnimators;
|
||||||
|
|
||||||
GameObject animatorObj = UIFactory.CreateToggle(firstGroup, $"Master Animation Toggle", out masterAnimatorToggle, out Text masterAnimatorText);
|
GameObject secondGroup = UIFactory.CreateHorizontalGroup(firstGroup, "HeaderRight", false, false, true, true, 3,
|
||||||
UIFactory.SetLayoutElement(animatorObj, minHeight: 25);
|
default, new Color(1, 1, 1, 0), TextAnchor.MiddleRight);
|
||||||
masterAnimatorToggle.isOn = true;
|
UIFactory.SetLayoutElement(secondGroup, minHeight: 25, flexibleWidth: 9999);
|
||||||
masterAnimatorToggle.onValueChanged.AddListener(value => MasterToggleAnimators(value));
|
|
||||||
masterAnimatorText.text = "Master Toggler";
|
|
||||||
|
|
||||||
GameObject secondGroup = UIFactory.CreateHorizontalGroup(ContentRoot, "MainOptions", false, false, true, true, 3,
|
ButtonRef updateAnimators = UIFactory.CreateButton(secondGroup, "RefreshAnimators", "Refresh Animators");
|
||||||
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
|
UIFactory.SetLayoutElement(updateAnimators.GameObject, minWidth: 150, minHeight: 25);
|
||||||
UIFactory.SetLayoutElement(firstGroup, minHeight: 25, flexibleWidth: 9999);
|
updateAnimators.OnClick += FindAllAnimators;
|
||||||
|
|
||||||
|
GameObject headerSpaceRight = UIFactory.CreateUIObject("HeaderSpaceRight", firstGroup);
|
||||||
|
UIFactory.SetLayoutElement(headerSpaceRight, minWidth: 25, flexibleWidth: 0);
|
||||||
|
|
||||||
animatorScrollPool = UIFactory.CreateScrollPool<AnimatorCell>(ContentRoot, "AnimatorsList", out GameObject scrollObj,
|
animatorScrollPool = UIFactory.CreateScrollPool<AnimatorCell>(ContentRoot, "AnimatorsList", out GameObject scrollObj,
|
||||||
out GameObject scrollContent, new Color(0.03f, 0.03f, 0.03f));
|
out GameObject scrollContent, new Color(0.03f, 0.03f, 0.03f));
|
||||||
@ -161,4 +200,71 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
public void OnCellBorrowed(AnimatorCell cell) { }
|
public void OnCellBorrowed(AnimatorCell cell) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ButtonRef wrapper to act as a toggle with clearer UI
|
||||||
|
public class AnimatorPausePlayButton
|
||||||
|
{
|
||||||
|
private ButtonRef innerButton;
|
||||||
|
public Button Component => innerButton.Component;
|
||||||
|
public GameObject GameObject => innerButton.GameObject;
|
||||||
|
private bool isPlaying;
|
||||||
|
|
||||||
|
public AnimatorPausePlayButton(GameObject ui, bool state = true)
|
||||||
|
{
|
||||||
|
innerButton = UIFactory.CreateButton(ui, "InnerAnimatorPlayButton", "");
|
||||||
|
UIFactory.SetLayoutElement(innerButton.GameObject, minHeight: 25, minWidth: 25);
|
||||||
|
innerButton.OnClick += SetToggleButtonState;
|
||||||
|
isPlaying = state;
|
||||||
|
UpdateButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnPlay(){
|
||||||
|
innerButton.ButtonText.text = "❚❚";
|
||||||
|
RuntimeHelper.SetColorBlockAuto(innerButton.Component, new(0.4f, 0.2f, 0.2f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnPause(){
|
||||||
|
innerButton.ButtonText.text = "►";
|
||||||
|
RuntimeHelper.SetColorBlockAuto(innerButton.Component, new(0.2f, 0.4f, 0.2f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateButton(){
|
||||||
|
if (isPlaying)
|
||||||
|
{
|
||||||
|
OnPlay();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnPause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetToggleButtonState()
|
||||||
|
{
|
||||||
|
isPlaying = !isPlaying;
|
||||||
|
UpdateButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool isOn
|
||||||
|
{
|
||||||
|
get {
|
||||||
|
return isPlaying;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (value != isPlaying){
|
||||||
|
SetToggleButtonState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action OnClick
|
||||||
|
{
|
||||||
|
get {
|
||||||
|
return innerButton.OnClick;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
innerButton.OnClick = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
385
src/UI/Panels/BonesPanel.cs
Normal file
385
src/UI/Panels/BonesPanel.cs
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
using UnityExplorer.Serializers;
|
||||||
|
using UniverseLib.UI;
|
||||||
|
using UniverseLib.UI.Models;
|
||||||
|
using UniverseLib.UI.Panels;
|
||||||
|
using UniverseLib.UI.Widgets.ScrollView;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Panels
|
||||||
|
{
|
||||||
|
public class BonesManager : PanelBase, ICellPoolDataSource<BonesCell>
|
||||||
|
{
|
||||||
|
public override string Name => $"Bones Manager";
|
||||||
|
public override int MinWidth => 1000;
|
||||||
|
public override int MinHeight => 800;
|
||||||
|
public override Vector2 DefaultAnchorMin => Vector2.zero;
|
||||||
|
public override Vector2 DefaultAnchorMax => Vector2.zero;
|
||||||
|
public Toggle turnOffAnimatorToggle;
|
||||||
|
|
||||||
|
private IAnimator animator;
|
||||||
|
private Text skeletonName;
|
||||||
|
private InputFieldRef saveLoadinputField;
|
||||||
|
private InputFieldRef searchBoneNameInput;
|
||||||
|
private List<Transform> bones = new List<Transform>();
|
||||||
|
private Dictionary<string, CachedBonesTransform> bonesOriginalState = new();
|
||||||
|
|
||||||
|
public List<BoneTree> boneTrees = new();
|
||||||
|
public ScrollPool<BonesCell> boneScrollPool;
|
||||||
|
public int ItemCount => boneTrees.Count;
|
||||||
|
private bool DoneScrollPoolInit;
|
||||||
|
|
||||||
|
public BonesManager(UIBase owner, List<Transform> bones, IAnimator animator) : base(owner)
|
||||||
|
{
|
||||||
|
this.bones = bones;
|
||||||
|
this.animator = animator;
|
||||||
|
skeletonName.text = $"Skeleton: {animator?.name} ";
|
||||||
|
BuildBoneTrees();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshBones(List<Transform> bones) {
|
||||||
|
this.bones = bones;
|
||||||
|
boneTrees.Clear();
|
||||||
|
BuildBoneTrees();
|
||||||
|
boneScrollPool.Refresh(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BuildBoneTrees(){
|
||||||
|
BoneTree root = new BoneTree(animator.wrappedObject.gameObject, bones);
|
||||||
|
if (root.obj != null){
|
||||||
|
root.AssignLevels();
|
||||||
|
boneTrees.Add(root);
|
||||||
|
} else {
|
||||||
|
foreach(BoneTree childTree in root.childTrees){
|
||||||
|
childTree.AssignLevels();
|
||||||
|
boneTrees.Add(childTree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CollapseBoneTrees(){
|
||||||
|
boneTrees.Clear();
|
||||||
|
BuildBoneTrees();
|
||||||
|
boneScrollPool.Refresh(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExpandBoneTrees(){
|
||||||
|
// We collapse before expanding to start from scratch and dont duplicate nodes
|
||||||
|
CollapseBoneTrees();
|
||||||
|
|
||||||
|
List<BoneTree> newBoneTrees = new();
|
||||||
|
|
||||||
|
foreach(BoneTree childTree in boneTrees){
|
||||||
|
newBoneTrees.AddRange(childTree.flatten());
|
||||||
|
}
|
||||||
|
|
||||||
|
boneTrees = newBoneTrees;
|
||||||
|
boneScrollPool.Refresh(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SearchBones(){
|
||||||
|
ExpandBoneTrees();
|
||||||
|
boneTrees = boneTrees.Where(tree => tree.obj.name.IndexOf(searchBoneNameInput.Component.text, 0, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
|
||||||
|
boneScrollPool.Refresh(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetActive(bool active)
|
||||||
|
{
|
||||||
|
base.SetActive(active);
|
||||||
|
|
||||||
|
if (active && !DoneScrollPoolInit)
|
||||||
|
{
|
||||||
|
LayoutRebuilder.ForceRebuildLayoutImmediate(this.Rect);
|
||||||
|
boneScrollPool.Initialize(this);
|
||||||
|
DoneScrollPoolInit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ConstructPanelContent()
|
||||||
|
{
|
||||||
|
GameObject bonesPanelHeader = UIFactory.CreateHorizontalGroup(ContentRoot, "BonesPanelHeader", false, false, true, true, 3,
|
||||||
|
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
|
||||||
|
UIFactory.SetLayoutElement(bonesPanelHeader, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
skeletonName = UIFactory.CreateLabel(bonesPanelHeader, $"SkeletonName", "");
|
||||||
|
UIFactory.SetLayoutElement(skeletonName.gameObject, minWidth: 100, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
skeletonName.fontSize = 16;
|
||||||
|
|
||||||
|
saveLoadinputField = UIFactory.CreateInputField(bonesPanelHeader, $"FileNameInput", "File name");
|
||||||
|
UIFactory.SetLayoutElement(saveLoadinputField.GameObject, minWidth: 380, minHeight: 25);
|
||||||
|
|
||||||
|
GameObject spacer1 = UIFactory.CreateUIObject("Spacer", bonesPanelHeader);
|
||||||
|
LayoutElement spaceLayout1 = UIFactory.SetLayoutElement(spacer1, minWidth: 20, flexibleWidth: 0);
|
||||||
|
|
||||||
|
ButtonRef savePose = UIFactory.CreateButton(bonesPanelHeader, "SavePoseButton", "Save pose");
|
||||||
|
UIFactory.SetLayoutElement(savePose.GameObject, minWidth: 100, minHeight: 25);
|
||||||
|
savePose.OnClick += SaveBones;
|
||||||
|
|
||||||
|
ButtonRef loadPose = UIFactory.CreateButton(bonesPanelHeader, "LoadPoseButton", "Load pose");
|
||||||
|
UIFactory.SetLayoutElement(loadPose.GameObject, minWidth: 100, minHeight: 25);
|
||||||
|
loadPose.OnClick += LoadBones;
|
||||||
|
|
||||||
|
GameObject header = UIFactory.CreateHorizontalGroup(ContentRoot, "BonesPanelHeader", false, false, true, true, 3,
|
||||||
|
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
|
||||||
|
UIFactory.SetLayoutElement(header, minHeight: 35, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
GameObject turnOffAnimatorToggleObj = UIFactory.CreateToggle(header, "Animator toggle", out turnOffAnimatorToggle, out Text turnOffAnimatorToggleText);
|
||||||
|
UIFactory.SetLayoutElement(turnOffAnimatorToggleObj, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
turnOffAnimatorToggle.onValueChanged.AddListener(OnTurnOffAnimatorToggle);
|
||||||
|
turnOffAnimatorToggleText.text = "Toggle animator (needs to be off to move bones)";
|
||||||
|
|
||||||
|
searchBoneNameInput = UIFactory.CreateInputField(header, $"SearchBoneInput", "Search bone name");
|
||||||
|
UIFactory.SetLayoutElement(searchBoneNameInput.GameObject, minWidth: 225, minHeight: 25);
|
||||||
|
|
||||||
|
ButtonRef searchButton = UIFactory.CreateButton(header, "SearchButton", "Search");
|
||||||
|
UIFactory.SetLayoutElement(searchButton.GameObject, minWidth: 75, minHeight: 25);
|
||||||
|
searchButton.OnClick += SearchBones;
|
||||||
|
|
||||||
|
ButtonRef resetButton = UIFactory.CreateButton(header, "ResetSearchButton", "Reset");
|
||||||
|
UIFactory.SetLayoutElement(resetButton.GameObject, minWidth: 75, minHeight: 25);
|
||||||
|
resetButton.OnClick += () => { CollapseBoneTrees(); searchBoneNameInput.Component.text = ""; };
|
||||||
|
|
||||||
|
GameObject spacer2 = UIFactory.CreateUIObject("Spacer", header);
|
||||||
|
LayoutElement spaceLayout2 = UIFactory.SetLayoutElement(spacer2, minWidth: 20, flexibleWidth: 0);
|
||||||
|
|
||||||
|
ButtonRef collapseAllButton = UIFactory.CreateButton(header, "CollapseAllButton", "Collapse all");
|
||||||
|
UIFactory.SetLayoutElement(collapseAllButton.GameObject, minWidth: 100, minHeight: 25);
|
||||||
|
collapseAllButton.OnClick += CollapseBoneTrees;
|
||||||
|
|
||||||
|
ButtonRef expandAllButton = UIFactory.CreateButton(header, "ExpandAllButton", "Expand all");
|
||||||
|
UIFactory.SetLayoutElement(expandAllButton.GameObject, minWidth: 100, minHeight: 25);
|
||||||
|
expandAllButton.OnClick += ExpandBoneTrees;
|
||||||
|
|
||||||
|
boneScrollPool = UIFactory.CreateScrollPool<BonesCell>(ContentRoot, "BonesList", out GameObject scrollObj,
|
||||||
|
out GameObject scrollContent, new Color(0.06f, 0.06f, 0.06f));
|
||||||
|
UIFactory.SetLayoutElement(scrollObj, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTurnOffAnimatorToggle(bool value)
|
||||||
|
{
|
||||||
|
if (value){
|
||||||
|
// Restore meshes manually in case some are not part of a skeleton and won't get restored automatically.
|
||||||
|
// Besides, this restores the scale, which the animator doesn't.
|
||||||
|
foreach (Transform bone in bones){
|
||||||
|
bonesOriginalState[bone.name].CopyToTransform(bone);
|
||||||
|
// We assume these were on before. If not we should save its state beforehand.
|
||||||
|
bone.gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bonesOriginalState.Clear();
|
||||||
|
foreach (Transform bone in bones){
|
||||||
|
bonesOriginalState[bone.name] = new CachedBonesTransform(bone.localPosition, bone.localEulerAngles, bone.localScale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
animator.enabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RestoreBoneState(string boneName)
|
||||||
|
{
|
||||||
|
foreach (Transform bone in bones){
|
||||||
|
if (bone.name == boneName){
|
||||||
|
bonesOriginalState[boneName].CopyToTransform(bone);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveBones(){
|
||||||
|
Dictionary<string, List<CachedBonesTransform>> bonesTreeCache = new();
|
||||||
|
// Get the list of bones based on the hierarchy order so we can deserialize it in the same order, instead of just using the bones list.
|
||||||
|
List<BoneTree> allBoneTrees = new();
|
||||||
|
foreach(BoneTree tree in boneTrees) {
|
||||||
|
allBoneTrees.AddRange(tree.flatten());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(BoneTree tree in allBoneTrees){
|
||||||
|
if (!bonesTreeCache.ContainsKey(tree.obj.name)){
|
||||||
|
bonesTreeCache.Add(tree.obj.name, new List<CachedBonesTransform>());
|
||||||
|
}
|
||||||
|
CachedBonesTransform entry = new CachedBonesTransform(tree.obj.transform.localPosition, tree.obj.transform.localEulerAngles, tree.obj.transform.localScale);
|
||||||
|
bonesTreeCache[tree.obj.name].Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
string filename = saveLoadinputField.Component.text;
|
||||||
|
if (filename.EndsWith(".cuepose") || filename.EndsWith(".CUEPOSE")) filename = filename.Substring(filename.Length-7);
|
||||||
|
if (string.IsNullOrEmpty(filename)) filename = $"{animator?.name}-{DateTime.Now.ToString("yyyy-M-d HH-mm-ss")}";
|
||||||
|
string posesPath = Path.Combine(ExplorerCore.ExplorerFolder, "Poses");
|
||||||
|
System.IO.Directory.CreateDirectory(posesPath);
|
||||||
|
|
||||||
|
// Serialize
|
||||||
|
string serializedData = BonesSerializer.Serialize(bonesTreeCache);
|
||||||
|
File.WriteAllText($"{posesPath}\\{filename}.cuepose", serializedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadBones(){
|
||||||
|
string filename = saveLoadinputField.Component.text;
|
||||||
|
if (filename.EndsWith(".cuepose") || filename.EndsWith(".CUEPOSE")) 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 posesPath = Path.Combine(ExplorerCore.ExplorerFolder, "Poses");
|
||||||
|
string xml;
|
||||||
|
try {
|
||||||
|
xml = File.ReadAllText($"{posesPath}\\{filename}.cuepose");
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
ExplorerCore.LogWarning(ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Dictionary<string, List<CachedBonesTransform>> deserializedDict;
|
||||||
|
try {
|
||||||
|
deserializedDict = BonesSerializer.Deserialize(xml);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
ExplorerCore.LogWarning(ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
turnOffAnimatorToggle.isOn = false;
|
||||||
|
foreach(Transform boneTransform in bones) {
|
||||||
|
List<CachedBonesTransform> cachedTransformList;
|
||||||
|
deserializedDict.TryGetValue(boneTransform.name, out cachedTransformList);
|
||||||
|
if (cachedTransformList != null && cachedTransformList.Count > 0){
|
||||||
|
CachedBonesTransform cachedTransform = cachedTransformList[0];
|
||||||
|
cachedTransform.CopyToTransform(boneTransform);
|
||||||
|
|
||||||
|
cachedTransformList.RemoveAt(0);
|
||||||
|
if (cachedTransformList.Count == 0) {
|
||||||
|
deserializedDict.Remove(boneTransform.name);
|
||||||
|
} else {
|
||||||
|
deserializedDict[boneTransform.name] = cachedTransformList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deserializedDict.Count > 0) {
|
||||||
|
ExplorerCore.LogWarning($"Couldn't apply every bone in the pose. Wrong entity?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCell(BonesCell cell, int index)
|
||||||
|
{
|
||||||
|
if (index >= boneTrees.Count)
|
||||||
|
{
|
||||||
|
cell.Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoneTree boneTree = boneTrees[index];
|
||||||
|
cell.SetBoneTree(boneTree, this);
|
||||||
|
cell.UpdateTransformControlValues(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCellBorrowed(BonesCell cell) {
|
||||||
|
cell.UpdateVectorSlider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
foreach(BonesCell boneCell in boneScrollPool.CellPool) {
|
||||||
|
boneCell.UpdateVectorSlider();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BoneTree
|
||||||
|
{
|
||||||
|
public GameObject obj;
|
||||||
|
public int level;
|
||||||
|
public List<BoneTree> childTrees = new();
|
||||||
|
|
||||||
|
public BoneTree(GameObject obj, List<Transform> bones){
|
||||||
|
// For some reason comparing GameObjects isn't working as intended in IL2CPP games, therefore we use their instance hash.
|
||||||
|
#if CPP
|
||||||
|
if (bones.Any(bone => bone.gameObject.GetInstanceID() == obj.GetInstanceID())) {
|
||||||
|
this.obj = obj;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (bones.Contains(obj.transform)) {
|
||||||
|
this.obj = obj;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (int i = 0; i < obj.transform.childCount; i++)
|
||||||
|
{
|
||||||
|
Transform child = obj.transform.GetChild(i);
|
||||||
|
if (child.gameObject.activeSelf){
|
||||||
|
childTrees.Add(new BoneTree(child.gameObject, bones));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Trim();
|
||||||
|
childTrees = childTrees.OrderBy(b => b.obj.name).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Trim(){
|
||||||
|
List<BoneTree> newList = new();
|
||||||
|
foreach (BoneTree childTree in childTrees)
|
||||||
|
{
|
||||||
|
if (childTree.obj == null){
|
||||||
|
newList.AddRange(childTree.childTrees);
|
||||||
|
} else {
|
||||||
|
newList.Add(childTree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.childTrees = newList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: refactor BoneTree so we don't need to call this after creating an instance.
|
||||||
|
public void AssignLevels(){
|
||||||
|
AssignLevel(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AssignLevel(int distanceFromRoot){
|
||||||
|
level = distanceFromRoot;
|
||||||
|
foreach (BoneTree childTree in childTrees)
|
||||||
|
{
|
||||||
|
childTree.AssignLevel(distanceFromRoot + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString(){
|
||||||
|
string return_string = "";
|
||||||
|
if (obj != null){
|
||||||
|
return_string = $"{obj.name} lvl: {level} - ";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (BoneTree childTree in childTrees)
|
||||||
|
{
|
||||||
|
return_string = return_string + childTree.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return return_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GameObject> getGameObjects(){
|
||||||
|
List<GameObject> return_list = new();
|
||||||
|
if (obj != null){
|
||||||
|
return_list.Add(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (BoneTree childTree in childTrees)
|
||||||
|
{
|
||||||
|
return_list.AddRange(childTree.getGameObjects());
|
||||||
|
}
|
||||||
|
|
||||||
|
return return_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BoneTree> flatten(){
|
||||||
|
List<BoneTree> return_list = new();
|
||||||
|
if (obj != null){
|
||||||
|
return_list.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (BoneTree childTree in childTrees)
|
||||||
|
{
|
||||||
|
return_list.AddRange(childTree.flatten());
|
||||||
|
}
|
||||||
|
|
||||||
|
return return_list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
using UniverseLib.Input;
|
using UnityEngine.SceneManagement;
|
||||||
|
using UnityExplorer.Serializers;
|
||||||
|
using UniverseLib.Input;
|
||||||
using UniverseLib.UI;
|
using UniverseLib.UI;
|
||||||
using UniverseLib.UI.Models;
|
using UniverseLib.UI.Models;
|
||||||
#if UNHOLLOWER
|
#if UNHOLLOWER
|
||||||
@ -9,11 +11,6 @@ using Il2CppInterop.Runtime.Injection;
|
|||||||
#endif
|
#endif
|
||||||
using UniverseLib.UI.Widgets.ScrollView;
|
using UniverseLib.UI.Widgets.ScrollView;
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Panels
|
namespace UnityExplorer.UI.Panels
|
||||||
{
|
{
|
||||||
public class CamPaths : UEPanel, ICellPoolDataSource<CamPathNodeCell>
|
public class CamPaths : UEPanel, ICellPoolDataSource<CamPathNodeCell>
|
||||||
@ -51,6 +48,8 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
Toggle visualizePathToggle;
|
Toggle visualizePathToggle;
|
||||||
public GameObject pathVisualizer;
|
public GameObject pathVisualizer;
|
||||||
|
Toggle closedLoopToggle;
|
||||||
|
InputFieldRef TimeInput;
|
||||||
|
|
||||||
bool unpauseOnPlay;
|
bool unpauseOnPlay;
|
||||||
bool waitBeforePlay;
|
bool waitBeforePlay;
|
||||||
@ -65,6 +64,9 @@ namespace UnityExplorer.UI.Panels
|
|||||||
Slider tensionCatmullRomSlider;
|
Slider tensionCatmullRomSlider;
|
||||||
float tensionCatmullRomValue = 0;
|
float tensionCatmullRomValue = 0;
|
||||||
|
|
||||||
|
private InputFieldRef saveLoadInputField;
|
||||||
|
private Toggle loadPathOnCamToggle;
|
||||||
|
|
||||||
public ScrollPool<CamPathNodeCell> nodesScrollPool;
|
public ScrollPool<CamPathNodeCell> nodesScrollPool;
|
||||||
public int ItemCount => controlPoints.Count;
|
public int ItemCount => controlPoints.Count;
|
||||||
private static bool DoneScrollPoolInit;
|
private static bool DoneScrollPoolInit;
|
||||||
@ -107,23 +109,22 @@ namespace UnityExplorer.UI.Panels
|
|||||||
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
|
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
|
||||||
UIFactory.SetLayoutElement(horiGroup, minHeight: 25, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(horiGroup, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
|
||||||
ButtonRef startButton = UIFactory.CreateButton(horiGroup, "Start", "▶");
|
ButtonRef startButton = UIFactory.CreateButton(horiGroup, "Start", "►", new Color(0.2f, 0.4f, 0.2f));
|
||||||
UIFactory.SetLayoutElement(startButton.GameObject, minWidth: 50, minHeight: 25);
|
UIFactory.SetLayoutElement(startButton.GameObject, minWidth: 50, minHeight: 25);
|
||||||
startButton.ButtonText.fontSize = 20;
|
|
||||||
startButton.OnClick += StartButton_OnClick;
|
startButton.OnClick += StartButton_OnClick;
|
||||||
|
|
||||||
ButtonRef pauseContinueButton = UIFactory.CreateButton(horiGroup, "Pause/Continue", "❚❚/▶");
|
ButtonRef pauseContinueButton = UIFactory.CreateButton(horiGroup, "Pause/Continue", "❚❚/►");
|
||||||
UIFactory.SetLayoutElement(pauseContinueButton.GameObject, minWidth: 50, minHeight: 25);
|
UIFactory.SetLayoutElement(pauseContinueButton.GameObject, minWidth: 50, minHeight: 25);
|
||||||
pauseContinueButton.ButtonText.fontSize = 20;
|
|
||||||
pauseContinueButton.OnClick += TogglePause_OnClick;
|
pauseContinueButton.OnClick += TogglePause_OnClick;
|
||||||
|
|
||||||
ButtonRef stopButton = UIFactory.CreateButton(horiGroup, "Stop", "■");
|
ButtonRef stopButton = UIFactory.CreateButton(horiGroup, "Stop", "■", new Color(0.4f, 0.2f, 0.2f));
|
||||||
UIFactory.SetLayoutElement(stopButton.GameObject, minWidth: 50, minHeight: 25);
|
UIFactory.SetLayoutElement(stopButton.GameObject, minWidth: 50, minHeight: 25);
|
||||||
stopButton.ButtonText.fontSize = 20;
|
stopButton.ButtonText.fontSize = 20;
|
||||||
stopButton.OnClick += Stop_OnClick;
|
stopButton.OnClick += Stop_OnClick;
|
||||||
|
|
||||||
ButtonRef AddNode = UIFactory.CreateButton(horiGroup, "AddCamNode", "+");
|
ButtonRef AddNode = UIFactory.CreateButton(horiGroup, "AddCamNode", "+");
|
||||||
UIFactory.SetLayoutElement(AddNode.GameObject, minWidth: 50, minHeight: 25);
|
UIFactory.SetLayoutElement(AddNode.GameObject, minWidth: 50, minHeight: 25);
|
||||||
|
AddNode.ButtonText.fontSize = 20;
|
||||||
AddNode.OnClick += AddNode_OnClick;
|
AddNode.OnClick += AddNode_OnClick;
|
||||||
|
|
||||||
ButtonRef DeletePath = UIFactory.CreateButton(horiGroup, "DeletePath", "Clear");
|
ButtonRef DeletePath = UIFactory.CreateButton(horiGroup, "DeletePath", "Clear");
|
||||||
@ -134,14 +135,13 @@ namespace UnityExplorer.UI.Panels
|
|||||||
MaybeRedrawPath();
|
MaybeRedrawPath();
|
||||||
};
|
};
|
||||||
|
|
||||||
Toggle closedLoopToggle = new Toggle();
|
closedLoopToggle = new Toggle();
|
||||||
GameObject toggleClosedLoopObj = UIFactory.CreateToggle(horiGroup, "Close path in a loop", out closedLoopToggle, out Text toggleClosedLoopText);
|
GameObject toggleClosedLoopObj = UIFactory.CreateToggle(horiGroup, "Close path in a loop", out closedLoopToggle, out Text toggleClosedLoopText);
|
||||||
UIFactory.SetLayoutElement(toggleClosedLoopObj, minHeight: 25, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(toggleClosedLoopObj, minHeight: 25, flexibleWidth: 9999);
|
||||||
closedLoopToggle.isOn = false;
|
closedLoopToggle.isOn = false;
|
||||||
closedLoopToggle.onValueChanged.AddListener((isClosedLoop) => {closedLoop = isClosedLoop; MaybeRedrawPath(); EventSystemHelper.SetSelectedGameObject(null);});
|
closedLoopToggle.onValueChanged.AddListener((isClosedLoop) => {closedLoop = isClosedLoop; MaybeRedrawPath(); EventSystemHelper.SetSelectedGameObject(null);});
|
||||||
toggleClosedLoopText.text = "Close path in a loop";
|
toggleClosedLoopText.text = "Close path in a loop";
|
||||||
|
|
||||||
InputFieldRef TimeInput = null;
|
|
||||||
AddInputField("Time", "Path time (in seconds at 60fps):", $"Default: {time}", out TimeInput, Time_OnEndEdit, 50, horiGroup);
|
AddInputField("Time", "Path time (in seconds at 60fps):", $"Default: {time}", out TimeInput, Time_OnEndEdit, 50, horiGroup);
|
||||||
TimeInput.Text = time.ToString();
|
TimeInput.Text = time.ToString();
|
||||||
|
|
||||||
@ -209,6 +209,29 @@ namespace UnityExplorer.UI.Panels
|
|||||||
MaybeRedrawPath();
|
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,
|
nodesScrollPool = UIFactory.CreateScrollPool<CamPathNodeCell>(ContentRoot, "NodeList", out GameObject scrollObj,
|
||||||
out GameObject scrollContent, new Color(0.03f, 0.03f, 0.03f));
|
out GameObject scrollContent, new Color(0.03f, 0.03f, 0.03f));
|
||||||
UIFactory.SetLayoutElement(scrollObj, flexibleWidth: 9999, flexibleHeight: 9999);
|
UIFactory.SetLayoutElement(scrollObj, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
@ -216,8 +239,6 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
void AlphaCatmullRom_OnEndEdit(string input)
|
void AlphaCatmullRom_OnEndEdit(string input)
|
||||||
{
|
{
|
||||||
EventSystemHelper.SetSelectedGameObject(null);
|
|
||||||
|
|
||||||
if (!ParseUtility.TryParse(input, out float parsed, out Exception parseEx))
|
if (!ParseUtility.TryParse(input, out float parsed, out Exception parseEx))
|
||||||
{
|
{
|
||||||
ExplorerCore.LogWarning($"Could not parse value: {parseEx.ReflectionExToString()}");
|
ExplorerCore.LogWarning($"Could not parse value: {parseEx.ReflectionExToString()}");
|
||||||
@ -229,12 +250,11 @@ namespace UnityExplorer.UI.Panels
|
|||||||
alphaCatmullRomSlider.value = alphaCatmullRomValue;
|
alphaCatmullRomSlider.value = alphaCatmullRomValue;
|
||||||
|
|
||||||
MaybeRedrawPath();
|
MaybeRedrawPath();
|
||||||
|
EventSystemHelper.SetSelectedGameObject(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TensionCatmullRom_OnEndEdit(string input)
|
void TensionCatmullRom_OnEndEdit(string input)
|
||||||
{
|
{
|
||||||
EventSystemHelper.SetSelectedGameObject(null);
|
|
||||||
|
|
||||||
if (!ParseUtility.TryParse(input, out float parsed, out Exception parseEx))
|
if (!ParseUtility.TryParse(input, out float parsed, out Exception parseEx))
|
||||||
{
|
{
|
||||||
ExplorerCore.LogWarning($"Could not parse value: {parseEx.ReflectionExToString()}");
|
ExplorerCore.LogWarning($"Could not parse value: {parseEx.ReflectionExToString()}");
|
||||||
@ -246,6 +266,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
tensionCatmullRomSlider.value = tensionCatmullRomValue;
|
tensionCatmullRomSlider.value = tensionCatmullRomValue;
|
||||||
|
|
||||||
MaybeRedrawPath();
|
MaybeRedrawPath();
|
||||||
|
EventSystemHelper.SetSelectedGameObject(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleVisualizePath(bool enable){
|
private void ToggleVisualizePath(bool enable){
|
||||||
@ -365,7 +386,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
CatmullRom.CatmullRomPoint point = new CatmullRom.CatmullRomPoint(
|
CatmullRom.CatmullRomPoint point = new CatmullRom.CatmullRomPoint(
|
||||||
FreeCamPanel.GetCameraPosition(),
|
FreeCamPanel.GetCameraPosition(),
|
||||||
FreeCamPanel.GetCameraRotation(),
|
FreeCamPanel.GetCameraRotation(),
|
||||||
FreeCamPanel.ourCamera.fieldOfView
|
FreeCamPanel.GetFreecam().fieldOfView
|
||||||
);
|
);
|
||||||
|
|
||||||
controlPoints.Add(point);
|
controlPoints.Add(point);
|
||||||
@ -377,7 +398,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
{
|
{
|
||||||
EventSystemHelper.SetSelectedGameObject(null);
|
EventSystemHelper.SetSelectedGameObject(null);
|
||||||
|
|
||||||
if (!ParseUtility.TryParse(input, out int parsed, out Exception parseEx))
|
if (!ParseUtility.TryParse(input, out float parsed, out Exception parseEx))
|
||||||
{
|
{
|
||||||
ExplorerCore.LogWarning($"Could not parse value: {parseEx.ReflectionExToString()}");
|
ExplorerCore.LogWarning($"Could not parse value: {parseEx.ReflectionExToString()}");
|
||||||
return;
|
return;
|
||||||
@ -494,6 +515,92 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
GetCameraPathsManager().CalculateLookahead();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.StartStopButton_OnClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
controlPoints.Clear();
|
||||||
|
// The first point will be the camera position, and we adapt the following points from there
|
||||||
|
AddNode_OnClick();
|
||||||
|
|
||||||
|
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
|
public class CamPointsUpdater : MonoBehaviour
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -48,6 +48,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
Toggle HighLodToggle;
|
Toggle HighLodToggle;
|
||||||
object qualitySettings = null;
|
object qualitySettings = null;
|
||||||
PropertyInfo lodBias = null;
|
PropertyInfo lodBias = null;
|
||||||
|
float _defaultLodBias = 1;
|
||||||
|
|
||||||
// We save the current properties of the Renderers and Lights to restore them after editing them with togglers
|
// We save the current properties of the Renderers and Lights to restore them after editing them with togglers
|
||||||
internal Dictionary<Renderer, bool> renderersReceiveShadows = new();
|
internal Dictionary<Renderer, bool> renderersReceiveShadows = new();
|
||||||
@ -122,9 +123,9 @@ namespace UnityExplorer.UI.Panels
|
|||||||
if (lodBias == null){
|
if (lodBias == null){
|
||||||
Type qualitySettingsType = qualitySettings is Type type ? type : qualitySettings.GetActualType();
|
Type qualitySettingsType = qualitySettings is Type type ? type : qualitySettings.GetActualType();
|
||||||
lodBias = qualitySettingsType.GetProperty("lodBias");
|
lodBias = qualitySettingsType.GetProperty("lodBias");
|
||||||
|
_defaultLodBias = (float)lodBias.GetValue(null, null);
|
||||||
}
|
}
|
||||||
|
lodBias.SetValue(null, areHighLodsOn ? 10000 : _defaultLodBias, null);
|
||||||
lodBias.SetValue(null, areHighLodsOn ? 10000 : 1, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleAllMeshesCastAndRecieveShadows(bool enable){
|
private void ToggleAllMeshesCastAndRecieveShadows(bool enable){
|
||||||
|
@ -183,7 +183,7 @@ namespace UnityExplorer.CatmullRom
|
|||||||
Vector3 camPos = FreeCamPanel.GetCameraPosition();
|
Vector3 camPos = FreeCamPanel.GetCameraPosition();
|
||||||
Quaternion camRot = FreeCamPanel.GetCameraRotation();
|
Quaternion camRot = FreeCamPanel.GetCameraRotation();
|
||||||
|
|
||||||
return new CatmullRomPoint(camPos, camRot, FreeCamPanel.ourCamera.fieldOfView);
|
return new CatmullRomPoint(camPos, camRot, FreeCamPanel.GetFreecam().fieldOfView);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AdvanceMover(float dt){
|
private void AdvanceMover(float dt){
|
||||||
@ -238,14 +238,14 @@ namespace UnityExplorer.CatmullRom
|
|||||||
|
|
||||||
FreeCamPanel.SetCameraRotation(CatmullRomPoint.Vector4ToQuaternion(newRot));
|
FreeCamPanel.SetCameraRotation(CatmullRomPoint.Vector4ToQuaternion(newRot));
|
||||||
FreeCamPanel.SetCameraPosition(FreeCamPanel.GetCameraPosition() + direction.position);
|
FreeCamPanel.SetCameraPosition(FreeCamPanel.GetCameraPosition() + direction.position);
|
||||||
FreeCamPanel.ourCamera.fieldOfView += direction.fov * actual / room;
|
FreeCamPanel.SetFOV(FreeCamPanel.GetFreecam().fieldOfView + direction.fov * actual / room);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MoveCameraToPoint(CatmullRomPoint newPoint){
|
public void MoveCameraToPoint(CatmullRomPoint newPoint){
|
||||||
FreeCamPanel.SetCameraRotation(newPoint.rotation);
|
FreeCamPanel.SetCameraRotation(newPoint.rotation);
|
||||||
FreeCamPanel.SetCameraPosition(newPoint.position);
|
FreeCamPanel.SetCameraPosition(newPoint.position);
|
||||||
|
|
||||||
FreeCamPanel.ourCamera.fieldOfView = newPoint.fov;
|
FreeCamPanel.SetFOV(newPoint.fov);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CatmullRomPoint GetPointFromPath(float d)
|
private CatmullRomPoint GetPointFromPath(float d)
|
||||||
|
@ -19,6 +19,7 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
|
|
||||||
ButtonRef ViewParentButton;
|
ButtonRef ViewParentButton;
|
||||||
ButtonRef FollowObjectButton;
|
ButtonRef FollowObjectButton;
|
||||||
|
ButtonRef LookAtObjectButton;
|
||||||
InputFieldRef PathInput;
|
InputFieldRef PathInput;
|
||||||
|
|
||||||
InputFieldRef NameInput;
|
InputFieldRef NameInput;
|
||||||
@ -295,6 +296,11 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
UIFactory.SetLayoutElement(FollowObjectButton.Component.gameObject, minHeight: 25, minWidth: 100);
|
UIFactory.SetLayoutElement(FollowObjectButton.Component.gameObject, minHeight: 25, minWidth: 100);
|
||||||
FollowObjectButton.OnClick += () => FreeCamPanel.FollowObjectAction(this.Target.gameObject);
|
FollowObjectButton.OnClick += () => FreeCamPanel.FollowObjectAction(this.Target.gameObject);
|
||||||
|
|
||||||
|
LookAtObjectButton = UIFactory.CreateButton(firstRow, "LookAtObjectButton", "Look at object with Freecam", new Color(0.2f, 0.2f, 0.2f));
|
||||||
|
LookAtObjectButton.ButtonText.fontSize = 13;
|
||||||
|
UIFactory.SetLayoutElement(LookAtObjectButton.Component.gameObject, minHeight: 25, minWidth: 100);
|
||||||
|
LookAtObjectButton.OnClick += () => FreeCamPanel.LookAtObjectAction(this.Target.gameObject);
|
||||||
|
|
||||||
this.PathInput = UIFactory.CreateInputField(firstRow, "PathInput", "...");
|
this.PathInput = UIFactory.CreateInputField(firstRow, "PathInput", "...");
|
||||||
PathInput.Component.textComponent.color = Color.grey;
|
PathInput.Component.textComponent.color = Color.grey;
|
||||||
PathInput.Component.textComponent.fontSize = 14;
|
PathInput.Component.textComponent.fontSize = 14;
|
||||||
@ -324,6 +330,19 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
NameInput.Component.textComponent.fontSize = 15;
|
NameInput.Component.textComponent.fontSize = 15;
|
||||||
NameInput.Component.GetOnEndEdit().AddListener((string val) => { OnNameEndEdit(val); });
|
NameInput.Component.GetOnEndEdit().AddListener((string val) => { OnNameEndEdit(val); });
|
||||||
|
|
||||||
|
ButtonRef MoveToCameraButton = UIFactory.CreateButton(titleRow, "MoveToCameraButton", "Move to Camera", new Color(0.2f, 0.2f, 0.2f));
|
||||||
|
MoveToCameraButton.ButtonText.fontSize = 13;
|
||||||
|
UIFactory.SetLayoutElement(MoveToCameraButton.Component.gameObject, minHeight: 25, minWidth: 120);
|
||||||
|
MoveToCameraButton.OnClick += () => {
|
||||||
|
if (FreeCamPanel.inFreeCamMode) {
|
||||||
|
Transform freecamTransform = FreeCamPanel.GetFreecam().transform;
|
||||||
|
this.Target.gameObject.transform.position = freecamTransform.position;
|
||||||
|
this.Target.gameObject.transform.rotation = freecamTransform.rotation;
|
||||||
|
} else {
|
||||||
|
ExplorerCore.LogWarning("Enable freecam before trying to move an object to the camera!");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// second row (toggles, instanceID, tag, buttons)
|
// second row (toggles, instanceID, tag, buttons)
|
||||||
|
|
||||||
GameObject secondRow = UIFactory.CreateUIObject("ParentRow", topInfoHolder);
|
GameObject secondRow = UIFactory.CreateUIObject("ParentRow", topInfoHolder);
|
||||||
|
@ -74,13 +74,26 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
{
|
{
|
||||||
if (float.TryParse(val, out float f))
|
if (float.TryParse(val, out float f))
|
||||||
{
|
{
|
||||||
if (f < slider.minValue || f > slider.maxValue){
|
if (f < slider.minValue){
|
||||||
ExplorerCore.LogWarning("Error, new time scale value outside of margins.");
|
ExplorerCore.LogWarning("Error, new time scale value outside of margins.");
|
||||||
timeInput.Text = desiredTime.ToString("0.00");
|
timeInput.Text = desiredTime.ToString("0.00");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
slider.value = f; // Will update the desiredTime value and extra things
|
// Allow registering timescale values above the slider max value
|
||||||
|
if (f >= slider.maxValue) {
|
||||||
|
// Move the slider to the right
|
||||||
|
slider.value = slider.maxValue;
|
||||||
|
|
||||||
|
desiredTime = f;
|
||||||
|
pause = false;
|
||||||
|
previousDesiredTime = desiredTime;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
slider.value = f; // Will update the desiredTime value and extra things
|
||||||
|
}
|
||||||
|
|
||||||
|
timeInput.Text = f.ToString("0.00");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user