Compare commits

..

No commits in common. "452faae79acae109f33d10310655808c67eb4692" and "6031db6620e4f1732064d85f49da20e46b5d387a" have entirely different histories.

17 changed files with 159 additions and 275 deletions

View file

@ -164,18 +164,18 @@ namespace gay.lilyy.aaccore
// Start overall timer // Start overall timer
var overallStopwatch = Stopwatch.StartNew(); var overallStopwatch = Stopwatch.StartNew();
AACLogger.LogInfo("Starting AAC generation..."); V5AACLogger.LogInfo("Starting Lillith V5 AAC generation...");
var root = ComponentHelper.GetComponentInChildrenWithError<AACRoot>(ctx.AvatarRootObject); var root = ComponentHelper.GetComponentInChildrenWithError<AACRoot>(ctx.AvatarRootObject);
if (root == null) if (root == null)
{ {
AACLogger.LogInfo("No AACRoot component found. Skipping."); V5AACLogger.LogInfo("No LillithV5AACRoot component found. Skipping.");
return; return;
} }
// Skip if the root GameObject or the component itself is disabled // Skip if the root GameObject or the component itself is disabled
if (!root.enabled || !root.gameObject.activeInHierarchy) if (!root.enabled || !root.gameObject.activeInHierarchy)
{ {
AACLogger.LogInfo($"AACRoot on GameObject '{root.name}' is disabled or inactive. Skipping."); V5AACLogger.LogInfo($"LillithV5AACRoot on GameObject '{root.name}' is disabled or inactive. Skipping.");
return; return;
} }
@ -193,7 +193,7 @@ namespace gay.lilyy.aaccore
// Time AAC initialization // Time AAC initialization
var initStopwatch = Stopwatch.StartNew(); var initStopwatch = Stopwatch.StartNew();
AACLogger.LogInfo("Initializing Animator As Code..."); V5AACLogger.LogInfo("Initializing Animator As Code...");
assets.aac = AacV1.Create(new AacConfiguration assets.aac = AacV1.Create(new AacConfiguration
{ {
SystemName = SystemName, SystemName = SystemName,
@ -209,11 +209,11 @@ namespace gay.lilyy.aaccore
assets.fx = assets.aac.NewAnimatorController(); assets.fx = assets.aac.NewAnimatorController();
initStopwatch.Stop(); initStopwatch.Stop();
AACLogger.LogInfo($"AAC initialization completed in {initStopwatch.ElapsedMilliseconds}ms"); V5AACLogger.LogInfo($"AAC initialization completed in {initStopwatch.ElapsedMilliseconds}ms");
// Process layer groups with individual timing // Process layer groups with individual timing
// Filter out the template LayerGroup (by SystemName) // Filter out the template LayerGroup (by SystemName)
AACLogger.LogInfo($"Processing {layerGroups.Count} layer groups..."); V5AACLogger.LogInfo($"Processing {layerGroups.Count} layer groups...");
var totalLayerGroupTime = 0L; var totalLayerGroupTime = 0L;
var processedCount = 0; var processedCount = 0;
@ -236,25 +236,25 @@ namespace gay.lilyy.aaccore
if (!layerGroup.enabled) if (!layerGroup.enabled)
{ {
AACLogger.LogWarning($"Skipping layer group: {layerGroup.DisplayName} (disabled)"); V5AACLogger.LogWarning($"Skipping layer group: {layerGroup.DisplayName} (disabled)");
skipped = true; skipped = true;
} }
else if (layerGroup.experimental && !assets.experimentalEnabled) else if (layerGroup.experimental && !assets.experimentalEnabled)
{ {
AACLogger.LogWarning($"Skipping layer group: {layerGroup.DisplayName} (Experimental)"); V5AACLogger.LogWarning($"Skipping layer group: {layerGroup.DisplayName} (Experimental)");
skipped = true; skipped = true;
} }
else if (!layerGroup.IsApplicable(assets)) else if (!layerGroup.IsApplicable(assets))
{ {
if (layerGroup.shouldWarnIfNotApplicable) if (layerGroup.shouldWarnIfNotApplicable)
AACLogger.LogWarning($"Skipping layer group: {layerGroup.DisplayName} (Not applicable)"); V5AACLogger.LogWarning($"Skipping layer group: {layerGroup.DisplayName} (Not applicable)");
skipped = true; skipped = true;
} }
if (!skipped) if (!skipped)
{ {
AACLogger.LogInfo($"Running layer group: {layerGroup.DisplayName}"); V5AACLogger.LogInfo($"Running layer group: {layerGroup.DisplayName}");
layerGroup.Run(assets); layerGroup.Run(assets);
processedCount++; processedCount++;
} }
@ -265,26 +265,26 @@ namespace gay.lilyy.aaccore
layerStopwatch.Stop(); layerStopwatch.Stop();
totalLayerGroupTime += layerStopwatch.ElapsedMilliseconds; totalLayerGroupTime += layerStopwatch.ElapsedMilliseconds;
AACLogger.LogInfo($"Layer group '{layerGroup.DisplayName}' completed in {layerStopwatch.ElapsedMilliseconds}ms"); V5AACLogger.LogInfo($"Layer group '{layerGroup.DisplayName}' completed in {layerStopwatch.ElapsedMilliseconds}ms");
} }
// Time Modular Avatar setup // Time Modular Avatar setup
var maStopwatch = Stopwatch.StartNew(); var maStopwatch = Stopwatch.StartNew();
AACLogger.LogInfo("Creating Modular Avatar merge animator..."); V5AACLogger.LogInfo("Creating Modular Avatar merge animator...");
assets.modularAvatar.NewMergeAnimator(assets.fx.AnimatorController, VRCAvatarDescriptor.AnimLayerType.FX); assets.modularAvatar.NewMergeAnimator(assets.fx.AnimatorController, VRCAvatarDescriptor.AnimLayerType.FX);
maStopwatch.Stop(); maStopwatch.Stop();
AACLogger.LogInfo($"Modular Avatar setup completed in {maStopwatch.ElapsedMilliseconds}ms"); V5AACLogger.LogInfo($"Modular Avatar setup completed in {maStopwatch.ElapsedMilliseconds}ms");
// Final timing summary // Final timing summary
overallStopwatch.Stop(); overallStopwatch.Stop();
AACLogger.LogSuccess($"Lillith AAC generation completed successfully!"); V5AACLogger.LogSuccess($"Lillith V5 AAC generation completed successfully!");
AACLogger.LogInfo($"=== TIMING SUMMARY ==="); V5AACLogger.LogInfo($"=== TIMING SUMMARY ===");
AACLogger.LogInfo($"Total time: {overallStopwatch.ElapsedMilliseconds}ms ({overallStopwatch.Elapsed.TotalSeconds:F2}s)"); V5AACLogger.LogInfo($"Total time: {overallStopwatch.ElapsedMilliseconds}ms ({overallStopwatch.Elapsed.TotalSeconds:F2}s)");
AACLogger.LogInfo($"AAC initialization: {initStopwatch.ElapsedMilliseconds}ms"); V5AACLogger.LogInfo($"AAC initialization: {initStopwatch.ElapsedMilliseconds}ms");
AACLogger.LogInfo($"Layer groups: {totalLayerGroupTime}ms (processed: {processedCount}, skipped: {skippedCount})"); V5AACLogger.LogInfo($"Layer groups: {totalLayerGroupTime}ms (processed: {processedCount}, skipped: {skippedCount})");
AACLogger.LogInfo($"Modular Avatar: {maStopwatch.ElapsedMilliseconds}ms"); V5AACLogger.LogInfo($"Modular Avatar: {maStopwatch.ElapsedMilliseconds}ms");
AACLogger.LogInfo($"Average per layer group: {(processedCount > 0 ? totalLayerGroupTime / processedCount : 0)}ms"); V5AACLogger.LogInfo($"Average per layer group: {(processedCount > 0 ? totalLayerGroupTime / processedCount : 0)}ms");
} }
} }

View file

@ -6,10 +6,10 @@ namespace gay.lilyy.aaccore {
{ {
public virtual bool enabled { get { return true; } } public virtual bool enabled { get { return true; } }
public virtual bool experimental { get { return false; } } public virtual bool experimental { get { return false; } }
private AACLayerGroupLogger _logger; private V5AACLayerGroupLogger _logger;
protected AACLayerGroupLogger Logger => _logger ??= new AACLayerGroupLogger(SystemName); protected V5AACLayerGroupLogger Logger => _logger ??= new V5AACLayerGroupLogger(SystemName);
private static readonly List<LayerGroup> instances = new(); private static readonly List<LayerGroup> instances = new();

View file

@ -3,10 +3,10 @@ using gay.lilyy.logger;
using UnityEngine; using UnityEngine;
namespace gay.lilyy.aaccore { namespace gay.lilyy.aaccore {
public class AACLogger : BaseLogger public class V5AACLogger : BaseLogger
{ {
private static AACLogger _instance; private static V5AACLogger _instance;
public static AACLogger Instance => _instance ??= new AACLogger(); public static V5AACLogger Instance => _instance ??= new V5AACLogger();
protected override string SystemName => "AACCore"; protected override string SystemName => "AACCore";
@ -36,11 +36,11 @@ namespace gay.lilyy.aaccore {
public static void LogDebug(string message, Object context = null) => Instance.Debug(message, context); public static void LogDebug(string message, Object context = null) => Instance.Debug(message, context);
} }
public class AACLayerGroupLogger : BaseLogger public class V5AACLayerGroupLogger : BaseLogger
{ {
private readonly string _layerName; private readonly string _layerName;
public AACLayerGroupLogger(string layerName) public V5AACLayerGroupLogger(string layerName)
{ {
_layerName = layerName; _layerName = layerName;
} }

View file

@ -6,7 +6,6 @@ namespace gay.lilyy.aaccore {
/// <summary> /// <summary>
/// Adding this to the avatar root will build the FX layer. /// Adding this to the avatar root will build the FX layer.
/// </summary> /// </summary>
[AddComponentMenu("LillithRosePup/AAC Root")]
public class AACRoot : MonoBehaviour, IEditorOnly { public class AACRoot : MonoBehaviour, IEditorOnly {
public bool experimentalPlayMode = true; public bool experimentalPlayMode = true;
public bool experimentalUpload = false; public bool experimentalUpload = false;

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 4fb4c374accfed2468cd020df25fe43c guid: dfb718cc85f807a4585cc3dd07098767
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View file

@ -1,8 +1,12 @@
{ {
"name": "PlatformSpecificFXRuntime", "name": "PlaneCamEditor",
"rootNamespace": "", "rootNamespace": "",
"references": [], "references": [
"includePlatforms": [], "GUID:a9f136790ce90f740a7142ab21ff971a"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,
"overrideReferences": false, "overrideReferences": false,

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 424958f3f014e9b43ab88ec66b389caf guid: 0010e90dfaa354f4ca094b433acef847
AssemblyDefinitionImporter: AssemblyDefinitionImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View file

@ -0,0 +1,121 @@
using UnityEngine;
using UnityEditor;
using gay.lilyy.PlaneCam;
namespace gay.lilyy.PlaneCam.Editor
{
[InitializeOnLoad]
public class PlaneCamEditor
{
static PlaneCamEditor()
{
EditorApplication.update += UpdatePlaneCams;
}
static void UpdatePlaneCams()
{
PlaneCam[] planeCams = Object.FindObjectsOfType<PlaneCam>(true);
foreach (PlaneCam planeCam in planeCams)
{
if (planeCam == null || planeCam.target == null)
continue;
Camera cam = planeCam.GetComponent<Camera>();
if (cam == null)
continue;
// Get the bounds of the target plane
Bounds bounds = GetBounds(planeCam.target);
if (bounds.size.magnitude < 0.001f)
continue;
// Get plane normal and center
Vector3 planeNormal = GetPlaneNormal(planeCam.target, planeCam.direction);
Vector3 planeCenter = bounds.center;
// Calculate the plane's dimensions in its local space
// Project bounds size onto the plane's local axes
Transform planeTransform = planeCam.target.transform;
Vector3 localSize = bounds.size;
// Find the two dimensions that define the plane (ignore the smallest dimension)
// This assumes the plane is flat, so one dimension should be very small
float minDim = Mathf.Min(localSize.x, localSize.y, localSize.z);
float maxDim = Mathf.Max(localSize.x, localSize.y, localSize.z);
float midDim = localSize.x + localSize.y + localSize.z - minDim - maxDim;
// Use the larger of the two plane dimensions to ensure it fills the square viewport
float planeSize = Mathf.Max(maxDim, midDim);
// Set aspect ratio to 1:1 for square viewport
cam.aspect = 1.0f;
// Calculate distance and position
if (cam.orthographic)
{
cam.orthographicSize = planeSize * 0.5f;
// Position camera perpendicular to the plane
cam.transform.position = planeCenter - planeNormal * 10f; // Distance doesn't matter for orthographic
cam.transform.LookAt(planeCenter, planeTransform.up);
}
else
{
// For perspective camera, calculate distance based on FOV
// distance = (size/2) / tan(FOV/2)
float halfFOV = cam.fieldOfView * 0.5f * Mathf.Deg2Rad;
float distance = (planeSize * 0.5f) / Mathf.Tan(halfFOV);
// Position camera perpendicular to the plane, looking at center
cam.transform.position = planeCenter - planeNormal * distance;
cam.transform.LookAt(planeCenter, planeTransform.up);
}
}
}
static Bounds GetBounds(GameObject target)
{
Renderer renderer = target.GetComponent<Renderer>();
if (renderer != null)
{
return renderer.bounds;
}
// If no renderer, try to get bounds from collider
Collider collider = target.GetComponent<Collider>();
if (collider != null)
{
return collider.bounds;
}
// Fallback: use transform scale
return new Bounds(target.transform.position, target.transform.lossyScale);
}
static Vector3 GetPlaneNormal(GameObject target, PlaneDirection direction)
{
Transform t = target.transform;
// Use the specified direction setting
switch (direction)
{
case PlaneDirection.XPositive:
return t.right;
case PlaneDirection.XNegative:
return -t.right;
case PlaneDirection.YPositive:
return t.up;
case PlaneDirection.YNegative:
return -t.up;
case PlaneDirection.ZPositive:
return t.forward;
case PlaneDirection.ZNegative:
return -t.forward;
default:
return t.forward;
}
}
}
}

View file

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 24bc2531ec6d029449c1cf0e322c6612 guid: 0e148c194888c8f4b80fbb649b2f1a02
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View file

@ -1,8 +1,6 @@
using UnityEngine; using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace gay.lilyy.PlaneCam namespace gay.lilyy.PlaneCam
{ {
@ -22,124 +20,5 @@ namespace gay.lilyy.PlaneCam
{ {
public GameObject target; public GameObject target;
public PlaneDirection direction = PlaneDirection.ZPositive; public PlaneDirection direction = PlaneDirection.ZPositive;
public void UpdateCamera()
{
if (target == null)
return;
Camera cam = GetComponent<Camera>();
if (cam == null)
return;
// Get the bounds of the target plane
Bounds bounds = GetBounds(target);
if (bounds.size.magnitude < 0.001f)
return;
// Get plane normal and center
Vector3 planeNormal = GetPlaneNormal(target, direction);
Vector3 planeCenter = bounds.center;
// Calculate the plane's dimensions in its local space
// Project bounds size onto the plane's local axes
Transform planeTransform = target.transform;
Vector3 localSize = bounds.size;
// Find the two dimensions that define the plane (ignore the smallest dimension)
// This assumes the plane is flat, so one dimension should be very small
float minDim = Mathf.Min(localSize.x, localSize.y, localSize.z);
float maxDim = Mathf.Max(localSize.x, localSize.y, localSize.z);
float midDim = localSize.x + localSize.y + localSize.z - minDim - maxDim;
// Use the larger of the two plane dimensions to ensure it fills the square viewport
float planeSize = Mathf.Max(maxDim, midDim);
// Set aspect ratio to 1:1 for square viewport
cam.aspect = 1.0f;
// Calculate distance and position
if (cam.orthographic)
{
cam.orthographicSize = planeSize * 0.5f;
// Position camera perpendicular to the plane
cam.transform.position = planeCenter - planeNormal * 10f; // Distance doesn't matter for orthographic
cam.transform.LookAt(planeCenter, planeTransform.up);
}
else
{
// For perspective camera, calculate distance based on FOV
// distance = (size/2) / tan(FOV/2)
float halfFOV = cam.fieldOfView * 0.5f * Mathf.Deg2Rad;
float distance = (planeSize * 0.5f) / Mathf.Tan(halfFOV);
// Position camera perpendicular to the plane, looking at center
cam.transform.position = planeCenter - planeNormal * distance;
cam.transform.LookAt(planeCenter, planeTransform.up);
}
}
Bounds GetBounds(GameObject target)
{
Renderer renderer = target.GetComponent<Renderer>();
if (renderer != null)
{
return renderer.bounds;
}
// If no renderer, try to get bounds from collider
Collider collider = target.GetComponent<Collider>();
if (collider != null)
{
return collider.bounds;
}
// Fallback: use transform scale
return new Bounds(target.transform.position, target.transform.lossyScale);
}
Vector3 GetPlaneNormal(GameObject target, PlaneDirection direction)
{
Transform t = target.transform;
// Use the specified direction setting
switch (direction)
{
case PlaneDirection.XPositive:
return t.right;
case PlaneDirection.XNegative:
return -t.right;
case PlaneDirection.YPositive:
return t.up;
case PlaneDirection.YNegative:
return -t.up;
case PlaneDirection.ZPositive:
return t.forward;
case PlaneDirection.ZNegative:
return -t.forward;
default:
return t.forward;
}
}
} }
#if UNITY_EDITOR
[CustomEditor(typeof(PlaneCam))]
public class PlaneCamInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
PlaneCam planeCam = (PlaneCam)target;
EditorGUILayout.Space();
if (GUILayout.Button("Update Camera"))
{
planeCam.UpdateCamera();
}
}
}
#endif
} }

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: f2577c1118081964793d2e54370ea95d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,22 +0,0 @@
{
"name": "PlatformSpecificFXEditor",
"rootNamespace": "",
"references": [
"GUID:62ced99b048af7f4d8dfe4bed8373d76",
"GUID:5718fb738711cd34ea54e9553040911d",
"GUID:901e56b065a857d4483a77f8cae73588",
"GUID:209cbd2a789c4f72963fdbf1f8a01909",
"GUID:424958f3f014e9b43ab88ec66b389caf"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: c8c6365cf8a2e724caa7fe8be7e75792
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,51 +0,0 @@
using System;
using System.Linq;
using gay.lilyy.platformspecificfx;
using nadena.dev.ndmf;
using nadena.dev.ndmf.vrchat;
using UnityEditor;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
[assembly: ExportsPlugin(typeof(PlatformSpecificFXPlugin))]
namespace gay.lilyy.platformspecificfx
{
public class PlatformSpecificFXPlugin : Plugin<PlatformSpecificFXPlugin>
{
public override string DisplayName => "Platform Specific FX";
public override string QualifiedName => "gay.lilyy.platformspecificfx";
protected override void Configure()
{
InPhase(BuildPhase.FirstChance).Run("PlatformSpecificFX", ctx =>
{
var configs = ctx.AvatarRootObject.GetComponentsInChildren<PlatformSpecificFX>(true);
if (configs == null || configs.Length == 0)
return;
var targetAnimator = EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows
|| EditorUserBuildSettings.activeBuildTarget == BuildTarget.StandaloneWindows64 ?
configs[0].desktop : configs[0].mobile;
if (targetAnimator == null) throw new Exception("Target animator not found");
var baseAnimationLayers = ctx.VRChatAvatarDescriptor().baseAnimationLayers.ToList();
// replace the fx layer with the target animator
var foundFXLayer = false;
for (int i = 0; i < baseAnimationLayers.Count; i++)
{
var layer = baseAnimationLayers[i];
if (layer.type == VRCAvatarDescriptor.AnimLayerType.FX) {
Debug.Log($"Found FX Layer, Replacing with {targetAnimator.name}");
layer.animatorController = targetAnimator;
baseAnimationLayers[i] = layer;
foundFXLayer = true;
}
}
if (!foundFXLayer) throw new Exception("FX Layer not found");
ctx.VRChatAvatarDescriptor().baseAnimationLayers = baseAnimationLayers.ToArray();
});
}
}
}

View file

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 1ce53e373aea68e4b97c79e7e8df274c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: fb1855544ce8de94794ec16500fad59c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -1,12 +0,0 @@
using UnityEngine;
namespace gay.lilyy.platformspecificfx
{
[AddComponentMenu("LillithRosePup/Platform Specific FX")]
public class PlatformSpecificFX : MonoBehaviour, VRC.SDKBase.IEditorOnly
{
public RuntimeAnimatorController desktop;
public RuntimeAnimatorController mobile;
}
}