Publish verified live restoration source snapshot and known limits

This commit is contained in:
0xcjy
2026-10-01 19:42:22 +08:00
parent 0dd0d3bdaa
commit d70a65c7ce
896 changed files with 105420 additions and 12745 deletions
+1
View File
@@ -0,0 +1 @@
+87 -81
View File
@@ -1,100 +1,106 @@
name: Build Windows Player and Android APK
name: Build and release Windows IL2CPP
on:
push:
branches:
- master
tags:
- "v*"
workflow_dispatch:
inputs:
release_tag:
description: "Optional release tag, for example v1.0.0 (leave empty for build only)"
required: false
type: string
permissions:
contents: write
concurrency:
group: unity-windows-il2cpp-${{ github.ref }}
cancel-in-progress: true
jobs:
buildForPlatform:
name: build Player
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
buildName:
- UmaViewer
projectPath:
- ./
unityVersion:
- 2022.3.56f1
targetPlatform:
- Android # Build an Android APK.
- StandaloneWindows64 # Build a Windows 64-bit standalone
build:
name: Windows x64 IL2CPP
# unity-builder@v4 uses a Docker-based Unity image, so it must run on Linux.
runs-on: ubuntu-24.04
timeout-minutes: 120
steps:
- uses: actions/checkout@v2
- name: Verify Unity license secrets
shell: pwsh
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
run: |
$missing = @()
if ([string]::IsNullOrWhiteSpace($env:UNITY_LICENSE) -and [string]::IsNullOrWhiteSpace($env:UNITY_SERIAL)) {
$missing += "UNITY_LICENSE or UNITY_SERIAL"
}
if ([string]::IsNullOrWhiteSpace($env:UNITY_EMAIL)) { $missing += "UNITY_EMAIL" }
if ([string]::IsNullOrWhiteSpace($env:UNITY_PASSWORD)) { $missing += "UNITY_PASSWORD" }
if ($missing.Count -gt 0) {
Write-Error "Missing repository secrets: $($missing -join ', '). See .github/UNITY_LICENSE_SETUP.md."
exit 1
}
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: true
- uses: game-ci/unity-builder@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
- name: Cache Unity Library
uses: actions/cache@v4
with:
buildName: ${{ matrix.buildName }}
projectPath: ${{ matrix.projectPath }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
path: Library
key: Library-${{ runner.os }}-StandaloneWindows64-${{ hashFiles('Assets/**', 'Packages/**', 'ProjectSettings/**') }}
restore-keys: |
Library-${{ runner.os }}-StandaloneWindows64-
- name: Zip Binary
if: matrix.targetPlatform == 'StandaloneWindows64'
run: zip -r UmaViewer_${{ matrix.targetPlatform }}.zip build/${{ matrix.targetPlatform }}/*
- name: Build Windows player with IL2CPP
uses: game-ci/unity-builder@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
projectPath: .
unityVersion: 2022.3.62f3
targetPlatform: StandaloneWindows64
buildName: UmaViewer
buildsPath: build
- name: Upload Artifact for Android
if: matrix.targetPlatform == 'Android'
- name: Package Windows build
shell: pwsh
run: >-
Compress-Archive
-Path "build/StandaloneWindows64/*"
-DestinationPath "UmaViewer-Windows-x64.zip"
-CompressionLevel Optimal
- name: Upload workflow artifact
uses: actions/upload-artifact@v4
with:
name: Build-${{ matrix.targetPlatform }}
path: build/${{ matrix.targetPlatform }}/*
name: UmaViewer-Windows-x64
path: UmaViewer-Windows-x64.zip
if-no-files-found: error
retention-days: 14
compression-level: 0
- name: Upload Artifact for Windows
if: matrix.targetPlatform == 'StandaloneWindows64'
uses: actions/upload-artifact@v4
with:
name: Build-${{ matrix.targetPlatform }}
path: UmaViewer_StandaloneWindows64.zip
createRelease:
needs: buildForPlatform
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
path: build/
- name: Create Release
id: create_release
uses: actions/create-release@v1
- name: Publish GitHub Release
if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && inputs.release_tag != '')
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
with:
tag_name: AutoBuild_${{ github.event.head_commit.id }}
release_name: UmaViewer_${{ github.event.head_commit.id }}
draft: false
prerelease: false
- name: Upload Windows Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./build/Build-StandaloneWindows64/UmaViewer_StandaloneWindows64.zip
asset_name: UmaViewer.zip
asset_content_type: application/zip
- name: Upload Android Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./build/Build-Android/UmaViewer.apk
asset_name: UmaViewer_Unstable.apk
asset_content_type: application/vnd.android.package-archive
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref_name }}
run: |
gh release create $env:RELEASE_TAG `
"UmaViewer-Windows-x64.zip#UmaViewer Windows x64 (IL2CPP)" `
--target $env:GITHUB_SHA `
--title "UmaViewer $env:RELEASE_TAG" `
--generate-notes
+71 -48
View File
@@ -1,8 +1,4 @@
# This .gitignore file should be placed at the root of your Unity project directory
#
# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
#
.utmp/
# Unity generated directories
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
@@ -10,28 +6,17 @@
/[Bb]uilds/
/[Ll]ogs/
/[Mm]emoryCaptures/
/Assets/AmplifyShaderEditor/
/Ignore/
/Screenshots/
/[Rr]ecordings/
# Local Unity/user state
/[Uu]ser[Ss]ettings/
*.log
/.utmp/
/.vs/
/.vscode/
/.idea/
/.gradle/
# Asset meta data should only be ignored when the corresponding asset is also ignored
!/[Aa]ssets/**/*.meta
# Uncomment this line if you wish to ignore the asset store tools plugin
# /[Aa]ssets/AssetStoreTools*
# Autogenerated Jetbrains Rider plugin
[Aa]ssets/Plugins/Editor/JetBrains*
# Visual Studio cache directory
.vs/
# Gradle cache directory
.gradle/
# Autogenerated VS/MD/Consulo solution and project files
# Generated IDE files
ExportedObj/
.consulo/
*.csproj
@@ -49,38 +34,76 @@ ExportedObj/
*.opendb
*.VC.db
# Unity3D generated meta files
# Unity generated metadata and crash files
*.pidb.meta
*.pdb.meta
*.mdb.meta
# Unity3D generated file on crash reports
sysinfo.txt
# Builds
# Build packages
*.apk
*.aab
*.unitypackage
# Crashlytics generated file
crashlytics-build.properties
/Assets/Plugins/RootMotion/PuppetMaster/_Integration
/VMDRecords
Config.json
/gltf
/Assets/Plugins/RootMotion/FinalIK/_Integration
/Poses
# OS files
.DS_Store
Thumbs.db
# UmaViewer local/generated data
/Assets/AmplifyShaderEditor/
/Assets/AmplifyShaderEditor.meta
/BuildDev
/UserSettings/Layouts/default-2022.dwlt
/UserSettings/EditorUserSettings.asset
/Assets/Plugins/Editor/JetBrains*
/Assets/Plugins/RootMotion/PuppetMaster/_Integration/
/Assets/Plugins/RootMotion/FinalIK/_Integration/
/Ignore/
/Screenshots/
/VMDRecords/
/Poses/
/gltf/
/BuildDev/
# Unity auto-generated on open
/Packages/packages-lock.json
/ProjectSettings/ShaderGraphSettings.asset
# Contains machine-specific game paths and preferences
/Config.json
# Investigation/build scripts (not part of Unity project)
umaviewer-effect-investigation.md
umamusume.csproj
/bin/
/obj/
# Local web viewer dependencies
/web/pmx-viewer/node_modules/
# Local validation, extracted game data, and investigation outputs
/tmp/
/captures/
/exports/
/_pr_backup_*/
**/__pycache__/
*.pyc
*.log
*.dmp
*.dump
/tools/**/bin/
/tools/**/obj/
# Game authorization payloads, native plugin, and extracted images
/Assets/StreamingAssets/cri_auth*
/Assets/StreamingAssets/sound_proj.acf*
/Assets/Plugins/CySpringPlugin.dll*
/Assets/Resources/text2D/still_*
/Assets/Resources/text2D/bg_*
/Assets/Resources/text2D/tex_env_*
/Assets/Resources/UI/Common/utx_*
/Assets/Resources/UI/Common/jukebox select square.png*
/Assets/Resources/UI/Screenshot *.png*
# Shader bundles and encrypted/runtime extraction artifacts (not native .a libraries)
shader.a
shader_extracted.a
global-metadata*.dat
*.assetbundle
*.dmp
.env
.env.*
*.pem
*.pfx
*.p12
# Handwritten tool project; Unity-generated root IDE projects stay ignored
!/tools/TargetMethodLookup/TargetMethodLookup.csproj
+2 -2
View File
@@ -56,10 +56,10 @@ Shader "Custom/CyalumeSimpleOfficialLike"
{
fixed4 tex = tex2D(_MainTex, i.uv);
// 锟斤拷锟斤拷 discard锟斤拷锟斤拷锟斤拷 alpha clip
// ���� discard������ alpha clip
fixed3 rgb = tex.rgb * _Tint.rgb * _Intensity;
// additive 锟斤拷 alpha 锟斤拷锟斤拷锟斤拷锟斤拷要锟斤拷锟斤拷 1 锟斤拷锟斤拷
// additive �� alpha ��������Ҫ���� 1 ����
return fixed4(rgb, 1.0);
}
ENDCG
+1 -1
View File
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 312ecc39ee9d48b4789a1d4f2dfcf36e
guid: 9c22d58608b730b428538bc5e262ce60
ShaderImporter:
externalObjects: {}
defaultTextures: []
+54
View File
@@ -0,0 +1,54 @@
using UnityEngine;
using UnityEngine.Rendering;
public class DumpFaceShaderProperties : MonoBehaviour
{
void Start()
{
foreach (var r in GetComponentsInChildren<Renderer>(true))
{
foreach (var m in r.sharedMaterials)
{
if (!m || !m.shader) continue;
string rn = r.name.ToLower();
string mn = m.name.ToLower();
string sn = m.shader.name.ToLower();
if (!rn.Contains("face") && !mn.Contains("face") && !sn.Contains("face")
&& !rn.Contains("head") && !mn.Contains("head"))
continue;
Debug.Log($"==== Renderer: {r.name} ====");
Debug.Log($"Material: {m.name}");
Debug.Log($"Shader: {m.shader.name}");
Debug.Log($"receiveShadows={r.receiveShadows}, shadowCastingMode={r.shadowCastingMode}");
var shader = m.shader;
int count = shader.GetPropertyCount();
for (int i = 0; i < count; i++)
{
string prop = shader.GetPropertyName(i);
var type = shader.GetPropertyType(i);
string lower = prop.ToLower();
bool important =
lower.Contains("shadow") ||
lower.Contains("light") ||
lower.Contains("face") ||
lower.Contains("head") ||
lower.Contains("toon") ||
lower.Contains("rim") ||
lower.Contains("normal") ||
lower.Contains("camera") ||
lower.Contains("view");
if (!important) continue;
Debug.Log($"[{type}] {prop}");
}
}
}
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 577536d7b04577e4f97cd0c33c4c54d9
guid: b990bda30f04d40a7b0a111835fddd08
MonoImporter:
externalObjects: {}
serializedVersion: 2
+88
View File
@@ -0,0 +1,88 @@
using System;
using UnityEngine;
public class DumpLoadedBundleAssets : MonoBehaviour
{
[TextArea]
public string[] keywords = new string[]
{
"pfb_env_live10108_blinklight_wash_roof_a_circle",
"pfb_env_live10108_blinklight_wash_wall_a_circle",
"anm_env_live10108_blinklight_wash_wall_a_circle",
"Take 001",
"wash_roof",
"wash_wall",
"live10108"
};
[ContextMenu("Dump Loaded AssetBundles")]
public void DumpLoadedAssetBundles()
{
int bundleCount = 0;
int hitCount = 0;
foreach (var bundle in AssetBundle.GetAllLoadedAssetBundles())
{
if (bundle == null) continue;
bundleCount++;
string bundleName = bundle.name;
if (string.IsNullOrEmpty(bundleName))
bundleName = "<unnamed bundle>";
string[] assetNames = null;
try
{
assetNames = bundle.GetAllAssetNames();
}
catch (Exception e)
{
Debug.Log($"[DumpLoadedBundleAssets] Failed reading asset names from bundle {bundleName}: {e.Message}");
continue;
}
bool bundleHeaderPrinted = false;
if (assetNames != null)
{
foreach (var assetName in assetNames)
{
if (string.IsNullOrEmpty(assetName)) continue;
if (IsMatch(assetName))
{
if (!bundleHeaderPrinted)
{
Debug.Log($"[DumpLoadedBundleAssets] HIT BUNDLE = {bundleName}");
bundleHeaderPrinted = true;
}
Debug.Log($"[DumpLoadedBundleAssets] Asset = {assetName}");
hitCount++;
}
}
}
}
Debug.Log($"[DumpLoadedBundleAssets] Scan finished. Loaded bundles = {bundleCount}, Hits = {hitCount}");
}
private bool IsMatch(string assetName)
{
string lower = assetName.ToLowerInvariant();
foreach (var k in keywords)
{
if (string.IsNullOrWhiteSpace(k)) continue;
if (lower.Contains(k.ToLowerInvariant()))
return true;
}
return false;
}
private void Start()
{
DumpLoadedAssetBundles();
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9459a406c0ff58349bd3255f1827c124
guid: 6ec9e248b5330f046bbc78de329486b1
MonoImporter:
externalObjects: {}
serializedVersion: 2
@@ -0,0 +1,220 @@
using System;
using System.IO;
using UnityEngine;
using UnityEngine.Rendering;
using Gallop.RenderPipeline;
// Isolated GPU contract tests for the shipped shaders, not a whole-Live parity test.
// Expected equations come from local DXBC (documented in LIVE_RENDER_RESTORATION.md).
public static class LiveBloomDiffusionRegression
{
static void Check(bool condition, string message)
{
if (!condition) throw new Exception("LiveBloomDiffusionRegression: " + message);
}
static void Near(Color actual, Color expected, string label)
{
for (int c = 0; c < 3; ++c)
Check(!float.IsNaN(actual[c]) && Math.Abs(actual[c] - expected[c]) < 0.00015f,
label + " channel " + c + " actual=" + actual[c] + " expected=" + expected[c]);
}
static RenderTexture Texture(int width, int height)
{
var rt = new RenderTexture(width, height, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear);
rt.filterMode = FilterMode.Bilinear;
rt.wrapMode = TextureWrapMode.Clamp;
rt.Create();
return rt;
}
static void Fill(RenderTexture rt, Color color)
{
RenderTexture.active = rt;
GL.Clear(false, true, color);
}
static Color Read(RenderTexture rt, Texture2D read)
{
RenderTexture.active = rt;
read.ReadPixels(new Rect(rt.width / 2, rt.height / 2, 1, 1), 0, 0);
read.Apply();
return read.GetPixel(0, 0);
}
static Color Composite(Color source, Color bloom, float weight, bool screen)
{
Color result = source;
for (int c = 0; c < 3; ++c)
result[c] = screen ? 1 - (1 - source[c]) * (1 - bloom[c] * weight) : source[c] + bloom[c] * weight;
return result;
}
static Color Diffusion(Color composite, Color blur, Vector4 parameters)
{
Color result = composite;
for (int c = 0; c < 3; ++c)
{
float square = composite[c] * composite[c];
result[c] = Mathf.Max(composite[c], Mathf.Max(0, square + blur[c] - blur[c] * square - parameters.w)) * parameters.x;
}
float luminance = result.r * 0.2125f + result.g * 0.7154f + result.b * 0.0721f;
for (int c = 0; c < 3; ++c)
result[c] = (luminance + parameters.y * (result[c] - luminance) - 0.5f) * parameters.z + 0.5f;
return result;
}
public static void RunGpu()
{
Check(Application.isBatchMode, "run only in an isolated batch-mode project");
string path = Environment.GetEnvironmentVariable("UMA_POSTFILM_TEST_BUNDLE");
Check(File.Exists(path), "missing local shader bundle");
var bundle = AssetBundle.LoadFromFile(path);
Check(bundle != null, "bundle load");
var previous = RenderTexture.active;
var source = Texture(64, 32);
var low = Texture(16, 8);
var vertical = Texture(16, 8);
var bloom = Texture(16, 8);
var target = Texture(64, 32);
var read = new Texture2D(1, 1, TextureFormat.RGBAFloat, false, true);
Material fast = null, post = null, diffusion = null, diffusionDof = null;
string[] floats = { "_bloomDofWeight", "_BloomIsScreenBlend" };
float[] oldFloats = Array.ConvertAll(floats, Shader.GetGlobalFloat);
string[] vectors = { "_Parameter", "_ColorParam", "_PixelSize" };
Vector4[] oldVectors = Array.ConvertAll(vectors, Shader.GetGlobalVector);
var oldBloom = Shader.GetGlobalTexture("_Bloom");
var oldRgb = Shader.GetGlobalTexture("_RgbTex");
var oldLowBackground = Shader.GetGlobalTexture("_TapLowBackground");
try
{
foreach (var shader in bundle.LoadAllAssets<Shader>())
{
if (shader.name == "Gallop/ImageEffects/FastBloom") fast = new Material(shader);
if (shader.name == "Gallop/ImageEffects/Rich/PostBloom_Rich") post = new Material(shader);
if (shader.name == "Gallop/ImageEffects/Rich/PostDiffusionBloom_Rich") diffusion = new Material(shader);
if (shader.name == "Gallop/ImageEffects/Rich/PostDiffusionDofBloom_Rich") diffusionDof = new Material(shader);
}
Check(fast && post && diffusion && diffusionDof, "four shipped shaders present");
Check(fast.shader.isSupported && post.shader.isSupported && diffusion.shader.isSupported &&
diffusionDof.shader.isSupported, "shaders supported");
post.SetColor("_DimmerColor", Color.white);
Color input = new Color(0.05f, 0.4f, 1.4f, 1);
Fill(source, input);
foreach (float threshold in new[] { 0f, 0.1f, 0.23f })
foreach (float intensity in new[] { 0f, 0.5f, 0.9f, 2f })
{
Color expected = input;
for (int c = 0; c < 3; ++c) expected[c] = Mathf.Max(0, input[c] - threshold) * intensity;
// Zero-radius native path: threshold/intensity both in downsample.
Shader.SetGlobalVector("_Parameter", new Vector4(0, 0, threshold, intensity));
Graphics.Blit(source, bloom, fast, 1);
Near(Read(bloom, read), expected, "zero-radius extraction");
// Positive radius path: neutral reduction, threshold only in vertical,
// intensity only in horizontal. Both Gaussian kernels sum to one.
Shader.SetGlobalVector("_Parameter", new Vector4(0, 0, 0, 1));
Graphics.Blit(source, low, fast, 1);
Shader.SetGlobalVector("_Parameter", new Vector4(1f / 1024, 1f / 512, threshold, intensity));
Graphics.Blit(low, vertical, fast, 2);
Graphics.Blit(vertical, bloom, fast, 3);
Near(Read(bloom, read), expected, "two-axis extraction (no double threshold/intensity)");
}
var bloomColor = new Color(0.1f, 0.3f, 0.8f, 1);
var blurColor = new Color(0.2f, 0.6f, 0.9f, 1);
Fill(bloom, bloomColor);
Fill(low, blurColor);
Shader.SetGlobalTexture("_Bloom", bloom);
Shader.SetGlobalTexture("_RgbTex", low);
foreach (bool screen in new[] { false, true, false })
foreach (float weight in new[] { 0f, 0.5f, 1f })
{
Shader.SetGlobalFloat("_bloomDofWeight", weight);
Shader.SetGlobalFloat("_BloomIsScreenBlend", screen ? 1 : 0);
var composite = Composite(input, bloomColor, weight, screen);
Graphics.Blit(source, target, post, 0);
Near(Read(target, read), composite, "Bloom Add/Screen HDR");
foreach (var parameters in new[] { new Vector4(1, 1, 1, 0), new Vector4(0.8f, 0.5f, 1.1f, 0.2f), new Vector4(1.2f, 0, 0.9f, 0.1f) })
{
Shader.SetGlobalVector("_ColorParam", parameters);
Graphics.Blit(source, target, diffusion, 2);
Near(Read(target, read), Diffusion(composite, blurColor, parameters), "Diffusion composite and color order");
}
}
// Pass 1 combines Bloom and the first film layer. Pass 3 consumes
// the previous output without sampling Bloom again.
var film = new Gallop.RenderPipeline.ScreenOverlayRender.Render();
var first = Gallop.RenderPipeline.ScreenOverlayRender.Parameter.Default();
first.PostFilmMode = Gallop.ImageEffect.ScreenOverlay.Overlay.PostFilmMode.Add;
first.PostFilmPower = 0.5f;
first.PostFilmColor0 = first.PostFilmColor1 = first.PostFilmColor2 = first.PostFilmColor3 =
new Color(0.4f, 0.2f, 0.1f, 1f);
var later = Gallop.RenderPipeline.ScreenOverlayRender.Parameter.Default();
later.PostFilmMode = Gallop.ImageEffect.ScreenOverlay.Overlay.PostFilmMode.Lerp;
later.PostFilmPower = 0.5f;
later.PostFilmColor0 = later.PostFilmColor1 = later.PostFilmColor2 = later.PostFilmColor3 = Color.black;
Fill(source, new Color(0.2f, 0.3f, 0.4f, 1f));
Fill(bloom, new Color(0.1f, 0.2f, 0.3f, 1f));
Shader.SetGlobalTexture("_Bloom", bloom);
Shader.SetGlobalFloat("_bloomDofWeight", 0.5f);
Shader.SetGlobalFloat("_BloomIsScreenBlend", 0f);
post.SetColor("_DimmerColor", Color.white);
using (var cmd = new CommandBuffer())
{
film.Blit(ref first, cmd, RenderTextureHandle.Make(source), RenderTextureHandle.Make(target), post, 1);
Graphics.ExecuteCommandBuffer(cmd);
cmd.Clear();
Near(Read(target, read), new Color(0.45f, 0.5f, 0.6f, 1f), "Bloom plus first film layer");
film.Blit(ref later, cmd, RenderTextureHandle.Make(target), RenderTextureHandle.Make(source), post, 3);
Graphics.ExecuteCommandBuffer(cmd);
cmd.Clear();
Near(Read(source, read), new Color(0.225f, 0.25f, 0.3f, 1f), "second film layer without repeated Bloom");
film.Blit(ref later, cmd, RenderTextureHandle.Make(source), RenderTextureHandle.Make(target), post, 3);
Graphics.ExecuteCommandBuffer(cmd);
Near(Read(target, read), new Color(0.1125f, 0.125f, 0.15f, 1f), "third film layer without repeated Bloom");
}
// The DiffusionDofBloom mode can remain selected when its Bloom flag
// is off. Execute binds black before the conditional extraction; verify
// that its first film pass does not reuse the previous frame's bloom.
var noBloomFilm = Gallop.RenderPipeline.ScreenOverlayRender.Parameter.Default();
noBloomFilm.PostFilmMode = Gallop.ImageEffect.ScreenOverlay.Overlay.PostFilmMode.Lerp;
noBloomFilm.PostFilmPower = 0.5f;
noBloomFilm.PostFilmColor0 = noBloomFilm.PostFilmColor1 =
noBloomFilm.PostFilmColor2 = noBloomFilm.PostFilmColor3 = Color.black;
diffusionDof.SetColor("_DimmerColor", Color.white);
Fill(source, new Color(0.2f, 0.3f, 0.4f, 1f));
Fill(low, new Color(0.2f, 0.3f, 0.4f, 1f));
Fill(bloom, new Color(0.9f, 0.9f, 0.9f, 1f));
// Suppress diffusion's extra lift so this fixture isolates stale Bloom.
Shader.SetGlobalVector("_ColorParam", new Vector4(1f, 1f, 1f, 1f));
Shader.SetGlobalTexture("_TapLowBackground", low);
Shader.SetGlobalTexture("_Bloom", bloom);
Shader.SetGlobalTexture("_Bloom", Texture2D.blackTexture);
using (var cmd = new CommandBuffer())
{
film.Blit(ref noBloomFilm, cmd, RenderTextureHandle.Make(source),
RenderTextureHandle.Make(target), diffusionDof, 11);
Graphics.ExecuteCommandBuffer(cmd);
}
Near(Read(target, read), new Color(0.1f, 0.15f, 0.2f, 1f),
"DiffusionDofBloom first film pass with Bloom disabled");
Debug.Log("LiveBloomDiffusionRegression GPU PASS: 24 extraction, 9 Bloom and 27 diffusion equations; HDR/Add/Screen, three-layer Bloom-once PostFilm, and Bloom-disabled DiffusionDofBloom first film pass");
}
finally
{
for (int i = 0; i < floats.Length; ++i) Shader.SetGlobalFloat(floats[i], oldFloats[i]);
for (int i = 0; i < vectors.Length; ++i) Shader.SetGlobalVector(vectors[i], oldVectors[i]);
Shader.SetGlobalTexture("_Bloom", oldBloom);
Shader.SetGlobalTexture("_RgbTex", oldRgb);
Shader.SetGlobalTexture("_TapLowBackground", oldLowBackground);
RenderTexture.active = previous;
foreach (var rt in new[] { source, low, vertical, bloom, target }) { rt.Release(); UnityEngine.Object.DestroyImmediate(rt); }
UnityEngine.Object.DestroyImmediate(read);
if (fast) UnityEngine.Object.DestroyImmediate(fast);
if (post) UnityEngine.Object.DestroyImmediate(post);
if (diffusion) UnityEngine.Object.DestroyImmediate(diffusion);
if (diffusionDof) UnityEngine.Object.DestroyImmediate(diffusionDof);
bundle.Unload(true);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e9872ed211ac4fa4bdbde21cc709704e
+81
View File
@@ -0,0 +1,81 @@
#if UNITY_EDITOR
using System;
using System.Linq;
using UnityEngine;
// Reference manifests describe timeline frames; video extraction offsets must NOT
// be applied a second time when seeking the Live Director.
[Serializable]
public sealed class LiveCapturePlan
{
public int songId = 1001;
public string stageId = "10102";
public string[] characters;
public int fps = 60, width = 1920, height = 1080;
public int[] frames;
public static LiveCapturePlan Default()
{
return new LiveCapturePlan {
characters = new[] { "1068_00", "1006_00", "1030_00" }
.Concat(Enumerable.Repeat("1003_00", 15)).ToArray(),
frames = new[] { 1710, 1732, 1740, 1755, 3000 }
};
}
public void Validate()
{
if (songId <= 0 || string.IsNullOrEmpty(stageId) || fps != 60 ||
width < 1 || height < 1 || width > 8192 || height > 8192 ||
frames == null || frames.Length == 0 || frames.Any(x => x < 0) ||
frames.Distinct().Count() != frames.Length || characters == null || characters.Length == 0)
throw new InvalidOperationException("Invalid Live capture manifest (60fps timeline required).");
foreach (string character in characters)
{
ParseCharacterIdentity(character, out _, out _);
}
}
// The first separator belongs to the capture identity; remaining separators
// are part of a generic costume ID (e.g. 0001_00), not extra characters.
public static void ParseCharacterIdentity(string identity, out int id, out string costume)
{
int separator = (identity ?? "").IndexOf('_');
id = 0;
costume = separator >= 0 ? identity.Substring(separator + 1) : "";
if (separator <= 0 || !int.TryParse(identity.Substring(0, separator), out id) || id <= 0 ||
costume.Split('_').Any(part => part.Length == 0 || part.Any(c => c < '0' || c > '9')))
throw new InvalidOperationException("Invalid reference character/costume identity.");
}
public void SelectFrames(int[] selected)
{
if (selected == null || selected.Length == 0 || selected.Any(x => !frames.Contains(x)))
throw new InvalidOperationException("Requested capture frames must exist in the reference manifest.");
var previous = frames;
frames = selected;
try { Validate(); } catch { frames = previous; throw; }
}
public bool MatchesIdentity(int song, string stage, string[] cast)
{
return song == songId && stage == stageId && cast != null && characters.SequenceEqual(cast);
}
}
// Require consecutive renders at the intended timeline frame, not editor update
// ticks. Duplicate camera callbacks in one Unity frame cannot advance the gate.
public sealed class LiveCaptureStabilityGate
{
readonly int required;
int count, lastUnityFrame = -1;
public LiveCaptureStabilityGate(int requiredFrames = 60) { required = requiredFrames; }
public void Reset() { count = 0; lastUnityFrame = -1; }
public bool Observe(float seconds, int timelineFrame, int fps, int unityFrame, bool ready)
{
if (unityFrame == lastUnityFrame) return false;
lastUnityFrame = unityFrame;
if (!ready || float.IsNaN(seconds) || float.IsInfinity(seconds) ||
Math.Abs((double)seconds * fps - timelineFrame) > .1)
{ count = 0; return false; }
return ++count >= required;
}
}
#endif
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7d316dcf24d44b9c853e3db15e3d802d
@@ -0,0 +1,105 @@
#if UNITY_EDITOR
using System;
using System.IO;
using UnityEngine;
using Gallop.RenderPipeline;
using Overlay = Gallop.ImageEffect.ScreenOverlay.Overlay;
public static class LiveCaptureContractRegression
{
static void Check(bool ok, string message)
{ if (!ok) throw new Exception("LiveCaptureContractRegression: " + message); }
static void Reject(Action action, string message)
{
bool rejected = false;
try { action(); } catch (InvalidOperationException) { rejected = true; }
Check(rejected, message);
}
public static void Run()
{
Check(Application.isBatchMode, "isolated batch only");
var plan = LiveCapturePlan.Default(); plan.Validate();
LiveCapturePlan.ParseCharacterIdentity("1068_0001_00", out int genericId, out string genericCostume);
Check(genericId == 1068 && genericCostume == "0001_00", "full generic costume preserved");
LiveCapturePlan.ParseCharacterIdentity("1068_00", out int normalId, out string normalCostume);
Check(normalId == 1068 && normalCostume == "00", "short costume preserved");
foreach (string bad in new[] { "", "1068", "1068_", "1068__00", "1068_00_", "1068_../00", "0_00" })
Reject(() => LiveCapturePlan.ParseCharacterIdentity(bad, out _, out _), "reject malformed identity");
var genericPlan = LiveCapturePlan.Default();
genericPlan.characters[0] = "1068_0001_00";
genericPlan.Validate();
var genericRoundTrip = JsonUtility.FromJson<LiveCapturePlan>(JsonUtility.ToJson(genericPlan));
genericRoundTrip.Validate();
Check(genericRoundTrip.MatchesIdentity(genericPlan.songId, genericPlan.stageId, genericPlan.characters), "generic identity survives reload");
Check(!genericRoundTrip.MatchesIdentity(genericPlan.songId, genericPlan.stageId, LiveCapturePlan.Default().characters), "generic costume not truncated during comparison");
Check(plan.characters.Length == 18 && plan.MatchesIdentity(1001, "10102", plan.characters), "default aligned identity");
Check(!plan.MatchesIdentity(1176, "10102", plan.characters) && !plan.MatchesIdentity(1001, "other", plan.characters), "reject wrong song/stage");
var cast = (string[])plan.characters.Clone(); cast[0] = "1068_02";
Check(!plan.MatchesIdentity(1001, "10102", cast), "reject wrong costume");
Reject(() => plan.SelectFrames(new[] { 1 }), "reject reference-missing frame");
Reject(() => plan.SelectFrames(new[] { 1740, 1740 }), "reject duplicate and restore plan");
Check(plan.frames.Length == 5, "bad subset must not mutate plan");
plan.SelectFrames(new[] { 3000, 1740 });
Check(plan.frames[0] == 3000, "backseek capture order retained");
var restored = JsonUtility.FromJson<LiveCapturePlan>(JsonUtility.ToJson(plan));
restored.Validate(); Check(restored.frames[1] == 1740 && restored.characters[2] == "1030_00", "domain-reload plan roundtrip");
foreach (string name in new[] { "UMA_CAPTURE_TEST_REFERENCE1001", "UMA_CAPTURE_TEST_REFERENCE1176" })
{
string path = Environment.GetEnvironmentVariable(name);
Check(Path.GetFileName(path) == "manifest.json" && File.Exists(path), "explicit real reference manifest");
var reference = JsonUtility.FromJson<LiveCapturePlan>(File.ReadAllText(path));
reference.Validate();
Check(reference.songId == (name.EndsWith("1176") ? 1176 : 1001), "actual song manifest identity");
reference.SelectFrames(new[] { reference.frames[1], reference.frames[reference.frames.Length - 1] });
}
var gate = new LiveCaptureStabilityGate();
for (int i = 0; i < 59; ++i)
{
Check(!gate.Observe(50, 3000, 60, i, true), "needs sixty distinct renders");
Check(!gate.Observe(50, 3000, 60, i, true), "duplicate callback doesn't advance");
}
Check(gate.Observe(50, 3000, 60, 59, true), "sixtieth correct render ready");
gate.Reset();
for (int i = 0; i < 80; ++i) Check(!gate.Observe(49, 3000, 60, i, true), "old frame cannot be mislabeled");
Check(!gate.Observe(float.NaN, 3000, 60, 80, true), "invalid clock");
for (int i = 81; i < 140; ++i) Check(!gate.Observe(50, 3000, 60, i, true), "settle after mismatch");
Check(!gate.Observe(50, 3000, 60, 140, false), "shader/feature unavailable clears settle");
for (int i = 141; i < 200; ++i) Check(!gate.Observe(50, 3000, 60, i, true), "settle after readiness loss");
Check(gate.Observe(50, 3000, 60, 200, true), "stable readiness restored");
// Simulate the exact scoped noFilm parameter mutation without a Live camera.
// The URP 14 callback order is begin -> AddRenderPasses -> Execute -> end;
// this contract tests struct write-back and restoration, not GPU pixels.
var runtime = PostImageEffectFeature.RuntimeParameter;
var old = runtime.DofDiffuionBloomOverlay;
try
{
var film = old;
film.Overlay1.PostFilmMode = Overlay.PostFilmMode.Add;
film.Overlay1.PostFilmPower = .31f;
film.Overlay2.PostFilmMode = Overlay.PostFilmMode.Mul;
film.Overlay2.PostFilmPower = .42f;
film.Overlay3.PostFilmMode = Overlay.PostFilmMode.ScreenBlend;
film.Overlay3.PostFilmPower = .53f;
runtime.DofDiffuionBloomOverlay = film;
LiveRenderCapture.SuppressFilm();
var suppressed = runtime.DofDiffuionBloomOverlay;
Check(!suppressed.IsEnableOverlay && suppressed.Overlay1.PostFilmPower == .31f &&
suppressed.Overlay2.PostFilmPower == .42f && suppressed.Overlay3.PostFilmPower == .53f,
"noFilm must suppress all three overlay validity flags without changing strengths");
Reject(() => LiveRenderCapture.SuppressFilm(), "nested film suppression must fail");
suppressed.BloomIntensity = .67f; // another callback can change unrelated timeline state
runtime.DofDiffuionBloomOverlay = suppressed;
LiveRenderCapture.RestoreFilm();
var restoredFilm = runtime.DofDiffuionBloomOverlay;
Check(restoredFilm.Overlay1.PostFilmMode == Overlay.PostFilmMode.Add &&
restoredFilm.Overlay2.PostFilmMode == Overlay.PostFilmMode.Mul &&
restoredFilm.Overlay3.PostFilmMode == Overlay.PostFilmMode.ScreenBlend &&
restoredFilm.BloomIntensity == .67f,
"restoration must preserve other timeline fields and all three modes");
LiveRenderCapture.RestoreFilm(); // harmless after an early cancellation
}
finally { LiveRenderCapture.RestoreFilm(); runtime.DofDiffuionBloomOverlay = old; }
Debug.Log("LiveCaptureContractRegression PASS: two actual reference manifests, identity/subset/reload, stale-frame/duplicate/readiness gates. No full Live was loaded.");
}
}
#endif
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6aeed533117c4250b51792e7cee86d78
@@ -0,0 +1,193 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using Gallop.Live.Cutt;
using Gallop.RenderPipeline;
using UnityEngine;
using UnityEngine.Rendering;
using Overlay = Gallop.ImageEffect.ScreenOverlay.Overlay;
// Film-only attribution on already captured real pixels. This is NOT a full Live replay:
// input is before DOF/Bloom/diffusion; actual depth is absent. Synced BlinkLight RGB
// is available only when explicitly recovered from same-frame runtime metadata.
// Raw-depth sweeps are explicitly counterfactual, not estimated scene depth.
public static class LiveCapturedFilmAttribution
{
[Serializable] public class CurveKey { public float time, value, inSlope, outSlope, inWeight, outWeight; public int weightedMode; }
[Serializable] public class Row { public LiveTimelineKeyPostFilmData key; public CurveKey[] curveKeys; public int preWrap, postWrap; }
[Serializable] public class Track { public string name; public int attribute, playMode; public Row[] rows; }
[Serializable] public class Frame { public int frame; public string imagePath, sha256; public float[] expectedPowers; public Color rawBlinkRgb; public Color[] expectedColor0; public string blinkLookup; }
[Serializable] public class Input { public Track[] tracks; public Frame[] frames; }
[Serializable] public class Stats {
public string image, stage; public int frame, layer, omittedLayer; public float rawDepth;
public float[] mean, maximum; public float anyChannelOverOne, allChannelsNearWhite;
public float shaderParityMaxError;
}
[Serializable] public class ParameterRecord {
public int frame, layer; public string mode; public float power, depthPower, depthClip;
public bool valid, inverse; public Vector2 offset; public Vector4 options; public Color[] colors;
}
[Serializable] public class Report {
public string limitation, colorSpace, inputPath; public List<ParameterRecord> parameters = new List<ParameterRecord>();
public List<Stats> stages = new List<Stats>();
}
static void Check(bool ok, string message) { if (!ok) throw new Exception("CapturedFilmAttribution: " + message); }
static RenderTexture RT(int w, int h, RenderTextureFormat format) {
var rt = new RenderTexture(w, h, 0, format, RenderTextureReadWrite.Default) { filterMode = FilterMode.Bilinear, wrapMode = TextureWrapMode.Clamp };
rt.Create(); return rt;
}
static Color[] Read(RenderTexture rt) {
var old = RenderTexture.active;
var read = new Texture2D(rt.width, rt.height, TextureFormat.RGBAFloat, false, true);
try { RenderTexture.active = rt; read.ReadPixels(new Rect(0,0,rt.width,rt.height),0,0); read.Apply(); return read.GetPixels(); }
finally { RenderTexture.active = old; UnityEngine.Object.DestroyImmediate(read); }
}
static void Save(RenderTexture rt, string path) {
var old = RenderTexture.active;
var read = new Texture2D(rt.width, rt.height, TextureFormat.RGBA32, false, true);
try { RenderTexture.active = rt; read.ReadPixels(new Rect(0,0,rt.width,rt.height),0,0); read.Apply(); File.WriteAllBytes(path, read.EncodeToPNG()); }
finally { RenderTexture.active = old; UnityEngine.Object.DestroyImmediate(read); }
}
static Stats Measure(Color[] pixels, int frame, float depth, int layer, string stage, int omitted = 0) {
var sums = new double[3]; var maximum = new float[3]; int over = 0, white = 0;
foreach (var p in pixels) {
bool any = false, all = true;
for (int c = 0; c < 3; ++c) {
Check(!float.IsNaN(p[c]) && !float.IsInfinity(p[c]), "non-finite output");
sums[c] += p[c]; maximum[c] = Mathf.Max(maximum[c], p[c]);
any |= p[c] > 1.0001f; all &= p[c] >= 250f / 255f;
}
if (any) ++over; if (all) ++white;
}
return new Stats { frame=frame, rawDepth=depth, layer=layer, stage=stage, omittedLayer=omitted,
mean=Array.ConvertAll(sums, x => (float)(x / pixels.Length)), maximum=maximum,
anyChannelOverOne=over/(float)pixels.Length, allChannelsNearWhite=white/(float)pixels.Length };
}
static void Draw(ScreenOverlayRender.Render renderer, ref ScreenOverlayRender.Parameter param,
Texture source, RenderTexture target, Material material, int pass) {
using (var cmd = new CommandBuffer()) {
if (source is RenderTexture rt)
renderer.Blit(ref param, cmd, RenderTextureHandle.Make(rt), RenderTextureHandle.Make(target), material, pass);
else throw new Exception("Source must have an explicit RT descriptor");
Graphics.ExecuteCommandBuffer(cmd);
}
}
public static void Run() {
Check(Application.isBatchMode, "isolated batch-mode only; do not use user's Editor");
Check(QualitySettings.activeColorSpace == ColorSpace.Gamma, "captured Gamma project required");
string inputPath = Environment.GetEnvironmentVariable("UMA_FILM_ATTRIBUTION_INPUT");
string output = Environment.GetEnvironmentVariable("UMA_FILM_ATTRIBUTION_OUTPUT");
string bundlePath = Environment.GetEnvironmentVariable("UMA_POSTFILM_TEST_BUNDLE");
Check(File.Exists(inputPath) && File.Exists(bundlePath) && !string.IsNullOrEmpty(output), "missing input environment");
Directory.CreateDirectory(output);
var input = JsonUtility.FromJson<Input>(File.ReadAllText(inputPath));
Check(input.tracks.Length == 3 && input.frames.Length == 3, "three authored tracks and three real frames");
var tracks = new LiveTimelineKeyPostFilmDataList[3];
for (int i = 0; i < 3; ++i) {
var t = input.tracks[i];
tracks[i] = new LiveTimelineKeyPostFilmDataList { attribute=(LiveTimelineKeyDataListAttr)t.attribute, playMode=(TimelineKeyPlayMode)t.playMode };
foreach (var row in t.rows) {
var keys = Array.ConvertAll(row.curveKeys, x => new Keyframe(x.time,x.value,x.inSlope,x.outSlope,x.inWeight,x.outWeight) { weightedMode=(WeightedMode)x.weightedMode });
row.key.curve = new AnimationCurve(keys) { preWrapMode=(WrapMode)row.preWrap, postWrapMode=(WrapMode)row.postWrap };
tracks[i].thisList.Add(row.key);
}
}
var report = new Report { inputPath=inputPath, colorSpace=QualitySettings.activeColorSpace.ToString(),
limitation="Film-only, before-DOF/Bloom real captured input. Raw hardware-depth sweep is counterfactual; no captured depth, captured BlinkLight RGB when present, no color correction or game-reference parity. Float probes measure shader outputs before ARGB32 clamping; next layer consumes ARGB32 just as native film ping-pong." };
var bundle = AssetBundle.LoadFromFile(bundlePath);
Check(bundle != null, "load official shader bundle");
Material film=null, combined=null;
var oldDepth=Shader.GetGlobalTexture("_CameraDepthTexture"); var oldScroll=Shader.GetGlobalVector("_GlobalScreenUVScrollParam");
var oldInverseSize=Shader.GetGlobalVector("_InvRenderTargetSize");
var depth = new Texture2D(1,1,TextureFormat.RFloat,false,true) { filterMode=FilterMode.Point };
try {
foreach (var shader in bundle.LoadAllAssets<Shader>()) {
if (shader.name == "Gallop/ImageEffects/Rich/PostBlit_Rich") film = new Material(shader);
if (shader.name == "Gallop/ImageEffects/Rich/PostDiffusionDofBloom_Rich") combined = new Material(shader);
}
Check(film != null && film.shader.isSupported && combined != null && combined.shader.isSupported, "shipped shaders supported");
film.SetColor("_DimmerColor",Color.white); combined.SetColor("_DimmerColor",Color.white);
Shader.DisableKeyword("COLOR_ONLY"); // Match production's color-layer Apply (no enabled blend keyword).
Shader.SetGlobalVector("_GlobalScreenUVScrollParam",Vector4.zero);
var renderer = new ScreenOverlayRender.Render();
foreach (var f in input.frames) {
using (var hash = SHA256.Create()) {
string actual = BitConverter.ToString(hash.ComputeHash(File.ReadAllBytes(f.imagePath))).Replace("-","").ToLowerInvariant();
Check(actual==f.sha256,"input hash " + f.frame);
}
var image = new Texture2D(2,2,TextureFormat.RGBA32,false,true);
Check(image.LoadImage(File.ReadAllBytes(f.imagePath),false),"load real pixels");
var source = RT(image.width,image.height,RenderTextureFormat.ARGB32);
var a = RT(image.width,image.height,RenderTextureFormat.ARGB32);
var b = RT(image.width,image.height,RenderTextureFormat.ARGB32);
var probe = RT(image.width,image.height,RenderTextureFormat.ARGBFloat);
var alternate = RT(image.width,image.height,RenderTextureFormat.ARGBFloat);
try {
Graphics.Blit(image,source);
Shader.SetGlobalVector("_InvRenderTargetSize",new Vector4(1f/image.width,1f/image.height,image.width,image.height));
var param = new ScreenOverlayRender.Parameter[3];
for (int i=0;i<3;++i) {
var overlay = new Overlay();
if (f.expectedColor0 != null && f.expectedColor0.Length == 3) {
// Captured current-stage raw RGB, recovered only from a unit-brightness
// sync layer. Native key brightness is applied inside Evaluate.
LivePostFilmTimeline.BlinkColorResolver resolve = delegate(string name,int hash,int index,out Color raw) {
raw = f.rawBlinkRgb; return true;
};
LivePostFilmTimeline.Evaluate(tracks[i],f.frame,TimelinePlayerMode.Default,overlay,resolve);
var expected=f.expectedColor0[i]; var actual=overlay.postFilmColor0;
Check(Mathf.Abs(actual.r-expected.r)<0.0002f && Mathf.Abs(actual.g-expected.g)<0.0002f &&
Mathf.Abs(actual.b-expected.b)<0.0002f && Mathf.Abs(actual.a-expected.a)<0.0002f,
"captured synced color0 frame="+f.frame+" layer="+(i+1)+" actual="+actual+" expected="+expected);
} else LivePostFilmTimeline.Evaluate(tracks[i],f.frame,TimelinePlayerMode.Default,overlay);
param[i]=ScreenOverlayRender.Parameter.Default(); param[i].Setup(overlay);
Check(Mathf.Abs(param[i].PostFilmPower-f.expectedPowers[i])<0.0002f,"evaluated power vs captured runtime frame="+f.frame+" layer="+(i+1)+" actual="+param[i].PostFilmPower+" expected="+f.expectedPowers[i]);
report.parameters.Add(new ParameterRecord { frame=f.frame, layer=i+1, mode=param[i].PostFilmMode.ToString(),power=param[i].PostFilmPower,depthPower=param[i].DepthPower,depthClip=param[i].DepthClip,valid=param[i].IsValidity,inverse=param[i].InverseVignette,offset=param[i].PostFilmOffsetParam,options=param[i].PostFilmOptionParam,colors=new[]{param[i].PostFilmColor0,param[i].PostFilmColor1,param[i].PostFilmColor2,param[i].PostFilmColor3} });
}
report.stages.Add(Measure(Read(source),f.frame,-1,0,"input"));
foreach (float raw in new[]{0f,0.02f,0.1f,1f}) {
depth.SetPixel(0,0,new Color(raw,0,0,1)); depth.Apply(); Shader.SetGlobalTexture("_CameraDepthTexture",depth);
RenderTexture current=source;
for (int i=0;i<3;++i) {
RenderTexture next = current==a ? b : a;
if (param[i].IsValidity) {
Draw(renderer,ref param[i],current,probe,film,i==0?1:3);
// Production later-pass arithmetic should match plain PostBlit on identical inputs.
Draw(renderer,ref param[i],current,alternate,combined,13);
var values=Read(probe); var other=Read(alternate); float maxError=0;
for (int n=0;n<values.Length;++n) for (int c=0;c<3;++c) maxError=Mathf.Max(maxError,Mathf.Abs(values[n][c]-other[n][c]));
Check(maxError<0.002f,"film variant parity " + maxError);
var measured=Measure(values,f.frame,raw,i+1,"before-output-clamp"); measured.shaderParityMaxError=maxError; report.stages.Add(measured);
Draw(renderer,ref param[i],current,next,film,i==0?1:3);
} else Graphics.Blit(current,next);
string name="frame"+f.frame.ToString("D6")+"_rawdepth"+raw.ToString("0.00",CultureInfo.InvariantCulture)+"_layer"+(i+1)+".png";
Save(next,Path.Combine(output,name)); var s=Measure(Read(next),f.frame,raw,i+1,"ARGB32"); s.image=name; report.stages.Add(s); current=next;
}
// Leave-one-layer-out experiments retain actual authored powers/order of all other layers.
for (int omitted=1;omitted<=3;++omitted) {
current=source;
for (int i=0;i<3;++i) {
RenderTexture next=current==a ? b : a;
if (i+1!=omitted && param[i].IsValidity) Draw(renderer,ref param[i],current,next,film,i==0?1:3);
else Graphics.Blit(current,next);
current=next;
}
string name="frame"+f.frame.ToString("D6")+"_rawdepth"+raw.ToString("0.00",CultureInfo.InvariantCulture)+"_omit"+omitted+".png";
Save(current,Path.Combine(output,name)); var s=Measure(Read(current),f.frame,raw,3,"counterfactual-omit",omitted); s.image=name; report.stages.Add(s);
}
}
} finally {
foreach (var rt in new[]{source,a,b,probe,alternate}) { rt.Release(); UnityEngine.Object.DestroyImmediate(rt); }
UnityEngine.Object.DestroyImmediate(image);
}
}
File.WriteAllText(Path.Combine(output,"report.json"),JsonUtility.ToJson(report,true));
Debug.Log("LiveCapturedFilmAttribution PASS: real PNG hashes, nine exact runtime powers, captured sync RGB when provided, authored curves, film variant parity, counterfactual depth sweeps and omit-one layers. NOT full Live parity. Output="+output);
} finally {
Shader.SetGlobalTexture("_CameraDepthTexture",oldDepth); Shader.SetGlobalVector("_GlobalScreenUVScrollParam",oldScroll); Shader.SetGlobalVector("_InvRenderTargetSize",oldInverseSize);
UnityEngine.Object.DestroyImmediate(depth); if(film!=null)UnityEngine.Object.DestroyImmediate(film); if(combined!=null)UnityEngine.Object.DestroyImmediate(combined); bundle.Unload(true);
}
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: d017b2b13aaf198478d9d166f535042e
guid: eb50b061425a55a418609340033b5c03
MonoImporter:
externalObjects: {}
serializedVersion: 2
@@ -0,0 +1,313 @@
using System;
using System.IO;
using Gallop.Live.Cutt;
using Gallop.RenderPipeline;
using UnityEngine;
using UnityEngine.Rendering;
public static class LiveColorCorrectionRegression
{
static void Check(bool condition, string message)
{
if (!condition) throw new Exception("LiveColorCorrectionRegression: " + message);
}
static void Draw(Material material, Texture lut, float saturation, Texture source, RenderTexture output)
{
using (var cmd = new CommandBuffer())
{
// Deliberately poison uniforms: production must establish all draw state.
Shader.SetGlobalFloat("_PostFilmPower", 1);
Shader.SetGlobalFloat("_Saturation", -8);
SimpleColorCorrectionPass.Render(cmd, material, lut, saturation,
new RenderTargetIdentifier(source), new RenderTargetIdentifier(output));
Graphics.ExecuteCommandBuffer(cmd);
Check(Shader.GetGlobalTexture("_RgbTex") == Texture2D.blackTexture, "release LUT global binding");
}
}
static void RunSchedule()
{
int checks = 0;
foreach (int evt in new[] { 500, 550, 600 })
for (int mask = 1; mask < 8; ++mask)
{
bool dof = (mask & 1) != 0, radial = (mask & 2) != 0, color = (mask & 4) != 0;
var schedule = PostImageEffectFeature.GetColorChainSchedule(dof, radial, color,
(UnityEngine.Rendering.Universal.RenderPassEvent)evt);
// Model source ownership in actual event order; a camera resolve consumes temps.
var steps = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<int, string>>();
Action<int, string> add = (e, name) => steps.Add(new System.Collections.Generic.KeyValuePair<int, string>(e, name));
add(schedule.FirstEvent, "source");
if (dof) add(evt, "dof");
if (radial) add(evt, "radial");
if (color) add(550, "color");
if (schedule.EarlyResolve) add(550, "resolve");
if (schedule.FinalResolve) add(schedule.LastEvent, "resolve");
// LINQ OrderBy is stable, like URP's pass event sort.
bool sourceIsCamera = true, initialized = false;
foreach (var step in System.Linq.Enumerable.OrderBy(steps, x => x.Key))
{
if (step.Value == "source") { initialized = true; continue; }
Check(initialized, "source setup must run first");
if ((step.Value == "dof" || step.Value == "radial") && step.Key > 550)
Check(sourceIsCamera, "late effects must not discard an unresolved color temp");
if (step.Value == "color" || step.Value == "radial" || (step.Value == "dof" && step.Key <= 550))
sourceIsCamera = false;
if (step.Value == "resolve") sourceIsCamera = true;
}
Check(sourceIsCamera, "final result must resolve to camera");
checks++;
}
Debug.Log("LiveColorCorrection schedule PASS: " + checks + " pass combinations/event boundaries (ownership model, not full URP execution)");
}
static void RunTimeline(LiveTimelineWorkSheet sheet)
{
RunSchedule();
var groups = sheet.colorCorrectionDataLists;
var keys = groups[0].keys.thisList;
Func<float, SimpleColorCorrectionPass.Parameter> sample = frame =>
LiveColorCorrectionTimeline.Evaluate(groups, frame, TimelinePlayerMode.Default);
Check(!sample(-1).Enabled && !sample(float.NaN).Enabled && !sample(float.PositiveInfinity).Enabled,
"pre-first and invalid time bypass");
Check(!LiveColorCorrectionTimeline.Evaluate(null, 0, TimelinePlayerMode.Default).Enabled, "absent worksheet clears");
using (var pass = new SimpleColorCorrectionPass())
{
Check((int)pass.renderPassEvent == 550, "native color event");
foreach (int i in new[] { 0, 1, 2, 3, 4, 5, 3, 0, 5 })
{
var value = sample(keys[i].frame);
Check(value.IsValid && value.Red == keys[i].redCurve, "exact cut/seek " + i);
if (i > 0) Check(sample(keys[i].frame - .25f).Red == keys[i-1].redCurve, "hold until cut " + i);
int before = pass.LutUploadCount;
Check(pass.Prepare(value), "prepare valid");
Check(pass.LutUploadCount == before + 1, "cut/seek uploads once");
var texture = pass.LutTexture;
for (int j = 0; j < 20; ++j) Check(pass.Prepare(value), "repeat valid");
value.Saturation = .7f; pass.Prepare(value);
Check(pass.LutUploadCount == before + 1 && texture == pass.LutTexture, "stable/saturation avoids upload");
}
Check(!pass.Prepare(default), "disable");
int uploads = pass.LutUploadCount;
pass.Prepare(sample(keys[5].frame));
Check(pass.LutUploadCount == uploads + 1, "reenable refresh");
pass.Dispose(); Check(pass.LutTexture == null, "pass LUT dispose");
pass.Prepare(sample(0)); Check(pass.LutTexture != null, "pass recreate");
}
var list = groups[0].keys;
var oldAttribute = list.attribute;
list.attribute = LiveTimelineKeyDataListAttr.Disable;
Check(!sample(0).Enabled, "disabled list"); list.attribute = oldAttribute;
var key = keys[0];
key.enable = 0; Check(!sample(0).Enabled, "disabled key"); key.enable = 1;
key.mode = 1; Check(!sample(0).Enabled, "advanced bypass"); key.mode = 0;
key.selective = 1; Check(!sample(0).Enabled, "selective bypass"); key.selective = 0;
groups.Add(groups[0]); Check(!sample(0).Enabled, "ambiguous groups bypass"); groups.RemoveAt(1);
PostImageEffectFeature.RuntimeParameter.ColorCorrection = sample(0);
var copy = new PostImageEffectFeature.Parameter();
copy.ShallowCopyFrom(PostImageEffectFeature.RuntimeParameter);
Check(copy.ColorCorrection.IsValid && copy.ColorCorrection.Red == keys[0].redCurve, "parameter copy preserves color");
PostImageEffectFeature.ResetRuntimeParameter();
Check(!PostImageEffectFeature.RuntimeParameter.ColorCorrection.Enabled, "live teardown reset");
Debug.Log("LiveColorCorrection timeline/cache PASS: six real cuts, reverse seek, unsupported modes, reset, LUT lifecycle");
}
// D3D normalized sampling: pixel centers at (i+.5)/width, exact RGB row centers.
// Do not use Texture2D.GetPixelBilinear as a GPU oracle (CPU coordinate convention).
static float SampleRow(Texture2D lut, float u, int row)
{
float x = Mathf.Clamp(u * 256f - 0.5f, 0, 255);
int a = Mathf.FloorToInt(x), b = Mathf.Min(a + 1, 255);
return Mathf.Lerp(lut.GetPixel(a, row).r, lut.GetPixel(b, row).r, x - a);
}
static Color Expected(Texture2D lut, Color source, float saturation)
{
var color = new Color(SampleRow(lut, source.r, 0),
SampleRow(lut, source.g, 1), SampleRow(lut, source.b, 2), source.a);
float luminance = color.r * 0.2126729f + color.g * 0.7151522f + color.b * 0.072175f;
for (int c = 0; c < 3; ++c) color[c] = luminance + saturation * (color[c] - luminance);
return color;
}
// Spatial fixture: all 256 LUT intervals, distinct RGB/alpha ramps, HDR edges.
// Also checks the observed native ordering (Bloom -> curves), not only isolated color swatches.
static void RunSpatial(AssetBundle bundle, Material material,
System.Collections.Generic.List<LiveTimelineKeyColorCorrectionData> keys, ColorCorrectionCurveLut lut)
{
const int width = 257, height = 9;
var pixels = new Color[width * height];
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x)
{
float r = x / 256f;
float g = ((x * 73 + y * 29) % 257) / 256f;
float b = ((x * 31 + y * 67) % 257) / 256f;
if (y == 0) { r -= .2f; g += .4f; }
pixels[y * width + x] = new Color(r, g, b, (y * width + x) / (float)(pixels.Length - 1));
}
var source = new Texture2D(width, height, TextureFormat.RGBAFloat, false, true);
var read = new Texture2D(width, height, TextureFormat.RGBAFloat, false, true);
var bloom = new Texture2D(1, 1, TextureFormat.RGBAFloat, false, true);
var intermediate = new RenderTexture(width, height, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear);
var output = new RenderTexture(width, height, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear);
var previous = RenderTexture.active;
var oldAniso = QualitySettings.anisotropicFiltering;
var oldBloom = Shader.GetGlobalTexture("_Bloom");
float oldWeight = Shader.GetGlobalFloat("_bloomDofWeight"), oldScreen = Shader.GetGlobalFloat("_BloomIsScreenBlend");
Material post = null;
int checks = 0, orderWitnesses = 0;
Color bloomColor = new Color(.16f, .08f, .24f, 1);
try
{
foreach (var shader in bundle.LoadAllAssets<Shader>())
if (shader.name == "Gallop/ImageEffects/Rich/PostBloom_Rich") post = new Material(shader);
Check(post && post.shader.isSupported, "spatial Bloom shader");
QualitySettings.anisotropicFiltering = AnisotropicFiltering.ForceEnable;
Check(lut.Texture.anisoLevel == 0, "data LUT must opt out of forced anisotropic filtering");
post.SetColor("_DimmerColor", Color.white);
source.filterMode = FilterMode.Point; source.wrapMode = TextureWrapMode.Clamp;
source.SetPixels(pixels); source.Apply(false, false);
bloom.SetPixel(0, 0, bloomColor); bloom.Apply(false, false);
intermediate.Create(); output.Create();
Shader.SetGlobalTexture("_Bloom", bloom);
Shader.SetGlobalFloat("_bloomDofWeight", .7f);
foreach (var key in keys)
{
lut.Update(key.redCurve, key.greenCurve, key.blueCurve);
Shader.SetGlobalTexture("_RgbTex", lut.Texture);
// -1 = curves alone, 0 = additive Bloom then curves, 1 = screen Bloom then curves.
foreach (int blend in new[] { -1, 0, 1 })
foreach (float saturation in new[] { 1f, 0f, 1.4f })
{
Shader.SetGlobalFloat("_Saturation", saturation);
if (blend >= 0)
{
Shader.SetGlobalFloat("_BloomIsScreenBlend", blend);
Graphics.Blit(source, intermediate, post, 0);
Draw(material, lut.Texture, saturation, intermediate, output);
}
else Draw(material, lut.Texture, saturation, source, output);
RenderTexture.active = output;
read.ReadPixels(new Rect(0, 0, width, height), 0, 0); read.Apply(false, false);
var actual = read.GetPixels();
for (int i = 0; i < pixels.Length; ++i)
{
Color composed = pixels[i];
if (blend >= 0)
{
for (int c = 0; c < 4; ++c)
composed[c] = blend == 0 ? composed[c] + bloomColor[c] * .7f :
1 - (1 - composed[c]) * (1 - bloomColor[c] * .7f);
// Native PostBloom_Rich applies Add/Screen to all four channels.
}
Color expected = Expected(lut.Texture, composed, saturation);
for (int c = 0; c < 4; ++c)
if (float.IsNaN(actual[i][c]) || Mathf.Abs(actual[i][c] - expected[c]) >= .0015f)
throw new Exception("LiveColorCorrectionRegression spatial frame=" + key.frame +
" blend=" + blend + " saturation=" + saturation + " pixel=" + i +
" channel=" + c + " got=" + actual[i][c] + " expected=" + expected[c] + " source=" + pixels[i].ToString("F7") + " rgba=" + actual[i].ToString("F7") + " expectedRGBA=" + expected.ToString("F7"));
if (blend == 0 && saturation == 1)
{
Color reversed = Expected(lut.Texture, pixels[i], saturation);
if (Mathf.Abs(expected.r - (reversed.r + bloomColor.r * .7f)) > .02f) orderWitnesses++;
}
checks++;
}
}
}
Check(orderWitnesses > 100, "fixture must detect accidentally moving curves before Bloom");
Debug.Log("LiveColorCorrectionRegression spatial PASS: " + checks +
" RGBA pixels; LUT ramps/HDR/alpha and Bloom-before-curves Add/Screen; order witnesses=" + orderWitnesses);
}
finally
{
QualitySettings.anisotropicFiltering = oldAniso;
Shader.SetGlobalTexture("_Bloom", oldBloom);
Shader.SetGlobalFloat("_bloomDofWeight", oldWeight); Shader.SetGlobalFloat("_BloomIsScreenBlend", oldScreen);
RenderTexture.active = previous;
intermediate.Release(); output.Release();
UnityEngine.Object.DestroyImmediate(intermediate); UnityEngine.Object.DestroyImmediate(output);
UnityEngine.Object.DestroyImmediate(source); UnityEngine.Object.DestroyImmediate(read); UnityEngine.Object.DestroyImmediate(bloom);
if (post) UnityEngine.Object.DestroyImmediate(post);
}
}
public static void RunGpu()
{
Check(Application.isBatchMode, "isolated batch mode only");
string path = Environment.GetEnvironmentVariable("UMA_COLOR_TEST_KEYS");
Check(File.Exists(path), "UMA_COLOR_TEST_KEYS must contain the local Live1176 worksheet fixture");
var sheet = ScriptableObject.CreateInstance<LiveTimelineWorkSheet>();
AssetBundle bundle = null;
Material material = null;
var source = new RenderTexture(16, 16, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear);
var output = new RenderTexture(16, 16, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear);
var read = new Texture2D(16, 16, TextureFormat.RGBAFloat, false, true);
var previous = RenderTexture.active;
var oldRgb = Shader.GetGlobalTexture("_RgbTex");
float oldSaturation = Shader.GetGlobalFloat("_Saturation"), oldPower = Shader.GetGlobalFloat("_PostFilmPower");
using (var lut = new ColorCorrectionCurveLut())
try
{
JsonUtility.FromJsonOverwrite(File.ReadAllText(path), sheet);
Check(sheet.colorCorrectionDataLists.Count == 1, "real group deserialization");
var keys = sheet.colorCorrectionDataLists[0].keys.thisList;
Check(keys.Count == 6 && keys[0].enable == 1 && keys[5].frame == 7066, "six actual enabled cuts");
RunTimeline(sheet);
Check(!keys[0].IsInterpolateKey(), "shipped keys do not have interpolation fields");
Check(keys[0].redCurve.length == 2 && Math.Abs(keys[0].redCurve.keys[0].outTangent - 0.7630331f) < 0.00001f,
"native AnimationCurve tangents survive deserialization");
Check(ColorCorrectionCurveLut.Quantize(-1) == 0 && ColorCorrectionCurveLut.Quantize(2) == 255 &&
ColorCorrectionCurveLut.Quantize(0.5f) == 127, "truncating native byte conversion");
var identity = AnimationCurve.Linear(0, 0, 1, 1);
var zero = AnimationCurve.Constant(0, 1, 0);
var one = AnimationCurve.Constant(0, 1, 1);
var pixels = new Color32[1024];
ColorCorrectionCurveLut.Fill(pixels, zero, one, identity, one, zero, identity, 0.5f);
Check(pixels[0].r == 127 && pixels[256].g == 127 && pixels[767].b == 255 && pixels[768].a == 0,
"row layout, endpoint, pre-quantization curve blend, unused row");
bundle = AssetBundle.LoadFromFile(Environment.GetEnvironmentVariable("UMA_POSTFILM_TEST_BUNDLE"));
foreach (var shader in bundle.LoadAllAssets<Shader>())
if (shader.name == "Gallop/ImageEffects/ColorCorrectionCurvesSimple") material = new Material(shader);
Check(material && material.shader.isSupported, "shipped simple shader supported");
// Mask power=0 means full correction in DXBC (not correction disabled).
Shader.SetGlobalFloat("_PostFilmPower", 0);
source.Create(); output.Create();
int checks = 0;
Texture2D firstTexture = null;
foreach (var key in keys)
{
lut.Update(key.redCurve, key.greenCurve, key.blueCurve);
if (firstTexture == null) firstTexture = lut.Texture;
Check(firstTexture == lut.Texture && lut.Texture.mipmapCount == 1, "reuse LUT without mips");
foreach (float saturation in new[] { 0f, 1f, 1.4f })
foreach (Color color in new[] { new Color(0.2f, 0.4f, 0.8f, 0.35f), new Color(0, 0.5f, 1, 0.8f), new Color(-0.2f, 1.4f, 0.7f, 1) })
{
Shader.SetGlobalTexture("_RgbTex", lut.Texture);
Shader.SetGlobalFloat("_Saturation", saturation);
RenderTexture.active = source; GL.Clear(false, true, color);
Draw(material, lut.Texture, saturation, source, output);
RenderTexture.active = output;
read.ReadPixels(new Rect(0, 0, 16, 16), 0, 0); read.Apply();
Color actual = read.GetPixel(8, 8), expected = Expected(lut.Texture, color, saturation);
for (int c = 0; c < 4; ++c)
Check(!float.IsNaN(actual[c]) && Mathf.Abs(actual[c] - expected[c]) < 0.0015f,
"GPU curve row/saturation/alpha input=" + color + " saturation=" + saturation + " frame=" + key.frame + " channel=" + c + " got=" + actual[c] + " expected=" + expected[c]);
checks++;
}
}
Debug.Log("Color LUT sampling diagnostics aniso=" + lut.Texture.anisoLevel + " filter=" + lut.Texture.filterMode + " qualityAniso=" + QualitySettings.anisotropicFiltering);
RunSpatial(bundle, material, keys, lut);
lut.Dispose(); Check(lut.Texture == null, "LUT released");
lut.Update(identity, identity, identity); Check(lut.Texture != null, "LUT recreated after dispose");
Debug.Log("LiveColorCorrectionRegression PASS: actual Live1176 six curve keys, byte packing and " + checks + " GPU color/alpha checks; production command-buffer draw verified; full Live render-context parity not tested");
}
finally
{
Shader.SetGlobalTexture("_RgbTex", oldRgb);
Shader.SetGlobalFloat("_Saturation", oldSaturation); Shader.SetGlobalFloat("_PostFilmPower", oldPower);
RenderTexture.active = previous;
source.Release(); output.Release();
UnityEngine.Object.DestroyImmediate(source); UnityEngine.Object.DestroyImmediate(output);
UnityEngine.Object.DestroyImmediate(read); UnityEngine.Object.DestroyImmediate(sheet);
if (material) UnityEngine.Object.DestroyImmediate(material);
if (bundle) bundle.Unload(true);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1725d4c85dcb4a06ac079f31d7dbd6c8
+150
View File
@@ -0,0 +1,150 @@
using System;
using System.Reflection;
using Gallop;
using Gallop.Live;
using Gallop.Live.Cutt;
using Gallop.RenderPipeline;
using UnityEditor;
using UnityEngine;
// Runs the production Director -> image-effect adapter -> render-parameter path.
public static class LiveDofFocusRegression
{
private const BindingFlags PrivateInstance = BindingFlags.Instance | BindingFlags.NonPublic;
private static void Require(bool value, string message)
{
if (!value) throw new InvalidOperationException("[LiveDofFocusRegression] " + message);
}
private static void Set(object obj, string field, object value)
{
obj.GetType().GetField(field, PrivateInstance).SetValue(obj, value);
}
private static void SetProperty(object obj, string name, object value)
{
Set(obj, "<" + name + ">k__BackingField", value);
}
private static void RequirePreparedFocus(float expectedDepth)
{
// Exercise the real shader-parameter preparation, not a duplicated formula.
var pass = new DofDiffusionBloomOverlayPass(
UnityEngine.Rendering.Universal.RenderPassEvent.BeforeRenderingPostProcessing);
using (var command = new UnityEngine.Rendering.CommandBuffer())
{
var state = PostImageEffectFeature.RuntimeParameter.DofDiffuionBloomOverlay;
object[] args = { default(UnityEngine.Rendering.ScriptableRenderContext), command,
state, RenderTextureHandle.Make(Shader.PropertyToID("_DofTestSource"), 1920, 1080) };
typeof(DofDiffusionBloomOverlayPass).GetMethod("PrepareDofParam", PrivateInstance)
.Invoke(pass, args);
Require(Mathf.Abs(DofDiffusionBloomOverlayPass.LastDofFocalDepth - expectedDepth) < 0.001f,
"Prepared focal depth must use the selected focus, not the default 1m value.");
float sharpEnd = DofDiffusionBloomOverlayPass.LastDofFocalEnd *
DofDiffusionBloomOverlayPass.LastDofDepthRange;
Require(Mathf.Abs(sharpEnd - expectedDepth - state.DofFocalSize * 0.5f) < 0.001f,
"Prepared sharp interval must be centered on the selected focal depth.");
}
}
[MenuItem("UmaViewer/Rendering/Validate DOF focus routing")]
public static void Run()
{
if (EditorApplication.isPlaying || Director.instance != null)
throw new InvalidOperationException("Run outside Live/Play Mode; validation must not replace active Live state.");
var previousRuntime = PostImageEffectFeature.RuntimeParameter;
bool previousDof = GallopImageEffect.UserDofEnabled;
var root = new GameObject("DofFocusRegression");
root.SetActive(false);
try
{
var director = root.AddComponent<Director>();
var timeline = root.AddComponent<LiveTimelineControl>();
director._liveTimelineControl = timeline;
var cameraObject = new GameObject("DofTestCamera");
cameraObject.transform.SetParent(root.transform);
var camera = cameraObject.AddComponent<Camera>();
camera.nearClipPlane = 0.8f;
camera.farClipPlane = 1000f;
camera.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
Set(director, "_cameraObjects", new[] { camera });
Set(director, "_activeCameraIndex", 0);
typeof(Director).GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, director);
var effect = cameraObject.AddComponent<GallopImageEffect>();
effect.DofDiffusionBloomOverlayParam.IsEnableBloom = true;
effect.DofDiffusionBloomOverlayParam.IsEnableDiffusion = true;
effect.DofDiffusionBloomOverlayParam.BloomIntensity = 0.63f;
SetProperty(timeline, "DofTrackCamera", camera);
SetProperty(timeline, "DofLookAtCamera", camera);
SetProperty(timeline, "HasDofCameraLookAt", true);
Vector3 focus = new Vector3(0, 1, 25);
SetProperty(timeline, "DofCameraLookAt", focus);
var key = new LiveTimelineKeyPostEffectDOFData
{
frame = 0, attribute = (LiveTimelineKeyAttribute)0x50000,
dofFocalPoint = 1f, forcalSize = 30f, blurSpread = 1.75f,
dofSmoothness = 0.5f, dofQuality = 1, charactor = 1
};
var update = typeof(Director).GetMethod("OnUpdatePostEffect_Dof", PrivateInstance);
Action apply = () => update.Invoke(director, new object[] { key });
PostImageEffectFeature.ResetRuntimeParameter();
GallopImageEffect.SetUserDofEnabled(true);
apply();
var state = PostImageEffectFeature.RuntimeParameter.DofDiffuionBloomOverlay;
Require(state.IsEnableDof, "Valid camera look-at must enable DOF.");
Require(state.DofFocalType == DepthBlurAndBloom.DofFocalType.Position,
"Camera look-at was overwritten by the 1m fallback setter (expected Position, got " + state.DofFocalType + ").");
Require(state.DofFocalPosition == focus, "Camera target must reach renderer unchanged.");
Require(PostImageEffectFeature.RuntimeParameter.IsUseDepthTexture, "DOF must request depth.");
RequirePreparedFocus(25f);
// A subsequent metric-distance key must deliberately switch modes.
key.attribute = (LiveTimelineKeyAttribute)0x70000;
key.dofFocalPoint = 8f;
apply();
state = PostImageEffectFeature.RuntimeParameter.DofDiffuionBloomOverlay;
Require(state.DofFocalType == DepthBlurAndBloom.DofFocalType.Point &&
Mathf.Approximately(state.DofFocalPoint, 8f), "Metric-distance focus must be retained.");
RequirePreparedFocus(8f - camera.nearClipPlane);
// Switch back on a later cut, with a different world-space target.
key.attribute = (LiveTimelineKeyAttribute)0x50000;
key.dofFocalPoint = 1f;
focus = new Vector3(1, 2, 4);
SetProperty(timeline, "DofCameraLookAt", focus);
apply();
state = PostImageEffectFeature.RuntimeParameter.DofDiffuionBloomOverlay;
Require(state.DofFocalType == DepthBlurAndBloom.DofFocalType.Position &&
state.DofFocalPosition == focus, "Cut back to positional focus must not retain metric mode.");
RequirePreparedFocus(4f);
// Invalid target bypasses only DOF, rather than falling back to 1m.
SetProperty(timeline, "HasDofCameraLookAt", false);
apply();
state = PostImageEffectFeature.RuntimeParameter.DofDiffuionBloomOverlay;
Require(!state.IsEnableDof && state.IsEnableBloom && state.IsEnableDiffusion,
"Missing focus must preserve Bloom/Diffusion and disable DOF.");
SetProperty(timeline, "HasDofCameraLookAt", true);
apply();
GallopImageEffect.SetUserDofEnabled(false);
state = PostImageEffectFeature.RuntimeParameter.DofDiffuionBloomOverlay;
Require(!state.IsEnableDof && state.UseDofDiffusionBloomType ==
Gallop.ImageEffect.DofDiffusionBloomOverlayParam.DofDiffusionBloomType.DiffusionBloom,
"User gate must bypass only DOF immediately.");
Require(Mathf.Approximately(state.BloomIntensity, 0.63f), "DOF gate must not alter Bloom intensity.");
GallopImageEffect.SetUserDofEnabled(true);
state = PostImageEffectFeature.RuntimeParameter.DofDiffuionBloomOverlay;
Require(state.IsEnableDof && state.DofFocalType == DepthBlurAndBloom.DofFocalType.Position,
"Re-enabling while paused must restore positional focus.");
Debug.Log("[LiveDofFocusRegression] PASS: startup look-at, metric focus, camera cut, missing target, depth request, prepared focal depths/sharp intervals, DOF gate, paused restore.");
}
finally
{
UnityEngine.Object.DestroyImmediate(root);
typeof(Director).GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, null);
typeof(PostImageEffectFeature).GetField("_runtimeParameter", BindingFlags.Static | BindingFlags.NonPublic)
.SetValue(null, previousRuntime);
GallopImageEffect.SetUserDofEnabled(previousDof);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0d4ad2f55d984032a979b59ec86be287
@@ -0,0 +1,113 @@
using System;
using System.Reflection;
using Gallop;
using Gallop.ImageEffect;
using Gallop.RenderPipeline;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// GPU regression for the recovered CoC bridge. No song/game assets required.
public static class LiveDofForegroundRegression
{
[MenuItem("UmaViewer/Rendering/Validate DOF foreground (GPU)")]
public static void Run()
{
if (EditorApplication.isPlaying) throw new InvalidOperationException("Run outside Play Mode.");
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null)
throw new InvalidOperationException("GPU test requires graphics; do not pass -nographics.");
var root = new GameObject("DofForegroundRegression");
var camera = root.AddComponent<Camera>();
camera.enabled = false;
camera.nearClipPlane = 0.8f;
camera.farClipPlane = 1000f;
var pass = new DofDiffusionBloomOverlayPass(RenderPassEvent.BeforeRenderingPostProcessing);
var shader = Resources.Load<Shader>("RenderPipeline/LiveDofCoC");
if (shader == null || !shader.isSupported || ShaderUtil.ShaderHasError(shader))
throw new InvalidOperationException("LiveDofCoC shader unavailable or failed compilation.");
float[] depths = { 5, 15, 25, 29.23675f, 31.04587f, 34.40935f, 40.81587f, 55.9f, 65, 90 };
var depth = new Texture2D(depths.Length, 1, TextureFormat.RFloat, false, true) { filterMode = FilterMode.Point };
var source = new Texture2D(depths.Length, 1, TextureFormat.RGBAFloat, false, true) { filterMode = FilterMode.Point };
var result = new RenderTexture(depths.Length, 1, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear);
var readback = new Texture2D(depths.Length, 1, TextureFormat.RGBAFloat, false, true);
var previousRT = RenderTexture.active;
var oldDepth = Shader.GetGlobalTexture("_CameraDepthTexture");
string[] vectors = { "_ZBufferParams", "_CurveParams", "_GlobalScreenUVScrollParam", "_InvRenderTargetSize" };
var oldVectors = Array.ConvertAll(vectors, name => Shader.GetGlobalVector(name));
float oldStart = Shader.GetGlobalFloat("_GallopDofFocalStart");
float oldSize = Shader.GetGlobalFloat("_dofForegroundSize");
float oldWeight = Shader.GetGlobalFloat("_bloomDofWeight");
const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
try
{
result.Create();
// Synthetic raw depth uses the same ZBufferParams as the shader, both depth conventions.
foreach (bool reversed in new[] { false, true })
foreach (float size in new[] { 30f, 0f })
foreach (float foreground in new[] { 1f, 0f })
{
float ratio = camera.farClipPlane / camera.nearClipPlane;
float x = reversed ? ratio - 1 : 1 - ratio;
float y = reversed ? 1 : ratio;
for (int i = 0; i < depths.Length; i++)
{
depth.SetPixel(i, 0, new Color((camera.farClipPlane / depths[i] - y) / x, 0, 0, 0));
source.SetPixel(i, 0, new Color(0.2f, 0.4f, 0.6f, 0.7f));
}
depth.Apply(); source.Apply();
var parameter = new DofDiffusionBloomOverlayPass.Parameter
{
TargetCamera = camera, DofFocalType = DepthBlurAndBloom.DofFocalType.Position,
DofFocalPosition = new Vector3(0, 0, 40.81587f), DofFocalSize = size,
DofSmoothness = 0.5f, DofForegroundSize = foreground,
DofQualityType = (DepthBlurAndBloom.DofQuality)5
};
var src = RenderTextureHandle.Make(new RenderTargetIdentifier(source));
src.Width = depths.Length; src.Height = 1;
var dst = RenderTextureHandle.Make(new RenderTargetIdentifier(result));
using (var cmd = new CommandBuffer())
{
typeof(DofDiffusionBloomOverlayPass).GetMethod("PrepareDofParam", flags).Invoke(pass,
new object[] { default(ScriptableRenderContext), cmd, parameter, src });
cmd.SetGlobalVector("_ZBufferParams", new Vector4(x, y, x / 1000, y / 1000));
cmd.SetGlobalVector("_GlobalScreenUVScrollParam", Vector4.zero);
cmd.SetGlobalTexture("_CameraDepthTexture", depth);
cmd.SetGlobalFloat("_dofForegroundSize", foreground);
typeof(DofDiffusionBloomOverlayPass).GetMethod("BlitDofCoc", flags).Invoke(pass,
new object[] { cmd, parameter, src, dst, null, 0 });
Graphics.ExecuteCommandBuffer(cmd);
}
RenderTexture.active = result;
readback.ReadPixels(new Rect(0, 0, depths.Length, 1), 0, 0); readback.Apply();
float range = camera.farClipPlane - camera.nearClipPlane;
float focus = 40.81587f / range, half = size * 0.5f / range;
for (int i = 0; i < depths.Length; i++)
{
float z = depths[i] / 1000;
float bg = Mathf.Clamp01((z - focus - half) / (focus * 0.5f));
float fg = Mathf.Clamp01((Mathf.Max(0, focus - half) - z) / (focus * 0.5f)) * foreground;
var actual = readback.GetPixel(i, 0);
if (Mathf.Abs(actual.a - Mathf.Max(bg, fg)) > 0.002f ||
Mathf.Abs(actual.r - 0.2f) > 0.002f || Mathf.Abs(actual.g - 0.4f) > 0.002f || Mathf.Abs(actual.b - 0.6f) > 0.002f)
throw new InvalidOperationException($"CoC mismatch depth={depths[i]} size={size} reversed={reversed}: {actual}");
if (size == 30 && foreground == 1 && !reversed)
Debug.Log($"[LiveDofForegroundRegression] depth={depths[i]:F2}m alpha={actual.a:F4}");
}
}
Debug.Log("[LiveDofForegroundRegression] PASS: 80 GPU samples, frame3000 focus, near/far CoC, zero-width band, foreground off, RGB preserved, normal/reversed Z.");
}
finally
{
RenderTexture.active = previousRT;
Shader.SetGlobalTexture("_CameraDepthTexture", oldDepth);
for (int i = 0; i < vectors.Length; i++) Shader.SetGlobalVector(vectors[i], oldVectors[i]);
Shader.SetGlobalFloat("_GallopDofFocalStart", oldStart);
Shader.SetGlobalFloat("_dofForegroundSize", oldSize);
Shader.SetGlobalFloat("_bloomDofWeight", oldWeight);
pass.Dispose(); result.Release();
foreach (UnityEngine.Object obj in new UnityEngine.Object[] { root, depth, source, result, readback })
UnityEngine.Object.DestroyImmediate(obj);
}
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 27698a601219c0c429243d86175f2d93
guid: d0b9deaed4d44dddbfe159cbdd9dcd2e
MonoImporter:
externalObjects: {}
serializedVersion: 2
+196
View File
@@ -0,0 +1,196 @@
using System;
using System.IO;
using Gallop.Live.Cutt;
using Gallop.RenderPipeline;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;
using Overlay = Gallop.ImageEffect.ScreenOverlay.Overlay;
public static class LivePostFilmRegression
{
static void Check(bool ok, string message) { if (!ok) throw new Exception("PostFilm: " + message); }
static void Near(float a, float b, string message) { Check(Mathf.Abs(a-b)<0.002f, message + ": " + a + " vs " + b); }
[MenuItem("UmaViewer/Rendering/Validate PostFilm timeline")]
public static void Run()
{
var keys = new LiveTimelineKeyPostFilmDataList();
var a = new LiveTimelineKeyPostFilmData { frame=10, filmMode=PostFilmMode.Add, filmPower=2,
attribute=(LiveTimelineKeyAttribute)0x100000, color0=Color.red, color1=Color.green,
color2=Color.blue, color3=Color.white, depthPower=-0.75f, DepthClip=2, FilmScale=Vector2.one };
var b = new LiveTimelineKeyPostFilmData { frame=30, filmMode=PostFilmMode.Mul, filmPower=4,
interpolateType=LiveCameraInterpolateType.Linear, color0=Color.red, color1=Color.green,
color2=Color.blue, color3=Color.white, depthPower=-0.25f, DepthClip=2, FilmScale=Vector2.one };
keys.thisList.Add(a); keys.thisList.Add(b);
var output = new Overlay();
foreach (PostColorType type in Enum.GetValues(typeof(PostColorType)))
{
a.colorType=type;
LivePostFilmTimeline.Evaluate(keys,20,TimelinePlayerMode.Default,output);
Near(output.postFilmPower,3,"raw unclamped power"); Near(output.depthPower,-0.5f,"signed depth");
Check(output.inverseVignette && output.IsEnableDepth,"inverse is not depth disable");
Check(output.postFilmMode==Overlay.PostFilmMode.Add,"mode must not switch before cut");
Check(output.postFilmColor0==Color.red,"corner0");
Check(output.postFilmColor1==(type==PostColorType.ColorAll || type==PostColorType.Color2TopBottom ? Color.red:Color.green),"corner1");
Check(output.postFilmColor2==(type==PostColorType.ColorAll || type==PostColorType.Color2LeftRight ? Color.red:type==PostColorType.Color2TopBottom ? Color.green:Color.blue),"corner2");
Check(output.postFilmColor3==(type==PostColorType.ColorAll ? Color.red:type==PostColorType.Color4 ? Color.white:Color.green),"corner3");
}
LivePostFilmTimeline.Evaluate(keys,30,TimelinePlayerMode.Default,output);
Check(output.postFilmMode==Overlay.PostFilmMode.Mul && !output.inverseVignette,"exact cut");
LivePostFilmTimeline.Evaluate(keys,20,TimelinePlayerMode.Default,output); Near(output.postFilmPower,3,"seek backward");
LivePostFilmTimeline.Evaluate(keys,0,TimelinePlayerMode.Default,output); Check(!output.IsValid(),"before first key");
keys.attribute=LiveTimelineKeyDataListAttr.Disable;
LivePostFilmTimeline.Evaluate(keys,20,TimelinePlayerMode.Default,output); Check(!output.IsValid(),"disabled track");
keys.attribute=0; a.layerMode=LiveTimelineKeyPostFilmData.LayerMode.UVMovie;
LivePostFilmTimeline.Evaluate(keys,20,TimelinePlayerMode.Default,output); Check(!output.IsValid(),"unsupported movie bypass");
a.layerMode=LiveTimelineKeyPostFilmData.LayerMode.Color; a.FilmScale=Vector2.zero; b.FilmScale=Vector2.zero;
LivePostFilmTimeline.Evaluate(keys,20,TimelinePlayerMode.Default,output); Near(output.ScaleParameter.x,1,"safe scale");
LivePostFilmTimeline.Evaluate(null,20,TimelinePlayerMode.Default,output); Check(!output.IsValid(),"missing track reset");
var sheet=ScriptableObject.CreateInstance<LiveTimelineWorkSheet>();
try {
JsonUtility.FromJsonOverwrite("{\"postFilmKeys\":{\"thisList\":[{\"frame\":0,\"filmMode\":5,\"attribute\":1048576,\"filmPower\":0.1}]},\"postFilm2Keys\":{\"thisList\":[]},\"postFilm3Keys\":{\"thisList\":[]}}",sheet);
Check(sheet.postFilmKeys.Count==1 && sheet.postFilmKeys.thisList[0].filmMode==PostFilmMode.VignetteAdd,"worksheet deserialization");
} finally { UnityEngine.Object.DestroyImmediate(sheet); }
ValidateBlinkSync();
Debug.Log("LivePostFilmRegression timeline PASS");
}
private static void ValidateBlinkSync()
{
var keys=new LiveTimelineKeyPostFilmDataList();
var a=new LiveTimelineKeyPostFilmData { frame=10, filmMode=PostFilmMode.Add,
layerMode=LiveTimelineKeyPostFilmData.LayerMode.Color, colorType=PostColorType.Color4,
attribute=(LiveTimelineKeyAttribute)0x1e00000,
BlinkLightName="stage_beam", BlinkLightContainerIndex=2,
BlinkLightBrightnessPower=1f, IsAdjustedBlinkLightColor=false,
color0=new Color(.1f,.2f,.3f,.11f), color1=new Color(.1f,.2f,.3f,.22f),
color2=new Color(.1f,.2f,.3f,.33f), color3=new Color(.1f,.2f,.3f,.44f), FilmScale=Vector2.one };
var b=new LiveTimelineKeyPostFilmData { frame=30, filmMode=PostFilmMode.Add,
interpolateType=LiveCameraInterpolateType.Linear, colorType=PostColorType.Color4,
color0=Color.white, color1=Color.white, color2=Color.white, color3=Color.white,
FilmScale=Vector2.one };
keys.thisList.Add(a); keys.thisList.Add(b);
var output=new Overlay();
int lookups=0;
LivePostFilmTimeline.BlinkColorResolver resolve=delegate(string name,int hash,int index,out Color raw) {
Check(name=="stage_beam" && index==2,"lookup identity");
lookups++; raw=new Color(.2f,.4f,.6f,.9f); return true;
};
LivePostFilmTimeline.Evaluate(keys,20,TimelinePlayerMode.Default,output,resolve);
Check(lookups==1,"one lookup shared by all four corners");
foreach(Color c in new[]{output.postFilmColor0,output.postFilmColor1,output.postFilmColor2,output.postFilmColor3})
Near(c.r,.6f,"synced R interpolates to next unsynced color");
Near(output.postFilmColor0.a,.555f,"synced corner0 preserves authored alpha");
Near(output.postFilmColor1.a,.61f,"synced corner1 preserves authored alpha");
Near(output.postFilmColor2.a,.665f,"synced corner2 preserves authored alpha");
Near(output.postFilmColor3.a,.72f,"synced corner3 preserves authored alpha");
b.attribute=(LiveTimelineKeyAttribute)0x1e00000;
LivePostFilmTimeline.Evaluate(keys,20,TimelinePlayerMode.Default,output,resolve);
foreach(Color c in new[]{output.postFilmColor0,output.postFilmColor1,output.postFilmColor2,output.postFilmColor3})
Near(c.r,.2f,"next sync holds current resolved RGB");
Near(output.postFilmColor3.a,.44f,"next sync holds current authored alpha");
a.attribute=0;
LivePostFilmTimeline.Evaluate(keys,20,TimelinePlayerMode.Default,output,resolve);
Near(output.postFilmColor0.r,.1f,"next sync holds authored current without lookup");
a.attribute=(LiveTimelineKeyAttribute)0x200000;
b.attribute=0;
a.BlinkLightBrightnessPower=0f; a.IsAdjustedBlinkLightColor=true;
LivePostFilmTimeline.Evaluate(keys,10,TimelinePlayerMode.Default,output,resolve);
Near(output.postFilmColor0.r,1f,"key brightness zero plus adjusted color");
Near(output.postFilmColor1.r,.1f,"only selected corner synced");
Near(output.postFilmColor0.a,.11f,"raw stage alpha not copied");
LivePostFilmTimeline.BlinkColorResolver missing=delegate(string name,int hash,int index,out Color raw) {
raw=Color.black; return false;
};
LivePostFilmTimeline.Evaluate(keys,10,TimelinePlayerMode.Default,output,missing);
Near(output.postFilmColor0.r,.1f,"failed lookup retains authored color");
a.BlinkLightBrightnessPower=3f; a.IsAdjustedBlinkLightColor=false;
LivePostFilmTimeline.Evaluate(keys,10,TimelinePlayerMode.Default,output,resolve);
Check(output.postFilmColor0.b>1f,"key brightness can produce HDR color");
Debug.Log("LivePostFilmRegression blink sync PASS");
}
// Only for an isolated batch-mode project: this test changes global shader state.
public static void RunGpu()
{
Check(Application.isBatchMode,"GPU regression must run in isolated batch mode");
Run();
string bundlePath=Environment.GetEnvironmentVariable("UMA_POSTFILM_TEST_BUNDLE");
Check(File.Exists(bundlePath),"set UMA_POSTFILM_TEST_BUNDLE to the locally extracted shader bundle");
var bundle=AssetBundle.LoadFromFile(bundlePath);
Material material=null;
var source=new RenderTexture(32,32,0,RenderTextureFormat.ARGBFloat,RenderTextureReadWrite.Linear);
var target=new RenderTexture(32,32,0,RenderTextureFormat.ARGBFloat,RenderTextureReadWrite.Linear);
var read=new Texture2D(32,32,TextureFormat.RGBAFloat,false,true);
var previous=RenderTexture.active;
try {
Shader shader=null;
foreach(var candidate in bundle.LoadAllAssets<Shader>()) if(candidate.name=="Gallop/ImageEffects/Rich/PostBlit_Rich") shader=candidate;
Check(shader!=null && shader.isSupported,"official shader supported");
material=new Material(shader);
source.Create(); target.Create();
RenderTexture.active=source; GL.Clear(false,true,new Color(0.2f,0.3f,0.4f,1));
var overlay=new Overlay(); LivePostFilmTimeline.Reset(overlay);
overlay.postFilmMode=Overlay.PostFilmMode.Add; overlay.postFilmPower=0.5f;
overlay.postFilmColor0=overlay.postFilmColor1=overlay.postFilmColor2=overlay.postFilmColor3=new Color(0.4f,0.2f,0.1f,1);
overlay.IsEnableDepth=true;
var renderer=new ScreenOverlayRender.Render();
var param=new ScreenOverlayRender.Parameter(); param.Setup(overlay);
foreach(bool explicitColor in new[]{false,true}) {
using(var cmd=new CommandBuffer()) {
if(explicitColor) cmd.EnableShaderKeyword("COLOR_ONLY"); else cmd.DisableShaderKeyword("COLOR_ONLY");
renderer.Blit(ref param,cmd,RenderTextureHandle.Make(source),RenderTextureHandle.Make(target),material,3);
Graphics.ExecuteCommandBuffer(cmd);
}
RenderTexture.active=target; read.ReadPixels(new Rect(0,0,32,32),0,0); read.Apply();
var pixel=read.GetPixel(16,16);
Debug.Log("PostFilm GPU COLOR_ONLY="+explicitColor+" pixel="+pixel.ToString("F5"));
Near(pixel.r,0.4f,"Add red"); Near(pixel.g,0.4f,"Add green"); Near(pixel.b,0.45f,"Add blue");
}
// Both first/later passes must execute the requested inverse-vignette variant.
overlay.postFilmMode=Overlay.PostFilmMode.VignetteAdd;
overlay.postFilmOptionParam=new Vector4(0.3f,0.8f,0,0);
overlay.postFilmOffsetParam=new Vector2(0,0.58f);
Color[] normal=null;
foreach(int pass in new[]{1,3})
foreach(bool inverse in new[]{false,true}) {
overlay.inverseVignette=inverse; param.Setup(overlay);
using(var cmd=new CommandBuffer()) {
cmd.DisableShaderKeyword("COLOR_ONLY");
renderer.Blit(ref param,cmd,RenderTextureHandle.Make(source),RenderTextureHandle.Make(target),material,pass);
Graphics.ExecuteCommandBuffer(cmd);
}
RenderTexture.active=target; read.ReadPixels(new Rect(0,0,32,32),0,0); read.Apply();
var pixels=read.GetPixels(); float maxDifference=0;
for(int i=0;i<pixels.Length;i++) {
Check(!float.IsNaN(pixels[i].r) && !float.IsInfinity(pixels[i].r),"finite vignette output");
if(inverse) maxDifference=Mathf.Max(maxDifference,Mathf.Abs(pixels[i].r-normal[i].r));
}
if(!inverse) normal=pixels;
else Check(maxDifference>0.01f,"inverse pass must change the spatial mask");
Debug.Log("PostFilm vignette pass="+pass+" inverse="+inverse+" difference="+maxDifference);
}
// Sequential layers use the previous layer's pixels, not the original source.
overlay.inverseVignette=false; overlay.postFilmMode=Overlay.PostFilmMode.Add; param.Setup(overlay);
using(var cmd=new CommandBuffer()) {
renderer.Blit(ref param,cmd,RenderTextureHandle.Make(source),RenderTextureHandle.Make(target),material,3);
overlay.postFilmMode=Overlay.PostFilmMode.Lerp; overlay.postFilmPower=0.5f;
overlay.postFilmColor0=overlay.postFilmColor1=overlay.postFilmColor2=overlay.postFilmColor3=Color.black;
param.Setup(overlay);
renderer.Blit(ref param,cmd,RenderTextureHandle.Make(target),RenderTextureHandle.Make(source),material,3);
Graphics.ExecuteCommandBuffer(cmd);
}
RenderTexture.active=source; read.ReadPixels(new Rect(0,0,32,32),0,0); read.Apply();
var stacked=read.GetPixel(16,16);
Near(stacked.r,0.2f,"ordered Add then Lerp red"); Near(stacked.g,0.2f,"ordered Add then Lerp green"); Near(stacked.b,0.225f,"ordered Add then Lerp blue");
Debug.Log("LivePostFilmRegression GPU PASS");
} finally {
RenderTexture.active=previous;
if(material!=null) UnityEngine.Object.DestroyImmediate(material);
UnityEngine.Object.DestroyImmediate(source); UnityEngine.Object.DestroyImmediate(target); UnityEngine.Object.DestroyImmediate(read);
if(bundle!=null) bundle.Unload(true);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4234cd02b8e84df5ad22e7939cc3c8cc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+153
View File
@@ -0,0 +1,153 @@
using System;
using System.IO;
using UnityEngine;
using UnityEngine.Rendering;
using Gallop.Live.Cutt;
using Gallop.RenderPipeline;
public static class LiveRadialBlurRegression
{
static void Check(bool ok, string message) { if (!ok) throw new Exception("RadialBlur: " + message); }
static void Near(float a, float b, string message) { Check(Mathf.Abs(a-b)<0.002f,message+" actual="+a+" expected="+b); }
public static void Run()
{
var sheet=ScriptableObject.CreateInstance<LiveTimelineWorkSheet>();
try {
JsonUtility.FromJsonOverwrite("{\"radialBlurKeys\":{\"thisList\":[{\"frame\":10,\"moveBlurType\":2,\"attribute\":65536,\"radialBlurDownsample\":3,\"radialBlurIteration\":2,\"radialBlurPower\":0.5,\"depthPowerBack\":38},{\"frame\":20,\"moveBlurType\":0}]}}",sheet);
var keys=sheet.radialBlurKeys;
Check(keys.Count==2,"worksheet track deserialization");
var p=LiveRadialBlurTimeline.Evaluate(keys,10,TimelinePlayerMode.Default);
Check(p.Type==2 && p.IsEnabledDepth && p.Downsample==3 && p.Iteration==2,"discrete key values");
Near(p.Power,0.5f,"raw strength"); Near(p.DepthPowerBack,38,"authored distance");
Check(!LiveRadialBlurTimeline.Evaluate(keys,20,TimelinePlayerMode.Default).IsValid,"off key");
Check(LiveRadialBlurTimeline.Evaluate(keys,10,TimelinePlayerMode.Default).IsValid,"reverse seek");
Check(!LiveRadialBlurTimeline.Evaluate(keys,0,TimelinePlayerMode.Default).IsValid,"pre first");
keys.attribute=LiveTimelineKeyDataListAttr.Disable;
Check(!LiveRadialBlurTimeline.Evaluate(keys,10,TimelinePlayerMode.Default).IsValid,"disabled");
Check(!LiveRadialBlurTimeline.Evaluate(null,10,TimelinePlayerMode.Default).IsValid,"absent track reset");
for(int mode=1;mode<=5;mode++) Check(RadialBlurPass.BlurPassIndex(mode)==2*(mode-1),"native pass mapping");
Check(RadialBlurPass.AreaDifference(0,0)==0.0001f,"zero interval guard");
Check(RadialBlurPass.AreaDifference(1,0.99999f)<0,"signed interval guard");
p.DepthCancelRect=new Vector4(0.2f,0.3f,0.4f,0.5f); p.IsExpandDepthCancelRect=true; p.DepthCancelBlendLength=0.1f;
var bounds=RadialBlurPass.CancelBounds(p); Near(bounds.x,0.1f,"expanded min"); Near(bounds.w,0.9f,"expanded max");
var feature=new PostImageEffectFeature.Parameter(); feature.RadialBlur=p;
Check(feature.IsUseDepthTexture,"radial requests depth independently");
var copy=new PostImageEffectFeature.Parameter(); copy.ShallowCopyParameters(feature);
Check(copy.RadialBlur.Type==2,"parameter copy");
} finally { UnityEngine.Object.DestroyImmediate(sheet); }
string fixture=Environment.GetEnvironmentVariable("UMA_RADIAL_TEST_KEYS");
if(!string.IsNullOrEmpty(fixture)) {
var realKeys=JsonUtility.FromJson<LiveTimelineKeyRadialBlurDataList>(File.ReadAllText(fixture));
Check(realKeys.Count==28,"Live1001 actual key count");
Check(LiveRadialBlurTimeline.Evaluate(realKeys,728,TimelinePlayerMode.Default).Type==0,"real before onset");
var onset=LiveRadialBlurTimeline.Evaluate(realKeys,729,TimelinePlayerMode.Default);
Check(onset.Type==2&&onset.IsEnabledDepth,"real onset");Near(onset.DepthPowerBack,38,"real mask distance");
Check(LiveRadialBlurTimeline.Evaluate(realKeys,988,TimelinePlayerMode.Default).Type==0,"real off cut");
Check(LiveRadialBlurTimeline.Evaluate(realKeys,3965,TimelinePlayerMode.Default).Type==1,"real radial impact");
for(int f=0;f<=7900;f++) {
var sample=LiveRadialBlurTimeline.Evaluate(realKeys,f,TimelinePlayerMode.Default);
Check(sample.Type==0||sample.IsValid,"real full track finite frame="+f);
}
Debug.Log("LiveRadialBlurRegression real Live1001 7901 timeline samples PASS");
}
Debug.Log("LiveRadialBlurRegression timeline PASS");
}
static float Difference(Color[] a, Color[] b)
{
float sum=0;
for(int i=0;i<a.Length;i++) {
Check(!float.IsNaN(a[i].r)&&!float.IsInfinity(a[i].r)&&!float.IsNaN(a[i].g)&&!float.IsInfinity(a[i].g),"finite pixels");
sum+=Mathf.Abs(a[i].r-b[i].r)+Mathf.Abs(a[i].g-b[i].g)+Mathf.Abs(a[i].b-b[i].b);
}
return sum/(a.Length*3);
}
public static void RunGpu()
{
Check(Application.isBatchMode,"isolated batch mode required"); Run();
var bundle=AssetBundle.LoadFromFile(Environment.GetEnvironmentVariable("UMA_POSTFILM_TEST_BUNDLE"));
Material material=null;
var source=new RenderTexture(64,64,0,RenderTextureFormat.ARGBFloat,RenderTextureReadWrite.Linear);
var target=new RenderTexture(64,64,0,RenderTextureFormat.ARGBFloat,RenderTextureReadWrite.Linear);
var texture=new Texture2D(64,64,TextureFormat.RGBAFloat,false,true);
var read=new Texture2D(64,64,TextureFormat.RGBAFloat,false,true);
var cameraObject=new GameObject("RadialRegressionCamera"); var camera=cameraObject.AddComponent<Camera>();
camera.nearClipPlane=0.3f;camera.farClipPlane=100f;
var previous=RenderTexture.active;
var depthTexture=new Texture2D(64,64,TextureFormat.RFloat,false,true) { filterMode=FilterMode.Point };
var previousDepth=Shader.GetGlobalTexture("_CameraDepthTexture");
var previousZ=Shader.GetGlobalVector("_ZBufferParams");
var previousScroll=Shader.GetGlobalVector("_GlobalScreenUVScrollParam");
try {
Shader shader=null; foreach(var candidate in bundle.LoadAllAssets<Shader>()) if(candidate.name=="Cygames/ImageEffects/RadialBlur") shader=candidate;
Check(shader!=null&&shader.isSupported,"official shader available"); material=new Material(shader);
source.Create();target.Create();
var pixels=new Color[64*64];
for(int y=0;y<64;y++) for(int x=0;x<64;x++) pixels[y*64+x]=new Color((x%8<4)?0.8f:0.1f,(y%8<4)?0.7f:0.2f,0.3f,1);
texture.SetPixels(pixels);texture.Apply();Graphics.Blit(texture,source);
Func<RadialBlurPass.Parameter,Color[]> render=p=>{
using(var cmd=new CommandBuffer()) {
RadialBlurPass.Render(cmd,material,p,camera,source,target,source.descriptor);
Graphics.ExecuteCommandBuffer(cmd);
}
RenderTexture.active=target;read.ReadPixels(new Rect(0,0,64,64),0,0);read.Apply();return read.GetPixels();
};
var p=RadialBlurPass.Parameter.Default();p.Type=1;p.Power=0;
var baseline=render(p);Near(Difference(baseline,pixels),0,"zero power identity");
for(int mode=1;mode<=5;mode++) {
p.Type=mode;p.Power=2;p.StartArea=0;p.EndArea=0;p.Downsample=2;p.Iteration=2;p.RollEulerAngles=35;
var result=render(p);float difference=Difference(result,baseline);
Check(difference>0.005f,"mode "+mode+" actually changes source");
Debug.Log("Radial GPU mode="+mode+" meanDifference="+difference);
}
// Real shader's depth gate: front < z <= back preserves source, farther pixels blur.
p.Type=2;p.Downsample=1;p.Iteration=1;p.Power=2;p.IsEnabledDepth=true;
p.DepthPowerFront=0;p.DepthPowerBack=38;
foreach(bool reversed in new[]{false,true}) {
float ratio=camera.farClipPlane/camera.nearClipPlane;
float zx=reversed?ratio-1:1-ratio, zy=reversed?1:ratio;
Shader.SetGlobalVector("_ZBufferParams",new Vector4(zx,zy,zx/100,zy/100));
Shader.SetGlobalVector("_GlobalScreenUVScrollParam",Vector4.zero);
for(int y=0;y<64;y++) for(int x=0;x<64;x++) {
float distance=x<32?10:60;
depthTexture.SetPixel(x,y,new Color((100/distance-zy)/zx,0,0,0));
}
depthTexture.Apply();Shader.SetGlobalTexture("_CameraDepthTexture",depthTexture);
var gated=render(p);float protectedError=0,farDifference=0;
for(int y=4;y<60;y++) for(int x=4;x<60;x++) {
float d=Mathf.Abs(gated[y*64+x].r-baseline[y*64+x].r);
if(x<28)protectedError=Mathf.Max(protectedError,d);
if(x>36)farDifference+=d;
}
Near(protectedError,0,"near object depth protection reversed="+reversed);
Check(farDifference>1,"far background blur reversed="+reversed);
p.IsEnabledDepthCancelRect=true;p.DepthCancelRect=new Vector4(0.1f,0.1f,0.3f,0.8f);
p.DepthCancelBlendLength=0.02f;
var cancelled=render(p);float restoredBlur=0;
for(int y=12;y<52;y++)for(int x=10;x<22;x++)restoredBlur+=Mathf.Abs(cancelled[y*64+x].r-gated[y*64+x].r);
Check(restoredBlur>1,"cancel rectangle overrides depth protection");
p.IsEnabledDepthCancelRect=false;
Debug.Log("Radial depth GPU reversed="+reversed+" protectedError="+protectedError+" farDifference="+farDifference+" rectDifference="+restoredBlur);
}
p.IsEnabledDepth=true;p.IsEnabledDepthCancelRect=true;p.DepthPowerFront=4;p.DepthPowerBack=38;
p.DepthCancelRect=new Vector4(0.2f,0.3f,0.4f,0.5f);p.DepthCancelBlendLength=0.1f;
RadialBlurPass.ApplyMaterial(material,p,camera);
Near(material.GetFloat("_DepthPowerFront"),(4-0.3f)/99.7f,"normalized front");
Near(material.GetFloat("_DepthPowerBack"),(38-0.3f)/99.7f,"normalized back");
Check(material.IsKeywordEnabled("ENABLE_DEPTH")&&material.IsKeywordEnabled("ENABLE_DEPTH_CANCEL_RECT"),"depth keywords");
p.IsEnabledDepth=false;RadialBlurPass.ApplyMaterial(material,p,camera);
Check(!material.IsKeywordEnabled("ENABLE_DEPTH")&&!material.IsKeywordEnabled("ENABLE_DEPTH_CANCEL_RECT"),"stale keyword reset");
Near(material.GetFloat("_DepthPowerBack"),0,"stale distance reset");
p.Downsample=0;p.Iteration=0;Difference(render(p),baseline);
Debug.Log("LiveRadialBlurRegression GPU PASS");
} finally {
RenderTexture.active=previous;
Shader.SetGlobalTexture("_CameraDepthTexture",previousDepth);
Shader.SetGlobalVector("_ZBufferParams",previousZ);
Shader.SetGlobalVector("_GlobalScreenUVScrollParam",previousScroll);
UnityEngine.Object.DestroyImmediate(depthTexture);
UnityEngine.Object.DestroyImmediate(material);UnityEngine.Object.DestroyImmediate(source);UnityEngine.Object.DestroyImmediate(target);
UnityEngine.Object.DestroyImmediate(texture);UnityEngine.Object.DestroyImmediate(read);UnityEngine.Object.DestroyImmediate(cameraObject);
if(bundle!=null) bundle.Unload(true);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: aa08d67efb5849ba86a6209017d814b6
+407
View File
@@ -0,0 +1,407 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Gallop;
using Gallop.Live;
using Gallop.RenderPipeline;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
/// <summary>Opt-in graphics smoke test. Does not alter saved scenes or user configuration.</summary>
[InitializeOnLoad]
public static class LiveRenderCapture
{
const string Armed = "UmaViewer.RenderCapture.Armed";
static double deadline;
static bool requested;
static int captureIndex;
const string PlanState = Armed + ".Plan", ExitState = Armed + ".Exit", FilmIsolationState = Armed + ".FilmIsolation";
static LiveCapturePlan plan;
static readonly LiveCaptureStabilityGate gate = new LiveCaptureStabilityGate();
static bool exitOnFinish;
static bool filmIsolation;
static bool filmSuppressed;
static Gallop.ImageEffect.ScreenOverlay.Overlay.PostFilmMode savedMode1, savedMode2, savedMode3;
static bool suppressedThisRender;
static int CaptureSlots => filmIsolation ? 3 : 2;
static int CaptureFrame => plan.frames[captureIndex / CaptureSlots];
static int CaptureVariant => captureIndex % CaptureSlots;
static string VariantName => CaptureVariant == 0 ? "off" : filmIsolation && CaptureVariant == 1 ? "noFilm" : "on";
static float previousProgress;
static bool previousSync, previousTouched, previousOuted;
static Director capturedDirector;
static Camera camera;
static RenderTexture target, previousTarget;
static PostImageEffectFeature feature;
static bool originalActive;
static bool originalRendering;
static string output;
static bool finishing;
static CapturePreview preview;
static LiveRenderCapture()
{
EditorApplication.update += Tick;
RenderPipelineManager.beginCameraRendering += BeforeCamera;
RenderPipelineManager.endCameraRendering += AfterCamera;
AssemblyReloadEvents.beforeAssemblyReload += () => {
if (SessionState.GetBool(Armed, false) && capturedDirector != null)
Finish("Cancelled before script reload", 0);
if (finishing) RestoreCaptureState();
};
}
static void Configure(bool exit)
{
string reference = Environment.GetEnvironmentVariable("UMA_LIVE_CAPTURE_REFERENCE");
if (string.IsNullOrEmpty(reference)) plan = LiveCapturePlan.Default();
else
{
// Only a capture manifest, never arbitrary application configuration.
if (!string.Equals(Path.GetFileName(reference), "manifest.json", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("UMA_LIVE_CAPTURE_REFERENCE must name a reference manifest.json.");
plan = JsonUtility.FromJson<LiveCapturePlan>(File.ReadAllText(reference));
}
plan.Validate();
string frames = Environment.GetEnvironmentVariable("UMA_LIVE_CAPTURE_FRAMES");
if (!string.IsNullOrWhiteSpace(frames)) plan.SelectFrames(frames.Split(',').Select(int.Parse).ToArray());
SessionState.SetString(PlanState, JsonUtility.ToJson(plan));
SessionState.SetBool(ExitState, exit);
exitOnFinish = exit;
filmIsolation = false;
SessionState.SetBool(FilmIsolationState, false);
captureIndex = 0; gate.Reset(); requested = false; deadline = 0;
}
public static void Start()
{
if (!Application.isBatchMode)
throw new InvalidOperationException("Automatic scene-loading capture is restricted to an isolated batch Editor. Use Capture current Live in an interactive Editor.");
if (SessionState.GetBool(Armed, false) || finishing) throw new InvalidOperationException("A capture is already running or restoring state.");
if (EditorApplication.isPlayingOrWillChangePlaymode)
throw new InvalidOperationException("Start capture from edit mode.");
Configure(true);
LiveRenderSetup.Install();
SessionState.SetBool(Armed, true);
EditorSceneManager.OpenScene("Assets/Scenes/Version2.unity");
EditorApplication.isPlaying = true;
}
[MenuItem("UmaViewer/Rendering/Capture current Live diagnostic frames (308,868,3848)")]
public static void CaptureCurrentDiagnosticFrames()
{
BeginCurrentLiveCapture(true);
}
[MenuItem("UmaViewer/Rendering/Capture current Live BlinkFilm diagnostic frames (900,1240,3900)")]
public static void CaptureCurrentBlinkFilmFrames()
{
// Live1176/stage10148 authored PostFilm sync intervals; report still requires
// identity/parameter checks if a different song is currently loaded.
BeginCurrentLiveCapture(true, new[] { 900, 1240, 3900 });
}
[MenuItem("UmaViewer/Rendering/Capture current Live off/noFilm/on (900,1240,3900)")]
public static void CaptureCurrentFilmIsolation()
{
BeginCurrentLiveCapture(true, new[] { 900, 1240, 3900 });
filmIsolation = true;
SessionState.SetBool(FilmIsolationState, true);
}
[MenuItem("UmaViewer/Rendering/Capture current Live post-effects off-on")]
public static void CaptureCurrentLive()
{
BeginCurrentLiveCapture(false);
}
private static void BeginCurrentLiveCapture(bool diagnostic, int[] diagnosticFrames = null)
{
if (SessionState.GetBool(Armed, false) || finishing) throw new InvalidOperationException("A capture is already running or restoring state.");
var director = Director.instance;
if (!EditorApplication.isPlaying || director == null || !director._isLiveSetup)
throw new InvalidOperationException("Load the reference Live and characters first.");
if (director.IsRecordVMD) throw new InvalidOperationException("Capture cannot seek while VMD recording is active.");
Configure(false);
if (diagnostic)
{
// A same-session off/on experiment, NOT a matched game-reference capture.
// Use actual cast identity instead of silently imposing the song1001 preset.
plan = new LiveCapturePlan {
songId = director.live.MusicId,
stageId = director.live.BackGroundId,
characters = director.CharaContainerScript.Select(x => x.CharaEntry.Id + "_" + x.VarCostumeIdLong).ToArray(),
frames = diagnosticFrames ?? new[] { 308, 868, 3848 }
};
plan.Validate();
if (plan.frames.Any(x => x / (float)plan.fps >= director.totalTime))
throw new InvalidOperationException("Diagnostic frame exceeds the loaded Live duration.");
SessionState.SetString(PlanState, JsonUtility.ToJson(plan));
}
requested = true;
deadline = EditorApplication.timeSinceStartup + 180;
captureIndex = 0; gate.Reset();
capturedDirector = director;
previousProgress = director.UI.ProgressBar.value;
previousSync = director._syncTime;
previousTouched = director.sliderControl.is_Touched;
previousOuted = director.sliderControl.is_Outed;
SessionState.SetBool(Armed, true);
preview = ScriptableObject.CreateInstance<CapturePreview>();
preview.titleContent = new GUIContent("Live capture preview");
preview.minSize = new Vector2(560, 380);
preview.ShowUtility();
Debug.Log("[LiveRenderCapture] Capturing off-screen: Live time is held temporarily. Use the preview to cancel; playback is restored on completion.");
}
[MenuItem("UmaViewer/Rendering/Cancel Live capture")]
public static void CancelCapture()
{
if (SessionState.GetBool(Armed, false)) Finish("Cancelled by user", 0);
}
private sealed class CapturePreview : EditorWindow
{
void OnInspectorUpdate() { Repaint(); }
void OnGUI()
{
EditorGUILayout.HelpBox("Capture uses an off-screen camera target. The Game view may say 'No cameras rendering'. Live time is held while each frame settles; this preview shows the capture target.", MessageType.Info);
if (plan != null && captureIndex < plan.frames.Length * CaptureSlots)
GUILayout.Label("Frame " + CaptureFrame + " / effects " + VariantName + " / saved " + captureIndex + " of " + plan.frames.Length * CaptureSlots);
if (GUILayout.Button("Cancel capture and restore playback")) CancelCapture();
var rect = GUILayoutUtility.GetRect(100, 100, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
if (target != null) GUI.DrawTexture(rect, target, ScaleMode.ScaleToFit, false);
}
void OnDisable() { if (!finishing) CancelCapture(); }
}
static void Tick()
{
if (!SessionState.GetBool(Armed, false)) return;
// Entering play mode reloads statics. Restore intent rather than hanging
// the isolated Editor after a successful batch capture.
if (plan == null)
{
plan = JsonUtility.FromJson<LiveCapturePlan>(SessionState.GetString(PlanState, ""));
exitOnFinish = SessionState.GetBool(ExitState, false);
filmIsolation = SessionState.GetBool(FilmIsolationState, false);
if (plan == null) { Finish("Capture plan missing after reload", 1); return; }
}
if (deadline == 0) deadline = EditorApplication.timeSinceStartup + System.Math.Max(600, plan.frames.Length * 20);
if (EditorApplication.timeSinceStartup > deadline) { Finish("Timed out loading/capturing Live", 1); return; }
if (!EditorApplication.isPlaying) return;
try
{
var director = Director.instance;
if (director == null || !director._isLiveSetup)
{
if (requested) return;
var main = UmaViewerMain.Instance;
var ui = UmaViewerUI.Instance;
if (main == null || ui == null || !ShaderManager.IsShaderBundleReady) return;
var live = main.Lives.FirstOrDefault(x => x.MusicId == plan.songId);
if (live == null || main.Characters.Count < live.MemberCount) return;
var selections = new List<LiveCharacterSelect>();
if (live.MemberCount != plan.characters.Length || live.BackGroundId != plan.stageId)
throw new InvalidOperationException("Reference song/stage/member count differs from loaded Live metadata.");
for (int i = 0; i < live.MemberCount; ++i)
{
var item = new GameObject("CaptureSelection_" + i).AddComponent<LiveCharacterSelect>();
LiveCapturePlan.ParseCharacterIdentity(plan.characters[i], out int id, out string costume);
item.CharaEntry = main.Characters.First(x => !x.IsMob && x.Id == id);
item.CostumeId = costume;
selections.Add(item);
}
ui.LiveMode = 1;
ui.isRequireStage = true;
ui.isRecordVMD = false;
requested = true;
Debug.Log("[LiveRenderCapture] loading live" + plan.songId + " characters=" + string.Join(",", selections.Select(x => x.CharaEntry.Id + "_" + x.CostumeId)));
UmaViewerBuilder.Instance.LoadLive(live, selections);
return;
}
if (capturedDirector == null)
{
if (director.IsRecordVMD) throw new InvalidOperationException("Capture cannot seek while VMD recording is active.");
capturedDirector = director;
previousProgress = director.UI.ProgressBar.value;
previousSync = director._syncTime;
previousTouched = director.sliderControl.is_Touched;
previousOuted = director.sliderControl.is_Outed;
}
if (!plan.MatchesIdentity(director.live.MusicId, director.live.BackGroundId,
director.CharaContainerScript.Select(x => x.CharaEntry.Id + "_" + x.VarCostumeIdLong).ToArray()))
throw new InvalidOperationException("Loaded Live cast/costumes/song/stage do not match capture reference.");
if (plan.frames.Any(x => x / (float)plan.fps >= director.totalTime))
throw new InvalidOperationException("Reference capture frame lies outside this Live timeline.");
director._syncTime = true;
director.sliderControl.is_Outed = false;
director.sliderControl.is_Touched = true;
director.UI.ProgressBar.SetValueWithoutNotify(CaptureFrame / ((float)plan.fps * director.totalTime));
if (target == null)
{
output = Path.GetFullPath("captures/render-smoke/" + DateTime.Now.ToString("yyyyMMdd_HHmmss_fff"));
Directory.CreateDirectory(output);
var renderer = AssetDatabase.LoadAssetAtPath<UniversalRendererData>("Assets/Resources/RenderPipeline/UMAUniversalRenderPipelineAsset_Renderer.asset");
feature = renderer.rendererFeatures.OfType<PostImageEffectFeature>().Single();
originalActive = feature.isActive;
originalRendering = feature.EnableRendering;
target = new RenderTexture(plan.width, plan.height, 24, RenderTextureFormat.ARGB32);
target.Create();
feature.SetActive(true);
feature.EnableRendering = false;
File.WriteAllText(Path.Combine(output, "capture-plan.json"), JsonUtility.ToJson(plan, true));
Debug.Log("[LiveRenderCapture] output=" + output);
}
if (camera != director.MainRenderCamera)
{
if (camera != null) camera.targetTexture = previousTarget;
camera = director.MainRenderCamera;
if (camera == null) return;
previousTarget = camera.targetTexture;
camera.targetTexture = target;
gate.Reset();
}
}
catch (Exception e) { Finish(e.ToString(), 1); }
}
static void BeforeCamera(ScriptableRenderContext context, Camera renderedCamera)
{
if (!SessionState.GetBool(Armed, false) || !filmIsolation || CaptureVariant != 1 ||
target == null || renderedCamera != camera || feature == null || !feature.isActive || filmSuppressed) return;
// Scoped to one camera's URP render. The timeline owns these values; never
// mutate them outside begin/endCameraRendering or persist them to scene assets.
SuppressFilm();
suppressedThisRender = true;
}
// Kept separate from the SRP callback so the exact struct write/restore
// contract can be checked in an isolated Editor without loading a Live.
internal static void SuppressFilm()
{
if (filmSuppressed) throw new InvalidOperationException("Film capture suppression is already active.");
var runtime = PostImageEffectFeature.RuntimeParameter;
var p = runtime.DofDiffuionBloomOverlay;
savedMode1 = p.Overlay1.PostFilmMode;
savedMode2 = p.Overlay2.PostFilmMode;
savedMode3 = p.Overlay3.PostFilmMode;
p.Overlay1.PostFilmMode = Gallop.ImageEffect.ScreenOverlay.Overlay.PostFilmMode.None;
p.Overlay2.PostFilmMode = Gallop.ImageEffect.ScreenOverlay.Overlay.PostFilmMode.None;
p.Overlay3.PostFilmMode = Gallop.ImageEffect.ScreenOverlay.Overlay.PostFilmMode.None;
runtime.DofDiffuionBloomOverlay = p;
filmSuppressed = true;
}
internal static void RestoreFilm()
{
if (!filmSuppressed) return;
var runtime = PostImageEffectFeature.RuntimeParameter;
var p = runtime.DofDiffuionBloomOverlay;
// Restore only the fields we changed. Other timeline-owned parameters
// must survive even if another camera callback refreshed them.
p.Overlay1.PostFilmMode = savedMode1;
p.Overlay2.PostFilmMode = savedMode2;
p.Overlay3.PostFilmMode = savedMode3;
runtime.DofDiffuionBloomOverlay = p;
filmSuppressed = false;
}
static void AfterCamera(ScriptableRenderContext context, Camera renderedCamera)
{
if (!SessionState.GetBool(Armed, false) || target == null || renderedCamera != camera) return;
bool wasSuppressed = filmSuppressed && suppressedThisRender;
RestoreFilm();
suppressedThisRender = false;
var director = Director.instance;
if (director == null || director != capturedDirector || camera != director.MainRenderCamera) { gate.Reset(); return; }
if (!gate.Observe(director._liveCurrentTime, CaptureFrame, plan.fps,
Time.frameCount, ShaderManager.IsShaderBundleReady && feature.isActive && feature.EnableRendering == (CaptureVariant != 0) &&
(CaptureVariant != 1 || !filmIsolation || wasSuppressed))) return;
Texture2D image = null;
var previous = RenderTexture.active;
try
{
context.Submit();
RenderTexture.active = target;
image = new Texture2D(target.width, target.height, TextureFormat.RGB24, false);
image.ReadPixels(new Rect(0, 0, target.width, target.height), 0, 0);
image.Apply();
string name = "frame" + CaptureFrame.ToString("D6") + "_" + VariantName;
File.WriteAllBytes(Path.Combine(output, name + ".png"), image.EncodeToPNG());
var p = PostImageEffectFeature.RuntimeParameter.DofDiffuionBloomOverlay;
File.WriteAllText(Path.Combine(output, name + ".txt"),
$"requestedFrame={CaptureFrame}\nfps={plan.fps}\ntime={Director.instance._liveCurrentTime:R}\ncamera={camera.name}\nmode={p.UseDofDiffusionBloomType}\nfeature={feature.isActive}\nimageEffects={feature.EnableRendering}\nfilmIsolation={filmIsolation && CaptureVariant == 1}\nshaderReady={ShaderManager.IsShaderBundleReady}\nbloomEnabled={p.IsEnableBloom}\ndiffusionEnabled={p.IsEnableDiffusion}\nbloomWeight={p.BloomDofWeight:R}\nintensity={p.BloomIntensity:R}\nthreshold={p.BloomThreshold:R}\nbloomRadius={p.BloomBlurSize:R}\nbloomBlend={p.BloomBlendMode}\ndiffusionBright={p.DiffusionBright:R}\ndiffusionThreshold={p.DiffusionThreshold:R}\ndiffusionSaturation={p.DiffusionSaturation:R}\ndiffusionContrast={p.DiffusionContrast:R}\nfilm1={p.Overlay1.PostFilmMode}:{p.Overlay1.PostFilmPower:R}\nfilm2={p.Overlay2.PostFilmMode}:{p.Overlay2.PostFilmPower:R}\nfilm3={p.Overlay3.PostFilmMode}:{p.Overlay3.PostFilmPower:R}\nfilmBlinkLookup={Director.instance._liveTimelineControl.PostFilmBlinkLookupStatus}\nfilmColor0={FilmColor0(p.Overlay1.PostFilmColor0)}|{FilmColor0(p.Overlay2.PostFilmColor0)}|{FilmColor0(p.Overlay3.PostFilmColor0)}\nformat={target.graphicsFormat}\ndof={p.IsEnableDof}\nfocusType={p.DofFocalType}\nfocusPosition={p.DofFocalPosition}\nfocalPoint={p.DofFocalPoint:R}\nlookAtValid={Director.instance._liveTimelineControl.HasDofCameraLookAt}\nlookAt={Director.instance._liveTimelineControl.DofCameraLookAt}\ncharacters={string.Join(",", Director.instance.CharaContainerScript.Select(x => x.CharaEntry.Id + "_" + x.VarCostumeIdLong))}\n");
Debug.Log("[LiveRenderCapture] saved " + name);
captureIndex++;
gate.Reset();
if (captureIndex == plan.frames.Length * CaptureSlots) { Finish("Completed: " + output, 0); return; }
feature.EnableRendering = CaptureVariant != 0;
}
catch (Exception e) { Finish(e.ToString(), 1); }
finally
{
RenderTexture.active = previous;
if (image != null) UnityEngine.Object.DestroyImmediate(image);
}
}
private static string FilmColor0(Color c) => string.Format(CultureInfo.InvariantCulture,
"{0:R},{1:R},{2:R},{3:R}", c.r, c.g, c.b, c.a);
static void Finish(string message, int code)
{
if (finishing) return;
finishCode = code;
RestoreFilm();
suppressedThisRender = false;
SessionState.SetBool(Armed, false);
if (feature != null)
{
feature.EnableRendering = originalRendering;
feature.SetActive(originalActive);
}
if (camera != null) camera.targetTexture = previousTarget;
Debug.Log("[LiveRenderCapture] " + message);
finishing = true;
// Leave the render callback before releasing render targets.
EditorApplication.delayCall += RestoreCaptureState;
}
static void RestoreCaptureState()
{
if (!finishing) return;
EditorApplication.delayCall -= RestoreCaptureState;
if (capturedDirector != null)
{
capturedDirector.UI.ProgressBar.SetValueWithoutNotify(previousProgress);
capturedDirector._syncTime = previousSync;
capturedDirector.sliderControl.is_Touched = previousTouched;
capturedDirector.sliderControl.is_Outed = previousOuted;
}
if (target != null) { target.Release(); UnityEngine.Object.DestroyImmediate(target); }
if (preview != null) preview.Close();
preview = null;
target = null;
camera = null;
feature = null;
capturedDirector = null;
deadline = 0;
plan = null;
SessionState.EraseString(PlanState);
SessionState.EraseBool(ExitState);
SessionState.EraseBool(FilmIsolationState);
filmIsolation = false;
suppressedThisRender = false;
finishing = false;
if (exitOnFinish) EditorApplication.Exit(finishCode);
}
static int finishCode;
}
#endif
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 29bffc7886f81234fb049722c9554849
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+93
View File
@@ -0,0 +1,93 @@
using System;
using System.Linq;
using Gallop.RenderPipeline;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering.Universal;
/// <summary>Idempotent renderer registration using Unity serialization, not YAML edits.</summary>
public static class LiveRenderSetup
{
[MenuItem("UmaViewer/Live/Install recovered post effects")]
public static void Install()
{
const string path = "Assets/Resources/RenderPipeline/UMAUniversalRenderPipelineAsset_Renderer.asset";
var renderer = AssetDatabase.LoadAssetAtPath<UniversalRendererData>(path);
if (renderer == null) throw new InvalidOperationException("Missing Live renderer: " + path);
var feature = renderer.rendererFeatures.OfType<PostImageEffectFeature>().FirstOrDefault();
if (feature == null)
{
feature = ScriptableObject.CreateInstance<PostImageEffectFeature>();
feature.name = "Gallop Live Post Effects";
AssetDatabase.AddObjectToAsset(feature, renderer);
renderer.rendererFeatures.Add(feature);
}
feature.SetActive(true);
renderer.SetDirty();
EditorUtility.SetDirty(feature);
EditorUtility.SetDirty(renderer);
AssetDatabase.SaveAssets();
Debug.Log("[LiveRenderSetup] Installed recovered post effects: " + path);
}
// Batch-mode contract checks. These validate state routing, not rendered pixels.
public static void Validate()
{
Install();
Install();
var renderer = AssetDatabase.LoadAssetAtPath<UniversalRendererData>(
"Assets/Resources/RenderPipeline/UMAUniversalRenderPipelineAsset_Renderer.asset");
Require(renderer.rendererFeatures.OfType<PostImageEffectFeature>().Count() == 1,
"Renderer registration must be idempotent");
var source = new Gallop.ImageEffect.DofDiffusionBloomOverlayParam();
var state = DofDiffusionBloomOverlayPass.Parameter.Default();
state.Setup(source);
state.DecideDrawType(false);
Require(!state.IsValidity, "Default must be a lossless bypass");
source.IsEnableBloom = true;
source.BloomIntensity = 0.63f;
state.Setup(source);
state.DecideDrawType(false);
Require(state.UseDofDiffusionBloomType == Gallop.ImageEffect.DofDiffusionBloomOverlayParam.DofDiffusionBloomType.Bloom,
"Bloom timeline must select Bloom mode");
Require(Mathf.Approximately(state.BloomIntensity, 0.63f), "Timeline intensity must not be rescaled");
source.IsEnableDiffusion = true;
state.Setup(source);
state.DecideDrawType(false);
Require(state.UseDofDiffusionBloomType == Gallop.ImageEffect.DofDiffusionBloomOverlayParam.DofDiffusionBloomType.DiffusionBloom,
"Diffusion must select the diffusion composition");
source.IsEnableDof = true;
source.DofFocalType = Gallop.DepthBlurAndBloom.DofFocalType.Position;
source.DofFocalPosition = new Vector3(1, 2, 3);
state.Setup(source);
state.DecideDrawType(true);
Require(new PostImageEffectFeature.Parameter { DofDiffuionBloomOverlay = state }.IsUseDepthTexture,
"URP DOF mode must request depth even without overlays");
Require(state.UseDofDiffusionBloomType == Gallop.ImageEffect.DofDiffusionBloomOverlayParam.DofDiffusionBloomType.DiffusionDofBloom,
"DOF must compose with existing Bloom/Diffusion");
Require(state.DofFocalPosition == source.DofFocalPosition, "World-space focus must reach renderer");
source.IsEnableBloom = false;
source.IsEnableDiffusion = false;
state.Setup(source);
state.DecideDrawType(true);
Require(new PostImageEffectFeature.Parameter { DofDiffuionBloomOverlay = state }.IsUseDepthTexture,
"Standalone DOF must request depth without Bloom or overlays");
source.IsEnableDof = false;
source.IsEnableBloom = false;
source.IsEnableDiffusion = false;
state.Setup(source);
state.DecideDrawType(false);
Require(!state.IsValidity, "Disabled key must clear the previous mode");
var previous = PostImageEffectFeature.RuntimeParameter;
previous.DofDiffuionBloomOverlay = state;
PostImageEffectFeature.ResetRuntimeParameter();
Require(!ReferenceEquals(previous, PostImageEffectFeature.RuntimeParameter), "New Live must reset shared state");
Require(!PostImageEffectFeature.RuntimeParameter.DofDiffuionBloomOverlay.IsValidity, "New Live must not inherit effects");
Debug.Log("[LiveRenderValidation] PASS: registration, default bypass, Bloom, Diffusion, disable, intensity, reset");
}
private static void Require(bool condition, string message)
{
if (!condition) throw new InvalidOperationException(message);
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6ee873031d455cb439a61c7e6670811e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,80 @@
#if UNITY_EDITOR
using System;
using Gallop.Live;
using UnityEditor;
using UnityEngine;
public static class Master5PhysicsProfileTests
{
[MenuItem("UmaViewer/Physics/Test master5 Live profile")]
public static void Run()
{
GameObject root = new GameObject("Master5PhysicsProfileTest");
root.hideFlags = HideFlags.HideAndDontSave;
try
{
CheckScale(null, 1f);
CheckScale("Hip_Skirt_L", 0.2f);
CheckScale("hip_mskirt_r", 0.2f);
CheckScale("Hip_Skirt", 1f);
CheckScale("Hip_Tail_L", 1f);
CheckScale("Thigh_MSkirt_L", 0.8f);
CheckScale("Thigh_Skirt_L", 1f);
var character = root.AddComponent<UmaContainerCharacter>();
character.IsLive = true;
var hip = root.AddComponent<DynamicBoneCollider>();
hip.ColliderName = "Hip_Skirt_L";
hip.m_Radius = 0.5f;
hip.m_Height = 1.4f;
var leg = root.AddComponent<DynamicBoneCollider>();
leg.ColliderName = "Thigh_MSkirt_R";
leg.m_Radius = 0.1f;
var other = root.AddComponent<DynamicBoneCollider>();
other.ColliderName = "Hip_Tail_L";
other.m_Radius = 0.25f;
var skirtBone = root.AddComponent<DynamicBone>();
skirtBone.m_ContinuousCollision = true;
var skirt = NewParticle();
skirtBone.Particles.Add(skirt);
var otherBone = root.AddComponent<DynamicBone>();
var hair = NewParticle();
otherBone.Particles.Add(hair);
var profile = root.AddComponent<Master5LivePhysicsProfile>();
for (int i = 0; i < 5; ++i) profile.ApplyToNewPhysics();
Near(hip.m_Radius, 0.1f, "hip radius / repeat application");
Near(leg.m_Radius, 0.08f, "leg radius / repeat application");
Near(other.m_Radius, 0.25f, "unrelated collider");
Near(hip.m_Height, 1.4f, "capsule height preserved");
Near(skirt.m_Damping, 0.3f, "skirt damping");
Near(skirt.m_Elasticity, 0.15f, "skirt elasticity");
Near(skirt.m_Stiffness, 0.2f, "skirt stiffness");
Near(skirt.m_Inert, 0.11f, "skirt inertia");
Near(hair.m_Damping, 0.9f, "other damping");
Near(hair.m_Stiffness, 0.833f, "other rigidity");
Near(hair.m_Elasticity, 0.1925f, "other rebound cap");
Near(hair.m_Inert, 0.121f, "other inertia");
// Newly rebuilt particles must receive the profile, but colliders must not shrink again.
skirtBone.Particles.Clear();
var rebuilt = NewParticle();
skirtBone.Particles.Add(rebuilt);
profile.ApplyToNewPhysics();
Near(rebuilt.m_Damping, 0.3f, "rebuilt particles");
Near(hip.m_Radius, 0.1f, "rebuild preserves radius");
Debug.Log("[PhysicsMigration] master5 profile regression checks PASSED.");
}
finally { UnityEngine.Object.DestroyImmediate(root); }
}
private static DynamicBone.Particle NewParticle() => new DynamicBone.Particle
{
m_Damping = 0.1f, m_Elasticity = 0.1f, m_Stiffness = 0.1f, m_Inert = 0.1f
};
private static void CheckScale(string name, float expected) =>
Near(Master5LivePhysicsProfile.RadiusScale(name), expected, "filter: " + name);
private static void Near(float actual, float expected, string label)
{
if (Mathf.Abs(actual - expected) > 0.00001f)
throw new Exception($"[PhysicsMigration] {label}: expected {expected}, got {actual}");
}
}
#endif
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 63c0f0a6306e44749fed26ef04f57036
+583
View File
@@ -0,0 +1,583 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Text;
using Unity.Collections;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
public static class CyalumeMeshDiagnostics
{
[MenuItem("Tools/Cyalume/Dump Selected Mesh Filters")]
private static void DumpSelectedMeshFilters()
{
var filters = CollectSelectedMeshFilters();
if (filters.Count == 0)
{
Debug.LogWarning("CyalumeMeshDiagnostics: Select one or more GameObjects with MeshFilter components.");
return;
}
var report = new StringBuilder();
report.AppendLine($"Cyalume mesh dump: {filters.Count} selection(s)");
foreach (var filter in filters)
{
report.AppendLine(BuildMeshReport(filter));
report.AppendLine(new string('-', 80));
}
Debug.Log(report.ToString());
}
[MenuItem("Tools/Cyalume/Compare First Two Selected Mesh Filters")]
private static void CompareFirstTwoSelectedMeshFilters()
{
var filters = CollectSelectedMeshFilters();
if (filters.Count < 2)
{
Debug.LogWarning("CyalumeMeshDiagnostics: Select at least two GameObjects with MeshFilter components.");
return;
}
var left = filters[0];
var right = filters[1];
var leftMesh = left.sharedMesh;
var rightMesh = right.sharedMesh;
var report = new StringBuilder();
report.AppendLine("Cyalume mesh compare");
report.AppendLine($"Left : {left.name}");
report.AppendLine($"Right: {right.name}");
report.AppendLine();
report.AppendLine(BuildMeshReport(left));
report.AppendLine(new string('=', 80));
report.AppendLine(BuildMeshReport(right));
report.AppendLine(new string('=', 80));
report.AppendLine(BuildComparison(leftMesh, rightMesh));
Debug.Log(report.ToString());
}
private static List<MeshFilter> CollectSelectedMeshFilters()
{
var results = new List<MeshFilter>();
var seen = new HashSet<MeshFilter>();
foreach (var gameObject in Selection.gameObjects)
{
if (gameObject == null)
{
continue;
}
var filter = gameObject.GetComponent<MeshFilter>();
if (filter != null && seen.Add(filter))
{
results.Add(filter);
}
}
return results;
}
private static string BuildMeshReport(MeshFilter filter)
{
var sb = new StringBuilder();
sb.AppendLine($"GameObject : {filter.name}");
var renderer = filter.GetComponent<Renderer>();
sb.AppendLine($"Renderer : {(renderer != null ? renderer.enabled.ToString() : "missing")}");
var mesh = filter.sharedMesh;
if (mesh == null)
{
sb.AppendLine("Mesh : <null>");
return sb.ToString();
}
sb.AppendLine($"Mesh : {mesh.name}");
sb.AppendLine($"Readable : {SafeRead(() => mesh.isReadable, false)}");
sb.AppendLine($"Vertices : {SafeRead(() => mesh.vertexCount, -1)}");
sb.AppendLine($"SubMeshes : {SafeRead(() => mesh.subMeshCount, -1)}");
sb.AppendLine($"Bounds : {SafeBounds(mesh)}");
sb.AppendLine($"AssetPath : {AssetDatabase.GetAssetPath(mesh)}");
sb.AppendLine($"Importer : {DescribeImporter(mesh)}");
sb.AppendLine($"Attrs : {BuildAttributeSummary(mesh)}");
sb.AppendLine($"AttrLayout : {BuildAttributeLayout(mesh)}");
sb.AppendLine($"RawData : {BuildRawAttributeSummary(mesh)}");
var uv = SafeArrayRead(() => mesh.uv);
sb.AppendLine($"UV Count : {(uv != null ? uv.Length.ToString() : "<unavailable>")}");
var colors32 = SafeArrayRead(() => mesh.colors32);
if (colors32 == null)
{
sb.AppendLine("Colors32 : <unavailable>");
}
else
{
sb.AppendLine($"Colors32 : {colors32.Length}");
sb.AppendLine($"ColorStats : {BuildColorStats(colors32)}");
sb.AppendLine($"ColorSample: {BuildColorSample(colors32, 8)}");
}
return sb.ToString();
}
private static string BuildComparison(Mesh left, Mesh right)
{
var sb = new StringBuilder();
if (left == null || right == null)
{
sb.AppendLine("Comparison unavailable: one of the meshes is null.");
return sb.ToString();
}
sb.AppendLine("Quick compare");
sb.AppendLine($"Readable equal : {SafeRead(() => left.isReadable, false) == SafeRead(() => right.isReadable, false)}");
sb.AppendLine($"Vertex count : {SafeRead(() => left.vertexCount, -1)} vs {SafeRead(() => right.vertexCount, -1)}");
sb.AppendLine($"UV count : {SafeArrayRead(() => left.uv)?.Length ?? -1} vs {SafeArrayRead(() => right.uv)?.Length ?? -1}");
sb.AppendLine($"Attrs : {BuildAttributeSummary(left)} vs {BuildAttributeSummary(right)}");
sb.AppendLine($"AttrLayout L : {BuildAttributeLayout(left)}");
sb.AppendLine($"AttrLayout R : {BuildAttributeLayout(right)}");
sb.AppendLine($"RawData L : {BuildRawAttributeSummary(left)}");
sb.AppendLine($"RawData R : {BuildRawAttributeSummary(right)}");
var leftColors = SafeArrayRead(() => left.colors32);
var rightColors = SafeArrayRead(() => right.colors32);
sb.AppendLine($"Colors count : {leftColors?.Length ?? -1} vs {rightColors?.Length ?? -1}");
if (leftColors != null && rightColors != null)
{
sb.AppendLine($"Alpha stats : {BuildAlphaSummary(leftColors)} vs {BuildAlphaSummary(rightColors)}");
}
return sb.ToString();
}
private static string DescribeImporter(Mesh mesh)
{
var path = AssetDatabase.GetAssetPath(mesh);
if (string.IsNullOrEmpty(path))
{
return "<no importer>";
}
var importer = AssetImporter.GetAtPath(path);
if (importer == null)
{
return "<missing importer>";
}
if (importer is ModelImporter modelImporter)
{
return $"ModelImporter(readable={modelImporter.isReadable})";
}
return importer.GetType().Name;
}
private static string BuildRawAttributeSummary(Mesh mesh)
{
try
{
using var dataArray = Mesh.AcquireReadOnlyMeshData(mesh);
if (dataArray.Length == 0)
{
return "empty";
}
var data = dataArray[0];
if (data.vertexCount <= 0)
{
return "vertexCount=0";
}
var descriptors = mesh.GetVertexAttributes();
if (descriptors == null || descriptors.Length == 0)
{
return "no descriptors";
}
var offsetsByStream = new Dictionary<int, int>();
var parts = new List<string>();
foreach (var descriptor in descriptors)
{
if (!offsetsByStream.TryGetValue(descriptor.stream, out var offset))
{
offset = 0;
}
var part = DescribeRawAttribute(data, descriptor, offset);
parts.Add(part);
offsetsByStream[descriptor.stream] = offset + GetFormatSize(descriptor.format) * descriptor.dimension;
}
return string.Join(" | ", parts);
}
catch (Exception ex)
{
return $"<error: {ex.Message}>";
}
}
private static string DescribeRawAttribute(Mesh.MeshData data, VertexAttributeDescriptor descriptor, int offset)
{
var stride = data.GetVertexBufferStride(descriptor.stream);
var raw = data.GetVertexData<byte>(descriptor.stream);
if (!raw.IsCreated || raw.Length == 0)
{
return $"{descriptor.attribute}:<no data>";
}
switch (descriptor.attribute)
{
case VertexAttribute.Color:
return $"{descriptor.attribute}:{DescribeColor(raw, data.vertexCount, stride, offset)}";
case VertexAttribute.TexCoord0:
case VertexAttribute.TexCoord1:
case VertexAttribute.TexCoord2:
return $"{descriptor.attribute}:{DescribeVector2(raw, data.vertexCount, stride, offset, descriptor.format)}";
default:
return $"{descriptor.attribute}:ok";
}
}
private static string BuildAttributeSummary(Mesh mesh)
{
return
$"pos={SafeHasAttr(mesh, VertexAttribute.Position)} " +
$"nrm={SafeHasAttr(mesh, VertexAttribute.Normal)} " +
$"tan={SafeHasAttr(mesh, VertexAttribute.Tangent)} " +
$"col={SafeHasAttr(mesh, VertexAttribute.Color)} " +
$"uv0={SafeHasAttr(mesh, VertexAttribute.TexCoord0)} " +
$"uv1={SafeHasAttr(mesh, VertexAttribute.TexCoord1)} " +
$"uv2={SafeHasAttr(mesh, VertexAttribute.TexCoord2)} " +
$"uv3={SafeHasAttr(mesh, VertexAttribute.TexCoord3)}";
}
private static string BuildAttributeLayout(Mesh mesh)
{
try
{
var attributes = mesh.GetVertexAttributes();
if (attributes == null || attributes.Length == 0)
{
return "empty";
}
var sb = new StringBuilder();
for (var i = 0; i < attributes.Length; i++)
{
var attribute = attributes[i];
if (i > 0)
{
sb.Append(" | ");
}
var stride = SafeRead(() => mesh.GetVertexBufferStride(attribute.stream), -1);
sb.Append(
$"{attribute.attribute}:{attribute.format}x{attribute.dimension}@s{attribute.stream}/stride{stride}");
}
return sb.ToString();
}
catch (Exception ex)
{
return $"<error: {ex.Message}>";
}
}
private static string DescribeColor(NativeArray<byte> raw, int vertexCount, int stride, int offset)
{
byte minR = byte.MaxValue;
byte minG = byte.MaxValue;
byte minB = byte.MaxValue;
byte minA = byte.MaxValue;
byte maxR = byte.MinValue;
byte maxG = byte.MinValue;
byte maxB = byte.MinValue;
byte maxA = byte.MinValue;
var zeroAlpha = 0;
var sample = new StringBuilder();
var sampleCount = Mathf.Min(vertexCount, 4);
for (var i = 0; i < vertexCount; i++)
{
var baseIndex = i * stride + offset;
var r = raw[baseIndex + 0];
var g = raw[baseIndex + 1];
var b = raw[baseIndex + 2];
var a = raw[baseIndex + 3];
if (r < minR) minR = r;
if (g < minG) minG = g;
if (b < minB) minB = b;
if (a < minA) minA = a;
if (r > maxR) maxR = r;
if (g > maxG) maxG = g;
if (b > maxB) maxB = b;
if (a > maxA) maxA = a;
if (a == 0) zeroAlpha++;
if (i < sampleCount)
{
if (sample.Length > 0)
{
sample.Append(",");
}
sample.Append($"({r},{g},{b},{a})");
}
}
return $"R[{minR},{maxR}] G[{minG},{maxG}] B[{minB},{maxB}] A[{minA},{maxA}] zeroA={zeroAlpha}/{vertexCount} sample={sample}";
}
private static string DescribeVector2(NativeArray<byte> raw, int vertexCount, int stride, int offset, VertexAttributeFormat format)
{
var minX = float.PositiveInfinity;
var minY = float.PositiveInfinity;
var maxX = float.NegativeInfinity;
var maxY = float.NegativeInfinity;
var sample = new StringBuilder();
var sampleCount = Mathf.Min(vertexCount, 4);
for (var i = 0; i < vertexCount; i++)
{
var baseIndex = i * stride + offset;
float x;
float y;
switch (format)
{
case VertexAttributeFormat.Float16:
x = HalfToFloat((ushort)(raw[baseIndex + 0] | (raw[baseIndex + 1] << 8)));
y = HalfToFloat((ushort)(raw[baseIndex + 2] | (raw[baseIndex + 3] << 8)));
break;
case VertexAttributeFormat.Float32:
x = BitConverter.ToSingle(new[]
{
raw[baseIndex + 0],
raw[baseIndex + 1],
raw[baseIndex + 2],
raw[baseIndex + 3]
}, 0);
y = BitConverter.ToSingle(new[]
{
raw[baseIndex + 4],
raw[baseIndex + 5],
raw[baseIndex + 6],
raw[baseIndex + 7]
}, 0);
break;
default:
return $"unsupported({format})";
}
if (x < minX) minX = x;
if (y < minY) minY = y;
if (x > maxX) maxX = x;
if (y > maxY) maxY = y;
if (i < sampleCount)
{
if (sample.Length > 0)
{
sample.Append(",");
}
sample.Append($"({x:0.###},{y:0.###})");
}
}
return $"x[{minX:0.###},{maxX:0.###}] y[{minY:0.###},{maxY:0.###}] sample={sample}";
}
private static int GetFormatSize(VertexAttributeFormat format)
{
switch (format)
{
case VertexAttributeFormat.Float32:
case VertexAttributeFormat.UInt32:
case VertexAttributeFormat.SInt32:
return 4;
case VertexAttributeFormat.Float16:
case VertexAttributeFormat.UNorm16:
case VertexAttributeFormat.SNorm16:
case VertexAttributeFormat.UInt16:
case VertexAttributeFormat.SInt16:
return 2;
case VertexAttributeFormat.UNorm8:
case VertexAttributeFormat.SNorm8:
case VertexAttributeFormat.UInt8:
case VertexAttributeFormat.SInt8:
return 1;
default:
return 0;
}
}
private static float HalfToFloat(ushort value)
{
var sign = (value >> 15) & 0x1;
var exponent = (value >> 10) & 0x1F;
var mantissa = value & 0x3FF;
if (exponent == 0)
{
if (mantissa == 0)
{
return sign == 0 ? 0f : -0f;
}
return (float)((sign == 0 ? 1 : -1) * Math.Pow(2, -14) * (mantissa / 1024.0));
}
if (exponent == 31)
{
if (mantissa == 0)
{
return sign == 0 ? float.PositiveInfinity : float.NegativeInfinity;
}
return float.NaN;
}
return (float)((sign == 0 ? 1 : -1) * Math.Pow(2, exponent - 15) * (1.0 + mantissa / 1024.0));
}
private static string SafeBounds(Mesh mesh)
{
try
{
var bounds = mesh.bounds;
return $"center={bounds.center}, extents={bounds.extents}";
}
catch (Exception ex)
{
return $"<error: {ex.Message}>";
}
}
private static T SafeRead<T>(Func<T> getter, T fallback)
{
try
{
return getter();
}
catch
{
return fallback;
}
}
private static T[] SafeArrayRead<T>(Func<T[]> getter)
{
try
{
return getter();
}
catch
{
return null;
}
}
private static bool SafeHasAttr(Mesh mesh, VertexAttribute attribute)
{
try
{
return mesh.HasVertexAttribute(attribute);
}
catch
{
return false;
}
}
private static string BuildColorStats(Color32[] colors)
{
if (colors.Length == 0)
{
return "empty";
}
byte minR = byte.MaxValue;
byte minG = byte.MaxValue;
byte minB = byte.MaxValue;
byte minA = byte.MaxValue;
byte maxR = byte.MinValue;
byte maxG = byte.MinValue;
byte maxB = byte.MinValue;
byte maxA = byte.MinValue;
var zeroAlphaCount = 0;
foreach (var color in colors)
{
if (color.r < minR) minR = color.r;
if (color.g < minG) minG = color.g;
if (color.b < minB) minB = color.b;
if (color.a < minA) minA = color.a;
if (color.r > maxR) maxR = color.r;
if (color.g > maxG) maxG = color.g;
if (color.b > maxB) maxB = color.b;
if (color.a > maxA) maxA = color.a;
if (color.a == 0) zeroAlphaCount++;
}
return $"R[{minR},{maxR}] G[{minG},{maxG}] B[{minB},{maxB}] A[{minA},{maxA}] zeroA={zeroAlphaCount}/{colors.Length}";
}
private static string BuildAlphaSummary(Color32[] colors)
{
if (colors.Length == 0)
{
return "empty";
}
byte minA = byte.MaxValue;
byte maxA = byte.MinValue;
var zeroAlphaCount = 0;
foreach (var color in colors)
{
if (color.a < minA) minA = color.a;
if (color.a > maxA) maxA = color.a;
if (color.a == 0) zeroAlphaCount++;
}
return $"A[{minA},{maxA}] zeroA={zeroAlphaCount}/{colors.Length}";
}
private static string BuildColorSample(Color32[] colors, int maxCount)
{
if (colors.Length == 0)
{
return "empty";
}
var count = Mathf.Min(colors.Length, maxCount);
var sb = new StringBuilder();
for (var i = 0; i < count; i++)
{
var color = colors[i];
if (i > 0)
{
sb.Append(" | ");
}
sb.Append($"#{i}:({color.r},{color.g},{color.b},{color.a})");
}
return sb.ToString();
}
}
#endif
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e66762c01b96f7c4280248196942465e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+111
View File
@@ -0,0 +1,111 @@
#if UNITY_EDITOR
using System;
using System.Diagnostics;
using System.IO;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
public static class PhysicsCollisionCacheTests
{
[MenuItem("UmaViewer/Physics/Toggle collision geometry cache")]
public static void ToggleCache()
{
DynamicBoneCollider.UseGeometryCache = !DynamicBoneCollider.UseGeometryCache;
Debug.Log("[PhysicsPerformance] Collision geometry cache: " + DynamicBoneCollider.UseGeometryCache);
}
[MenuItem("UmaViewer/Physics/Test collision cache equivalence")]
public static void Run()
{
bool previous = DynamicBoneCollider.UseGeometryCache;
var root = new GameObject("CollisionCacheTest") { hideFlags = HideFlags.HideAndDontSave };
int checks = 0;
try
{
var collider = root.AddComponent<DynamicBoneCollider>();
var random = new System.Random(1709);
var samples = new Vector3[512];
for (int i = 0; i < samples.Length; ++i)
samples[i] = new Vector3((float)random.NextDouble() * 4 - 2,
(float)random.NextDouble() * 4 - 2, (float)random.NextDouble() * 4 - 2);
for (int shape = 0; shape < 2; ++shape)
for (int bound = 0; bound < 2; ++bound)
for (int axis = 0; axis < 3; ++axis)
for (int pose = 0; pose < 4; ++pose)
{
root.transform.position = new Vector3(pose * 0.1f, -0.2f, 0.3f);
root.transform.rotation = Quaternion.Euler(pose * 23, pose * 11, -17);
root.transform.localScale = new Vector3(pose == 3 ? -1.2f : 1.2f, 0.7f + pose * 0.1f, 1.5f);
collider.m_Radius = 0.4f + pose * 0.02f;
collider.m_Height = shape == 0 ? 0 : 2;
collider.m_Center = new Vector3(0.1f, -0.15f, 0.2f);
collider.m_Direction = (DynamicBoneColliderBase.Direction)axis;
collider.m_Bound = (DynamicBoneColliderBase.Bound)bound;
using (DynamicBoneCollider.BeginCollisionBatch())
{
foreach (Vector3 sample in samples)
{
Check(collider, sample, 0.015f);
++checks;
}
using (DynamicBoneCollider.BeginCollisionBatch())
Check(collider, samples[0], 0.015f);
Check(collider, samples[1], 0.015f);
}
// No batch: a moved collider must be read immediately, not from the last cache.
root.transform.position += Vector3.up * 0.2f;
Check(collider, samples[2], 0.03f);
}
collider.m_Bound = DynamicBoneColliderBase.Bound.Outside;
collider.m_Height = 2;
const int rounds = 1024;
// Warm both paths and use two orders to reduce ordering/JIT bias.
Measure(collider, samples, 8, false);
Measure(collider, samples, 8, true);
double uncached = Measure(collider, samples, rounds, false);
double cached = Measure(collider, samples, rounds, true);
cached += Measure(collider, samples, rounds, true);
uncached += Measure(collider, samples, rounds, false);
string report = $"[PhysicsPerformance] PASS: {checks} point comparisons + nested scopes and moved colliders. " +
$"Synthetic {rounds * samples.Length * 2} queries/path: uncached={uncached:F2}ms, cached={cached:F2}ms, " +
$"ratio={uncached / Math.Max(cached, 0.001):F2}x. This is NOT a Live frame-time benchmark.";
Debug.Log(report);
string output = Environment.GetEnvironmentVariable("UMA_PHYSICS_TEST_REPORT");
if (!string.IsNullOrEmpty(output)) File.WriteAllText(output, report);
}
finally
{
DynamicBoneCollider.UseGeometryCache = previous;
UnityEngine.Object.DestroyImmediate(root);
}
}
private static void Check(DynamicBoneCollider collider, Vector3 input, float radius)
{
Vector3 expected = input, actual = input;
DynamicBoneCollider.UseGeometryCache = false;
bool expectedHit = collider.Collide(ref expected, radius);
DynamicBoneCollider.UseGeometryCache = true;
bool actualHit = collider.Collide(ref actual, radius);
if (expectedHit != actualHit || (actual - expected).sqrMagnitude > 1e-12f ||
float.IsNaN(actual.x) || float.IsNaN(actual.y) || float.IsNaN(actual.z))
throw new Exception($"Collision cache mismatch: expected {expectedHit}/{expected}, actual {actualHit}/{actual}");
}
private static double Measure(DynamicBoneCollider collider, Vector3[] samples, int rounds, bool cached)
{
DynamicBoneCollider.UseGeometryCache = cached;
var timer = Stopwatch.StartNew();
for (int i = 0; i < rounds; ++i)
using (DynamicBoneCollider.BeginCollisionBatch())
foreach (var sample in samples)
{
Vector3 point = sample;
collider.Collide(ref point, 0.015f);
}
timer.Stop();
return timer.Elapsed.TotalMilliseconds;
}
}
#endif
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 4111da92019549a083a824c4a20b7e8c
@@ -0,0 +1,52 @@
#if UNITY_EDITOR
using System;
using System.Linq;
using Gallop;
using Gallop.Live;
using UnityEditor;
using UnityEngine;
/// <summary>Checks loaded characters without changing their pose or stopping playback.</summary>
[InitializeOnLoad]
public static class PhysicsMigrationValidation
{
static PhysicsMigrationValidation() { EditorApplication.delayCall += ValidateIfPlaying; }
static void ValidateIfPlaying() { if (EditorApplication.isPlaying) Validate(); }
[MenuItem("UmaViewer/Physics/Validate master5 migration")]
public static void Validate()
{
var characters = UnityEngine.Object.FindObjectsOfType<UmaContainerCharacter>();
foreach (var character in characters.Where(c => !c.IsMini))
{
var bones = character.GetComponentsInChildren<DynamicBone>(true);
var surface = character.GetComponent<SkirtSurfaceCollisionSolver>();
var oldController = character.GetComponent<CySpringController>();
var oldSkirt = character.GetComponent<SkirtController>();
string report = $"[PhysicsMigration] character={character.name} dynamicBones={bones.Length} " +
$"rings={(surface != null ? surface.RingCount : 0)} " +
$"thighColliders={(surface != null ? surface.ColliderCount : 0)} " +
$"oldCySpring={(oldController != null)} oldSkirt={(oldSkirt != null)}";
if (oldController != null || oldSkirt != null || bones.Length == 0)
Debug.LogWarning(report + " Reload this character/Live after script migration.");
else Debug.Log(report);
if (character.IsLive)
{
var profile = character.GetComponent<Master5LivePhysicsProfile>();
if (profile == null)
Debug.LogWarning("[PhysicsMigration] Missing master5 Live profile; reload Live to apply radius tuning.");
else
Debug.Log($"[PhysicsMigration] master5 profile colliders={profile.TunedColliderCount} horizontalStiffness={surface?.HorizontalStiffness}");
foreach (var collider in character.GetComponentsInChildren<DynamicBoneCollider>(true))
{
float scale = Master5LivePhysicsProfile.RadiusScale(collider.ColliderName);
if (scale == 1f) continue;
Debug.Log($"[PhysicsMigration] {collider.ColliderName}: localRadius={collider.m_Radius:F5}, " +
$"height={collider.m_Height:F5}, lossyScale={collider.transform.lossyScale}, expectedRadiusMultiplier={scale:F2}", collider);
}
}
}
if (characters.Length == 0) Debug.Log("[PhysicsMigration] No loaded characters; load a character to validate runtime binding.");
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a970a346d5902f34cac957aba3135a41
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+126
View File
@@ -0,0 +1,126 @@
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
[DisallowMultipleComponent]
public sealed class LiveBloomProbe : MonoBehaviour
{
[Header("Bloom 测试参数")]
[Range(0f, 10f)]
public float bloomIntensity = 2.5f;
[Range(0f, 5f)]
public float bloomThreshold = 0.7f;
[Range(0f, 1f)]
public float bloomScatter = 0.75f;
[Header("曝光测试")]
[Range(-3f, 3f)]
public float postExposure = 0.5f;
private Volume _volume;
private VolumeProfile _runtimeProfile;
private Bloom _bloom;
private ColorAdjustments _colorAdjustments;
private Tonemapping _tonemapping;
private void Awake()
{
EnablePostProcessingOnAllCameras();
CreateRuntimeVolume();
ApplyParameters();
}
private void LateUpdate()
{
// 你的 Live 会不断切换启用中的 Camera,
// 所以测试阶段持续保证所有 Live Camera 都启用 HDR/Post Processing。
EnablePostProcessingOnAllCameras();
ApplyParameters();
}
private void EnablePostProcessingOnAllCameras()
{
Camera[] cameras = FindObjectsOfType<Camera>(true);
for (int i = 0; i < cameras.Length; i++)
{
Camera camera = cameras[i];
if (camera == null ||
!camera.gameObject.scene.IsValid())
{
continue;
}
camera.allowHDR = true;
UniversalAdditionalCameraData cameraData =
camera.GetComponent<UniversalAdditionalCameraData>();
if (cameraData == null)
{
cameraData = camera.gameObject.AddComponent<
UniversalAdditionalCameraData>();
}
cameraData.renderPostProcessing = true;
}
}
private void CreateRuntimeVolume()
{
_volume = GetComponent<Volume>();
if (_volume == null)
_volume = gameObject.AddComponent<Volume>();
_volume.isGlobal = true;
_volume.priority = 1000f;
_volume.weight = 1f;
_runtimeProfile =
ScriptableObject.CreateInstance<VolumeProfile>();
_volume.sharedProfile = _runtimeProfile;
_bloom = _runtimeProfile.Add<Bloom>(true);
_colorAdjustments =
_runtimeProfile.Add<ColorAdjustments>(true);
_tonemapping =
_runtimeProfile.Add<Tonemapping>(true);
}
private void ApplyParameters()
{
if (_bloom != null)
{
_bloom.active = true;
_bloom.intensity.Override(bloomIntensity);
_bloom.threshold.Override(bloomThreshold);
_bloom.scatter.Override(bloomScatter);
_bloom.clamp.Override(1000f);
_bloom.highQualityFiltering.Override(true);
}
if (_colorAdjustments != null)
{
_colorAdjustments.active = true;
_colorAdjustments.postExposure.Override(postExposure);
}
if (_tonemapping != null)
{
_tonemapping.active = true;
_tonemapping.mode.Override(TonemappingMode.ACES);
}
}
private void OnDestroy()
{
if (_runtimeProfile != null)
Destroy(_runtimeProfile);
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 093a9d00e66b14ee2aa312cc2a47e438
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+147
View File
@@ -0,0 +1,147 @@
Shader "Gallop/3D/Live/Cyalume/MobShadowOfficialLike"
{
Properties
{
_MainTex ("Mob Shadow Texture", 2D) = "white" {}
_ClipThreshold ("Clip Threshold", Range(0,1)) = 0.01
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"Queue"="AlphaTest"
"RenderType"="Opaque"
}
Pass
{
Name "UniversalForward"
Tags { "LightMode"="UniversalForward" }
Cull Off
ZWrite On
ZTest LEqual
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 4.5
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
float _ClipThreshold;
CBUFFER_END
float4 _MobColor;
float4 _MaskPosArray[11];
float4x4 _CyalumeGroupMatrix[11];
struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float3 worldPos : TEXCOORD0;
float2 uv : TEXCOORD1;
};
Varyings vert(Attributes IN)
{
Varyings OUT;
VertexPositionInputs posInputs = GetVertexPositionInputs(IN.positionOS.xyz);
OUT.positionCS = posInputs.positionCS;
OUT.worldPos = posInputs.positionWS;
OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex);
return OUT;
}
float ComputeMaskAttenuation(int idx, float3 worldPos)
{
float4 m = _MaskPosArray[idx];
if (m.z <= 0.0001)
return 1.0;
float2 d = worldPos.xz - m.xy;
float dist = length(d);
float radius = m.z;
float softness = max(m.w, 0.0001);
return 1.0 - smoothstep(radius - softness, radius, dist);
}
float3 WorldToGroupLocal(float4x4 m, float3 worldPos)
{
float3 translation = float3(m[0][3], m[1][3], m[2][3]);
float3 axisX = float3(m[0][0], m[1][0], m[2][0]);
float3 axisY = float3(m[0][1], m[1][1], m[2][1]);
float3 axisZ = float3(m[0][2], m[1][2], m[2][2]);
float3 d = worldPos - translation;
float sx2 = max(dot(axisX, axisX), 1e-8);
float sy2 = max(dot(axisY, axisY), 1e-8);
float sz2 = max(dot(axisZ, axisZ), 1e-8);
return float3(
dot(d, axisX) / sx2,
dot(d, axisY) / sy2,
dot(d, axisZ) / sz2
);
}
float4 frag(Varyings IN) : SV_Target
{
float bestAlpha = 0.0;
float3 bestRgb = 1.0.xxx;
[unroll]
for (int i = 0; i < 11; i++)
{
float4x4 groupMtx = _CyalumeGroupMatrix[i];
float3 localPos = WorldToGroupLocal(groupMtx, IN.worldPos);
float2 projUV = localPos.xz + 0.5;
bool inside =
projUV.x >= 0.0 && projUV.x <= 1.0 &&
projUV.y >= 0.0 && projUV.y <= 1.0;
if (!inside)
continue;
float4 texCol = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, projUV);
float maskAtten = ComputeMaskAttenuation(i, IN.worldPos);
float a = texCol.a * maskAtten;
if (a > bestAlpha)
{
bestAlpha = a;
bestRgb = texCol.rgb;
}
}
float4 finalColor;
finalColor.rgb = bestRgb * _MobColor.rgb;
finalColor.a = bestAlpha * _MobColor.a;
clip(finalColor.a - _ClipThreshold);
return finalColor;
}
ENDHLSL
}
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 38174debbaebae343a07ad1ae46e1038
guid: 31a91d96e9d0032419e4753658a987a7
ShaderImporter:
externalObjects: {}
defaultTextures: []
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 7e487cf3ec7419243a47a197ca9f7062
guid: 4c8b4b04d48ad4b97aa4a9d9f2fdba75
folderAsset: yes
DefaultImporter:
externalObjects: {}
Binary file not shown.
@@ -0,0 +1,47 @@
fileFormatVersion: 2
guid: 9d6ab3648065b48a3b3161af5a1f993f
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 0
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 1
settings:
CPU: ARM64
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,80 @@
fileFormatVersion: 2
guid: a9d87eadf9dc1483aa113082dd5e8a97
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 0
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 1
settings:
CPU: ARM64
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: x86_64
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: x86
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: x86_64
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: ecfe64f6a78444342a929112bd228b60
guid: b5952eb1d863e4e0bb8661bff5fc2b48
folderAsset: yes
DefaultImporter:
externalObjects: {}
+21
View File
@@ -0,0 +1,21 @@
fileFormatVersion: 2
guid: a50bd9a009c8dfc4ebd88cc8101225a7
labels:
- Tween
- Tweening
- Animation
- HOTween
- Paths
- iTween
- DFTween
- LeanTween
- Ease
- Easing
- Shake
- Punch
- 2DToolkit
- TextMeshPro
- Text
folderAsset: yes
DefaultImporter:
userData:
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: 34192c5e0d14aee43a0e86cc4823268a
TextScriptImporter:
userData:
Binary file not shown.
@@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: a811bde74b26b53498b4f6d872b09b6d
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Any:
enabled: 1
settings: {}
Editor:
enabled: 0
settings:
DefaultValueInitialized: true
WindowsStoreApps:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: b27f58ae5d5c33a4bb2d1f4f34bd036d
folderAsset: yes
DefaultImporter:
userData:
@@ -0,0 +1,189 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>DOTweenEditor</name>
</assembly>
<members>
<member name="T:DG.DOTweenEditor.EditorCompatibilityUtils">
<summary>
Contains compatibility methods taken from DemiEditor (for when DOTween is without it)
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorCompatibilityUtils.FindObjectOfType``1(System.Boolean)">
<summary>
Warning: some versions of this method don't have the includeInactive parameter so it won't be taken into account
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorCompatibilityUtils.FindObjectOfType(System.Type,System.Boolean)">
<summary>
Warning: some versions of this method don't have the includeInactive parameter so it won't be taken into account
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorCompatibilityUtils.FindObjectsOfType``1(System.Boolean)">
<summary>
Warning: some versions of this method don't have the includeInactive parameter so it won't be taken into account
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorCompatibilityUtils.FindObjectsOfType(System.Type,System.Boolean)">
<summary>
Warning: some versions of this method don't have the includeInactive parameter so it won't be taken into account
</summary>
</member>
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.Start(System.Action)">
<summary>
Starts the update loop of tween in the editor. Has no effect during playMode.
</summary>
<param name="onPreviewUpdated">Eventual callback to call after every update</param>
</member>
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.Stop(System.Boolean,System.Boolean)">
<summary>
Stops the update loop and clears the onPreviewUpdated callback.
</summary>
<param name="resetTweenTargets">If TRUE also resets the tweened objects to their original state.
Note that this works by calling Rewind on all tweens, so it will work correctly
only if you have a single tween type per object and it wasn't killed</param>
<param name="clearTweens">If TRUE also kills any cached tween</param>
</member>
<member name="M:DG.DOTweenEditor.DOTweenEditorPreview.PrepareTweenForPreview(DG.Tweening.Tween,System.Boolean,System.Boolean,System.Boolean)">
<summary>
Readies the tween for editor preview by setting its UpdateType to Manual plus eventual extra settings.
</summary>
<param name="t">The tween to ready</param>
<param name="clearCallbacks">If TRUE (recommended) removes all callbacks (OnComplete/Rewind/etc)</param>
<param name="preventAutoKill">If TRUE prevents the tween from being auto-killed at completion</param>
<param name="andPlay">If TRUE starts playing the tween immediately</param>
</member>
<member name="F:DG.DOTweenEditor.EditorVersion.Version">
<summary>Full major version + first minor version (ex: 2018.1f)</summary>
</member>
<member name="F:DG.DOTweenEditor.EditorVersion.MajorVersion">
<summary>Major version</summary>
</member>
<member name="F:DG.DOTweenEditor.EditorVersion.MinorVersion">
<summary>First minor version (ex: in 2018.1 it would be 1)</summary>
</member>
<member name="P:DG.DOTweenEditor.EditorUtils.adbPrefix">
<summary>With final slash. "Packages/" if this is a package, "Assets/" otherwise</summary>
</member>
<member name="P:DG.DOTweenEditor.EditorUtils.demigiantDir">
<summary>With final slash (system based) - might be NULL in case users are not using a parent Demigiant folder</summary>
</member>
<member name="P:DG.DOTweenEditor.EditorUtils.dotweenDir">
<summary>With final slash (system based). If this is a package, represents the package's Runtime dir</summary>
</member>
<member name="P:DG.DOTweenEditor.EditorUtils.editorDir">
<summary>With final slash (system based). If this is a package, represents the package's Editor dir</summary>
</member>
<member name="P:DG.DOTweenEditor.EditorUtils.editorADBDir">
<summary>DOTween editor path from Assets (not included) with final slash, in AssetDatabase format (/)</summary>
</member>
<member name="P:DG.DOTweenEditor.EditorUtils.dotweenProEditorDir">
<summary>With final slash (system based)</summary>
</member>
<member name="P:DG.DOTweenEditor.EditorUtils.dotweenModulesDir">
<summary>With final slash (system based)</summary>
</member>
<member name="P:DG.DOTweenEditor.EditorUtils.dotweenTimelineDir">
<summary>With final slash (system based)</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.SetEditorTexture(UnityEngine.Texture2D,UnityEngine.FilterMode,System.Int32)">
<summary>
Checks that the given editor texture use the correct import settings,
and applies them if they're incorrect.
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.DOTweenSetupRequired">
<summary>
Returns TRUE if setup is required
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.AssetExists(System.String)">
<summary>
Returns TRUE if the file/directory at the given path exists.
</summary>
<param name="adbPath">Path, relative to Unity's project folder</param>
<returns></returns>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.ADBPathToFullPath(System.String)">
<summary>
Converts the given project-relative path to a full path,
with backward (\) slashes).
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.FullPathToADBPath(System.String)">
<summary>
Converts the given full path to a path usable with AssetDatabase methods
(relative to Unity's project folder, and with the correct Unity forward (/) slashes).
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.ConnectToSourceAsset``1(System.String,System.Boolean)">
<summary>
Connects to a <see cref="T:UnityEngine.ScriptableObject"/> asset.
If the asset already exists at the given path, loads it and returns it.
Otherwise, either returns NULL or automatically creates it before loading and returning it
(depending on the given parameters).
</summary>
<typeparam name="T">Asset type</typeparam>
<param name="adbFilePath">File path (relative to Unity's project folder)</param>
<param name="createIfMissing">If TRUE and the requested asset doesn't exist, forces its creation</param>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.GetAssemblyFilePath(System.Reflection.Assembly)">
<summary>
Full path for the given loaded assembly, assembly file included
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.AddGlobalDefine(System.String)">
<summary>
Adds the given global define if it's not already present
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.RemoveGlobalDefine(System.String)">
<summary>
Removes the given global define if it's present
</summary>
</member>
<member name="M:DG.DOTweenEditor.EditorUtils.HasGlobalDefine(System.String,System.Nullable{UnityEditor.BuildTargetGroup})">
<summary>
Returns TRUE if the given global define is present in all the <see cref="T:UnityEditor.BuildTargetGroup"/>
or only in the given <see cref="T:UnityEditor.BuildTargetGroup"/>, depending on passed parameters.<para/>
</summary>
<param name="id"></param>
<param name="buildTargetGroup"><see cref="T:UnityEditor.BuildTargetGroup"/>to use. Leave NULL to check in all of them.</param>
</member>
<member name="T:DG.DOTweenEditor.DOTweenDefines">
<summary>
Not used as menu item anymore, but as a utility function
</summary>
</member>
<member name="M:DG.DOTweenEditor.DOTweenDefines.RefreshDOTweenSettings(DG.Tweening.Core.DOTweenSettings)">
<summary>Sets the modules bool values to the current defines state</summary>
</member>
<member name="M:DG.DOTweenEditor.DOTweenDefines.RefreshAll">
<summary>Refreshes the enabled state of all defines</summary>
</member>
<member name="M:DG.DOTweenEditor.DOTweenDefines.ApplyGUIEnabledToAll">
<summary>Applies the guiEnabled value state to each define, adding or removing them based on that</summary>
</member>
<member name="M:DG.DOTweenEditor.DOTweenDefines.RemoveAll">
<summary>Removes all DOTween defines including the ones for external assets</summary>
</member>
<member name="M:DG.DOTweenEditor.DOTweenDefines.RemoveAllLegacy">
<summary>Removes all legacy defines</summary>
</member>
<member name="M:DG.DOTweenEditor.DOTweenDefines.Def.Remove">
<summary>Removes the define if present</summary>
</member>
<member name="M:DG.DOTweenEditor.DOTweenDefines.Def.Add">
<summary>Adds the define if it's not already present</summary>
</member>
<member name="F:DG.DOTweenEditor.UnityEditorVersion.Version">
<summary>Full major version + first minor version (ex: 2018.1f)</summary>
</member>
<member name="F:DG.DOTweenEditor.UnityEditorVersion.MajorVersion">
<summary>Major version</summary>
</member>
<member name="F:DG.DOTweenEditor.UnityEditorVersion.MinorVersion">
<summary>First minor version (ex: in 2018.1 it would be 1)</summary>
</member>
</members>
</doc>
@@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: 2e2c6224d345d9249acfa6e8ef40bb2d
TextScriptImporter:
userData:
@@ -0,0 +1,22 @@
fileFormatVersion: 2
guid: 45d5034162d6cf04dbe46da84fc7d074
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Any:
enabled: 0
settings: {}
Editor:
enabled: 1
settings:
DefaultValueInitialized: true
WindowsStoreApps:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 0034ebae0c2a9344e897db1160d71b6d
folderAsset: yes
DefaultImporter:
userData:
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,47 @@
fileFormatVersion: 2
guid: 8da095e39e9b4df488dfd436f81116d6
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: .25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 128
textureSettings:
filterMode: 1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: .5, y: .5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
spritePackingTag:
userData:
Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

@@ -0,0 +1,68 @@
fileFormatVersion: 2
guid: 61521df2e071645488ba3d05e49289ae
timeCreated: 1602317874
licenseType: Store
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 4
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
filterMode: -1
aniso: -1
mipBias: -1
wrapMode: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

@@ -0,0 +1,47 @@
fileFormatVersion: 2
guid: 7051dba417b3d53409f2918f1ea4938d
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: .25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 256
textureSettings:
filterMode: 1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: .5, y: .5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
spritePackingTag:
userData:
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

@@ -0,0 +1,47 @@
fileFormatVersion: 2
guid: 519694efe2bb2914788b151fbd8c01f4
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 1
linearTexture: 0
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: .25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
seamlessCubemap: 0
textureFormat: -1
maxTextureSize: 1024
textureSettings:
filterMode: -1
aniso: -1
mipBias: -1
wrapMode: -1
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: .5, y: .5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 0
textureType: -1
buildTargetSettings: []
spriteSheet:
sprites: []
spritePackingTag:
userData:
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

@@ -0,0 +1,47 @@
fileFormatVersion: 2
guid: 78a59ca99f8987941adb61f9e14a06a7
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
linearTexture: 1
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: .25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
seamlessCubemap: 0
textureFormat: -3
maxTextureSize: 512
textureSettings:
filterMode: 1
aniso: 1
mipBias: -1
wrapMode: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: .5, y: .5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 1
textureType: 2
buildTargetSettings: []
spriteSheet:
sprites: []
spritePackingTag:
userData:
@@ -0,0 +1,5 @@
fileFormatVersion: 2
guid: 143604b8bad857d47a6f7cc7a533e2dc
folderAsset: yes
DefaultImporter:
userData:
@@ -0,0 +1,198 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
#if !DOTWEEN_NOAUDIO // MODULE_MARKER
using System;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
using UnityEngine;
using UnityEngine.Audio; // Required for AudioMixer
#pragma warning disable 1591
namespace DG.Tweening
{
public static class DOTweenModuleAudio
{
#region Shortcuts
#region Audio
/// <summary>Tweens an AudioSource's volume to the given value.
/// Also stores the AudioSource as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach (0 to 1)</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DOFade(this AudioSource target, float endValue, float duration)
{
if (endValue < 0) endValue = 0;
else if (endValue > 1) endValue = 1;
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.volume, x => target.volume = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens an AudioSource's pitch to the given value.
/// Also stores the AudioSource as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DOPitch(this AudioSource target, float endValue, float duration)
{
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.pitch, x => target.pitch = x, endValue, duration);
t.SetTarget(target);
return t;
}
#endregion
#region AudioMixer
/// <summary>Tweens an AudioMixer's exposed float to the given value.
/// Also stores the AudioMixer as the tween's target so it can be used for filtered operations.
/// Note that you need to manually expose a float in an AudioMixerGroup in order to be able to tween it from an AudioMixer.</summary>
/// <param name="floatName">Name given to the exposed float to set</param>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DOSetFloat(this AudioMixer target, string floatName, float endValue, float duration)
{
TweenerCore<float, float, FloatOptions> t = DOTween.To(()=> {
float currVal;
target.GetFloat(floatName, out currVal);
return currVal;
}, x=> target.SetFloat(floatName, x), endValue, duration);
t.SetTarget(target);
return t;
}
#region Operation Shortcuts
/// <summary>
/// Completes all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens completed
/// (meaning the tweens that don't have infinite loops and were not already complete)
/// </summary>
/// <param name="withCallbacks">For Sequences only: if TRUE also internal Sequence callbacks will be fired,
/// otherwise they will be ignored</param>
public static int DOComplete(this AudioMixer target, bool withCallbacks = false)
{
return DOTween.Complete(target, withCallbacks);
}
/// <summary>
/// Kills all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens killed.
/// </summary>
/// <param name="complete">If TRUE completes the tween before killing it</param>
public static int DOKill(this AudioMixer target, bool complete = false)
{
return DOTween.Kill(target, complete);
}
/// <summary>
/// Flips the direction (backwards if it was going forward or viceversa) of all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens flipped.
/// </summary>
public static int DOFlip(this AudioMixer target)
{
return DOTween.Flip(target);
}
/// <summary>
/// Sends to the given position all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens involved.
/// </summary>
/// <param name="to">Time position to reach
/// (if higher than the whole tween duration the tween will simply reach its end)</param>
/// <param name="andPlay">If TRUE will play the tween after reaching the given position, otherwise it will pause it</param>
public static int DOGoto(this AudioMixer target, float to, bool andPlay = false)
{
return DOTween.Goto(target, to, andPlay);
}
/// <summary>
/// Pauses all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens paused.
/// </summary>
public static int DOPause(this AudioMixer target)
{
return DOTween.Pause(target);
}
/// <summary>
/// Plays all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens played.
/// </summary>
public static int DOPlay(this AudioMixer target)
{
return DOTween.Play(target);
}
/// <summary>
/// Plays backwards all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens played.
/// </summary>
public static int DOPlayBackwards(this AudioMixer target)
{
return DOTween.PlayBackwards(target);
}
/// <summary>
/// Plays forward all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens played.
/// </summary>
public static int DOPlayForward(this AudioMixer target)
{
return DOTween.PlayForward(target);
}
/// <summary>
/// Restarts all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens restarted.
/// </summary>
public static int DORestart(this AudioMixer target)
{
return DOTween.Restart(target);
}
/// <summary>
/// Rewinds all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens rewinded.
/// </summary>
public static int DORewind(this AudioMixer target)
{
return DOTween.Rewind(target);
}
/// <summary>
/// Smoothly rewinds all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens rewinded.
/// </summary>
public static int DOSmoothRewind(this AudioMixer target)
{
return DOTween.SmoothRewind(target);
}
/// <summary>
/// Toggles the paused state (plays if it was paused, pauses if it was playing) of all tweens that have this target as a reference
/// (meaning tweens that were started from this target, or that had this target added as an Id)
/// and returns the total number of tweens involved.
/// </summary>
public static int DOTogglePause(this AudioMixer target)
{
return DOTween.TogglePause(target);
}
#endregion
#endregion
#endregion
}
}
#endif
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b766d08851589514b97afb23c6f30a70
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,146 @@
using UnityEngine;
#if DOTWEEN_EPO || EPO_DOTWEEN // MODULE_MARKER
using EPOOutline;
using DG.Tweening.Plugins.Options;
using DG.Tweening;
using DG.Tweening.Core;
namespace DG.Tweening
{
public static class DOTweenModuleEPOOutline
{
public static int DOKill(this SerializedPass target, bool complete)
{
return DOTween.Kill(target, complete);
}
public static TweenerCore<float, float, FloatOptions> DOFloat(this SerializedPass target, string propertyName, float endValue, float duration)
{
var tweener = DOTween.To(() => target.GetFloat(propertyName), x => target.SetFloat(propertyName, x), endValue, duration);
tweener.SetOptions(true).SetTarget(target);
return tweener;
}
public static TweenerCore<Color, Color, ColorOptions> DOFade(this SerializedPass target, string propertyName, float endValue, float duration)
{
var tweener = DOTween.ToAlpha(() => target.GetColor(propertyName), x => target.SetColor(propertyName, x), endValue, duration);
tweener.SetOptions(true).SetTarget(target);
return tweener;
}
public static TweenerCore<Color, Color, ColorOptions> DOColor(this SerializedPass target, string propertyName, Color endValue, float duration)
{
var tweener = DOTween.To(() => target.GetColor(propertyName), x => target.SetColor(propertyName, x), endValue, duration);
tweener.SetOptions(false).SetTarget(target);
return tweener;
}
public static TweenerCore<Vector4, Vector4, VectorOptions> DOVector(this SerializedPass target, string propertyName, Vector4 endValue, float duration)
{
var tweener = DOTween.To(() => target.GetVector(propertyName), x => target.SetVector(propertyName, x), endValue, duration);
tweener.SetOptions(false).SetTarget(target);
return tweener;
}
public static TweenerCore<float, float, FloatOptions> DOFloat(this SerializedPass target, int propertyId, float endValue, float duration)
{
var tweener = DOTween.To(() => target.GetFloat(propertyId), x => target.SetFloat(propertyId, x), endValue, duration);
tweener.SetOptions(true).SetTarget(target);
return tweener;
}
public static TweenerCore<Color, Color, ColorOptions> DOFade(this SerializedPass target, int propertyId, float endValue, float duration)
{
var tweener = DOTween.ToAlpha(() => target.GetColor(propertyId), x => target.SetColor(propertyId, x), endValue, duration);
tweener.SetOptions(true).SetTarget(target);
return tweener;
}
public static TweenerCore<Color, Color, ColorOptions> DOColor(this SerializedPass target, int propertyId, Color endValue, float duration)
{
var tweener = DOTween.To(() => target.GetColor(propertyId), x => target.SetColor(propertyId, x), endValue, duration);
tweener.SetOptions(false).SetTarget(target);
return tweener;
}
public static TweenerCore<Vector4, Vector4, VectorOptions> DOVector(this SerializedPass target, int propertyId, Vector4 endValue, float duration)
{
var tweener = DOTween.To(() => target.GetVector(propertyId), x => target.SetVector(propertyId, x), endValue, duration);
tweener.SetOptions(false).SetTarget(target);
return tweener;
}
public static int DOKill(this Outlinable.OutlineProperties target, bool complete = false)
{
return DOTween.Kill(target, complete);
}
public static int DOKill(this Outliner target, bool complete = false)
{
return DOTween.Kill(target, complete);
}
/// <summary>
/// Controls the alpha (transparency) of the outline
/// </summary>
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Outlinable.OutlineProperties target, float endValue, float duration)
{
var tweener = DOTween.ToAlpha(() => target.Color, x => target.Color = x, endValue, duration);
tweener.SetOptions(true).SetTarget(target);
return tweener;
}
/// <summary>
/// Controls the color of the outline
/// </summary>
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Outlinable.OutlineProperties target, Color endValue, float duration)
{
var tweener = DOTween.To(() => target.Color, x => target.Color = x, endValue, duration);
tweener.SetOptions(false).SetTarget(target);
return tweener;
}
/// <summary>
/// Controls the amount of blur applied to the outline
/// </summary>
public static TweenerCore<float, float, FloatOptions> DOBlurShift(this Outlinable.OutlineProperties target, float endValue, float duration, bool snapping = false)
{
var tweener = DOTween.To(() => target.BlurShift, x => target.BlurShift = x, endValue, duration);
tweener.SetOptions(snapping).SetTarget(target);
return tweener;
}
/// <summary>
/// Controls the amount of blur applied to the outline
/// </summary>
public static TweenerCore<float, float, FloatOptions> DOBlurShift(this Outliner target, float endValue, float duration, bool snapping = false)
{
var tweener = DOTween.To(() => target.BlurShift, x => target.BlurShift = x, endValue, duration);
tweener.SetOptions(snapping).SetTarget(target);
return tweener;
}
/// <summary>
/// Controls the amount of dilation applied to the outline
/// </summary>
public static TweenerCore<float, float, FloatOptions> DODilateShift(this Outlinable.OutlineProperties target, float endValue, float duration, bool snapping = false)
{
var tweener = DOTween.To(() => target.DilateShift, x => target.DilateShift = x, endValue, duration);
tweener.SetOptions(snapping).SetTarget(target);
return tweener;
}
/// <summary>
/// Controls the amount of dilation applied to the outline
/// </summary>
public static TweenerCore<float, float, FloatOptions> DODilateShift(this Outliner target, float endValue, float duration, bool snapping = false)
{
var tweener = DOTween.To(() => target.DilateShift, x => target.DilateShift = x, endValue, duration);
tweener.SetOptions(snapping).SetTarget(target);
return tweener;
}
}
}
#endif
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e944529dcaee98f4e9498d80e541d93e
timeCreated: 1602593330
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,216 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
#if !DOTWEEN_NOPHYSICS // MODULE_MARKER
using System;
using DG.Tweening.Core;
using DG.Tweening.Core.Enums;
using DG.Tweening.Plugins;
using DG.Tweening.Plugins.Core.PathCore;
using DG.Tweening.Plugins.Options;
using UnityEngine;
#pragma warning disable 1591
namespace DG.Tweening
{
public static class DOTweenModulePhysics
{
#region Shortcuts
#region Rigidbody
/// <summary>Tweens a Rigidbody's position to the given value.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMove(this Rigidbody target, Vector3 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody's X position to the given value.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveX(this Rigidbody target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector3(endValue, 0, 0), duration);
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody's Y position to the given value.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveY(this Rigidbody target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector3(0, endValue, 0), duration);
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody's Z position to the given value.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveZ(this Rigidbody target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector3(0, 0, endValue), duration);
t.SetOptions(AxisConstraint.Z, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody's rotation to the given value.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="mode">Rotation mode</param>
public static TweenerCore<Quaternion, Vector3, QuaternionOptions> DORotate(this Rigidbody target, Vector3 endValue, float duration, RotateMode mode = RotateMode.Fast)
{
TweenerCore<Quaternion, Vector3, QuaternionOptions> t = DOTween.To(() => target.rotation, target.MoveRotation, endValue, duration);
t.SetTarget(target);
t.plugOptions.rotateMode = mode;
return t;
}
/// <summary>Tweens a Rigidbody's rotation so that it will look towards the given position.
/// Also stores the rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="towards">The position to look at</param><param name="duration">The duration of the tween</param>
/// <param name="axisConstraint">Eventual axis constraint for the rotation</param>
/// <param name="up">The vector that defines in which direction up is (default: Vector3.up)</param>
public static TweenerCore<Quaternion, Vector3, QuaternionOptions> DOLookAt(this Rigidbody target, Vector3 towards, float duration, AxisConstraint axisConstraint = AxisConstraint.None, Vector3? up = null)
{
TweenerCore<Quaternion, Vector3, QuaternionOptions> t = DOTween.To(() => target.rotation, target.MoveRotation, towards, duration)
.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetLookAt);
t.plugOptions.axisConstraint = axisConstraint;
t.plugOptions.up = (up == null) ? Vector3.up : (Vector3)up;
return t;
}
#region Special
/// <summary>Tweens a Rigidbody's position to the given value, while also applying a jump effect along the Y axis.
/// Returns a Sequence instead of a Tweener.
/// Also stores the Rigidbody as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param>
/// <param name="jumpPower">Power of the jump (the max height of the jump is represented by this plus the final Y offset)</param>
/// <param name="numJumps">Total number of jumps</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Sequence DOJump(this Rigidbody target, Vector3 endValue, float jumpPower, int numJumps, float duration, bool snapping = false)
{
if (numJumps < 1) numJumps = 1;
float startPosY = 0;
float offsetY = -1;
bool offsetYSet = false;
Sequence s = DOTween.Sequence();
Tween yTween = DOTween.To(() => target.position, target.MovePosition, new Vector3(0, jumpPower, 0), duration / (numJumps * 2))
.SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
.SetLoops(numJumps * 2, LoopType.Yoyo)
.OnStart(() => startPosY = target.position.y);
s.Append(DOTween.To(() => target.position, target.MovePosition, new Vector3(endValue.x, 0, 0), duration)
.SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear)
).Join(DOTween.To(() => target.position, target.MovePosition, new Vector3(0, 0, endValue.z), duration)
.SetOptions(AxisConstraint.Z, snapping).SetEase(Ease.Linear)
).Join(yTween)
.SetTarget(target).SetEase(DOTween.defaultEaseType);
yTween.OnUpdate(() => {
if (!offsetYSet) {
offsetYSet = true;
offsetY = s.isRelative ? endValue.y : endValue.y - startPosY;
}
Vector3 pos = target.position;
pos.y += DOVirtual.EasedValue(0, offsetY, yTween.ElapsedPercentage(), Ease.OutQuad);
target.MovePosition(pos);
});
return s;
}
/// <summary>Tweens a Rigidbody's position through the given path waypoints, using the chosen path algorithm.
/// Also stores the Rigidbody as the tween's target so it can be used for filtered operations.
/// <para>NOTE: to tween a rigidbody correctly it should be set to kinematic at least while being tweened.</para>
/// <para>BEWARE: doesn't work on Windows Phone store (waiting for Unity to fix their own bug).
/// If you plan to publish there you should use a regular transform.DOPath.</para></summary>
/// <param name="path">The waypoints to go through</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="pathType">The type of path: Linear (straight path), CatmullRom (curved CatmullRom path) or CubicBezier (curved with control points)</param>
/// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
/// <param name="resolution">The resolution of the path (useless in case of Linear paths): higher resolutions make for more detailed curved paths but are more expensive.
/// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
/// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
public static TweenerCore<Vector3, Path, PathOptions> DOPath(
this Rigidbody target, Vector3[] path, float duration, PathType pathType = PathType.Linear,
PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
)
{
if (resolution < 1) resolution = 1;
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => target.position, target.MovePosition, new Path(pathType, path, resolution, gizmoColor), duration)
.SetTarget(target).SetUpdate(UpdateType.Fixed);
t.plugOptions.isRigidbody = true;
t.plugOptions.mode = pathMode;
return t;
}
/// <summary>Tweens a Rigidbody's localPosition through the given path waypoints, using the chosen path algorithm.
/// Also stores the Rigidbody as the tween's target so it can be used for filtered operations
/// <para>NOTE: to tween a rigidbody correctly it should be set to kinematic at least while being tweened.</para>
/// <para>BEWARE: doesn't work on Windows Phone store (waiting for Unity to fix their own bug).
/// If you plan to publish there you should use a regular transform.DOLocalPath.</para></summary>
/// <param name="path">The waypoint to go through</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="pathType">The type of path: Linear (straight path), CatmullRom (curved CatmullRom path) or CubicBezier (curved with control points)</param>
/// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
/// <param name="resolution">The resolution of the path: higher resolutions make for more detailed curved paths but are more expensive.
/// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
/// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
public static TweenerCore<Vector3, Path, PathOptions> DOLocalPath(
this Rigidbody target, Vector3[] path, float duration, PathType pathType = PathType.Linear,
PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
)
{
if (resolution < 1) resolution = 1;
Transform trans = target.transform;
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => trans.localPosition, x => target.MovePosition(trans.parent == null ? x : trans.parent.TransformPoint(x)), new Path(pathType, path, resolution, gizmoColor), duration)
.SetTarget(target).SetUpdate(UpdateType.Fixed);
t.plugOptions.isRigidbody = true;
t.plugOptions.mode = pathMode;
t.plugOptions.useLocalPosition = true;
return t;
}
// Used by path editor when creating the actual tween, so it can pass a pre-compiled path
internal static TweenerCore<Vector3, Path, PathOptions> DOPath(
this Rigidbody target, Path path, float duration, PathMode pathMode = PathMode.Full3D
)
{
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => target.position, target.MovePosition, path, duration)
.SetTarget(target);
t.plugOptions.isRigidbody = true;
t.plugOptions.mode = pathMode;
return t;
}
internal static TweenerCore<Vector3, Path, PathOptions> DOLocalPath(
this Rigidbody target, Path path, float duration, PathMode pathMode = PathMode.Full3D
)
{
Transform trans = target.transform;
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => trans.localPosition, x => target.MovePosition(trans.parent == null ? x : trans.parent.TransformPoint(x)), path, duration)
.SetTarget(target);
t.plugOptions.isRigidbody = true;
t.plugOptions.mode = pathMode;
t.plugOptions.useLocalPosition = true;
return t;
}
#endregion
#endregion
#endregion
}
}
#endif
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dae9aa560b4242648a3affa2bfabc365
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,193 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
#if !DOTWEEN_NOPHYSICS2D // MODULE_MARKER
using System;
using DG.Tweening.Core;
using DG.Tweening.Plugins;
using DG.Tweening.Plugins.Core.PathCore;
using DG.Tweening.Plugins.Options;
using UnityEngine;
#pragma warning disable 1591
namespace DG.Tweening
{
public static class DOTweenModulePhysics2D
{
#region Shortcuts
#region Rigidbody2D Shortcuts
/// <summary>Tweens a Rigidbody2D's position to the given value.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMove(this Rigidbody2D target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody2D's X position to the given value.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMoveX(this Rigidbody2D target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector2(endValue, 0), duration);
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody2D's Y position to the given value.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMoveY(this Rigidbody2D target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.position, target.MovePosition, new Vector2(0, endValue), duration);
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a Rigidbody2D's rotation to the given value.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DORotate(this Rigidbody2D target, float endValue, float duration)
{
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.rotation, target.MoveRotation, endValue, duration);
t.SetTarget(target);
return t;
}
#region Special
/// <summary>Tweens a Rigidbody2D's position to the given value, while also applying a jump effect along the Y axis.
/// Returns a Sequence instead of a Tweener.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations.
/// <para>IMPORTANT: a rigidbody2D can't be animated in a jump arc using MovePosition, so the tween will directly set the position</para></summary>
/// <param name="endValue">The end value to reach</param>
/// <param name="jumpPower">Power of the jump (the max height of the jump is represented by this plus the final Y offset)</param>
/// <param name="numJumps">Total number of jumps</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Sequence DOJump(this Rigidbody2D target, Vector2 endValue, float jumpPower, int numJumps, float duration, bool snapping = false)
{
if (numJumps < 1) numJumps = 1;
float startPosY = 0;
float offsetY = -1;
bool offsetYSet = false;
Sequence s = DOTween.Sequence();
Tween yTween = DOTween.To(() => target.position, x => target.position = x, new Vector2(0, jumpPower), duration / (numJumps * 2))
.SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
.SetLoops(numJumps * 2, LoopType.Yoyo)
.OnStart(() => startPosY = target.position.y);
s.Append(DOTween.To(() => target.position, x => target.position = x, new Vector2(endValue.x, 0), duration)
.SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear)
).Join(yTween)
.SetTarget(target).SetEase(DOTween.defaultEaseType);
yTween.OnUpdate(() => {
if (!offsetYSet) {
offsetYSet = true;
offsetY = s.isRelative ? endValue.y : endValue.y - startPosY;
}
Vector3 pos = target.position;
pos.y += DOVirtual.EasedValue(0, offsetY, yTween.ElapsedPercentage(), Ease.OutQuad);
target.MovePosition(pos);
});
return s;
}
/// <summary>Tweens a Rigidbody2D's position through the given path waypoints, using the chosen path algorithm.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations.
/// <para>NOTE: to tween a Rigidbody2D correctly it should be set to kinematic at least while being tweened.</para>
/// <para>BEWARE: doesn't work on Windows Phone store (waiting for Unity to fix their own bug).
/// If you plan to publish there you should use a regular transform.DOPath.</para></summary>
/// <param name="path">The waypoints to go through</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="pathType">The type of path: Linear (straight path), CatmullRom (curved CatmullRom path) or CubicBezier (curved with control points)</param>
/// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
/// <param name="resolution">The resolution of the path (useless in case of Linear paths): higher resolutions make for more detailed curved paths but are more expensive.
/// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
/// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
public static TweenerCore<Vector3, Path, PathOptions> DOPath(
this Rigidbody2D target, Vector2[] path, float duration, PathType pathType = PathType.Linear,
PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
)
{
if (resolution < 1) resolution = 1;
int len = path.Length;
Vector3[] path3D = new Vector3[len];
for (int i = 0; i < len; ++i) path3D[i] = path[i];
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => target.position, x => target.MovePosition(x), new Path(pathType, path3D, resolution, gizmoColor), duration)
.SetTarget(target).SetUpdate(UpdateType.Fixed);
t.plugOptions.isRigidbody2D = true;
t.plugOptions.mode = pathMode;
return t;
}
/// <summary>Tweens a Rigidbody2D's localPosition through the given path waypoints, using the chosen path algorithm.
/// Also stores the Rigidbody2D as the tween's target so it can be used for filtered operations
/// <para>NOTE: to tween a Rigidbody2D correctly it should be set to kinematic at least while being tweened.</para>
/// <para>BEWARE: doesn't work on Windows Phone store (waiting for Unity to fix their own bug).
/// If you plan to publish there you should use a regular transform.DOLocalPath.</para></summary>
/// <param name="path">The waypoint to go through</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="pathType">The type of path: Linear (straight path), CatmullRom (curved CatmullRom path) or CubicBezier (curved with control points)</param>
/// <param name="pathMode">The path mode: 3D, side-scroller 2D, top-down 2D</param>
/// <param name="resolution">The resolution of the path: higher resolutions make for more detailed curved paths but are more expensive.
/// Defaults to 10, but a value of 5 is usually enough if you don't have dramatic long curves between waypoints</param>
/// <param name="gizmoColor">The color of the path (shown when gizmos are active in the Play panel and the tween is running)</param>
public static TweenerCore<Vector3, Path, PathOptions> DOLocalPath(
this Rigidbody2D target, Vector2[] path, float duration, PathType pathType = PathType.Linear,
PathMode pathMode = PathMode.Full3D, int resolution = 10, Color? gizmoColor = null
)
{
if (resolution < 1) resolution = 1;
int len = path.Length;
Vector3[] path3D = new Vector3[len];
for (int i = 0; i < len; ++i) path3D[i] = path[i];
Transform trans = target.transform;
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => trans.localPosition, x => target.MovePosition(trans.parent == null ? x : trans.parent.TransformPoint(x)), new Path(pathType, path3D, resolution, gizmoColor), duration)
.SetTarget(target).SetUpdate(UpdateType.Fixed);
t.plugOptions.isRigidbody2D = true;
t.plugOptions.mode = pathMode;
t.plugOptions.useLocalPosition = true;
return t;
}
// Used by path editor when creating the actual tween, so it can pass a pre-compiled path
internal static TweenerCore<Vector3, Path, PathOptions> DOPath(
this Rigidbody2D target, Path path, float duration, PathMode pathMode = PathMode.Full3D
)
{
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => target.position, x => target.MovePosition(x), path, duration)
.SetTarget(target);
t.plugOptions.isRigidbody2D = true;
t.plugOptions.mode = pathMode;
return t;
}
internal static TweenerCore<Vector3, Path, PathOptions> DOLocalPath(
this Rigidbody2D target, Path path, float duration, PathMode pathMode = PathMode.Full3D
)
{
Transform trans = target.transform;
TweenerCore<Vector3, Path, PathOptions> t = DOTween.To(PathPlugin.Get(), () => trans.localPosition, x => target.MovePosition(trans.parent == null ? x : trans.parent.TransformPoint(x)), path, duration)
.SetTarget(target);
t.plugOptions.isRigidbody2D = true;
t.plugOptions.mode = pathMode;
t.plugOptions.useLocalPosition = true;
return t;
}
#endregion
#endregion
#endregion
}
}
#endif
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 230fe34542e175245ba74b4659dae700
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,93 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
#if !DOTWEEN_NOSPRITES // MODULE_MARKER
using System;
using UnityEngine;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
#pragma warning disable 1591
namespace DG.Tweening
{
public static class DOTweenModuleSprite
{
#region Shortcuts
#region SpriteRenderer
/// <summary>Tweens a SpriteRenderer's color to the given value.
/// Also stores the spriteRenderer as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOColor(this SpriteRenderer target, Color endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Material's alpha color to the given value.
/// Also stores the spriteRenderer as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOFade(this SpriteRenderer target, float endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a SpriteRenderer's color using the given gradient
/// (NOTE 1: only uses the colors of the gradient, not the alphas - NOTE 2: creates a Sequence, not a Tweener).
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="gradient">The gradient to use</param><param name="duration">The duration of the tween</param>
public static Sequence DOGradientColor(this SpriteRenderer target, Gradient gradient, float duration)
{
Sequence s = DOTween.Sequence();
GradientColorKey[] colors = gradient.colorKeys;
int len = colors.Length;
for (int i = 0; i < len; ++i) {
GradientColorKey c = colors[i];
if (i == 0 && c.time <= 0) {
target.color = c.color;
continue;
}
float colorDuration = i == len - 1
? duration - s.Duration(false) // Verifies that total duration is correct
: duration * (i == 0 ? c.time : c.time - colors[i - 1].time);
s.Append(target.DOColor(c.color, colorDuration).SetEase(Ease.Linear));
}
s.SetTarget(target);
return s;
}
#endregion
#region Blendables
#region SpriteRenderer
/// <summary>Tweens a SpriteRenderer's color to the given value,
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
/// instead than fight each other as multiple DOColor would do.
/// Also stores the SpriteRenderer as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
public static Tweener DOBlendableColor(this SpriteRenderer target, Color endValue, float duration)
{
endValue = endValue - target.color;
Color to = new Color(0, 0, 0, 0);
return DOTween.To(() => to, x => {
Color diff = x - to;
to = x;
target.color += diff;
}, endValue, duration)
.Blendable().SetTarget(target);
}
#endregion
#endregion
#endregion
}
}
#endif
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 188918ab119d93148aa0de59ccf5286b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,662 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
#if !DOTWEEN_NOUI // MODULE_MARKER
using System;
using System.Globalization;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening.Core;
using DG.Tweening.Core.Enums;
using DG.Tweening.Plugins;
using DG.Tweening.Plugins.Options;
using Outline = UnityEngine.UI.Outline;
using Text = UnityEngine.UI.Text;
#pragma warning disable 1591
namespace DG.Tweening
{
public static class DOTweenModuleUI
{
#region Shortcuts
#region CanvasGroup
/// <summary>Tweens a CanvasGroup's alpha color to the given value.
/// Also stores the canvasGroup as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DOFade(this CanvasGroup target, float endValue, float duration)
{
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.alpha, x => target.alpha = x, endValue, duration);
t.SetTarget(target);
return t;
}
#endregion
#region Graphic
/// <summary>Tweens an Graphic's color to the given value.
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Graphic target, Color endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens an Graphic's alpha color to the given value.
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Graphic target, float endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
#endregion
#region Image
/// <summary>Tweens an Image's color to the given value.
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Image target, Color endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens an Image's alpha color to the given value.
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Image target, float endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens an Image's fillAmount to the given value.
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach (0 to 1)</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DOFillAmount(this Image target, float endValue, float duration)
{
if (endValue > 1) endValue = 1;
else if (endValue < 0) endValue = 0;
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.fillAmount, x => target.fillAmount = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens an Image's colors using the given gradient
/// (NOTE 1: only uses the colors of the gradient, not the alphas - NOTE 2: creates a Sequence, not a Tweener).
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="gradient">The gradient to use</param><param name="duration">The duration of the tween</param>
public static Sequence DOGradientColor(this Image target, Gradient gradient, float duration)
{
Sequence s = DOTween.Sequence();
GradientColorKey[] colors = gradient.colorKeys;
int len = colors.Length;
for (int i = 0; i < len; ++i) {
GradientColorKey c = colors[i];
if (i == 0 && c.time <= 0) {
target.color = c.color;
continue;
}
float colorDuration = i == len - 1
? duration - s.Duration(false) // Verifies that total duration is correct
: duration * (i == 0 ? c.time : c.time - colors[i - 1].time);
s.Append(target.DOColor(c.color, colorDuration).SetEase(Ease.Linear));
}
s.SetTarget(target);
return s;
}
#endregion
#region LayoutElement
/// <summary>Tweens an LayoutElement's flexibleWidth/Height to the given value.
/// Also stores the LayoutElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOFlexibleSize(this LayoutElement target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => new Vector2(target.flexibleWidth, target.flexibleHeight), x => {
target.flexibleWidth = x.x;
target.flexibleHeight = x.y;
}, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens an LayoutElement's minWidth/Height to the given value.
/// Also stores the LayoutElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMinSize(this LayoutElement target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => new Vector2(target.minWidth, target.minHeight), x => {
target.minWidth = x.x;
target.minHeight = x.y;
}, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens an LayoutElement's preferredWidth/Height to the given value.
/// Also stores the LayoutElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPreferredSize(this LayoutElement target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => new Vector2(target.preferredWidth, target.preferredHeight), x => {
target.preferredWidth = x.x;
target.preferredHeight = x.y;
}, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
#endregion
#region Outline
/// <summary>Tweens a Outline's effectColor to the given value.
/// Also stores the Outline as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Outline target, Color endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.effectColor, x => target.effectColor = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Outline's effectColor alpha to the given value.
/// Also stores the Outline as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Outline target, float endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.effectColor, x => target.effectColor = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Outline's effectDistance to the given value.
/// Also stores the Outline as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOScale(this Outline target, Vector2 endValue, float duration)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.effectDistance, x => target.effectDistance = x, endValue, duration);
t.SetTarget(target);
return t;
}
#endregion
#region RectTransform
/// <summary>Tweens a RectTransform's anchoredPosition to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorPos(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition X to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorPosX(this RectTransform target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(endValue, 0), duration);
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition Y to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorPosY(this RectTransform target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(0, endValue), duration);
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition3D to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3D(this RectTransform target, Vector3 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition3D X to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3DX(this RectTransform target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, new Vector3(endValue, 0, 0), duration);
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition3D Y to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3DY(this RectTransform target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, new Vector3(0, endValue, 0), duration);
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchoredPosition3D Z to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOAnchorPos3DZ(this RectTransform target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t = DOTween.To(() => target.anchoredPosition3D, x => target.anchoredPosition3D = x, new Vector3(0, 0, endValue), duration);
t.SetOptions(AxisConstraint.Z, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchorMax to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorMax(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchorMax, x => target.anchorMax = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's anchorMin to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOAnchorMin(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.anchorMin, x => target.anchorMin = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's pivot to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPivot(this RectTransform target, Vector2 endValue, float duration)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.pivot, x => target.pivot = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's pivot X to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPivotX(this RectTransform target, float endValue, float duration)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.pivot, x => target.pivot = x, new Vector2(endValue, 0), duration);
t.SetOptions(AxisConstraint.X).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's pivot Y to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOPivotY(this RectTransform target, float endValue, float duration)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.pivot, x => target.pivot = x, new Vector2(0, endValue), duration);
t.SetOptions(AxisConstraint.Y).SetTarget(target);
return t;
}
/// <summary>Tweens a RectTransform's sizeDelta to the given value.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOSizeDelta(this RectTransform target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.sizeDelta, x => target.sizeDelta = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Punches a RectTransform's anchoredPosition towards the given direction and then back to the starting one
/// as if it was connected to the starting position via an elastic.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="punch">The direction and strength of the punch (added to the RectTransform's current position)</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="vibrato">Indicates how much will the punch vibrate</param>
/// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting position when bouncing backwards.
/// 1 creates a full oscillation between the punch direction and the opposite direction,
/// while 0 oscillates only between the punch and the start position</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Tweener DOPunchAnchorPos(this RectTransform target, Vector2 punch, float duration, int vibrato = 10, float elasticity = 1, bool snapping = false)
{
return DOTween.Punch(() => target.anchoredPosition, x => target.anchoredPosition = x, punch, duration, vibrato, elasticity)
.SetTarget(target).SetOptions(snapping);
}
/// <summary>Shakes a RectTransform's anchoredPosition with the given values.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="duration">The duration of the tween</param>
/// <param name="strength">The shake strength</param>
/// <param name="vibrato">Indicates how much will the shake vibrate</param>
/// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
/// Setting it to 0 will shake along a single direction.</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
/// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
/// <param name="randomnessMode">Randomness mode</param>
public static Tweener DOShakeAnchorPos(this RectTransform target, float duration, float strength = 100, int vibrato = 10, float randomness = 90, bool snapping = false, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
{
return DOTween.Shake(() => target.anchoredPosition, x => target.anchoredPosition = x, duration, strength, vibrato, randomness, true, fadeOut, randomnessMode)
.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake).SetOptions(snapping);
}
/// <summary>Shakes a RectTransform's anchoredPosition with the given values.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="duration">The duration of the tween</param>
/// <param name="strength">The shake strength on each axis</param>
/// <param name="vibrato">Indicates how much will the shake vibrate</param>
/// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
/// Setting it to 0 will shake along a single direction.</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
/// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
/// <param name="randomnessMode">Randomness mode</param>
public static Tweener DOShakeAnchorPos(this RectTransform target, float duration, Vector2 strength, int vibrato = 10, float randomness = 90, bool snapping = false, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
{
return DOTween.Shake(() => target.anchoredPosition, x => target.anchoredPosition = x, duration, strength, vibrato, randomness, fadeOut, randomnessMode)
.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake).SetOptions(snapping);
}
#region Special
/// <summary>Tweens a RectTransform's anchoredPosition to the given value, while also applying a jump effect along the Y axis.
/// Returns a Sequence instead of a Tweener.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param>
/// <param name="jumpPower">Power of the jump (the max height of the jump is represented by this plus the final Y offset)</param>
/// <param name="numJumps">Total number of jumps</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Sequence DOJumpAnchorPos(this RectTransform target, Vector2 endValue, float jumpPower, int numJumps, float duration, bool snapping = false)
{
if (numJumps < 1) numJumps = 1;
float startPosY = 0;
float offsetY = -1;
bool offsetYSet = false;
// Separate Y Tween so we can elaborate elapsedPercentage on that insted of on the Sequence
// (in case users add a delay or other elements to the Sequence)
Sequence s = DOTween.Sequence();
Tween yTween = DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(0, jumpPower), duration / (numJumps * 2))
.SetOptions(AxisConstraint.Y, snapping).SetEase(Ease.OutQuad).SetRelative()
.SetLoops(numJumps * 2, LoopType.Yoyo)
.OnStart(()=> startPosY = target.anchoredPosition.y);
s.Append(DOTween.To(() => target.anchoredPosition, x => target.anchoredPosition = x, new Vector2(endValue.x, 0), duration)
.SetOptions(AxisConstraint.X, snapping).SetEase(Ease.Linear)
).Join(yTween)
.SetTarget(target).SetEase(DOTween.defaultEaseType);
s.OnUpdate(() => {
if (!offsetYSet) {
offsetYSet = true;
offsetY = s.isRelative ? endValue.y : endValue.y - startPosY;
}
Vector2 pos = target.anchoredPosition;
pos.y += DOVirtual.EasedValue(0, offsetY, s.ElapsedDirectionalPercentage(), Ease.OutQuad);
target.anchoredPosition = pos;
});
return s;
}
#endregion
#endregion
#region ScrollRect
/// <summary>Tweens a ScrollRect's horizontal/verticalNormalizedPosition to the given value.
/// Also stores the ScrollRect as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Tweener DONormalizedPos(this ScrollRect target, Vector2 endValue, float duration, bool snapping = false)
{
return DOTween.To(() => new Vector2(target.horizontalNormalizedPosition, target.verticalNormalizedPosition),
x => {
target.horizontalNormalizedPosition = x.x;
target.verticalNormalizedPosition = x.y;
}, endValue, duration)
.SetOptions(snapping).SetTarget(target);
}
/// <summary>Tweens a ScrollRect's horizontalNormalizedPosition to the given value.
/// Also stores the ScrollRect as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Tweener DOHorizontalNormalizedPos(this ScrollRect target, float endValue, float duration, bool snapping = false)
{
return DOTween.To(() => target.horizontalNormalizedPosition, x => target.horizontalNormalizedPosition = x, endValue, duration)
.SetOptions(snapping).SetTarget(target);
}
/// <summary>Tweens a ScrollRect's verticalNormalizedPosition to the given value.
/// Also stores the ScrollRect as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Tweener DOVerticalNormalizedPos(this ScrollRect target, float endValue, float duration, bool snapping = false)
{
return DOTween.To(() => target.verticalNormalizedPosition, x => target.verticalNormalizedPosition = x, endValue, duration)
.SetOptions(snapping).SetTarget(target);
}
#endregion
#region Slider
/// <summary>Tweens a Slider's value to the given value.
/// Also stores the Slider as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<float, float, FloatOptions> DOValue(this Slider target, float endValue, float duration, bool snapping = false)
{
TweenerCore<float, float, FloatOptions> t = DOTween.To(() => target.value, x => target.value = x, endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
#endregion
#region Text
/// <summary>Tweens a Text's color to the given value.
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOColor(this Text target, Color endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.To(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>
/// Tweens a Text's text from one integer to another, with options for thousands separators
/// </summary>
/// <param name="fromValue">The value to start from</param>
/// <param name="endValue">The end value to reach</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="addThousandsSeparator">If TRUE (default) also adds thousands separators</param>
/// <param name="culture">The <see cref="CultureInfo"/> to use (InvariantCulture if NULL)</param>
public static TweenerCore<int, int, NoOptions> DOCounter(
this Text target, int fromValue, int endValue, float duration, bool addThousandsSeparator = true, CultureInfo culture = null
){
int v = fromValue;
CultureInfo cInfo = !addThousandsSeparator ? null : culture ?? CultureInfo.InvariantCulture;
TweenerCore<int, int, NoOptions> t = DOTween.To(() => v, x => {
v = x;
target.text = addThousandsSeparator
? v.ToString("N0", cInfo)
: v.ToString();
}, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Text's alpha color to the given value.
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Color, Color, ColorOptions> DOFade(this Text target, float endValue, float duration)
{
TweenerCore<Color, Color, ColorOptions> t = DOTween.ToAlpha(() => target.color, x => target.color = x, endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Text's text to the given value.
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end string to tween to</param><param name="duration">The duration of the tween</param>
/// <param name="richTextEnabled">If TRUE (default), rich text will be interpreted correctly while animated,
/// otherwise all tags will be considered as normal text</param>
/// <param name="scrambleMode">The type of scramble mode to use, if any</param>
/// <param name="scrambleChars">A string containing the characters to use for scrambling.
/// Use as many characters as possible (minimum 10) because DOTween uses a fast scramble mode which gives better results with more characters.
/// Leave it to NULL (default) to use default ones</param>
public static TweenerCore<string, string, StringOptions> DOText(this Text target, string endValue, float duration, bool richTextEnabled = true, ScrambleMode scrambleMode = ScrambleMode.None, string scrambleChars = null)
{
if (endValue == null) {
if (Debugger.logPriority > 0) Debugger.LogWarning("You can't pass a NULL string to DOText: an empty string will be used instead to avoid errors");
endValue = "";
}
TweenerCore<string, string, StringOptions> t = DOTween.To(() => target.text, x => target.text = x, endValue, duration);
t.SetOptions(richTextEnabled, scrambleMode, scrambleChars)
.SetTarget(target);
return t;
}
#endregion
#region Blendables
#region Graphic
/// <summary>Tweens a Graphic's color to the given value,
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
/// instead than fight each other as multiple DOColor would do.
/// Also stores the Graphic as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
public static Tweener DOBlendableColor(this Graphic target, Color endValue, float duration)
{
endValue = endValue - target.color;
Color to = new Color(0, 0, 0, 0);
return DOTween.To(() => to, x => {
Color diff = x - to;
to = x;
target.color += diff;
}, endValue, duration)
.Blendable().SetTarget(target);
}
#endregion
#region Image
/// <summary>Tweens a Image's color to the given value,
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
/// instead than fight each other as multiple DOColor would do.
/// Also stores the Image as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
public static Tweener DOBlendableColor(this Image target, Color endValue, float duration)
{
endValue = endValue - target.color;
Color to = new Color(0, 0, 0, 0);
return DOTween.To(() => to, x => {
Color diff = x - to;
to = x;
target.color += diff;
}, endValue, duration)
.Blendable().SetTarget(target);
}
#endregion
#region Text
/// <summary>Tweens a Text's color BY the given value,
/// in a way that allows other DOBlendableColor tweens to work together on the same target,
/// instead than fight each other as multiple DOColor would do.
/// Also stores the Text as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The value to tween to</param><param name="duration">The duration of the tween</param>
public static Tweener DOBlendableColor(this Text target, Color endValue, float duration)
{
endValue = endValue - target.color;
Color to = new Color(0, 0, 0, 0);
return DOTween.To(() => to, x => {
Color diff = x - to;
to = x;
target.color += diff;
}, endValue, duration)
.Blendable().SetTarget(target);
}
#endregion
#endregion
#region Shapes
/// <summary>Tweens a RectTransform's anchoredPosition so that it draws a circle around the given center.
/// Also stores the RectTransform as the tween's target so it can be used for filtered operations.<para/>
/// IMPORTANT: SetFrom(value) requires a <see cref="Vector2"/> instead of a float, where the X property represents the "from degrees value"</summary>
/// <param name="center">Circle-center/pivot around which to rotate (in UI anchoredPosition coordinates)</param>
/// <param name="endValueDegrees">The end value degrees to reach (to rotate counter-clockwise pass a negative value)</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="relativeCenter">If TRUE the <see cref="center"/> coordinates will be considered as relative to the target's current anchoredPosition</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, CircleOptions> DOShapeCircle(
this RectTransform target, Vector2 center, float endValueDegrees, float duration, bool relativeCenter = false, bool snapping = false
)
{
TweenerCore<Vector2, Vector2, CircleOptions> t = DOTween.To(
CirclePlugin.Get(), () => target.anchoredPosition, x => target.anchoredPosition = x, center, duration
);
t.SetOptions(endValueDegrees, relativeCenter, snapping).SetTarget(target);
return t;
}
#endregion
#endregion
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
// ███ INTERNAL CLASSES ████████████████████████████████████████████████████████████████████████████████████████████████
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
public static class Utils
{
/// <summary>
/// Converts the anchoredPosition of the first RectTransform to the second RectTransform,
/// taking into consideration offset, anchors and pivot, and returns the new anchoredPosition
/// </summary>
public static Vector2 SwitchToRectTransform(RectTransform from, RectTransform to)
{
Vector2 localPoint;
Vector2 fromPivotDerivedOffset = new Vector2(from.rect.width * 0.5f + from.rect.xMin, from.rect.height * 0.5f + from.rect.yMin);
Vector2 screenP = RectTransformUtility.WorldToScreenPoint(null, from.position);
screenP += fromPivotDerivedOffset;
RectTransformUtility.ScreenPointToLocalPointInRectangle(to, screenP, null, out localPoint);
Vector2 pivotDerivedOffset = new Vector2(to.rect.width * 0.5f + to.rect.xMin, to.rect.height * 0.5f + to.rect.yMin);
return to.anchoredPosition + localPoint - pivotDerivedOffset;
}
}
}
}
#endif
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a060394c03331a64392db53a10e7f2d1
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,161 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2026/01/08
#if DOTWEEN_UITOOLKIT && UNITY_2021_3_OR_NEWER // MODULE_MARKER
using UnityEngine;
using UnityEngine.UIElements;
using DG.Tweening.Core;
using DG.Tweening.Core.Enums;
using DG.Tweening.Plugins.Options;
#pragma warning disable 1591
namespace DG.Tweening
{
public static class DOTweenModuleUIToolkit
{
#region Shortcuts
#region VisualElement
/// <summary>Tweens a VisualElement's position (via style.translate) to the given value.
/// Also stores the VisualElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMove(this VisualElement target, Vector3 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t
= DOTween.To(() => target.resolvedStyle.translate, x => target.style.translate = new Translate(x.x, x.y, x.z), endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a VisualElement's position (via style.translate) to the given value.
/// Also stores the VisualElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOMove(this VisualElement target, Vector2 endValue, float duration, bool snapping = false)
{
TweenerCore<Vector2, Vector2, VectorOptions> t
= DOTween.To(() => (Vector2)target.resolvedStyle.translate, x => target.style.translate = new Translate(x.x, x.y, 0), endValue, duration);
t.SetOptions(snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a VisualElement's X position (via style.translate) to the given value.
/// Also stores the VisualElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveX(this VisualElement target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t
= DOTween.To(() => target.resolvedStyle.translate, x => target.style.translate = new Translate(x.x, x.y, x.z), new Vector3(endValue, 0, 0), duration);
t.SetOptions(AxisConstraint.X, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a VisualElement's Y position (via style.translate) to the given value.
/// Also stores the VisualElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveY(this VisualElement target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t
= DOTween.To(() => target.resolvedStyle.translate, x => target.style.translate = new Translate(x.x, x.y, x.z), new Vector3(0, endValue, 0), duration);
t.SetOptions(AxisConstraint.Y, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a VisualElement's Z position (via style.translate) to the given value.
/// Also stores the VisualElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static TweenerCore<Vector3, Vector3, VectorOptions> DOMoveZ(this VisualElement target, float endValue, float duration, bool snapping = false)
{
TweenerCore<Vector3, Vector3, VectorOptions> t
= DOTween.To(() => target.resolvedStyle.translate, x => target.style.translate = new Translate(x.x, x.y, x.z), new Vector3(0, 0, endValue), duration);
t.SetOptions(AxisConstraint.Z, snapping).SetTarget(target);
return t;
}
/// <summary>Tweens a VisualElement's scale (via style.scale) to the given value.
/// Also stores the VisualElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOScale(this VisualElement target, Vector2 endValue, float duration)
{
TweenerCore<Vector2, Vector2, VectorOptions> t
= DOTween.To(() => (Vector2)target.resolvedStyle.scale.value, x => target.style.scale = new Scale(x), endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a VisualElement's scale (via style.scale) to the given value.
/// Also stores the VisualElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOScale(this VisualElement target, float endValue, float duration)
{
TweenerCore<Vector2, Vector2, VectorOptions> t
= DOTween.To(() => (Vector2)target.resolvedStyle.scale.value, x => target.style.scale = new Scale(x), new Vector2(endValue, endValue), duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a VisualElement's rotation (via style.rotate) to the given value.
/// Also stores the VisualElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param><param name="duration">The duration of the tween</param>
public static TweenerCore<float, float, FloatOptions> DORotate(this VisualElement target, float endValue, float duration)
{
TweenerCore<float, float, FloatOptions> t
= DOTween.To(() => target.resolvedStyle.rotate.angle.value, x => target.style.rotate = new Rotate(x), endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Punches a VisualElement's position towards the given direction and then back to the starting one
/// as if it was connected to the starting position via an elastic.
/// Also stores the VisualElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="punch">The direction and strength of the punch (added to the VisualElement's current position)</param>
/// <param name="duration">The duration of the tween</param>
/// <param name="vibrato">Indicates how much will the punch vibrate</param>
/// <param name="elasticity">Represents how much (0 to 1) the vector will go beyond the starting position when bouncing backwards.
/// 1 creates a full oscillation between the punch direction and the opposite direction,
/// while 0 oscillates only between the punch and the start position</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
public static Tweener DOPunch(this VisualElement target, Vector3 punch, float duration, int vibrato = 10, float elasticity = 1, bool snapping = false)
{
return DOTween.Punch(() => target.resolvedStyle.translate, x => target.style.translate = new Translate(x.x, x.y, x.z), punch, duration, vibrato, elasticity)
.SetTarget(target).SetOptions(snapping);
}
/// <summary>Shakes a VisualElement's position with the given values.
/// Also stores the VisualElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="duration">The duration of the tween</param>
/// <param name="strength">The shake strength</param>
/// <param name="vibrato">Indicates how much will the shake vibrate</param>
/// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
/// Setting it to 0 will shake along a single direction.</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
/// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
/// <param name="randomnessMode">Randomness mode</param>
public static Tweener DOShake(this VisualElement target, float duration, float strength = 100, int vibrato = 10, float randomness = 90, bool snapping = false, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
{
return DOTween.Shake(() => target.resolvedStyle.translate, x => target.style.translate = new Translate(x.x, x.y, x.z), duration, strength, vibrato, randomness, true, fadeOut, randomnessMode)
.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake).SetOptions(snapping);
}
/// <summary>Shakes a VisualElement's position with the given values.
/// Also stores the VisualElement as the tween's target so it can be used for filtered operations</summary>
/// <param name="duration">The duration of the tween</param>
/// <param name="strength">The shake strength on each axis</param>
/// <param name="vibrato">Indicates how much will the shake vibrate</param>
/// <param name="randomness">Indicates how much the shake will be random (0 to 180 - values higher than 90 kind of suck, so beware).
/// Setting it to 0 will shake along a single direction.</param>
/// <param name="snapping">If TRUE the tween will smoothly snap all values to integers</param>
/// <param name="fadeOut">If TRUE the shake will automatically fadeOut smoothly within the tween's duration, otherwise it will not</param>
/// <param name="randomnessMode">Randomness mode</param>
public static Tweener DOShake(this VisualElement target, float duration, Vector2 strength, int vibrato = 10, float randomness = 90, bool snapping = false, bool fadeOut = true, ShakeRandomnessMode randomnessMode = ShakeRandomnessMode.Full)
{
return DOTween.Shake(() => target.resolvedStyle.translate, x => target.style.translate = new Translate(x.x, x.y, x.z), duration, strength, vibrato, randomness, fadeOut, randomnessMode)
.SetTarget(target).SetSpecialStartupMode(SpecialStartupMode.SetShake).SetOptions(snapping);
}
#endregion
#endregion
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c0a6d2683fb84e4193f2aa968264881
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,389 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
using System;
using UnityEngine;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
//#if UNITY_2018_1_OR_NEWER && (NET_4_6 || NET_STANDARD_2_0)
//using Task = System.Threading.Tasks.Task;
//#endif
#pragma warning disable 1591
namespace DG.Tweening
{
/// <summary>
/// Shortcuts/functions that are not strictly related to specific Modules
/// but are available only on some Unity versions
/// </summary>
public static class DOTweenModuleUnityVersion
{
#region Material
/// <summary>Tweens a Material's color using the given gradient
/// (NOTE 1: only uses the colors of the gradient, not the alphas - NOTE 2: creates a Sequence, not a Tweener).
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="gradient">The gradient to use</param><param name="duration">The duration of the tween</param>
public static Sequence DOGradientColor(this Material target, Gradient gradient, float duration)
{
Sequence s = DOTween.Sequence();
GradientColorKey[] colors = gradient.colorKeys;
int len = colors.Length;
for (int i = 0; i < len; ++i) {
GradientColorKey c = colors[i];
if (i == 0 && c.time <= 0) {
target.color = c.color;
continue;
}
float colorDuration = i == len - 1
? duration - s.Duration(false) // Verifies that total duration is correct
: duration * (i == 0 ? c.time : c.time - colors[i - 1].time);
s.Append(target.DOColor(c.color, colorDuration).SetEase(Ease.Linear));
}
s.SetTarget(target);
return s;
}
/// <summary>Tweens a Material's named color property using the given gradient
/// (NOTE 1: only uses the colors of the gradient, not the alphas - NOTE 2: creates a Sequence, not a Tweener).
/// Also stores the image as the tween's target so it can be used for filtered operations</summary>
/// <param name="gradient">The gradient to use</param>
/// <param name="property">The name of the material property to tween (like _Tint or _SpecColor)</param>
/// <param name="duration">The duration of the tween</param>
public static Sequence DOGradientColor(this Material target, Gradient gradient, string property, float duration)
{
Sequence s = DOTween.Sequence();
GradientColorKey[] colors = gradient.colorKeys;
int len = colors.Length;
for (int i = 0; i < len; ++i) {
GradientColorKey c = colors[i];
if (i == 0 && c.time <= 0) {
target.SetColor(property, c.color);
continue;
}
float colorDuration = i == len - 1
? duration - s.Duration(false) // Verifies that total duration is correct
: duration * (i == 0 ? c.time : c.time - colors[i - 1].time);
s.Append(target.DOColor(c.color, property, colorDuration).SetEase(Ease.Linear));
}
s.SetTarget(target);
return s;
}
#endregion
#region CustomYieldInstructions
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed or complete.
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForCompletion(true);</code>
/// </summary>
public static CustomYieldInstruction WaitForCompletion(this Tween t, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForCompletion(t);
}
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed or rewinded.
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForRewind();</code>
/// </summary>
public static CustomYieldInstruction WaitForRewind(this Tween t, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForRewind(t);
}
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed.
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForKill();</code>
/// </summary>
public static CustomYieldInstruction WaitForKill(this Tween t, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForKill(t);
}
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed or has gone through the given amount of loops.
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForElapsedLoops(2);</code>
/// </summary>
/// <param name="elapsedLoops">Elapsed loops to wait for</param>
public static CustomYieldInstruction WaitForElapsedLoops(this Tween t, int elapsedLoops, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForElapsedLoops(t, elapsedLoops);
}
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed
/// or has reached the given time position (loops included, delays excluded).
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForPosition(2.5f);</code>
/// </summary>
/// <param name="position">Position (loops included, delays excluded) to wait for</param>
public static CustomYieldInstruction WaitForPosition(this Tween t, float position, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForPosition(t, position);
}
/// <summary>
/// Returns a <see cref="CustomYieldInstruction"/> that waits until the tween is killed or started
/// (meaning when the tween is set in a playing state the first time, after any eventual delay).
/// It can be used inside a coroutine as a yield.
/// <para>Example usage:</para><code>yield return myTween.WaitForStart();</code>
/// </summary>
public static CustomYieldInstruction WaitForStart(this Tween t, bool returnCustomYieldInstruction)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return null;
}
return new DOTweenCYInstruction.WaitForStart(t);
}
#endregion
#if UNITY_2018_1_OR_NEWER
#region Unity 2018.1 or Newer
#region Material
/// <summary>Tweens a Material's named texture offset property with the given ID to the given value.
/// Also stores the material as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param>
/// <param name="propertyID">The ID of the material property to tween (also called nameID in Unity's manual)</param>
/// <param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOOffset(this Material target, Vector2 endValue, int propertyID, float duration)
{
if (!target.HasProperty(propertyID)) {
if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(propertyID);
return null;
}
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.GetTextureOffset(propertyID), x => target.SetTextureOffset(propertyID, x), endValue, duration);
t.SetTarget(target);
return t;
}
/// <summary>Tweens a Material's named texture scale property with the given ID to the given value.
/// Also stores the material as the tween's target so it can be used for filtered operations</summary>
/// <param name="endValue">The end value to reach</param>
/// <param name="propertyID">The ID of the material property to tween (also called nameID in Unity's manual)</param>
/// <param name="duration">The duration of the tween</param>
public static TweenerCore<Vector2, Vector2, VectorOptions> DOTiling(this Material target, Vector2 endValue, int propertyID, float duration)
{
if (!target.HasProperty(propertyID)) {
if (Debugger.logPriority > 0) Debugger.LogMissingMaterialProperty(propertyID);
return null;
}
TweenerCore<Vector2, Vector2, VectorOptions> t = DOTween.To(() => target.GetTextureScale(propertyID), x => target.SetTextureScale(propertyID, x), endValue, duration);
t.SetTarget(target);
return t;
}
#endregion
#region .NET 4.6 or Newer
#if UNITY_2018_1_OR_NEWER && (NET_4_6 || NET_STANDARD_2_0)
#region Async Instructions
/// <summary>
/// Returns an async <see cref="System.Threading.Tasks.Task"/> that waits until the tween is killed or complete.
/// It can be used inside an async operation.
/// <para>Example usage:</para><code>await myTween.WaitForCompletion();</code>
/// </summary>
public static async System.Threading.Tasks.Task AsyncWaitForCompletion(this Tween t)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return;
}
while (t.active && !t.IsComplete()) await System.Threading.Tasks.Task.Yield();
}
/// <summary>
/// Returns an async <see cref="System.Threading.Tasks.Task"/> that waits until the tween is killed or rewinded.
/// It can be used inside an async operation.
/// <para>Example usage:</para><code>await myTween.AsyncWaitForRewind();</code>
/// </summary>
public static async System.Threading.Tasks.Task AsyncWaitForRewind(this Tween t)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return;
}
while (t.active && (!t.playedOnce || t.position * (t.CompletedLoops() + 1) > 0)) await System.Threading.Tasks.Task.Yield();
}
/// <summary>
/// Returns an async <see cref="System.Threading.Tasks.Task"/> that waits until the tween is killed.
/// It can be used inside an async operation.
/// <para>Example usage:</para><code>await myTween.AsyncWaitForKill();</code>
/// </summary>
public static async System.Threading.Tasks.Task AsyncWaitForKill(this Tween t)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return;
}
while (t.active) await System.Threading.Tasks.Task.Yield();
}
/// <summary>
/// Returns an async <see cref="System.Threading.Tasks.Task"/> that waits until the tween is killed or has gone through the given amount of loops.
/// It can be used inside an async operation.
/// <para>Example usage:</para><code>await myTween.AsyncWaitForElapsedLoops();</code>
/// </summary>
/// <param name="elapsedLoops">Elapsed loops to wait for</param>
public static async System.Threading.Tasks.Task AsyncWaitForElapsedLoops(this Tween t, int elapsedLoops)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return;
}
while (t.active && t.CompletedLoops() < elapsedLoops) await System.Threading.Tasks.Task.Yield();
}
/// <summary>
/// Returns an async <see cref="System.Threading.Tasks.Task"/> that waits until the tween is killed or started
/// (meaning when the tween is set in a playing state the first time, after any eventual delay).
/// It can be used inside an async operation.
/// <para>Example usage:</para><code>await myTween.AsyncWaitForPosition();</code>
/// </summary>
/// <param name="position">Position (loops included, delays excluded) to wait for</param>
public static async System.Threading.Tasks.Task AsyncWaitForPosition(this Tween t, float position)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return;
}
while (t.active && t.position * (t.CompletedLoops() + 1) < position) await System.Threading.Tasks.Task.Yield();
}
/// <summary>
/// Returns an async <see cref="System.Threading.Tasks.Task"/> that waits until the tween is killed.
/// It can be used inside an async operation.
/// <para>Example usage:</para><code>await myTween.AsyncWaitForKill();</code>
/// </summary>
public static async System.Threading.Tasks.Task AsyncWaitForStart(this Tween t)
{
if (!t.active) {
if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t);
return;
}
while (t.active && !t.playedOnce) await System.Threading.Tasks.Task.Yield();
}
#endregion
#endif
#endregion
#endregion
#endif
}
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
// ███ CLASSES █████████████████████████████████████████████████████████████████████████████████████████████████████████
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
public static class DOTweenCYInstruction
{
public class WaitForCompletion : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active && !t.IsComplete();
}}
readonly Tween t;
public WaitForCompletion(Tween tween)
{
t = tween;
}
}
public class WaitForRewind : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active && (!t.playedOnce || t.position * (t.CompletedLoops() + 1) > 0);
}}
readonly Tween t;
public WaitForRewind(Tween tween)
{
t = tween;
}
}
public class WaitForKill : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active;
}}
readonly Tween t;
public WaitForKill(Tween tween)
{
t = tween;
}
}
public class WaitForElapsedLoops : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active && t.CompletedLoops() < elapsedLoops;
}}
readonly Tween t;
readonly int elapsedLoops;
public WaitForElapsedLoops(Tween tween, int elapsedLoops)
{
t = tween;
this.elapsedLoops = elapsedLoops;
}
}
public class WaitForPosition : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active && t.position * (t.CompletedLoops() + 1) < position;
}}
readonly Tween t;
readonly float position;
public WaitForPosition(Tween tween, float position)
{
t = tween;
this.position = position;
}
}
public class WaitForStart : CustomYieldInstruction
{
public override bool keepWaiting { get {
return t.active && !t.playedOnce;
}}
readonly Tween t;
public WaitForStart(Tween tween)
{
t = tween;
}
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 63c02322328255542995bd02b47b0457
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,174 @@
// Author: Daniele Giardini - http://www.demigiant.com
// Created: 2018/07/13
using System;
using System.Reflection;
using UnityEngine;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Core.PathCore;
using DG.Tweening.Plugins.Options;
#pragma warning disable 1591
namespace DG.Tweening
{
/// <summary>
/// Utility functions that deal with available Modules and rewrite them.
/// Modules defines:
/// - DOTAUDIO
/// - DOTPHYSICS
/// - DOTPHYSICS2D
/// - DOTSPRITE
/// - DOTUI
/// Extra defines set and used for implementation of external assets:
/// - DOTWEEN_TMP ► TextMesh Pro
/// - DOTWEEN_TK2D ► 2D Toolkit
/// </summary>
public static class DOTweenModuleUtils
{
#pragma warning disable UDR0001
static bool _initialized;
#pragma warning restore UDR0001
#region Reflection
/// <summary>
/// Called via Reflection by DOTweenComponent on Awake
/// </summary>
#pragma warning disable UDR0001
#if UNITY_2018_1_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
public static void Init()
{
if (_initialized) return;
_initialized = true;
DOTweenExternalCommand.SetOrientationOnPath -= Physics.SetOrientationOnPath;
DOTweenExternalCommand.SetOrientationOnPath += Physics.SetOrientationOnPath;
#if UNITY_EDITOR
#if UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_2017_1
UnityEditor.EditorApplication.playmodeStateChanged -= PlaymodeStateChanged;
UnityEditor.EditorApplication.playmodeStateChanged += PlaymodeStateChanged;
#else
UnityEditor.EditorApplication.playModeStateChanged -= PlaymodeStateChanged;
UnityEditor.EditorApplication.playModeStateChanged += PlaymodeStateChanged;
#endif
#endif
}
#pragma warning restore UDR0001
#if UNITY_2018_1_OR_NEWER
#pragma warning disable
[UnityEngine.Scripting.Preserve]
// Just used to preserve methods when building, never called
static void Preserver()
{
Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
MethodInfo mi = typeof(MonoBehaviour).GetMethod("Stub");
}
#pragma warning restore
#endif
#endregion
#if UNITY_EDITOR
// Fires OnApplicationPause in DOTweenComponent even when Editor is paused (otherwise it's only fired at runtime)
#if UNITY_4_3 || UNITY_4_4 || UNITY_4_5 || UNITY_4_6 || UNITY_5 || UNITY_2017_1
static void PlaymodeStateChanged()
#else
static void PlaymodeStateChanged(UnityEditor.PlayModeStateChange state)
#endif
{
if (DOTween.instance == null) return;
DOTween.instance.OnApplicationPause(UnityEditor.EditorApplication.isPaused);
}
#endif
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
// ███ INTERNAL CLASSES ████████████████████████████████████████████████████████████████████████████████████████████████
// █████████████████████████████████████████████████████████████████████████████████████████████████████████████████████
public static class Physics
{
// Called via DOTweenExternalCommand callback
public static void SetOrientationOnPath(PathOptions options, Tween t, Quaternion newRot, Transform trans)
{
#if !DOTWEEN_NOPHYSICS // PHYSICS_MARKER
if (options.isRigidbody) ((Rigidbody)t.target).rotation = newRot;
else trans.rotation = newRot;
#else
trans.rotation = newRot;
#endif
}
// Returns FALSE if the DOTween's Physics2D Module is disabled, or if there's no Rigidbody2D attached
public static bool HasRigidbody2D(Component target)
{
#if !DOTWEEN_NOPHYSICS2D // PHYSICS2D_MARKER
return target.GetComponent<Rigidbody2D>() != null;
#else
return false;
#endif
}
#region Called via Reflection
// Called via Reflection by DOTweenPathInspector
// Returns FALSE if the DOTween's Physics Module is disabled, or if there's no rigidbody attached
#if UNITY_2018_1_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
public static bool HasRigidbody(Component target)
{
#if !DOTWEEN_NOPHYSICS // PHYSICS_MARKER
return target.GetComponent<Rigidbody>() != null;
#else
return false;
#endif
}
// Called via Reflection by DOTweenPath
#if UNITY_2018_1_OR_NEWER
[UnityEngine.Scripting.Preserve]
#endif
public static TweenerCore<Vector3, Path, PathOptions> CreateDOTweenPathTween(
MonoBehaviour target, bool tweenRigidbody, bool isLocal, Path path, float duration, PathMode pathMode
){
TweenerCore<Vector3, Path, PathOptions> t = null;
bool rBodyFoundAndTweened = false;
#if !DOTWEEN_NOPHYSICS // PHYSICS_MARKER
if (tweenRigidbody) {
Rigidbody rBody = target.GetComponent<Rigidbody>();
if (rBody != null) {
rBodyFoundAndTweened = true;
t = isLocal
? rBody.DOLocalPath(path, duration, pathMode)
: rBody.DOPath(path, duration, pathMode);
}
}
#endif
#if !DOTWEEN_NOPHYSICS2D // PHYSICS2D_MARKER
if (!rBodyFoundAndTweened && tweenRigidbody) {
Rigidbody2D rBody2D = target.GetComponent<Rigidbody2D>();
if (rBody2D != null) {
rBodyFoundAndTweened = true;
t = isLocal
? rBody2D.DOLocalPath(path, duration, pathMode)
: rBody2D.DOPath(path, duration, pathMode);
}
}
#endif
if (!rBodyFoundAndTweened) {
t = isLocal
? target.transform.DOLocalPath(path, duration, pathMode)
: target.transform.DOPath(path, duration, pathMode);
}
return t;
}
#endregion
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7bcaf917d9cf5b84090421a5a2abe42e
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,31 @@
DOTween and DOTween Pro are copyright (c) 2014-2026 Daniele Giardini - Demigiant
// IMPORTANT!!! /////////////////////////////////////////////
// Upgrading DOTween from versions older than 1.2.815 ///////
// or DOTween Pro older than 1.0.405 ////////////////////////
// (February 6 2026) ////////////////////////////////////////
-------------------------------------------------------------
1) Import the new version in the same folder as the previous one (Plugins/Demigiant), overwriting old files. A lot of errors will appear but don't worry.
2) Close and reopen Unity (and your project) without entering safe mode. This is fundamental: skipping this step will cause a bloodbath
3) Open DOTween's Utility Panel (Tools > Demigiant > DOTween Utility Panel) if it doesn't open automatically, then press "Setup DOTween...": this will run the upgrade setup
4) From the Add/Remove Modules panel that opens, activate/deactivate Modules for Unity systems and for external assets (Pro version only)
// GET STARTED //////////////////////////////////////////////
- After importing a new DOTween update, select DOTween's Utility Panel from the "Tools/Demigiant" menu (if it doesn't open automatically) and press the "Setup DOTween..." button to activate/deactivate Modules. You can also access a Preferences Tab from there to choose default settings for DOTween.
- In your code, add "using DG.Tweening" to each class where you want to use DOTween.
- You're ready to tween. Check out the links below for full documentation and license info.
// LINKS ///////////////////////////////////////////////////////
DOTween website (documentation, examples, etc): http://dotween.demigiant.com
DOTween license: http://dotween.demigiant.com/license.php
DOTween repository (GitHub): https://github.com/Demigiant/dotween
Demigiant website: http://www.demigiant.com
// NOTES //////////////////////////////////////////////////////
- DOTween's Utility Panel can be found under "Tools > Demigiant > DOTween Utility Panel" and also contains other useful options, plus a tab to set DOTween's preferences
@@ -0,0 +1,4 @@
fileFormatVersion: 2
guid: fccfc62abf2eb0a4db614853430894fd
TextScriptImporter:
userData:
@@ -677,12 +677,10 @@ namespace NAudio.Lame.DLL
internal static class NativeMethods
{
#if UNITY_EDITOR
#if UNITY_EDITOR_OSX
const string libname = @"__Internal";
#else
#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
const string libname = @"lame";
#elif UNITY_EDITOR
const string libname = @"libmp3lame.dll";
#endif
#elif UNITY_IOS
const string libname = @"__Internal";
#elif UNITY_ANDROID
+1 -1
View File
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 57e25b4a578dba94c9353f4633b20549
guid: ecd7dab9b385ae34c8c077615f561b9f
folderAsset: yes
DefaultImporter:
externalObjects: {}
@@ -1,6 +1,6 @@
using System;
using System;
namespace SFB {
namespace SFB.LegacyWindows {
public interface IStandaloneFileBrowser {
string[] OpenFilePanel(string title, string directory, ExtensionFilter[] extensions, bool multiselect);
string[] OpenFolderPanel(string title, string directory, bool multiselect);
@@ -11,3 +11,4 @@ namespace SFB {
void SaveFilePanelAsync(string title, string directory, string defaultName, ExtensionFilter[] extensions, Action<string> cb);
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 7609f7b6787a54496aa41a3053fcc76a
guid: 3f930ddac4d3ac04b9a3b236446ad59c
timeCreated: 1483902788
licenseType: Pro
MonoImporter:
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: ddc4e7b83981f244ba9a26b88c18cb67
guid: 3b296afa4ee50cb43be9c7e1e023027f
folderAsset: yes
DefaultImporter:
externalObjects: {}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 110fdfb459db4fc448a2ccd37e200fa4
guid: 99fb74d0dc9a71b4bac7e5a132fb0e5d
folderAsset: yes
PluginImporter:
externalObjects: {}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 265aaf20a6d564e0fb00a9c4a7a9c300
guid: 4622f638922a1e741b11260cda2fad29
PluginImporter:
externalObjects: {}
serializedVersion: 2
@@ -1,6 +1,6 @@
using System;
using System;
namespace SFB {
namespace SFB.LegacyWindows {
public struct ExtensionFilter {
public string Name;
public string[] Extensions;
@@ -15,14 +15,13 @@ namespace SFB {
private static IStandaloneFileBrowser _platformWrapper = null;
static StandaloneFileBrowser() {
#if UNITY_STANDALONE_OSX
_platformWrapper = new StandaloneFileBrowserMac();
#elif UNITY_STANDALONE_WIN
#if UNITY_STANDALONE_WIN
_platformWrapper = new StandaloneFileBrowserWindows();
#elif UNITY_STANDALONE_LINUX
_platformWrapper = new StandaloneFileBrowserLinux();
#elif UNITY_EDITOR
_platformWrapper = new StandaloneFileBrowserEditor();
#else
// This historical implementation is retained only for Windows compatibility.
throw new PlatformNotSupportedException("SFB.LegacyWindows supports only Windows players. Use SFB for cross-platform dialogs.");
#endif
}
@@ -151,3 +150,4 @@ namespace SFB {
}
}
}

Some files were not shown because too many files have changed in this diff Show More