diff --git a/README.ENG.md b/README.ENG.md index 85d6b67..b23c937 100644 --- a/README.ENG.md +++ b/README.ENG.md @@ -58,7 +58,7 @@ Source code of OSC: https://github.com/Shell4026/ShellProtectorOSC Detailed principle:https://github.com/seanedwards/vrc-worldobject/blob/main/docs/parameter-multiplexing.md -This is a parameter-saving technique. After checking, OSC must always be turned on and Parameter-multiplexing must be checked in the OSC program. +This is a parameter-saving technique. After checking, OSC must always be **turned on and Parameter-multiplexing** must be checked in the OSC program. This will slightly increase the time it takes to get back to your original avatar appearance in-game. @@ -68,6 +68,8 @@ When using parameter multiplexing, depending on the server or network conditions In this case, try increasing the refresh rate slightly, which was added in OSC 1.5.0. +It is applied by default from version 2.5.0beta. + ### Avatar fallback A feature that allows anyone with Safety On when encryption is in place to appear as a degraded version of themselves when viewing your avatar.
It appears as a fallback texture even when the encryption hasn't been decrypted. @@ -78,7 +80,7 @@ If you find the issue, please raise it in Issues. **[is not supported texture format! Error]** -Select the texture and change the compression format to either DXT1 or DXT5 in the Inspector. (DXT5 for textures with transparency) +Select the texture and change the compression format to either DXT1/RGB24 or DXT5/RGBA32 in the Inspector. (DXT5 for textures with transparency) ![texture](https://github.com/Shell4026/ShellProtector/assets/104874910/872f9d15-7b89-4381-b940-00514bd60638) @@ -104,6 +106,8 @@ When using parameter multiplexing, depending on the server or network conditions In this case, try increasing the refresh rate slightly, which was added in OSC 1.5.0. +This can also occur when there are parameter-compressed assets. + **[When a Texture that was present in a Material is missing]** When using a texture such as a main color/texture, it will be stripped for security reasons. diff --git a/README.JP.md b/README.JP.md index a1170f8..4a533b2 100644 --- a/README.JP.md +++ b/README.JP.md @@ -59,7 +59,7 @@ OSCのソースコード: https://github.com/Shell4026/ShellProtectorOSC 詳細原理:https://github.com/seanedwards/vrc-worldobject/blob/main/docs/parameter-multiplexing.md -パラメータ節約技術です。チェック後、OSCを常にオンにしておく必要があり、OSCプログラムにもParameter-multiplexingをチェックする必要があります。 +パラメータ節約技術です。チェック後、OSCを常にオンにしておく必要があり、OSCプログラムにも**Parameter-multiplexingをチェック**する必要があります。 ゲーム内で元の姿に戻るまでの時間が若干増加します。 @@ -69,6 +69,8 @@ OSCのソースコード: https://github.com/Shell4026/ShellProtectorOSC この場合、OSC 1.5.0で追加されたRefresh rateを少し上げてみてください。 +2.5.0betaからデフォルトで適用されます。 + ### アバターフォールバック 暗号化がかかっているときにSafetyがオンになっている人は、アバターを見るときに劣化したバージョンに見えるようにする機能です。
暗号化が解除されていない場合でもフォールバックテクスチャとして表示されます。 @@ -79,7 +81,7 @@ OSCのソースコード: https://github.com/Shell4026/ShellProtectorOSC **** -テクスチャを選択後、Inspectorで圧縮フォーマットをDXT1またはDXT5のいずれかに変更してください。 (透明度のあるテクスチャはDXT5) +テクスチャを選択後、Inspectorで圧縮フォーマットをDXT1/RGB24またはDXT5/RGBA32のいずれかに変更してください。 (透明度のあるテクスチャはDXT5) ![texture](https://github.com/Shell4026/ShellProtector/assets/104874910/872f9d15-7b89-4381-b940-00514bd60638) @@ -105,6 +107,8 @@ lilToonのバグなので、無視してアップロードするか、3つの方 この場合、OSC 1.5.0で追加されたRefresh rateを少し上げてみてください。 +パラメータ圧縮されたアセットがある場合にも発生する可能性があります。 + **<マテリアルにあったテクスチャが消える場合>** メインカラーと同じテクスチャを使用する場合、セキュリティ上の理由でそのテクスチャは削除されます。 diff --git a/README.md b/README.md index 3e02412..3596671 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2FShell4026%2FShellProtector&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) # VCC : https://shell4026.github.io/VCC/ +347042998-b6e62237-67fe-4a4b-a415-12080ca64f01 +https://Shell4026.github.io/VCC/index.json + + 권장 유니티 버전 : 2022 ## | 한국어 | [English](./README.ENG.md) | [日本語](./README.JP.md) | @@ -57,7 +61,7 @@ OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC 세부 원리:https://github.com/seanedwards/vrc-worldobject/blob/main/docs/parameter-multiplexing.md -파라미터 절약 기술입니다. 체크 후 OSC를 항상 켜둬야하며 OSC프로그램에도 Parameter-multiplexing을 체크 해야합니다. +파라미터 절약 기술입니다. 체크 후 OSC를 항상 켜둬야하며 OSC프로그램에도 **Parameter-multiplexing을 체크** 해야 합니다. 인게임에서 원래 모습으로 돌아오기까지 시간이 약간 증가합니다. @@ -67,6 +71,8 @@ OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC 이 경우 OSC 1.5.0에 추가된 Refresh rate를 조금 올려보시길 바랍니다. +2.5.0beta부터 기본 적용 됩니다. + ### 아바타 폴백 암호화가 걸려있을 때 세이프티가 켜져있는 사람은 아바타를 볼 때 열화된 버전으로 보이게 하는 기능입니다.
암호화가 풀리지 않았을 때도 폴백 텍스쳐로 보입니다. @@ -77,7 +83,7 @@ OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC **** -텍스쳐를 선택 후 Inspector에서 압축 포멧을 DXT1이나 DXT5중 하나로 바꿔주세요. (투명도가 있는 텍스쳐는 DXT5) +텍스쳐를 선택 후 Inspector에서 압축 포멧을 DXT1/RGB24나 DXT5/RGBA32중 하나로 바꿔주세요. (투명도가 있는 텍스쳐는 DXT5) ![texture](https://github.com/Shell4026/ShellProtector/assets/104874910/872f9d15-7b89-4381-b940-00514bd60638) @@ -103,6 +109,8 @@ OSC 소스 코드: https://github.com/Shell4026/ShellProtectorOSC 이 경우 OSC 1.5.0에 추가된 Refresh rate를 조금 올려보시길 바랍니다. +파라미터 압축 에셋이 있는 경우에도 발생할 수 있습니다. + **<메테리얼에서 있던 텍스쳐가 빠지는 경우>** 메인 컬러와 같은 텍스쳐 사용시 보안상의 이유로 해당 텍스쳐는 빠집니다. diff --git a/Runtime/Scripts/AnimatorManager.cs b/Runtime/Scripts/AnimatorManager.cs index 6714c06..67a7ebc 100644 --- a/Runtime/Scripts/AnimatorManager.cs +++ b/Runtime/Scripts/AnimatorManager.cs @@ -17,15 +17,45 @@ public class AnimatorManager : ScriptableObject public static AnimatorController DuplicateAnimator(RuntimeAnimatorController anim, OutputPaths paths, AssetWriter writer) { - string dir = AssetDatabase.GetAssetPath(anim); - if (!writer.CopyAssetToFolder(dir, paths.Folders.AnimGuid, paths.ControllerName(anim), out string output)) + return DuplicateAnimators(new[] { anim }, paths, writer)[0]; + } + + public static AnimatorController[] DuplicateAnimators(RuntimeAnimatorController[] animators, OutputPaths paths, AssetWriter writer) + { + var sourcePaths = new string[animators.Length]; + var targetPaths = new Dictionary(); + + for (int i = 0; i < animators.Length; ++i) { - Debug.LogErrorFormat("Failed to copy a animator: {0}", anim.name); + RuntimeAnimatorController animator = animators[i]; + if (animator == null) + continue; + + string sourcePath = AssetDatabase.GetAssetPath(animator); + sourcePaths[i] = sourcePath; + if (targetPaths.ContainsKey(sourcePath)) + continue; + + if (!writer.CopyAssetToFolder(sourcePath, paths.Folders.AnimGuid, paths.ControllerName(animator), out string targetPath)) + { + Debug.LogErrorFormat("Failed to copy a animator: {0}", animator.name); + continue; + } + + targetPaths.Add(sourcePath, targetPath); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); - return AssetDatabase.LoadAssetAtPath(output, typeof(RuntimeAnimatorController)) as AnimatorController; + + var duplicatedAnimators = new AnimatorController[animators.Length]; + for (int i = 0; i < sourcePaths.Length; ++i) + { + if (!string.IsNullOrEmpty(sourcePaths[i]) && targetPaths.TryGetValue(sourcePaths[i], out string targetPath)) + duplicatedAnimators[i] = AssetDatabase.LoadAssetAtPath(targetPath); + } + + return duplicatedAnimators; } public static void CreateKeyAnimations(string animationDir, OutputPaths paths, AssetWriter writer, GameObject[] objs) diff --git a/Runtime/Scripts/Pipeline/MaterialEncryptor.cs b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs index fd35645..b138787 100644 --- a/Runtime/Scripts/Pipeline/MaterialEncryptor.cs +++ b/Runtime/Scripts/Pipeline/MaterialEncryptor.cs @@ -18,7 +18,7 @@ public MaterialEncryptor(AssetWriter writer, bool turnOnAllSafetyFallback, int a this.rounds = rounds; } - public Material CreateEncryptedMaterial(string folderGuid, string fileName, Material source, Shader shader, Texture2D fallback, Texture2D mip, AuxiliaryTextures auxiliary, ProcessedTexture texture, byte[] keyBytes, IEncryptor encryptor, Injector injector) + public Material CreateEncryptedMaterial(string folderGuid, string fileName, Material source, Shader shader, Texture2D fallback, Texture2D mip, AuxiliaryTextures auxiliary, ProcessedTexture texture, byte[] keyBytes, int fixedKeySize, IEncryptor encryptor, Injector injector) { Material result = new Material(source.shader); result.CopyPropertiesFromMaterial(source); @@ -39,7 +39,7 @@ public Material CreateEncryptedMaterial(string folderGuid, string fileName, Mate var (widthOffset, heightOffset) = TextureEncryptManager.CalculateOffsets(originalTex); result.SetInteger(ShaderProperties.WidthOffset, widthOffset); result.SetInteger(ShaderProperties.HeightOffset, heightOffset); - for (int i = 0; i < keyBytes.Length; ++i) + for (int i = 0; i < fixedKeySize; ++i) result.SetFloat(ShaderProperties.KeyPrefix + i, keyBytes[i]); if (algorithm == (int)ShellProtectorAlgorithm.Chacha) diff --git a/Runtime/Scripts/ShellProtector.cs b/Runtime/Scripts/ShellProtector.cs index a58919e..48b37d3 100644 --- a/Runtime/Scripts/ShellProtector.cs +++ b/Runtime/Scripts/ShellProtector.cs @@ -726,14 +726,26 @@ static Texture2D GetLargestFallback(ProcessedTexture processedTexture) public void SetAnimations(GameObject avatar, bool clone) { var av3 = avatar.GetComponent(); - AnimatorController fx; OutputPaths paths = EnsureOutputFolders(); + AnimatorController fx; if (clone) - fx = AnimatorManager.DuplicateAnimator(av3.baseAnimationLayers[4].animatorController, paths, _assetWriter); + { + var sourceControllers = new RuntimeAnimatorController[av3.baseAnimationLayers.Length]; + for (int i = 0; i < av3.baseAnimationLayers.Length; ++i) + sourceControllers[i] = av3.baseAnimationLayers[i].animatorController; + + AnimatorController[] duplicatedLayers = AnimatorManager.DuplicateAnimators(sourceControllers, paths, _assetWriter); + for (int i = 0; i < duplicatedLayers.Length; ++i) + { + if (duplicatedLayers[i] != null) + av3.baseAnimationLayers[i].animatorController = duplicatedLayers[i]; + } + + fx = duplicatedLayers[4]; + } else fx = av3.baseAnimationLayers[4].animatorController as AnimatorController; - av3.baseAnimationLayers[4].animatorController = fx; string animationDir = _assetWriter.ResolveFolderPath(paths.Folders.AnimGuid); GameObject[] meshArray = new GameObject[Meshes.Count]; @@ -905,7 +917,7 @@ public void ObfuscateBlendShape(GameObject avatar, bool clone) #endif for (int i = 0; i <= 4; ++i) { - AnimatorController playableLayer = GetFx(avatar, 0); + AnimatorController playableLayer = GetFx(avatar, i); if (playableLayer == null) continue; obfuscator.ObfuscateBlendshapeInAnim(playableLayer, selectRenderer.gameObject, paths, _assetWriter); @@ -1264,7 +1276,7 @@ Texture2D GenerateFallbackTexture(string fileName, MatOption option, Texture2D m Material GenerateEncryptedMaterial(string fileName, Material mat, Shader encryptedShader, Texture2D fallback, Texture2D mip, AuxiliaryTextures otherTex, ProcessedTexture processedTexture, byte[] keyBytes, IEncryptor encryptor) { MaterialEncryptor materialEncryptor = new MaterialEncryptor(_assetWriter, _turnOnAllSafetyFallback, _algorithm, _rounds); - Material newMat = materialEncryptor.CreateEncryptedMaterial(GetOutputPaths().Folders.MatGuid, fileName, mat, encryptedShader, fallback, mip, otherTex, processedTexture, keyBytes, encryptor, _injector); + Material newMat = materialEncryptor.CreateEncryptedMaterial(GetOutputPaths().Folders.MatGuid, fileName, mat, encryptedShader, fallback, mip, otherTex, processedTexture, keyBytes, 16 - _keySize, encryptor, _injector); Debug.LogFormat("{0} : create encrypted material : {1}", mat.name, AssetDatabase.GetAssetPath(newMat)); if (!EncryptedMaterials.ContainsKey(mat)) diff --git a/Tests/Editor/Integration/PipelineTests.cs b/Tests/Editor/Integration/PipelineTests.cs index 16fb9e1..17e2562 100644 --- a/Tests/Editor/Integration/PipelineTests.cs +++ b/Tests/Editor/Integration/PipelineTests.cs @@ -53,6 +53,38 @@ public void ManualEncrypt_CreatesEncryptedAvatarAndRewritesProtectionAssets() AssertAnimationMaterialWasRewritten(encryptedAvatar, fixture.Material); } + [Test] + public void ManualEncrypt_DuplicatesNonFxControllerBeforeObfuscatingBlendShapes() + { + Fixture fixture = CreateFixture("ControllerIsolation"); + VRCAvatarDescriptor originalDescriptor = fixture.Avatar.GetComponent(); + AnimationClip originalClip = CreateBlendShapeClip("ControllerIsolation"); + AnimatorController originalController = CreateBlendShapeController("ControllerIsolation", originalClip); + originalDescriptor.baseAnimationLayers[0] = new VRCAvatarDescriptor.CustomAnimLayer + { + type = VRCAvatarDescriptor.AnimLayerType.Base, + isDefault = false, + animatorController = originalController + }; + + GameObject encryptedAvatar = fixture.Protector.Encrypt(false); + sceneObjects.Add(encryptedAvatar); + + AnimatorController sourceController = originalDescriptor.baseAnimationLayers[0].animatorController as AnimatorController; + AnimatorController encryptedController = encryptedAvatar.GetComponent() + .baseAnimationLayers[0].animatorController as AnimatorController; + AnimationClip sourceClip = GetFirstClip(sourceController); + AnimationClip encryptedClip = GetFirstClip(encryptedController); + + Assert.That(sourceController, Is.SameAs(originalController)); + Assert.That(encryptedController, Is.Not.SameAs(originalController)); + Assert.That(sourceClip, Is.SameAs(originalClip)); + Assert.That(encryptedClip, Is.Not.SameAs(originalClip)); + Assert.That(AnimationUtility.GetCurveBindings(sourceClip).Single().propertyName, Is.EqualTo("blendShape.Smile")); + Assert.That(AnimationUtility.GetCurveBindings(encryptedClip).Single().propertyName, Does.StartWith("blendShape.")); + Assert.That(AnimationUtility.GetCurveBindings(encryptedClip).Single().propertyName, Is.Not.EqualTo("blendShape.Smile")); + } + [Test] public void InPlaceEncrypt_RewritesOriginalAvatarWhenNdmfStyleStepsRun() { @@ -190,6 +222,39 @@ private static AnimationClip CreateMaterialClip(string name, Material material) return AssetDatabase.LoadAssetAtPath(path); } + private static AnimationClip CreateBlendShapeClip(string name) + { + AnimationClip clip = new AnimationClip(); + clip.name = name + "BlendShape"; + EditorCurveBinding binding = new EditorCurveBinding + { + path = "Body", + type = typeof(SkinnedMeshRenderer), + propertyName = "blendShape.Smile" + }; + AnimationUtility.SetEditorCurve(clip, binding, AnimationCurve.Constant(0f, 1f, 100f)); + string path = TestAssetScope.CreateAsset(clip, name + "/blendShape.anim"); + return AssetDatabase.LoadAssetAtPath(path); + } + + private static AnimatorController CreateBlendShapeController(string name, AnimationClip clip) + { + string path = TestAssetScope.GeneratedRoot + "/" + name + "/base.controller"; + TestAssetScope.EnsureFolder(TestAssetScope.GeneratedRoot + "/" + name); + AnimatorController controller = AnimatorController.CreateAnimatorControllerAtPath(path); + AnimatorControllerLayer layer = controller.layers[0]; + AnimatorState state = layer.stateMachine.AddState("BlendShape"); + state.motion = clip; + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + return AssetDatabase.LoadAssetAtPath(path); + } + + private static AnimationClip GetFirstClip(AnimatorController controller) + { + return controller.layers[0].stateMachine.states[0].state.motion as AnimationClip; + } + private static AnimatorController CreateFxController(string name, AnimationClip materialClip) { string path = TestAssetScope.GeneratedRoot + "/" + name + "/fx.controller"; diff --git a/package.json b/package.json index 1cf5999..b609102 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "shell.protector", "displayName": "Shell Protector", - "version": "2.6.0", + "version": "2.6.3", "unity": "2022.3", "description": "Texture encryption for VRChat. Save avatars from ripping", "author": { diff --git a/version.json b/version.json index 15fecae..3c35077 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{ "latestVersion": "2.6.0", "downloadPage": "/releases/tag/2.6.0" } \ No newline at end of file +{ "latestVersion": "2.6.3", "downloadPage": "/releases/tag/2.6.3" } \ No newline at end of file