Added Unity project files
This commit is contained in:
373
Packages/com.vrchat.base/.Examples/DPIDMipmapper.cs
Normal file
373
Packages/com.vrchat.base/.Examples/DPIDMipmapper.cs
Normal file
@ -0,0 +1,373 @@
|
||||
/*
|
||||
* BSD 3-Clause License
|
||||
*
|
||||
* Copyright (c) 2016, Nicolas Weber, Sandra C. Amend / GCC / TU-Darmstadt
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* HLSL/Unity version Copyright (c) 2024, Chelsea "Feilen" Jaggi and VRChat Inc
|
||||
* The following is also licensed under the BSD 3-Clause License.
|
||||
*
|
||||
* This is a HLSL/Unity Compute shader implementation of the original cuda code
|
||||
* available at https://github.com/mergian/dpid, adjusted to be used as an
|
||||
* AssetPostprocessor (in-SDK) and a fully GPU-side texture updater (in-game)
|
||||
* for generating mipmaps in Unity. The algorithm is intended to intentionally
|
||||
* over-emphasize 'perceptually relevant' details, avoiding the need for
|
||||
* post-sharpening typical in traditional downscaling algorithms.
|
||||
*
|
||||
* This file gets compiled into the SDKBase DLL, but we want to keep it open
|
||||
* source, so it's been copied to .Examples directory to keep Unity from trying
|
||||
* to compile it.
|
||||
*/
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using System.Collections;
|
||||
using System;
|
||||
using VRC.Core;
|
||||
using VRC.SDKBase.Editor;
|
||||
using System.IO;
|
||||
|
||||
public class DPIDMipmapper
|
||||
{
|
||||
private static DPIDMipmapper Instance { get { return _instance ?? (_instance = new DPIDMipmapper()); } }
|
||||
private static DPIDMipmapper _instance;
|
||||
|
||||
private ComputeShader computeShader;
|
||||
private int kernelDownsampling;
|
||||
private int kernelGuidance;
|
||||
|
||||
public static bool ComputeShaderReady { get { return Instance.computeShader != null; } }
|
||||
|
||||
private const int THREADS = 64;
|
||||
|
||||
private static int TmpGuidanceProperty = Shader.PropertyToID("_TmpGuidance");
|
||||
private static int OutputProperty = Shader.PropertyToID("_Output");
|
||||
|
||||
private DPIDMipmapper()
|
||||
{
|
||||
if (Application.isBatchMode)
|
||||
{
|
||||
Debug.Log("DPIDMipmapper is not supported in batch mode.");
|
||||
return;
|
||||
}
|
||||
computeShader = Resources.Load<ComputeShader>("PerceptualMipmapping/PerceptualPostProcessor");
|
||||
|
||||
if (computeShader == null)
|
||||
{
|
||||
// ComputeShaders load after Textures import. We set a flag here and force all textures imported this way to re-import after the compute shader.
|
||||
// This also lets us detect when we need to forcibly re-import Kaiser textures the first time.
|
||||
return;
|
||||
}
|
||||
kernelGuidance = computeShader.FindKernel("KernelGuidance");
|
||||
kernelDownsampling = computeShader.FindKernel("KernelDownsampling");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate DPID mipmaps for a texture.
|
||||
/// </summary>
|
||||
/// <param name="input">The input texture to generate mipmaps for.</param>
|
||||
/// <param name="output">The output texture to write the mipmaps to.</param>
|
||||
/// <param name="alphaIsTransparency">Whether the alpha channel should be treated as transparency (premultiplied to prevent edge bleed).</param>
|
||||
/// <param name="sRGB">Whether the output texture should be sRGB.</param>
|
||||
/// <param name="asyncOnGPU">Whether to only use the GPU for mip generation - this doesn't read-back mips from the GPU, and expects input and output formats to be the same. This will immediately return control.</param>
|
||||
/// <param name="inPlace">Whether to generate mipmaps in-place (using the first mipmap as the input texture)</param>
|
||||
/// <param name="inputIsGuidance">Whether we expect our input texture is already what we use for a guidance texture, e.g. a box-mipped texture</param>
|
||||
/// <param name="conservative">Whether to use a more conservative algorithm that doesn't over-emphasize details</param>
|
||||
/// <param name="minimumSize">The minimum size we care about scaling. At the default of 4x4, we stop processing anything below 5 pixels in width or height (and therefore save 3 passes)</param>
|
||||
public static void GenerateDPIDMipmaps(Texture2D input, Texture2D output, bool alphaIsTransparency, bool sRGB, bool asyncOnGPU = false, bool inPlace = true, bool inputIsGuidance = false, bool conservative = false, uint minimumSize = 4U, bool normalMap = false)
|
||||
{
|
||||
Instance.ExecuteComputeShader(input, output, alphaIsTransparency, sRGB, asyncOnGPU, inPlace, inputIsGuidance, conservative, minimumSize, normalMap: normalMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate DPID mipmaps for a texture - convienience method for in-place, CPU+GPU generation for highest quality.
|
||||
/// </summary>
|
||||
/// <param name="input">The input texture to generate mipmaps for.</param>
|
||||
/// <param name="output">The output texture to write the mipmaps to.</param>
|
||||
/// <param name="alphaIsTransparency">Whether the alpha channel should be treated as transparency (premultiplied to prevent edge bleed).</param>
|
||||
/// <param name="sRGB">Whether the output texture should be sRGB.</param>
|
||||
/// <param name="conservative">Whether to use a more conservative algorithm that doesn't over-emphasize details</param>
|
||||
public static void GenerateDPIDMipmapsQuality(Texture2D input, Texture2D output, bool alphaIsTransparency, bool sRGB, bool conservative = false, bool normalMap = false)
|
||||
{
|
||||
bool inPlace = input.width == output.width && input.height == output.height;
|
||||
Instance.ExecuteComputeShader(input, output, alphaIsTransparency, sRGB, false, inPlace, false, conservative, normalMap: normalMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate DPID mipmaps for a texture - convienience method for in-place, GPU-only generation.
|
||||
/// </summary>
|
||||
/// <param name="texture">The texture to generate mipmaps for.</param>
|
||||
/// <param name="alphaIsTransparency">Whether the alpha channel should be treated as transparency (premultiplied to prevent edge bleed).</param>
|
||||
/// <param name="sRGB">Whether the output texture should be sRGB.</param>
|
||||
/// <param name="conservative">Whether to use a more conservative algorithm that doesn't over-emphasize details</param>
|
||||
public static void GenerateDPIDMipmapsFast(Texture2D texture, bool alphaIsTransparency, bool sRGB, bool conservative = false, bool normalMap = false)
|
||||
{
|
||||
Instance.ExecuteComputeShader(texture, texture, alphaIsTransparency, sRGB, true, true, true, conservative, normalMap: normalMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute the compute shader to generate mipmaps using the DPID algorithm.
|
||||
/// </summary>
|
||||
/// <param name="input">The input texture to generate mipmaps for.</param>
|
||||
/// <param name="output">The output texture to write the mipmaps to.</param>
|
||||
/// <param name="alphaIsTransparency">Whether the alpha channel should be treated as transparency (premultiplied to prevent edge bleed).</param>
|
||||
/// <param name="sRGB">Whether the output texture should be sRGB.</param>
|
||||
/// <param name="asyncOnGPU">Whether to only use the GPU for mip generation - this doesn't read-back mips from the GPU, and expects input and output formats to be the same. This will immediately return control.</param>
|
||||
/// <param name="inPlace">Whether to generate mipmaps in-place (using the first mipmap as the input texture)</param>
|
||||
/// <param name="inputIsGuidance">Whether we expect our input texture is already what we use for a guidance texture, e.g. a box-mipped texture</param>
|
||||
/// <param name="minimumSize">The minimum size we care about scaling. At the default of 4x4, we stop processing anything below 5 pixels in width or height (and therefore save 3 passes)</param>
|
||||
private void ExecuteComputeShader(Texture2D input, Texture2D output, bool alphaIsTransparency, bool sRGB, bool asyncOnGPU = false, bool inPlace = true, bool inputIsGuidance = false, bool conservative = false, uint minimumSize = 4U, bool normalMap = false)
|
||||
{
|
||||
if (input == null || output == null)
|
||||
{
|
||||
Debug.LogError("Input and output textures must be non-null, input: {input != null}, output: {output != null}");
|
||||
return;
|
||||
}
|
||||
if (asyncOnGPU && (output.format != TextureFormat.RGBA32) && (output.format != TextureFormat.ARGB32))
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.Log($"Currently DPIDMipmapper only supports ARGB textures when on-GPU, currently: {output.format}");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (Application.isBatchMode)
|
||||
{
|
||||
Debug.LogError("DPIDMipmapper is not supported in batch mode.");
|
||||
return;
|
||||
}
|
||||
if (inputIsGuidance && (alphaIsTransparency || sRGB))
|
||||
{
|
||||
// Unity's runtime mipmapping always runs with the equivalent of alphaIsTransparency == false, so we have to run our own
|
||||
// InputIsGuidance also doesn't seem to like sRGB
|
||||
inputIsGuidance = false;
|
||||
}
|
||||
if (inputIsGuidance &&
|
||||
(input.format == TextureFormat.RGB24) ||
|
||||
(input.format == TextureFormat.RGB565) ||
|
||||
(input.format == TextureFormat.RGBA4444) ||
|
||||
(input.format == TextureFormat.R8) ||
|
||||
(input.format == TextureFormat.R16) ||
|
||||
(input.format == TextureFormat.RG16) ||
|
||||
(input.format == TextureFormat.ARGB4444) ||
|
||||
(input.format == TextureFormat.RGBA64))
|
||||
{
|
||||
// inputIsGuidance doesn't like non-RGBA formats
|
||||
inputIsGuidance = false;
|
||||
}
|
||||
if (sRGB && (input.format == TextureFormat.R16 ||
|
||||
input.format == TextureFormat.RG16 ||
|
||||
input.format == TextureFormat.RGFloat ||
|
||||
input.format == TextureFormat.RFloat ||
|
||||
input.format == TextureFormat.RHalf ||
|
||||
input.format == TextureFormat.RGBA64 ||
|
||||
input.format == TextureFormat.RGBAFloat ||
|
||||
input.format == TextureFormat.RGBAHalf ||
|
||||
input.format == TextureFormat.R8 ||
|
||||
input.format == TextureFormat.RGBA4444 ||
|
||||
input.format == TextureFormat.RGB565 ||
|
||||
input.format == TextureFormat.ARGB4444))
|
||||
{
|
||||
// Texture format does not support sRGB, will be tested as Linear to maintain consistency with Unity importer behavior
|
||||
sRGB = false;
|
||||
}
|
||||
if (normalMap)
|
||||
{
|
||||
alphaIsTransparency = false;
|
||||
}
|
||||
if (!ComputeShaderReady)
|
||||
{
|
||||
// Double check that this is in fact the first import
|
||||
computeShader = (ComputeShader)Resources.Load("PerceptualMipmapping/PerceptualPostProcessor");
|
||||
if (!ComputeShaderReady)
|
||||
{
|
||||
// Textures must be marked for re-import outside of DPIDMipmapper, since we don't have UnityEditor access.
|
||||
return;
|
||||
}
|
||||
// If not, clear the flag and proceed as normal
|
||||
kernelGuidance = computeShader.FindKernel("KernelGuidance");
|
||||
kernelDownsampling = computeShader.FindKernel("KernelDownsampling");
|
||||
}
|
||||
|
||||
CommandBuffer commandBuffer = new CommandBuffer();
|
||||
if (asyncOnGPU)
|
||||
{
|
||||
commandBuffer.SetExecutionFlags(CommandBufferExecutionFlags.AsyncCompute);
|
||||
}
|
||||
|
||||
int startMip = inPlace? 1 : 0;
|
||||
|
||||
commandBuffer.SetComputeTextureParam(computeShader, kernelDownsampling, "_Input", input);
|
||||
commandBuffer.SetComputeTextureParam(computeShader, kernelGuidance, "_Input", input);
|
||||
int iWidth = input.width;
|
||||
int iHeight = input.height;
|
||||
float lambda = conservative ? 0.5f : 1.0f;
|
||||
commandBuffer.SetComputeIntParam(computeShader, "iWidth", iWidth);
|
||||
commandBuffer.SetComputeIntParam(computeShader, "iHeight", iHeight);
|
||||
commandBuffer.SetComputeFloatParam(computeShader, "lambda", lambda);
|
||||
commandBuffer.SetComputeIntParam(computeShader, "premultiplyAlpha", alphaIsTransparency ? 1 : 0);
|
||||
// output sRGB from compute shader with a variant, avoid blits
|
||||
commandBuffer.SetComputeIntParam(computeShader, "sRGB", sRGB ? 1 : 0);
|
||||
commandBuffer.SetComputeIntParam(computeShader, "normalMap", normalMap ? 1 : 0);
|
||||
|
||||
int rtWidth = output.width;
|
||||
int rtHeight = output.height;
|
||||
|
||||
RenderTextureFormat intermediateFormat = RenderTextureFormat.ARGB32;
|
||||
switch(input.format)
|
||||
{
|
||||
case TextureFormat.RGBAFloat:
|
||||
intermediateFormat = RenderTextureFormat.ARGBFloat;
|
||||
break;
|
||||
case TextureFormat.RGBA32:
|
||||
case TextureFormat.ARGB32:
|
||||
case TextureFormat.RGB24:
|
||||
case TextureFormat.RGB565: // Not supported for random write
|
||||
intermediateFormat = RenderTextureFormat.ARGB32;
|
||||
break;
|
||||
case TextureFormat.RGBAHalf:
|
||||
intermediateFormat = RenderTextureFormat.ARGBHalf;
|
||||
break;
|
||||
case TextureFormat.RGBA4444:
|
||||
intermediateFormat = RenderTextureFormat.ARGB4444;
|
||||
break;
|
||||
case TextureFormat.RFloat:
|
||||
intermediateFormat = RenderTextureFormat.RFloat;
|
||||
break;
|
||||
case TextureFormat.RGFloat:
|
||||
intermediateFormat = RenderTextureFormat.RGFloat;
|
||||
break;
|
||||
case TextureFormat.RHalf:
|
||||
intermediateFormat = RenderTextureFormat.RHalf;
|
||||
break;
|
||||
case TextureFormat.RGHalf:
|
||||
intermediateFormat = RenderTextureFormat.RGHalf;
|
||||
break;
|
||||
case TextureFormat.R8:
|
||||
intermediateFormat = RenderTextureFormat.R8;
|
||||
break;
|
||||
case TextureFormat.R16:
|
||||
intermediateFormat = RenderTextureFormat.R16;
|
||||
break;
|
||||
}
|
||||
|
||||
RenderTextureDescriptor outputTextureDesc = new RenderTextureDescriptor(rtWidth, rtHeight, intermediateFormat, 0);
|
||||
outputTextureDesc.sRGB = false;
|
||||
outputTextureDesc.autoGenerateMips = false;
|
||||
outputTextureDesc.enableRandomWrite = true;
|
||||
outputTextureDesc.useMipMap = true;
|
||||
commandBuffer.GetTemporaryRT(OutputProperty, outputTextureDesc);
|
||||
|
||||
RenderTargetIdentifier outputTexture = new RenderTargetIdentifier(OutputProperty);
|
||||
RenderTargetIdentifier inputTexture = new RenderTargetIdentifier(input);
|
||||
|
||||
for (int mip = startMip; mip < output.mipmapCount; mip++)
|
||||
{
|
||||
int downscaleFactor = 1 << mip;
|
||||
|
||||
int oWidth = Math.Max(output.width / downscaleFactor, 1);
|
||||
int oHeight = Math.Max(output.height / downscaleFactor, 1);
|
||||
if (asyncOnGPU && (oWidth <= minimumSize && oHeight <= minimumSize))
|
||||
{
|
||||
break;
|
||||
}
|
||||
float pWidth = (float)input.width / oWidth;
|
||||
float pHeight = (float)input.height / oHeight;
|
||||
|
||||
commandBuffer.SetComputeIntParam(computeShader, "oWidth", oWidth);
|
||||
commandBuffer.SetComputeIntParam(computeShader, "oHeight", oHeight);
|
||||
commandBuffer.SetComputeFloatParam(computeShader, "pWidth", pWidth);
|
||||
commandBuffer.SetComputeFloatParam(computeShader, "pHeight", pHeight);
|
||||
|
||||
// TODO: apparently you can't bind input Texture2Ds by-mip, it'll just silently bind mip 0. Before removing this,
|
||||
// confirm with DEBUG_GUIDANCE in the compute shader.
|
||||
RenderTextureDescriptor tmpGuidanceTextureRTDesc = new RenderTextureDescriptor(oWidth, oHeight, intermediateFormat, 0);
|
||||
tmpGuidanceTextureRTDesc.sRGB = false;
|
||||
tmpGuidanceTextureRTDesc.autoGenerateMips = false;
|
||||
tmpGuidanceTextureRTDesc.enableRandomWrite = !inputIsGuidance;
|
||||
tmpGuidanceTextureRTDesc.useMipMap = false;
|
||||
commandBuffer.GetTemporaryRT(TmpGuidanceProperty, tmpGuidanceTextureRTDesc);
|
||||
RenderTargetIdentifier tmpGuidanceTexture = new RenderTargetIdentifier(TmpGuidanceProperty);
|
||||
|
||||
if (inputIsGuidance)
|
||||
{
|
||||
commandBuffer.CopyTexture(inputTexture, 0, mip, tmpGuidanceTexture, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
commandBuffer.SetComputeTextureParam(computeShader, kernelGuidance, "_Output", tmpGuidanceTexture);
|
||||
commandBuffer.DispatchCompute(computeShader, kernelGuidance, Math.Max(oWidth, 1), Math.Max(oHeight, 1), 1);
|
||||
}
|
||||
|
||||
// TODO: I might be able to modify the algorithm to perform some form of 'hinting' by having a cutoff for contribution at a certain range. Then you'd get even sharper edges on text and such
|
||||
// TODO: compute shader could batch based on samples, not output pixels - this would spread more evenly
|
||||
// TODO: how can we get rid of the alpha channel? downcasting RGBA to RGB is apparently weirdly difficult
|
||||
commandBuffer.SetComputeTextureParam(computeShader, kernelDownsampling, "_Guidance", tmpGuidanceTexture);
|
||||
commandBuffer.SetComputeTextureParam(computeShader, kernelDownsampling, "_Output", outputTexture, mip);
|
||||
commandBuffer.DispatchCompute(computeShader, kernelDownsampling, Math.Max(oWidth, 1), Math.Max(oHeight, 1), 1);
|
||||
if(asyncOnGPU)
|
||||
{
|
||||
commandBuffer.CopyTexture(outputTexture, 0, mip, output, 0, mip);
|
||||
}
|
||||
commandBuffer.ReleaseTemporaryRT(TmpGuidanceProperty);
|
||||
}
|
||||
if (!asyncOnGPU)
|
||||
{
|
||||
RenderTexture exportTexture = new RenderTexture(outputTextureDesc);
|
||||
for (int mip = startMip; mip < output.mipmapCount; mip++)
|
||||
{
|
||||
int downscaleFactor = 1 << mip;
|
||||
|
||||
int oWidth = Math.Max(output.width / downscaleFactor, 1);
|
||||
int oHeight = Math.Max(output.height / downscaleFactor, 1);
|
||||
if (asyncOnGPU && (oWidth <= minimumSize && oHeight <= minimumSize))
|
||||
{
|
||||
break;
|
||||
}
|
||||
commandBuffer.CopyTexture(outputTexture, 0, mip, exportTexture, 0, mip);
|
||||
int mipCopy = mip;
|
||||
commandBuffer.RequestAsyncReadback(exportTexture, mip, output.graphicsFormat, (AsyncGPUReadbackRequest req) =>
|
||||
{
|
||||
if (req.hasError)
|
||||
{
|
||||
Debug.LogError("GPU readback error detected.");
|
||||
return;
|
||||
}
|
||||
if (req.done)
|
||||
{
|
||||
output.SetPixelData(req.GetData<byte>(), mipCopy);
|
||||
}
|
||||
});
|
||||
}
|
||||
commandBuffer.WaitAllAsyncReadbackRequests();
|
||||
commandBuffer.ReleaseTemporaryRT(OutputProperty);
|
||||
Graphics.ExecuteCommandBuffer(commandBuffer);
|
||||
exportTexture.Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
commandBuffer.ReleaseTemporaryRT(OutputProperty);
|
||||
Graphics.ExecuteCommandBufferAsync(commandBuffer, ComputeQueueType.Background);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
Packages/com.vrchat.base/Editor.meta
Normal file
8
Packages/com.vrchat.base/Editor.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3783acec36b0a84ba0c81ccfd1fc308
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/com.vrchat.base/Editor/VRCSDK.meta
Normal file
8
Packages/com.vrchat.base/Editor/VRCSDK.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fbb143d2122482848aff63a633c20704
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/com.vrchat.base/Editor/VRCSDK/Dependencies.meta
Normal file
8
Packages/com.vrchat.base/Editor/VRCSDK/Dependencies.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a5eb0ad795b9544799dec3b042b6af9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16568113592da1e4c9924393ab2ab4ae
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d7067d2f71f9a4468d9d28dd19eeccf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,101 @@
|
||||
/************************************************************************************
|
||||
Filename : ONSPAudioSourceEditor.cs
|
||||
Content : This script adds editor functionality to OculusSpatializerUserParams script.
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
Licensed under the Oculus SDK Version 3.5 (the "License");
|
||||
you may not use the Oculus SDK except in compliance with the License,
|
||||
which is provided at the time of installation or download, or which
|
||||
otherwise accompanies this software in either electronic or hard copy form.
|
||||
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://developer.oculus.com/licenses/sdk-3.5/
|
||||
|
||||
Unless required by applicable law or agreed to in writing, the Oculus SDK
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
************************************************************************************/
|
||||
#define CUSTOM_LAYOUT
|
||||
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[CustomEditor(typeof(ONSPAudioSource))]
|
||||
|
||||
public class OculusSpatializerUserParamsEditor : Editor
|
||||
{
|
||||
// target component
|
||||
private ONSPAudioSource m_Component;
|
||||
|
||||
// OnEnable
|
||||
void OnEnable()
|
||||
{
|
||||
m_Component = (ONSPAudioSource)target;
|
||||
}
|
||||
|
||||
// OnInspectorGUI
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
GUI.color = Color.white;
|
||||
Undo.RecordObject(m_Component, "OculusSpatializerUserParams");
|
||||
|
||||
{
|
||||
EditorGUILayout.HelpBox("Please use a VRC_SpatialAudioSource in the future.", MessageType.Error);
|
||||
#if CUSTOM_LAYOUT
|
||||
m_Component.EnableSpatialization = EditorGUILayout.Toggle("Spatialization Enabled", m_Component.EnableSpatialization);
|
||||
m_Component.EnableRfl = EditorGUILayout.Toggle("Reflections Enabled", m_Component.EnableRfl);
|
||||
m_Component.Gain = EditorGUILayout.FloatField("Gain", m_Component.Gain);
|
||||
|
||||
Separator();
|
||||
|
||||
Label ("OCULUS ATTENUATION");
|
||||
m_Component.UseInvSqr = EditorGUILayout.Toggle("Enabled", m_Component.UseInvSqr);
|
||||
Label ("");
|
||||
Label("RANGE (0.0 - 1000000.0 meters)");
|
||||
m_Component.Near = EditorGUILayout.FloatField("Minimum", m_Component.Near);
|
||||
m_Component.Far = EditorGUILayout.FloatField("Maximum", m_Component.Far);
|
||||
|
||||
Label("");
|
||||
Label("VOLUMETRIC RADIUS (0.0 - 1000.0 meters)");
|
||||
m_Component.VolumetricRadius = EditorGUILayout.FloatField("Radius", m_Component.VolumetricRadius);
|
||||
|
||||
Separator();
|
||||
|
||||
Label("REVERB SEND LEVEL (-60.0 - 20.0 decibels)");
|
||||
m_Component.ReverbSend = EditorGUILayout.FloatField(" ", m_Component.ReverbSend);
|
||||
|
||||
Separator();
|
||||
|
||||
#else
|
||||
DrawDefaultInspector ();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (GUI.changed)
|
||||
{
|
||||
EditorUtility.SetDirty(m_Component);
|
||||
}
|
||||
}
|
||||
|
||||
// Utilities, move out of here (or copy over to other editor script)
|
||||
|
||||
// Separator
|
||||
void Separator()
|
||||
{
|
||||
GUI.color = new Color(1, 1, 1, 0.25f);
|
||||
GUILayout.Box("", "HorizontalSlider", GUILayout.Height(16));
|
||||
GUI.color = Color.white;
|
||||
}
|
||||
|
||||
// Label
|
||||
void Label(string label)
|
||||
{
|
||||
EditorGUILayout.LabelField (label);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb850b86de9091d4db4595959c56f954
|
||||
timeCreated: 1442269815
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02f6a9b57a5644647b5ccc8b9c8acf28
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4c0a58edfe7425bb1e4b7a5de9a87b1
|
||||
timeCreated: 1683056861
|
||||
@ -0,0 +1,8 @@
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
public interface IVRCContent
|
||||
{
|
||||
string ID { get; set; }
|
||||
string Name { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2970d5a709404efb9249205c86afaf20
|
||||
timeCreated: 1687867386
|
||||
@ -0,0 +1,32 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
public struct VRCAgreement
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string ID { get; set; }
|
||||
public string AgreementCode { get; set; }
|
||||
public string ContentId { get; set; }
|
||||
public string AgreementFulltext { get; set; }
|
||||
public int Version { get; set; }
|
||||
public string[] Tags { get; set; }
|
||||
}
|
||||
|
||||
public struct VRCAgreementCheckRequest
|
||||
{
|
||||
public string UserId { get; set; }
|
||||
public string AgreementCode { get; set; }
|
||||
public string ContentId { get; set; }
|
||||
public int Version { get; set; }
|
||||
}
|
||||
|
||||
public struct VRCAgreementCheckResponse
|
||||
{
|
||||
public bool Agreed { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string AgreementCode { get; set; }
|
||||
public string ContentId { get; set; }
|
||||
public int Version { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4a6b18f112e459c871dee35e8dc2858
|
||||
timeCreated: 1739229610
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cffdc642f18a45c9865fc930726518fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using UnityEditor;
|
||||
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
internal static class VRCApiCache
|
||||
{
|
||||
private static long DEFAULT_CACHE_TIME;
|
||||
private static Dictionary<string, (DateTime timestamp, object data)> _cache;
|
||||
|
||||
static VRCApiCache()
|
||||
{
|
||||
_cache = new Dictionary<string, (DateTime timestamp, object data)>();
|
||||
DEFAULT_CACHE_TIME = 1000 * 60 * 1; // 1 minute
|
||||
}
|
||||
|
||||
public static T Add<T>(string key, T value)
|
||||
{
|
||||
_cache[key] = (DateTime.UtcNow, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static T Get<T>(string key, out bool cached)
|
||||
{
|
||||
if (_cache.TryGetValue(key, out var value))
|
||||
{
|
||||
if (DateTime.UtcNow.Subtract(value.timestamp).TotalMilliseconds < DEFAULT_CACHE_TIME)
|
||||
{
|
||||
cached = true;
|
||||
return (T) value.data;
|
||||
}
|
||||
|
||||
cached = false;
|
||||
return default;
|
||||
}
|
||||
|
||||
cached = false;
|
||||
return default;
|
||||
}
|
||||
|
||||
public static void Invalidate(string key)
|
||||
{
|
||||
_cache.Remove(key);
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
_cache = new Dictionary<string, (DateTime timestamp, object data)>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2861b3597c849e08c2126c83ef3fca7
|
||||
timeCreated: 1683145545
|
||||
@ -0,0 +1,12 @@
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
public struct VRCApiError
|
||||
{
|
||||
public VRCApiErrorContent Error { get; set; }
|
||||
|
||||
public struct VRCApiErrorContent {
|
||||
public string Message { get; set; }
|
||||
public int Code { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67fd710f3e92422eba267779143a9c5f
|
||||
timeCreated: 1687964156
|
||||
@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
[assembly: InternalsVisibleTo("VRC.SDK3A.Editor")]
|
||||
[assembly: InternalsVisibleTo("VRC.SDK3.Editor")]
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// VRCApi request failed
|
||||
/// </summary>
|
||||
public class RequestFailedException : Exception
|
||||
{
|
||||
public HttpResponseMessage HttpMessage;
|
||||
public HttpStatusCode StatusCode;
|
||||
|
||||
public RequestFailedException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public RequestFailedException(string message, HttpResponseMessage httpMessage) : base(message)
|
||||
{
|
||||
HttpMessage = httpMessage;
|
||||
}
|
||||
|
||||
public RequestFailedException(string message, HttpResponseMessage httpMessage, HttpStatusCode statusCode) : base(message)
|
||||
{
|
||||
HttpMessage = httpMessage;
|
||||
StatusCode = statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
public class JsonSerializationException : Exception
|
||||
{
|
||||
public JsonSerializationException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// VRChat API returned an error
|
||||
/// </summary>
|
||||
public class ApiErrorException : Exception
|
||||
{
|
||||
public HttpResponseMessage HttpMessage;
|
||||
public HttpStatusCode StatusCode;
|
||||
public string ErrorMessage;
|
||||
|
||||
public ApiErrorException(HttpResponseMessage httpMessage, string jsonContent)
|
||||
{
|
||||
var json = JsonConvert.DeserializeObject<VRCApiError>(jsonContent, VRCApi.JSON_OPTIONS);
|
||||
HttpMessage = httpMessage;
|
||||
StatusCode = httpMessage.StatusCode;
|
||||
ErrorMessage = json.Error.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 536cf9130a7e41a19f0d7e5d7900210d
|
||||
timeCreated: 1683135644
|
||||
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
public struct VRCAssetReviewNotesRequest
|
||||
{
|
||||
public string ReviewNotes { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37f2155d6021416b8ad57feb5216957d
|
||||
timeCreated: 1743116496
|
||||
@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using VRC.Core;
|
||||
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
public struct VRCAvatar: IVRCContent
|
||||
{
|
||||
public enum AvatarVariant
|
||||
{
|
||||
Standard,
|
||||
Impostor
|
||||
}
|
||||
|
||||
public struct AvatarStyles
|
||||
{
|
||||
public string Primary { get; set; }
|
||||
public string Secondary { get; set; }
|
||||
}
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string ID { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public List<string> Tags { get; set; }
|
||||
|
||||
public string AuthorName { get; set; }
|
||||
public string AuthorId { get; set; }
|
||||
|
||||
public string ImageUrl { get; set; }
|
||||
public string ThumbnailImageUrl { get; set; }
|
||||
|
||||
public string ReleaseStatus { get; set; }
|
||||
|
||||
public AvatarStyles Styles { get; set; }
|
||||
public bool Lock { get; set; }
|
||||
public string ActiveAssetReviewId { get; set; }
|
||||
|
||||
public bool PendingUpload { get; set; }
|
||||
|
||||
[JsonProperty("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
[JsonProperty("updated_at")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
public int Version { get; set; }
|
||||
public List<VRCUnityPackage> UnityPackages { get; set; }
|
||||
|
||||
public bool Featured { get; set; }
|
||||
public string UnityPackageUrl { get; set; }
|
||||
public Dictionary<string, string> UnityPackageUrlObject { get; set; }
|
||||
|
||||
public string GetLatestAssetUrlForPlatform(string platform)
|
||||
{
|
||||
string assetUrl = null;
|
||||
var preferredUnityVersion = new UnityVersion();
|
||||
if (this.UnityPackages == null) return null;
|
||||
|
||||
foreach (var unityPackage in this.UnityPackages)
|
||||
{
|
||||
if (UnityVersion.Parse(unityPackage.UnityVersion).CompareTo(preferredUnityVersion) < 0) continue;
|
||||
|
||||
if (unityPackage.Variant != null)
|
||||
{
|
||||
AvatarVariant variant = (AvatarVariant)Enum.Parse(typeof(AvatarVariant), unityPackage.Variant, true);
|
||||
if (variant == AvatarVariant.Impostor) continue;
|
||||
}
|
||||
|
||||
if (unityPackage.Platform != platform) continue;
|
||||
assetUrl = unityPackage.AssetUrl;
|
||||
preferredUnityVersion = UnityVersion.Parse(unityPackage.UnityVersion);
|
||||
}
|
||||
|
||||
return assetUrl;
|
||||
}
|
||||
|
||||
public string GetLatestAssetUrlForCurrentPlatform()
|
||||
{
|
||||
return GetLatestAssetUrlForPlatform(Tools.Platform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if user-editable data is equal between avatar records
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
public bool ContentInfoEqual(VRCAvatar target)
|
||||
{
|
||||
return target.Name.Equals(Name) &&
|
||||
target.Description.Equals(Description) &&
|
||||
target.Tags.SequenceEqual(Tags) &&
|
||||
target.ReleaseStatus.Equals(ReleaseStatus);
|
||||
}
|
||||
}
|
||||
|
||||
// Only a subset of fields is allowed to be changed through the SDK
|
||||
public struct VRCAvatarChanges {
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public List<string> Tags { get; set; }
|
||||
public string ReleaseStatus { get; set; }
|
||||
|
||||
// Styles should be submitted via style ID and not the style Name
|
||||
public string PrimaryStyle { get; set; }
|
||||
public string SecondaryStyle { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93e3e8f8e94746a6b83e7431081ef622
|
||||
timeCreated: 1683135174
|
||||
@ -0,0 +1,12 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
public struct VRCAvatarStyle
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string ID { get; set; }
|
||||
|
||||
public string StyleName { get; set; }
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a53e0d0d4a9549dbbb6fedba8fec4f2d
|
||||
timeCreated: 1744068776
|
||||
@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
public struct VRCFile
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string ID { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string OwnerId { get; set; }
|
||||
public string MimeType { get; set; }
|
||||
public string Extension { get; set; }
|
||||
public List<VersionEntry> Versions { get; set; }
|
||||
|
||||
public class VersionEntry
|
||||
{
|
||||
public int Version { get; set; }
|
||||
public string Status { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public bool Deleted { get; set; }
|
||||
public FileDescriptor File { get; set; }
|
||||
public FileDescriptor Signature { get; set; }
|
||||
public FileDescriptor Delta { get; set; }
|
||||
|
||||
public class FileDescriptor
|
||||
{
|
||||
public string Status { get; set; }
|
||||
public string URL { get; set; }
|
||||
public string MD5 { get; set; }
|
||||
public string Category { get; set; }
|
||||
public int SizeInBytes { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public string UploadId { get; set; }
|
||||
[JsonProperty("cdns")]
|
||||
public List<string> CDNs { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public int GetLatestVersion()
|
||||
{
|
||||
return (this.Versions?.Count ?? 0) - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is a valid version that is not deleted.
|
||||
/// </summary>
|
||||
public bool HasExistingOrPendingVersion()
|
||||
{
|
||||
var latestVersion = GetLatestVersion();
|
||||
if (latestVersion > 0)
|
||||
{
|
||||
latestVersion -= Versions.Count(v => v == null || v.Deleted);
|
||||
}
|
||||
|
||||
return latestVersion > 0;
|
||||
}
|
||||
|
||||
public bool IsLatestVersionWaiting()
|
||||
{
|
||||
if (!HasExistingOrPendingVersion()) return false;
|
||||
var version = Versions[GetLatestVersion()];
|
||||
if (version.Status == "waiting") return true;
|
||||
return ((version.File?.Status == "waiting") ||
|
||||
(version.Signature?.Status == "waiting"));
|
||||
}
|
||||
|
||||
public bool IsLatestVersionQueued()
|
||||
{
|
||||
if (!HasExistingOrPendingVersion()) return false;
|
||||
var version = Versions[GetLatestVersion()];
|
||||
if (version.Status == "queued") return true;
|
||||
return ((version.File?.Status == "queued") ||
|
||||
(version.Signature?.Status == "queued"));
|
||||
}
|
||||
|
||||
public bool IsLatestVersionErrored()
|
||||
{
|
||||
if (!HasExistingOrPendingVersion()) return false;
|
||||
var version = Versions[GetLatestVersion()];
|
||||
if (version.Status == "error") return true;
|
||||
return ((version.File?.Status == "error") ||
|
||||
(version.Signature?.Status == "error"));
|
||||
}
|
||||
|
||||
public bool HasQueuedOperation()
|
||||
{
|
||||
if (IsLatestVersionWaiting()) return false;
|
||||
|
||||
return HasExistingOrPendingVersion() && IsLatestVersionQueued();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45cf16ea678f4615920c2d510d154633
|
||||
timeCreated: 1686655895
|
||||
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using VRC.Core;
|
||||
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
internal class VRCProgressContent: HttpContent
|
||||
{
|
||||
private readonly HttpContent _source;
|
||||
private readonly Action<float> _onProgress;
|
||||
|
||||
public VRCProgressContent(HttpContent source, Action<float> onProgress)
|
||||
{
|
||||
_source = source;
|
||||
_onProgress = onProgress;
|
||||
}
|
||||
|
||||
protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
|
||||
{
|
||||
var sendBuffer = new byte[4 * 1024 * 1024];
|
||||
var totalBytesRead = 0L;
|
||||
|
||||
using (var inputStream = await _source.ReadAsStreamAsync())
|
||||
{
|
||||
var totalBytes = inputStream.Length;
|
||||
var bytesRead = 0;
|
||||
|
||||
while ((bytesRead = await inputStream.ReadAsync(sendBuffer, 0, sendBuffer.Length)) > 0)
|
||||
{
|
||||
await stream.WriteAsync(sendBuffer, 0, bytesRead);
|
||||
|
||||
totalBytesRead += bytesRead;
|
||||
_onProgress((float)totalBytesRead / totalBytes);
|
||||
Core.Logger.Log($"Sent {totalBytesRead} out of {totalBytes}", API.LOG_CATEGORY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool TryComputeLength(out long length)
|
||||
{
|
||||
length = _source.Headers.ContentLength.GetValueOrDefault();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 685a233b2bbf4a99a5f62f2d9727f484
|
||||
timeCreated: 1686669873
|
||||
@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
[assembly: InternalsVisibleTo("VRC.SDK3A.Editor")]
|
||||
[assembly: InternalsVisibleTo("VRC.SDK3.Editor")]
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
internal class VRCTools
|
||||
{
|
||||
internal static byte[] GetFileMD5(string filePath)
|
||||
{
|
||||
var hash = MD5.Create();
|
||||
using var fileStream = File.OpenRead(filePath);
|
||||
return hash.ComputeHash(fileStream);
|
||||
}
|
||||
|
||||
internal static async Task<string> GenerateFileSignature(string sourceFilePath, string targetFilePath)
|
||||
{
|
||||
await using var fileStream = File.OpenRead(sourceFilePath);
|
||||
await using var inStream = librsync.net.Librsync.ComputeSignature(fileStream);
|
||||
await using var signatureStream = File.Open(targetFilePath, FileMode.Create, FileAccess.Write);
|
||||
await inStream.CopyToAsync(signatureStream);
|
||||
return targetFilePath;
|
||||
}
|
||||
|
||||
internal static async Task CleanupTempFiles(string fileId)
|
||||
{
|
||||
var folder = VRC.Tools.GetTempFolderPath(fileId);
|
||||
|
||||
while (Directory.Exists(folder))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(folder))
|
||||
Directory.Delete(folder, true);
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
await Task.Delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetMimeTypeFromExtension(string extension)
|
||||
{
|
||||
if (extension == ".vrcw")
|
||||
return "application/x-world";
|
||||
if (extension == ".vrca")
|
||||
return "application/x-avatar";
|
||||
if (extension == ".vrcp")
|
||||
return "application/x-prop";
|
||||
if (extension == ".dll")
|
||||
return "application/x-msdownload";
|
||||
if (extension == ".unitypackage")
|
||||
return "application/gzip";
|
||||
if (extension == ".gz")
|
||||
return "application/gzip";
|
||||
if (extension == ".jpg")
|
||||
return "image/jpg";
|
||||
if (extension == ".png")
|
||||
return "image/png";
|
||||
if (extension == ".sig")
|
||||
return "application/x-rsync-signature";
|
||||
if (extension == ".delta")
|
||||
return "application/x-rsync-delta";
|
||||
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This expands the send buffer size from around 256KB up to 4MB (our preferred send buffer size)
|
||||
/// This dramatically speeds up file uploads
|
||||
///
|
||||
/// Unfortunately, APIs meant to be used for this aren't available in Mono version of .NET in Unity
|
||||
/// So we have to use reflection to get to the private fields and properties we need
|
||||
/// </summary>
|
||||
/// <param name="targetUrl">Url to expand the send buffer for</param>
|
||||
/// <param name="sendRequest">Request task to expand the buffer for</param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
internal static async Task IncreaseSendBuffer(Uri targetUrl, Task sendRequest,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var servicePointScheduler = typeof(ServicePoint).GetProperty("Scheduler", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var servicePointGroups = servicePointScheduler?.PropertyType.GetField("groups", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var groupsList = servicePointGroups?.FieldType.GetProperty("Values");
|
||||
|
||||
var connectionStateType = servicePointGroups?.FieldType.GenericTypeArguments[1];
|
||||
var connectionsList = connectionStateType?.GetField("connections", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
var connection = connectionsList?.FieldType.GenericTypeArguments[0];
|
||||
var socket = connection?.GetField("socket", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
while (!sendRequest.IsCompleted && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(250, cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
var servicePoint = ServicePointManager.FindServicePoint(targetUrl);
|
||||
var scheduler = servicePointScheduler?.GetValue(servicePoint);
|
||||
if (scheduler == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// This can be null on mac/linux, so we add an extra check
|
||||
var servicePointGroup = servicePointGroups?.GetValue(scheduler);
|
||||
if (servicePointGroup == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var groups = (IEnumerable) groupsList?.GetValue(servicePointGroup);
|
||||
|
||||
// we're going to retry finding the active service point
|
||||
if (groups == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
var connections = (IEnumerable) connectionsList?.GetValue(group);
|
||||
if (connections == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var webConnection in connections)
|
||||
{
|
||||
if (webConnection == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var socketInstance = (Socket) socket?.GetValue(webConnection);
|
||||
if (socketInstance != null && socketInstance.Connected &&
|
||||
socketInstance.SendBufferSize < 4 * 1024 * 1024)
|
||||
{
|
||||
socketInstance.SendBufferSize = 4 * 1024 * 1024;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Core.Logger.LogWarning($"Failed to increase send buffer {e.Message}", Core.API.LOG_CATEGORY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e259a399dbb4ef19a7a89d2b4233247
|
||||
timeCreated: 1686658578
|
||||
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
public struct VRCUnityPackage
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string ID { get; set; }
|
||||
public string AssetUrl { get; set; }
|
||||
public string UnityVersion { get; set; }
|
||||
public long UnitySortNumber { get; set; }
|
||||
public int AssetVersion { get; set; }
|
||||
public string Platform { get; set; }
|
||||
[JsonProperty("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public string Variant { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7f09500761d4a019e606df903a022c8
|
||||
timeCreated: 1683134817
|
||||
@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using VRC.Core;
|
||||
|
||||
namespace VRC.SDKBase.Editor.Api
|
||||
{
|
||||
public struct VRCWorld: IVRCContent
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string ID { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string PreviewYoutubeId { get; set; }
|
||||
public List<string> Tags { get; set; }
|
||||
public List<string> UdonProducts { get; set; }
|
||||
|
||||
public string AuthorName { get; set; }
|
||||
public string AuthorId { get; set; }
|
||||
|
||||
public string ImageUrl { get; set; }
|
||||
public string ThumbnailImageUrl { get; set; }
|
||||
|
||||
public string ReleaseStatus { get; set; }
|
||||
|
||||
public int Capacity { get; set; }
|
||||
public int RecommendedCapacity { get; set; }
|
||||
public int Favorites { get; set; }
|
||||
public int Visits { get; set; }
|
||||
// public int Popularity { get; set; } // this field doesn't deserialize correctly for VERY popular worlds, ignoring in SDK for now
|
||||
public int Heat { get; set; }
|
||||
public int Occupants { get; set; }
|
||||
public int PublicOccupants { get; set; }
|
||||
public int PrivateOccupants { get; set; }
|
||||
|
||||
public List<Instance> Instances { get; set; }
|
||||
|
||||
public struct Instance
|
||||
{
|
||||
public string ID { get; set; }
|
||||
public int Occupants { get; set; }
|
||||
}
|
||||
|
||||
[JsonProperty("created_at")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
[JsonProperty("updated_at")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
public DateTime PublicationDate { get; set; }
|
||||
public DateTime LabsPublicationDate { get; set; }
|
||||
|
||||
public int Version { get; set; }
|
||||
public List<VRCUnityPackage> UnityPackages { get; set; }
|
||||
|
||||
public string Organization { get; set; }
|
||||
public bool Featured { get; set; }
|
||||
|
||||
public string GetLatestAssetUrlForPlatform(string platform)
|
||||
{
|
||||
string assetUrl = null;
|
||||
var preferredUnityVersion = new UnityVersion();
|
||||
if (this.UnityPackages == null) return null;
|
||||
foreach (var unityPackage in this.UnityPackages)
|
||||
{
|
||||
if (UnityVersion.Parse(unityPackage.UnityVersion).CompareTo(preferredUnityVersion) < 0) continue;
|
||||
if (unityPackage.Platform != platform) continue;
|
||||
assetUrl = unityPackage.AssetUrl;
|
||||
preferredUnityVersion = UnityVersion.Parse(unityPackage.UnityVersion);
|
||||
}
|
||||
|
||||
return assetUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// Only a subset of fields is allowed to be changed through the SDK
|
||||
public struct VRCWorldChanges {
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public int Capacity { get; set; }
|
||||
public int RecommendedCapacity { get; set; }
|
||||
public string PreviewYoutubeId { get; set; }
|
||||
public List<string> Tags { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bcf1554494894265b68fbf2e7c089782
|
||||
timeCreated: 1683134291
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41dfa12618ada604f98c12a95a3f455a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e2ea84994e53bd458fa73dfcfd0d671
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,18 @@
|
||||
#if VRC_SDK_PIPELINE_SAMPLES
|
||||
|
||||
using UnityEditor;
|
||||
|
||||
namespace VRC.SDKBase.Editor.BuildPipeline.Samples
|
||||
{
|
||||
public class VRCSDKBuildRequestedCallbackSample : IVRCSDKBuildRequestedCallback
|
||||
{
|
||||
public int callbackOrder => 0;
|
||||
|
||||
public bool OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType)
|
||||
{
|
||||
return EditorUtility.DisplayDialog("Build Confirmation", "Are you sure you want to build?", "Yes", "Not Yes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a1d20f4241085e46bdddc71b691465b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "VRC.SDKBase.Editor.BuildPipeline",
|
||||
"references": [],
|
||||
"optionalUnityReferences": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": []
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21332e1f0d937794d916d2402ba1943a
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 027731dadc7571046abaf43e4f52e9ae
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,230 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using UnityEditor;
|
||||
using VRC.SDKBase;
|
||||
|
||||
[InitializeOnLoad]
|
||||
public class AutoAddSpatialAudioComponents
|
||||
{
|
||||
|
||||
public static bool Enabled = true;
|
||||
|
||||
static AutoAddSpatialAudioComponents()
|
||||
{
|
||||
EditorApplication.hierarchyChanged += OnHierarchyWindowChanged;
|
||||
EditorApplication.projectChanged += OnProjectWindowChanged;
|
||||
RegisterCallbacks();
|
||||
}
|
||||
|
||||
static void OnHierarchyWindowChanged()
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
EditorApplication.hierarchyChanged -= OnHierarchyWindowChanged;
|
||||
return;
|
||||
}
|
||||
|
||||
// check for proper use of VRCSP, and warn
|
||||
//TryToAddSpatializationToAllAudioSources(true, false);
|
||||
}
|
||||
|
||||
static void OnProjectWindowChanged()
|
||||
{
|
||||
RegisterCallbacks();
|
||||
}
|
||||
|
||||
static void RegisterCallbacks()
|
||||
{
|
||||
VRCSdkControlPanel._EnableSpatialization = VRCSDKControlPanel_EnableSpatialization;
|
||||
}
|
||||
|
||||
// callback from VrcSdkControlPanel in dll
|
||||
public static void VRCSDKControlPanel_EnableSpatialization()
|
||||
{
|
||||
Debug.Log("Enabling spatialization on 3D AudioSources...");
|
||||
TryToAddSpatializationToAllAudioSources(false, true);
|
||||
}
|
||||
|
||||
static bool ApplyDefaultSpatializationToAudioSource(AudioSource audioSrc, bool force = false)
|
||||
{
|
||||
if (audioSrc == null)
|
||||
return false;
|
||||
|
||||
var vrcsp = audioSrc.gameObject.GetComponent<VRC.SDKBase.VRC_SpatialAudioSource>();
|
||||
|
||||
// don't make changes if we already have a vrcsp and we aren't forcing
|
||||
if (vrcsp != null && !force)
|
||||
return false;
|
||||
|
||||
if (force)
|
||||
audioSrc.spatialBlend = 1;
|
||||
|
||||
bool initValues = force;
|
||||
|
||||
// is audio source set to be 2D?
|
||||
bool is2D = audioSrc.spatialBlend == 0;
|
||||
|
||||
if (vrcsp == null)
|
||||
{
|
||||
// no onsp and no vrcsp, so add
|
||||
vrcsp = audioSrc.gameObject.AddComponent<VRC.SDKBase.VRC_SpatialAudioSource>();
|
||||
if (is2D)
|
||||
{
|
||||
// this audio source was marked as 2D, leave the vrcsp disabled
|
||||
vrcsp.EnableSpatialization = false;
|
||||
}
|
||||
initValues = true;
|
||||
}
|
||||
|
||||
audioSrc.spatialize = vrcsp.EnableSpatialization;
|
||||
vrcsp.enabled = true;
|
||||
|
||||
if (initValues)
|
||||
{
|
||||
bool isAvatar = audioSrc.GetComponentInParent<VRC.SDKBase.VRC_AvatarDescriptor>();
|
||||
|
||||
vrcsp.Gain = isAvatar ? AudioManagerSettings.AvatarAudioMaxGain : AudioManagerSettings.RoomAudioGain;
|
||||
vrcsp.Near = 0;
|
||||
vrcsp.Far = isAvatar ? AudioManagerSettings.AvatarAudioMaxRange : AudioManagerSettings.RoomAudioMaxRange;
|
||||
vrcsp.UseAudioSourceVolumeCurve = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void TryToAddSpatializationToAllAudioSources(bool newAudioSourcesOnly, bool includeInactive)
|
||||
{
|
||||
AudioSource[] allAudioSources = includeInactive ? Resources.FindObjectsOfTypeAll<AudioSource>() : Object.FindObjectsByType<AudioSource>(FindObjectsSortMode.None);
|
||||
foreach (AudioSource src in allAudioSources)
|
||||
{
|
||||
if (src == null || src.gameObject == null || src.gameObject.scene != UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newAudioSourcesOnly)
|
||||
{
|
||||
if (!IsNewAudioSource(src))
|
||||
continue;
|
||||
|
||||
UnityEngine.Audio.AudioMixerGroup mixer = AssetDatabase.LoadAssetAtPath<UnityEngine.Audio.AudioMixerGroup>("Assets/VRCSDK/Dependencies/OSPNative/scenes/mixers/SpatializerMixer.mixer");
|
||||
if (mixer != null)
|
||||
{
|
||||
src.outputAudioMixerGroup = mixer;
|
||||
}
|
||||
}
|
||||
|
||||
if (ApplyDefaultSpatializationToAudioSource(src, false))
|
||||
{
|
||||
Debug.Log("Automatically added VRC_SpatialAudioSource component to " + GetGameObjectPath(src.gameObject) + "!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsNewAudioSource(AudioSource src)
|
||||
{
|
||||
var vrcsp = src.GetComponent<VRC_SpatialAudioSource>();
|
||||
if (vrcsp != null)
|
||||
return false;
|
||||
|
||||
if (src.clip != null)
|
||||
return false;
|
||||
if (src.outputAudioMixerGroup != null)
|
||||
return false;
|
||||
|
||||
if (src.mute || src.bypassEffects || src.bypassReverbZones || !src.playOnAwake || src.loop)
|
||||
return false;
|
||||
|
||||
if (src.priority != 128 ||
|
||||
!Mathf.Approximately(src.volume, 1.0f) ||
|
||||
!Mathf.Approximately(src.pitch, 1.0f) ||
|
||||
!Mathf.Approximately(src.panStereo, 0.0f) ||
|
||||
!Mathf.Approximately(src.spatialBlend, 0.0f) ||
|
||||
!Mathf.Approximately(src.reverbZoneMix, 1.0f))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Mathf.Approximately(src.dopplerLevel, 1.0f) ||
|
||||
!Mathf.Approximately(src.spread, 0.0f) ||
|
||||
src.rolloffMode != AudioRolloffMode.Logarithmic ||
|
||||
!Mathf.Approximately(src.minDistance, 1.0f) ||
|
||||
!Mathf.Approximately(src.maxDistance, 500.0f))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static string GetGameObjectPath(GameObject obj)
|
||||
{
|
||||
string path = "/" + obj.name;
|
||||
while (obj.transform.parent != null)
|
||||
{
|
||||
obj = obj.transform.parent.gameObject;
|
||||
path = "/" + obj.name + path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static void ConvertONSPAudioSource(AudioSource src)
|
||||
{
|
||||
if (src == null) return;
|
||||
|
||||
var onsp = src.GetComponent<ONSPAudioSource>();
|
||||
if (onsp != null)
|
||||
{
|
||||
var vrcsp = src.gameObject.GetComponent<VRC.SDKBase.VRC_SpatialAudioSource>();
|
||||
if (vrcsp == null)
|
||||
{
|
||||
// copy the values from deprecated component
|
||||
vrcsp = src.gameObject.AddComponent<VRC.SDKBase.VRC_SpatialAudioSource>();
|
||||
vrcsp.Gain = onsp.Gain;
|
||||
vrcsp.Near = onsp.Near;
|
||||
vrcsp.Far = onsp.Far;
|
||||
vrcsp.UseAudioSourceVolumeCurve = !onsp.UseInvSqr;
|
||||
vrcsp.EnableSpatialization = onsp.EnableSpatialization;
|
||||
}
|
||||
// remove deprecated component
|
||||
Component.DestroyImmediate(onsp);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddVRCSpatialToBareAudioSource(AudioSource src)
|
||||
{
|
||||
if (src == null) return;
|
||||
|
||||
var vrcsp = src.gameObject.GetComponent<VRC.SDKBase.VRC_SpatialAudioSource>();
|
||||
if (vrcsp != null) return;
|
||||
|
||||
#if VRC_SDK_VRCSDK2
|
||||
vrcsp = src.gameObject.AddComponent<VRCSDK2.VRC_SpatialAudioSource>();
|
||||
#elif UDON
|
||||
vrcsp = src.gameObject.AddComponent<VRC.SDK3.Components.VRCSpatialAudioSource>();
|
||||
#endif
|
||||
|
||||
// add default values
|
||||
bool isAvatar = src.gameObject.GetComponentInParent<VRC.SDKBase.VRC_AvatarDescriptor>();
|
||||
|
||||
vrcsp.Gain = isAvatar ? AudioManagerSettings.AvatarAudioMaxGain : AudioManagerSettings.RoomAudioGain;
|
||||
vrcsp.Near = 0;
|
||||
vrcsp.Far = isAvatar ? AudioManagerSettings.AvatarAudioMaxRange : AudioManagerSettings.RoomAudioMaxRange;
|
||||
vrcsp.UseAudioSourceVolumeCurve = false;
|
||||
|
||||
// enable spatialization if src is not 2D
|
||||
vrcsp.EnableSpatialization = false;
|
||||
AnimationCurve curve = src.GetCustomCurve(AudioSourceCurveType.SpatialBlend);
|
||||
if (curve != null)
|
||||
{
|
||||
foreach (var key in curve.keys)
|
||||
{
|
||||
if (key.value != 0 || key.inTangent != 0)
|
||||
{
|
||||
vrcsp.EnableSpatialization = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a364ece829b6234888c59987a305a00
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
public class EditorCoroutine
|
||||
{
|
||||
public static EditorCoroutine Start( IEnumerator _routine )
|
||||
{
|
||||
EditorCoroutine coroutine = new EditorCoroutine(_routine);
|
||||
coroutine.start();
|
||||
return coroutine;
|
||||
}
|
||||
|
||||
|
||||
public static EditorCoroutine Start(System.Action _action)
|
||||
{
|
||||
EditorCoroutine coroutine = new EditorCoroutine(_action);
|
||||
coroutine.start();
|
||||
return coroutine;
|
||||
}
|
||||
|
||||
readonly IEnumerator routine;
|
||||
EditorCoroutine( IEnumerator _routine )
|
||||
{
|
||||
routine = _routine;
|
||||
}
|
||||
|
||||
readonly System.Action action;
|
||||
EditorCoroutine(System.Action _action)
|
||||
{
|
||||
action = _action;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
EditorApplication.update += update;
|
||||
}
|
||||
public void stop()
|
||||
{
|
||||
EditorApplication.update -= update;
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
if (routine != null)
|
||||
{
|
||||
if (!routine.MoveNext())
|
||||
stop();
|
||||
}
|
||||
else if (action != null)
|
||||
{
|
||||
action();
|
||||
stop();
|
||||
}
|
||||
else
|
||||
stop();
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89005ebc9543e0a4284893c09ca19b1d
|
||||
timeCreated: 1473271738
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,17 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
[InitializeOnLoad]
|
||||
public static class EditorHandling
|
||||
{
|
||||
static EditorHandling()
|
||||
{
|
||||
UnityEditor.SceneManagement.EditorSceneManager.sceneOpened += SceneOpenedCallback;
|
||||
}
|
||||
|
||||
static void SceneOpenedCallback( Scene scene, UnityEditor.SceneManagement.OpenSceneMode mode)
|
||||
{
|
||||
// refresh window when scene is opened to display content images correctly
|
||||
if (null != VRCSdkControlPanel.window) VRCSdkControlPanel.window.Reset();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d6c2e367eaa9564ebf6267ec163cfbd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,358 @@
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using VRC.SDKBase;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
#if VRC_SDK_VRCSDK2
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_EventHandler))]
|
||||
public class EventHandlerEditor : UnityEditor.Editor
|
||||
{
|
||||
bool showDeferredEvents = false;
|
||||
|
||||
static VRCSDK2.VRC_EventHandler.VrcEventType lastAddedEventType = VRCSDK2.VRC_EventHandler.VrcEventType.SendMessage;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
VRCSDK2.VRC_EventHandler myTarget = (VRCSDK2.VRC_EventHandler)target;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("ID:");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (myTarget.GetComponent<VRCSDK2.VRC_Trigger>() != null)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Add Events via the VRC_Trigger on this object.");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
RenderOldEditor(myTarget);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
|
||||
}
|
||||
|
||||
if (myTarget.deferredEvents.Count > 0)
|
||||
{
|
||||
showDeferredEvents = EditorGUILayout.Foldout(showDeferredEvents, "Deferred Events");
|
||||
if (showDeferredEvents)
|
||||
RenderEvents(myTarget.deferredEvents);
|
||||
}
|
||||
}
|
||||
|
||||
int[] sendMessageMethodIndicies;
|
||||
private void RenderOldEditor(VRCSDK2.VRC_EventHandler myTarget)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Please use a VRC_Trigger in the future.", MessageType.Error);
|
||||
|
||||
if (GUILayout.Button("Add Event Handler"))
|
||||
myTarget.Events.Add(new VRCSDK2.VRC_EventHandler.VrcEvent());
|
||||
|
||||
bool first = true;
|
||||
int deleteEventIndex = -1;
|
||||
if (sendMessageMethodIndicies == null || sendMessageMethodIndicies.Length != myTarget.Events.Count)
|
||||
sendMessageMethodIndicies = new int[myTarget.Events.Count + 1];
|
||||
|
||||
for (int i = 0; i < myTarget.Events.Count; ++i)
|
||||
{
|
||||
if (!first)
|
||||
EditorGUILayout.Separator();
|
||||
first = false;
|
||||
|
||||
EditorGUILayout.LabelField("Event " + (i + 1).ToString());
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Event Name");
|
||||
myTarget.Events[i].Name = EditorGUILayout.TextField(myTarget.Events[i].Name);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Event Type");
|
||||
myTarget.Events[i].EventType = (VRCSDK2.VRC_EventHandler.VrcEventType)EditorGUILayout.EnumPopup(myTarget.Events[i].EventType);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
switch (myTarget.Events[i].EventType)
|
||||
{
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.AnimationBool:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Variable");
|
||||
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Operation");
|
||||
myTarget.Events[i].ParameterBoolOp = (VRCSDK2.VRC_EventHandler.VrcBooleanOp)EditorGUILayout.EnumPopup(myTarget.Events[i].ParameterBoolOp);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.AnimationFloat:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Variable");
|
||||
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Value");
|
||||
myTarget.Events[i].ParameterFloat = EditorGUILayout.FloatField(myTarget.Events[i].ParameterFloat);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.AnimationTrigger:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Trigger");
|
||||
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.AudioTrigger:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("AudioSource");
|
||||
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.MeshVisibility:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Mesh");
|
||||
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Operation");
|
||||
myTarget.Events[i].ParameterBoolOp = (VRCSDK2.VRC_EventHandler.VrcBooleanOp)EditorGUILayout.EnumPopup(myTarget.Events[i].ParameterBoolOp);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.PlayAnimation:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Target");
|
||||
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Animation");
|
||||
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.RunConsoleCommand:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Command");
|
||||
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.SendMessage:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Receiver");
|
||||
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Message");
|
||||
|
||||
// sorry for this shit show. Below allows us to show a list of public methods, but also allow custom messages
|
||||
var methods = VRC_EditorTools.GetAccessibleMethodsOnGameObject(myTarget.Events[i].ParameterObject);
|
||||
List<string> methodList = methods.Values.Aggregate(new List<string>(), (acc, lst) => { acc.AddRange(lst.Select(mi => mi.Name)); return acc; });
|
||||
methodList.Add("Custom Message");
|
||||
|
||||
string[] _choices = methodList.ToArray();
|
||||
|
||||
int currentIndex = methodList.Count - 1;
|
||||
|
||||
if (methodList.Contains(myTarget.Events[i].ParameterString))
|
||||
currentIndex = methodList.IndexOf(myTarget.Events[i].ParameterString);
|
||||
|
||||
sendMessageMethodIndicies[i] = EditorGUILayout.Popup(currentIndex, _choices);
|
||||
|
||||
if (sendMessageMethodIndicies[i] != methodList.Count - 1)
|
||||
{
|
||||
myTarget.Events[i].ParameterString = _choices[sendMessageMethodIndicies[i]];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (methodList.Contains(myTarget.Events[i].ParameterString))
|
||||
myTarget.Events[i].ParameterString = "";
|
||||
|
||||
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.SetGameObjectActive:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("GameObject");
|
||||
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Operation");
|
||||
myTarget.Events[i].ParameterBoolOp = (VRCSDK2.VRC_EventHandler.VrcBooleanOp)EditorGUILayout.EnumPopup(myTarget.Events[i].ParameterBoolOp);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.SetParticlePlaying:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Target");
|
||||
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Operation");
|
||||
myTarget.Events[i].ParameterBoolOp = (VRCSDK2.VRC_EventHandler.VrcBooleanOp)EditorGUILayout.EnumPopup(myTarget.Events[i].ParameterBoolOp);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.TeleportPlayer:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Location");
|
||||
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Align Room To Destination");
|
||||
myTarget.Events[i].ParameterBoolOp = (VRCSDK2.VRC_EventHandler.VrcBooleanOp)EditorGUILayout.EnumPopup(myTarget.Events[i].ParameterBoolOp);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.SetWebPanelURI:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("URI");
|
||||
myTarget.Events[i].ParameterString = EditorGUILayout.TextField(myTarget.Events[i].ParameterString);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Panel");
|
||||
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
case VRCSDK2.VRC_EventHandler.VrcEventType.SetWebPanelVolume:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Volume");
|
||||
myTarget.Events[i].ParameterFloat = EditorGUILayout.FloatField(myTarget.Events[i].ParameterFloat);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Panel");
|
||||
myTarget.Events[i].ParameterObject = (GameObject)EditorGUILayout.ObjectField(myTarget.Events[i].ParameterObject, typeof(GameObject), true);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
default:
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUIStyle redText = new GUIStyle();
|
||||
redText.normal.textColor = Color.red;
|
||||
EditorGUILayout.LabelField("Unsupported event type", redText);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
break;
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Delete " + myTarget.Events[i].Name + "?");
|
||||
if (GUILayout.Button("delete"))
|
||||
deleteEventIndex = i;
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (myTarget.Events[i].ParameterObject == null)
|
||||
myTarget.Events[i].ParameterObject = myTarget.gameObject;
|
||||
}
|
||||
|
||||
|
||||
if (deleteEventIndex != -1)
|
||||
myTarget.Events.RemoveAt(deleteEventIndex);
|
||||
}
|
||||
|
||||
private void RenderEvents(IEnumerable<VRCSDK2.VRC_EventHandler.EventInfo> entries)
|
||||
{
|
||||
foreach (VRCSDK2.VRC_EventHandler.EventInfo entry in entries)
|
||||
{
|
||||
EditorGUILayout.PrefixLabel("Target");
|
||||
EditorGUILayout.ObjectField(entry.evt.ParameterObject, typeof(GameObject), true);
|
||||
|
||||
EditorGUILayout.LabelField(string.Format("Name: {0}", entry.evt.Name));
|
||||
EditorGUILayout.LabelField(string.Format("Type: {0}", entry.evt.EventType));
|
||||
EditorGUILayout.LabelField(string.Format("Bool: {0}", entry.evt.ParameterBool));
|
||||
EditorGUILayout.LabelField(string.Format("Float: {0}", entry.evt.ParameterFloat));
|
||||
EditorGUILayout.LabelField(string.Format("Int: {0}", entry.evt.ParameterInt));
|
||||
EditorGUILayout.LabelField(string.Format("String: {0}", entry.evt.ParameterString));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
}
|
||||
|
||||
public static void RenderEditor(VRCSDK2.VRC_EventHandler myTarget)
|
||||
{
|
||||
bool first = true;
|
||||
int deleteEventIndex = -1;
|
||||
|
||||
for (int i = 0; i < myTarget.Events.Count; ++i)
|
||||
{
|
||||
if (!first)
|
||||
EditorGUILayout.Separator();
|
||||
first = false;
|
||||
|
||||
if (RenderEventHeader(myTarget, myTarget.Events[i]))
|
||||
deleteEventIndex = i;
|
||||
|
||||
RenderEventHeader(myTarget, myTarget.Events[i]);
|
||||
|
||||
if (myTarget.Events[i].ParameterObject == null)
|
||||
myTarget.Events[i].ParameterObject = myTarget.gameObject;
|
||||
}
|
||||
|
||||
if (deleteEventIndex != -1)
|
||||
myTarget.Events.RemoveAt(deleteEventIndex);
|
||||
}
|
||||
|
||||
public static VRCSDK2.VRC_EventHandler.VrcEvent RenderAddEvent(VRCSDK2.VRC_EventHandler myTarget)
|
||||
{
|
||||
VRCSDK2.VRC_EventHandler.VrcEvent newEvent = null;
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
lastAddedEventType = VRC_EditorTools.FilteredEnumPopup("New Event Type", lastAddedEventType, (v) => v != VRCSDK2.VRC_EventHandler.VrcEventType.SpawnObject && v != VRCSDK2.VRC_EventHandler.VrcEventType.SendMessage);
|
||||
if (GUILayout.Button("Add"))
|
||||
{
|
||||
newEvent = new VRCSDK2.VRC_EventHandler.VrcEvent
|
||||
{
|
||||
EventType = lastAddedEventType,
|
||||
ParameterObject = myTarget.gameObject
|
||||
};
|
||||
myTarget.Events.Add(newEvent);
|
||||
EditorUtility.SetDirty(myTarget);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
return newEvent;
|
||||
}
|
||||
|
||||
public static bool RenderEventHeader(VRCSDK2.VRC_EventHandler myTarget, VRCSDK2.VRC_EventHandler.VrcEvent evt)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
evt.EventType = VRC_EditorTools.FilteredEnumPopup("New Event Type", evt.EventType, (v) => v != VRCSDK2.VRC_EventHandler.VrcEventType.SpawnObject && v != VRCSDK2.VRC_EventHandler.VrcEventType.SendMessage);
|
||||
bool delete = GUILayout.Button("Remove");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
return delete;
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(VRC.SDKBase.VRC_EventHandler))]
|
||||
public class SDKBaseEventHandlerEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("Event Handlers are not supported in VRCSDK3.");
|
||||
if (GUILayout.Button("replace me with the correct VRC_EventHandler"))
|
||||
{
|
||||
var go = ((VRC.SDKBase.VRC_EventHandler)target).gameObject;
|
||||
DestroyImmediate(target);
|
||||
go.AddComponent<VRCSDK2.VRC_EventHandler>();
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
[CustomEditor(typeof(VRC.SDKBase.VRC_EventHandler))]
|
||||
public class SDKBaseEventHandlerEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("Event Handlers are not supported in VRCSDK3.");
|
||||
if( GUILayout.Button("delete me") )
|
||||
DestroyImmediate(target);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4810e652e8242384c834320970702290
|
||||
timeCreated: 1454469344
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,48 @@
|
||||
#if VRC_SDK_VRCSDK2 && UNITY_EDITOR
|
||||
|
||||
#pragma warning disable 0618
|
||||
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_KeyEvents))]
|
||||
public class VRC_KeyEventsEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.HelpBox("Obsolete. Please use a VRC_Trigger instead.", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_UseEvents))]
|
||||
public class VRC_UseEventsEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.HelpBox("Obsolete. Please use a VRC_Trigger instead.", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_TriggerColliderEventTrigger))]
|
||||
public class VRC_TriggerColliderEventTriggerEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.HelpBox("Obsolete. Please use a VRC_Trigger instead.", MessageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_TimedEvents))]
|
||||
public class VRC_TimedEventsEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.HelpBox("Obsolete. Please use a VRC_Trigger instead.", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore 0618
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 482185bf29f12074dada194ffef6a682
|
||||
timeCreated: 1475877803
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,225 @@
|
||||
#if VRC_SDK_VRCSDK2 && !VRC_CLIENT
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using VRC.SDK3.Editor;
|
||||
using VRC.SDKBase.Editor;
|
||||
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_AvatarDescriptor))]
|
||||
public class AvatarDescriptorEditor : Editor
|
||||
{
|
||||
VRCSDK2.VRC_AvatarDescriptor avatarDescriptor;
|
||||
VRC.Core.PipelineManager pipelineManager;
|
||||
|
||||
SkinnedMeshRenderer selectedMesh;
|
||||
List<string> blendShapeNames = null;
|
||||
|
||||
bool shouldRefreshVisemes = false;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
if (avatarDescriptor == null)
|
||||
avatarDescriptor = (VRCSDK2.VRC_AvatarDescriptor)target;
|
||||
|
||||
if (pipelineManager == null)
|
||||
{
|
||||
pipelineManager = avatarDescriptor.GetComponent<VRC.Core.PipelineManager>();
|
||||
if (pipelineManager == null)
|
||||
avatarDescriptor.gameObject.AddComponent<VRC.Core.PipelineManager>();
|
||||
}
|
||||
|
||||
// DrawDefaultInspector();
|
||||
|
||||
if(VRCSdkControlPanel.window != null)
|
||||
{
|
||||
if( GUILayout.Button( "Select this avatar in the SDK control panel" ) )
|
||||
VRCSdkControlPanelAvatarBuilder.SelectAvatar(avatarDescriptor);
|
||||
}
|
||||
|
||||
avatarDescriptor.ViewPosition = EditorGUILayout.Vector3Field("View Position", avatarDescriptor.ViewPosition);
|
||||
//avatarDescriptor.Name = EditorGUILayout.TextField("Avatar Name", avatarDescriptor.Name);
|
||||
avatarDescriptor.Animations = (VRCSDK2.VRC_AvatarDescriptor.AnimationSet)EditorGUILayout.EnumPopup("Default Animation Set", avatarDescriptor.Animations);
|
||||
avatarDescriptor.CustomStandingAnims = (AnimatorOverrideController)EditorGUILayout.ObjectField("Custom Standing Anims", avatarDescriptor.CustomStandingAnims, typeof(AnimatorOverrideController), true, null);
|
||||
avatarDescriptor.CustomSittingAnims = (AnimatorOverrideController)EditorGUILayout.ObjectField("Custom Sitting Anims", avatarDescriptor.CustomSittingAnims, typeof(AnimatorOverrideController), true, null);
|
||||
avatarDescriptor.ScaleIPD = EditorGUILayout.Toggle("Scale IPD", avatarDescriptor.ScaleIPD);
|
||||
|
||||
avatarDescriptor.lipSync = (VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle)EditorGUILayout.EnumPopup("Lip Sync", avatarDescriptor.lipSync);
|
||||
switch (avatarDescriptor.lipSync)
|
||||
{
|
||||
case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.Default:
|
||||
if (GUILayout.Button("Auto Detect!"))
|
||||
AutoDetectLipSync();
|
||||
break;
|
||||
|
||||
case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape:
|
||||
avatarDescriptor.VisemeSkinnedMesh = (SkinnedMeshRenderer)EditorGUILayout.ObjectField("Face Mesh", avatarDescriptor.VisemeSkinnedMesh, typeof(SkinnedMeshRenderer), true);
|
||||
if (avatarDescriptor.VisemeSkinnedMesh != null)
|
||||
{
|
||||
DetermineBlendShapeNames();
|
||||
|
||||
int current = -1;
|
||||
for (int b = 0; b < blendShapeNames.Count; ++b)
|
||||
if (avatarDescriptor.MouthOpenBlendShapeName == blendShapeNames[b])
|
||||
current = b;
|
||||
|
||||
string title = "Jaw Flap Blend Shape";
|
||||
int next = EditorGUILayout.Popup(title, current, blendShapeNames.ToArray());
|
||||
if (next >= 0)
|
||||
avatarDescriptor.MouthOpenBlendShapeName = blendShapeNames[next];
|
||||
}
|
||||
break;
|
||||
|
||||
case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBone:
|
||||
avatarDescriptor.lipSyncJawBone = (Transform)EditorGUILayout.ObjectField("Jaw Bone", avatarDescriptor.lipSyncJawBone, typeof(Transform), true);
|
||||
break;
|
||||
|
||||
case VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape:
|
||||
SkinnedMeshRenderer prev = avatarDescriptor.VisemeSkinnedMesh;
|
||||
avatarDescriptor.VisemeSkinnedMesh = (SkinnedMeshRenderer)EditorGUILayout.ObjectField("Face Mesh", avatarDescriptor.VisemeSkinnedMesh, typeof(SkinnedMeshRenderer), true);
|
||||
if (avatarDescriptor.VisemeSkinnedMesh != prev)
|
||||
shouldRefreshVisemes = true;
|
||||
if (avatarDescriptor.VisemeSkinnedMesh != null)
|
||||
{
|
||||
DetermineBlendShapeNames();
|
||||
|
||||
if (avatarDescriptor.VisemeBlendShapes == null || avatarDescriptor.VisemeBlendShapes.Length != (int)VRCSDK2.VRC_AvatarDescriptor.Viseme.Count)
|
||||
avatarDescriptor.VisemeBlendShapes = new string[(int)VRCSDK2.VRC_AvatarDescriptor.Viseme.Count];
|
||||
for (int i = 0; i < (int)VRCSDK2.VRC_AvatarDescriptor.Viseme.Count; ++i)
|
||||
{
|
||||
int current = -1;
|
||||
for (int b = 0; b < blendShapeNames.Count; ++b)
|
||||
if (avatarDescriptor.VisemeBlendShapes[i] == blendShapeNames[b])
|
||||
current = b;
|
||||
|
||||
string title = "Viseme: " + ((VRCSDK2.VRC_AvatarDescriptor.Viseme)i).ToString();
|
||||
int next = EditorGUILayout.Popup(title, current, blendShapeNames.ToArray());
|
||||
if (next >= 0)
|
||||
avatarDescriptor.VisemeBlendShapes[i] = blendShapeNames[next];
|
||||
}
|
||||
|
||||
if (shouldRefreshVisemes)
|
||||
AutoDetectVisemes();
|
||||
}
|
||||
break;
|
||||
}
|
||||
EditorGUILayout.LabelField("Unity Version", avatarDescriptor.unityVersion);
|
||||
}
|
||||
|
||||
void DetermineBlendShapeNames()
|
||||
{
|
||||
if (avatarDescriptor.VisemeSkinnedMesh != null &&
|
||||
avatarDescriptor.VisemeSkinnedMesh != selectedMesh)
|
||||
{
|
||||
blendShapeNames = new List<string>();
|
||||
blendShapeNames.Add("-none-");
|
||||
selectedMesh = avatarDescriptor.VisemeSkinnedMesh;
|
||||
if ((selectedMesh != null) && (selectedMesh.sharedMesh != null))
|
||||
{
|
||||
for (int i = 0; i < selectedMesh.sharedMesh.blendShapeCount; ++i)
|
||||
blendShapeNames.Add(selectedMesh.sharedMesh.GetBlendShapeName(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AutoDetectVisemes()
|
||||
{
|
||||
|
||||
// prioritize strict - but fallback to looser - naming and don't touch user-overrides
|
||||
|
||||
List<string> blendShapes = new List<string>(blendShapeNames);
|
||||
blendShapes.Remove("-none-");
|
||||
|
||||
for (int v = 0; v < avatarDescriptor.VisemeBlendShapes.Length; v++)
|
||||
{
|
||||
if (string.IsNullOrEmpty(avatarDescriptor.VisemeBlendShapes[v]))
|
||||
{
|
||||
string viseme = ((VRCSDK2.VRC_AvatarDescriptor.Viseme)v).ToString().ToLowerInvariant();
|
||||
|
||||
foreach (string s in blendShapes)
|
||||
{
|
||||
if (s.ToLowerInvariant() == "vrc.v_" + viseme)
|
||||
{
|
||||
avatarDescriptor.VisemeBlendShapes[v] = s;
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
foreach (string s in blendShapes)
|
||||
{
|
||||
if (s.ToLowerInvariant() == "v_" + viseme)
|
||||
{
|
||||
avatarDescriptor.VisemeBlendShapes[v] = s;
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
foreach (string s in blendShapes)
|
||||
{
|
||||
if (s.ToLowerInvariant().EndsWith(viseme))
|
||||
{
|
||||
avatarDescriptor.VisemeBlendShapes[v] = s;
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
foreach (string s in blendShapes)
|
||||
{
|
||||
if (s.ToLowerInvariant() == viseme)
|
||||
{
|
||||
avatarDescriptor.VisemeBlendShapes[v] = s;
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
foreach (string s in blendShapes)
|
||||
{
|
||||
if (s.ToLowerInvariant().Contains(viseme))
|
||||
{
|
||||
avatarDescriptor.VisemeBlendShapes[v] = s;
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
next: { }
|
||||
}
|
||||
}
|
||||
|
||||
shouldRefreshVisemes = false;
|
||||
|
||||
}
|
||||
|
||||
void AutoDetectLipSync()
|
||||
{
|
||||
var smrs = avatarDescriptor.GetComponentsInChildren<SkinnedMeshRenderer>();
|
||||
foreach (var smr in smrs)
|
||||
{
|
||||
if (smr.sharedMesh.blendShapeCount > 0)
|
||||
{
|
||||
avatarDescriptor.lipSyncJawBone = null;
|
||||
|
||||
if (smr.sharedMesh.blendShapeCount > 1)
|
||||
{
|
||||
avatarDescriptor.lipSync = VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.VisemeBlendShape;
|
||||
avatarDescriptor.VisemeSkinnedMesh = smr;
|
||||
shouldRefreshVisemes = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
avatarDescriptor.lipSync = VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBlendShape;
|
||||
avatarDescriptor.VisemeSkinnedMesh = null;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Animator a = avatarDescriptor.GetComponent<Animator>();
|
||||
if (!a)
|
||||
EditorUtility.DisplayDialog("Ooops", "This avatar has no Animator and can have no lipsync.", "OK");
|
||||
else if (a.GetBoneTransform(HumanBodyBones.Jaw) != null)
|
||||
{
|
||||
avatarDescriptor.lipSync = VRCSDK2.VRC_AvatarDescriptor.LipSyncStyle.JawFlapBone;
|
||||
avatarDescriptor.lipSyncJawBone = avatarDescriptor.GetComponent<Animator>().GetBoneTransform(HumanBodyBones.Jaw);
|
||||
avatarDescriptor.VisemeSkinnedMesh = null;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e83254bb97e84795ac882692ae124ba
|
||||
timeCreated: 1450462624
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,23 @@
|
||||
#if VRC_SDK_VRCSDK2
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_ObjectSpawn))]
|
||||
public class VRCObjectSpawnEditor : Editor
|
||||
{
|
||||
VRCSDK2.VRC_ObjectSpawn spawn;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (spawn == null)
|
||||
spawn = (VRCSDK2.VRC_ObjectSpawn)target;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawDefaultInspector();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26a75599848adb449b7aceed5090e35c
|
||||
timeCreated: 1463516633
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,25 @@
|
||||
#if VRC_SDK_VRCSDK2
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_ObjectSync))]
|
||||
public class VRCObjectSyncEditor : Editor
|
||||
{
|
||||
VRCSDK2.VRC_ObjectSync sync;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (sync == null)
|
||||
sync = (VRCSDK2.VRC_ObjectSync)target;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
sync.SynchronizePhysics = EditorGUILayout.Toggle("Synchronize Physics",sync.SynchronizePhysics);
|
||||
sync.AllowCollisionTransfer = EditorGUILayout.Toggle("Allow Collision Transfer", sync.AllowCollisionTransfer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed4aad2698d3b62408e69b57c7748791
|
||||
timeCreated: 1463516212
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,37 @@
|
||||
#if VRC_SDK_VRCSDK2
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
public class VRCPlayerModEditorWindow : EditorWindow {
|
||||
|
||||
public delegate void AddModCallback();
|
||||
public static AddModCallback addModCallback;
|
||||
|
||||
private static VRCSDK2.VRC_PlayerMods myTarget;
|
||||
|
||||
private static VRCSDK2.VRCPlayerModFactory.PlayerModType type;
|
||||
|
||||
public static void Init (VRCSDK2.VRC_PlayerMods target, AddModCallback callback)
|
||||
{
|
||||
// Get existing open window or if none, make a new one:
|
||||
EditorWindow.GetWindow (typeof (VRCPlayerModEditorWindow));
|
||||
addModCallback = callback;
|
||||
myTarget = target;
|
||||
|
||||
type = VRCSDK2.VRCPlayerModFactory.PlayerModType.Jump;
|
||||
}
|
||||
|
||||
void OnGUI ()
|
||||
{
|
||||
type = (VRCSDK2.VRCPlayerModFactory.PlayerModType)EditorGUILayout.EnumPopup("Mods", type);
|
||||
if(GUILayout.Button("Add Mod"))
|
||||
{
|
||||
VRCSDK2.VRCPlayerMod mod = VRCSDK2.VRCPlayerModFactory.Create(type);
|
||||
myTarget.AddMod(mod);
|
||||
addModCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8986a640e24a0754ea0aded12234b808
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,109 @@
|
||||
#if VRC_SDK_VRCSDK2
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_PlayerMods))]
|
||||
public class VRCPlayerModsEditor : UnityEditor.Editor
|
||||
{
|
||||
VRCSDK2.VRC_PlayerMods myTarget;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if(myTarget == null)
|
||||
myTarget = (VRCSDK2.VRC_PlayerMods)target;
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
myTarget.isRoomPlayerMods = EditorGUILayout.Toggle("isRoomPlayerMods", myTarget.isRoomPlayerMods);
|
||||
|
||||
List<VRCSDK2.VRCPlayerMod> playerMods = myTarget.playerMods;
|
||||
for(int i=0; i<playerMods.Count; ++i)
|
||||
{
|
||||
VRCSDK2.VRCPlayerMod mod = playerMods[i];
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
EditorGUILayout.LabelField(mod.name, EditorStyles.boldLabel);
|
||||
if( mod.allowNameEdit )
|
||||
mod.name = EditorGUILayout.TextField( "Mod Name: ", mod.name );
|
||||
for(int j=0; j<mod.properties.Count; ++j)
|
||||
{
|
||||
VRCSDK2.VRCPlayerModProperty prop = mod.properties[j];
|
||||
myTarget.playerMods[i].properties[j] = DrawFieldForProp(prop);
|
||||
}
|
||||
if(GUILayout.Button ("Remove Mod"))
|
||||
{
|
||||
myTarget.RemoveMod(mod);
|
||||
break;
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
if(GUILayout.Button("Add Mods"))
|
||||
{
|
||||
VRCPlayerModEditorWindow.AddModCallback adcb = OnInspectorGUI;
|
||||
VRCPlayerModEditorWindow.Init(myTarget, adcb);
|
||||
}
|
||||
}
|
||||
|
||||
VRCSDK2.VRCPlayerModProperty DrawFieldForProp(VRCSDK2.VRCPlayerModProperty property)
|
||||
{
|
||||
if(property.type.SystemType == typeof(int))
|
||||
{
|
||||
property.intValue = EditorGUILayout.IntField(property.name, property.intValue);
|
||||
}
|
||||
else if(property.type.SystemType == typeof(float))
|
||||
{
|
||||
property.floatValue = EditorGUILayout.FloatField(property.name, property.floatValue);
|
||||
}
|
||||
else if(property.type.SystemType == typeof(string))
|
||||
{
|
||||
property.stringValue = EditorGUILayout.TextField(property.name, property.stringValue);
|
||||
}
|
||||
else if(property.type.SystemType == typeof(bool))
|
||||
{
|
||||
property.boolValue = EditorGUILayout.Toggle(property.name, property.boolValue);
|
||||
}
|
||||
else if(property.type.SystemType == typeof(GameObject))
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField( property.name );
|
||||
property.gameObjectValue = (GameObject) EditorGUILayout.ObjectField( property.gameObjectValue, typeof( GameObject ), true );
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else if(property.type.SystemType == typeof(KeyCode))
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField( property.name );
|
||||
property.keyCodeValue = (KeyCode) EditorGUILayout.EnumPopup( property.keyCodeValue );
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else if(property.type.SystemType == typeof(VRCSDK2.VRC_EventHandler.VrcBroadcastType))
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField( property.name );
|
||||
property.broadcastValue = (VRCSDK2.VRC_EventHandler.VrcBroadcastType) EditorGUILayout.EnumPopup( property.broadcastValue );
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else if(property.type.SystemType == typeof(VRCSDK2.VRCPlayerModFactory.HealthOnDeathAction))
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField( property.name );
|
||||
property.onDeathActionValue = (VRCSDK2.VRCPlayerModFactory.HealthOnDeathAction) EditorGUILayout.EnumPopup( property.onDeathActionValue);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
else if(property.type.SystemType == typeof(RuntimeAnimatorController))
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField( property.name );
|
||||
property.animationController = (RuntimeAnimatorController) EditorGUILayout.ObjectField( property.animationController, typeof( RuntimeAnimatorController ), false );
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
return property;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 792e7964a56e51f4188e1221751642e9
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,46 @@
|
||||
#if VRC_SDK_VRCSDK2
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_Station))]
|
||||
public class VRCPlayerStationEditor : Editor
|
||||
{
|
||||
VRCSDK2.VRC_Station myTarget;
|
||||
|
||||
SerializedProperty onRemoteEnter;
|
||||
SerializedProperty onRemoteExit;
|
||||
SerializedProperty onLocalEnter;
|
||||
SerializedProperty onLocalExit;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if(myTarget == null)
|
||||
myTarget = (VRCSDK2.VRC_Station)target;
|
||||
onRemoteEnter = serializedObject.FindProperty("OnRemotePlayerEnterStation");
|
||||
onRemoteExit = serializedObject.FindProperty("OnRemotePlayerExitStation");
|
||||
onLocalEnter = serializedObject.FindProperty("OnLocalPlayerEnterStation");
|
||||
onLocalExit = serializedObject.FindProperty("OnLocalPlayerExitStation");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
myTarget.PlayerMobility = (VRC.SDKBase.VRCStation.Mobility)EditorGUILayout.EnumPopup("Player Mobility", myTarget.PlayerMobility);
|
||||
myTarget.canUseStationFromStation = EditorGUILayout.Toggle("Can Use Station From Station", myTarget.canUseStationFromStation);
|
||||
myTarget.animatorController = (RuntimeAnimatorController)EditorGUILayout.ObjectField("Animator Controller", myTarget.animatorController, typeof(RuntimeAnimatorController), false);
|
||||
myTarget.disableStationExit = EditorGUILayout.Toggle("Disable Station Exit", myTarget.disableStationExit);
|
||||
myTarget.seated = EditorGUILayout.Toggle("Seated", myTarget.seated);
|
||||
myTarget.stationEnterPlayerLocation = (Transform)EditorGUILayout.ObjectField("Player Enter Location", myTarget.stationEnterPlayerLocation, typeof(Transform), true);
|
||||
myTarget.stationExitPlayerLocation = (Transform)EditorGUILayout.ObjectField("Player Exit Location", myTarget.stationExitPlayerLocation, typeof(Transform), true);
|
||||
myTarget.controlsObject = (VRC.SDKBase.VRC_ObjectApi)EditorGUILayout.ObjectField("API Object", myTarget.controlsObject, typeof(VRC.SDKBase.VRC_ObjectApi), false);
|
||||
|
||||
EditorGUILayout.PropertyField(onRemoteEnter, new GUIContent("On Remote Player Enter"));
|
||||
EditorGUILayout.PropertyField(onRemoteExit, new GUIContent("On Remote Player Exit"));
|
||||
EditorGUILayout.PropertyField(onLocalEnter, new GUIContent("On Local Player Enter"));
|
||||
EditorGUILayout.PropertyField(onLocalExit, new GUIContent("On Local Player Exit"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5262a02c32e41e047bdfdfc3b63db8ff
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,29 @@
|
||||
#if VRC_SDK_VRCSDK2
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
[CustomEditor (typeof(VRCSDK2.VRC_SceneDescriptor))]
|
||||
public class VRCSceneDescriptorEditor : Editor
|
||||
{
|
||||
VRCSDK2.VRC_SceneDescriptor sceneDescriptor;
|
||||
VRC.Core.PipelineManager pipelineManager;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
if(sceneDescriptor == null)
|
||||
sceneDescriptor = (VRCSDK2.VRC_SceneDescriptor)target;
|
||||
|
||||
if(pipelineManager == null)
|
||||
{
|
||||
pipelineManager = sceneDescriptor.GetComponent<VRC.Core.PipelineManager>();
|
||||
if(pipelineManager == null)
|
||||
sceneDescriptor.gameObject.AddComponent<VRC.Core.PipelineManager>();
|
||||
}
|
||||
|
||||
DrawDefaultInspector();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9cbc493bbbc443fb92898aa84d221ec
|
||||
timeCreated: 1450463561
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,130 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
//[CustomPropertyDrawer(typeof(VRC_AvatarVariations.VariationCategory))]
|
||||
//public class PropertyDrawer_AvatarVariation_VariationCategory : PropertyDrawer
|
||||
//{
|
||||
// public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
|
||||
// {
|
||||
// //EditorGUILayout.Label("blah");
|
||||
|
||||
// if (property == null)
|
||||
// return;
|
||||
|
||||
// SerializedProperty nameProperty = property.FindPropertyRelative("name");
|
||||
// //SerializedProperty mirrorProperty = property.FindPropertyRelative("mirror");
|
||||
// //SerializedProperty typeProperty = property.FindPropertyRelative("type");
|
||||
// //SerializedProperty valueProperty = null;
|
||||
// //switch (typeProperty.enumValueIndex)
|
||||
// //{
|
||||
// // case (int)VRC_DataStorage.VrcDataType.Bool:
|
||||
// // valueProperty = property.FindPropertyRelative("valueBool");
|
||||
// // break;
|
||||
// // case (int)VRC_DataStorage.VrcDataType.Float:
|
||||
// // valueProperty = property.FindPropertyRelative("valueFloat");
|
||||
// // break;
|
||||
// // case (int)VRC_DataStorage.VrcDataType.Int:
|
||||
// // valueProperty = property.FindPropertyRelative("valueInt");
|
||||
// // break;
|
||||
// // case (int)VRC_DataStorage.VrcDataType.String:
|
||||
// // valueProperty = property.FindPropertyRelative("valueString");
|
||||
// // break;
|
||||
// // case (int)VRC_DataStorage.VrcDataType.SerializeObject:
|
||||
// // valueProperty = property.FindPropertyRelative("serializeComponent");
|
||||
// // break;
|
||||
// // case (int)VRC_DataStorage.VrcDataType.None:
|
||||
// // case (int)VRC_DataStorage.VrcDataType.SerializeBytes:
|
||||
// // break;
|
||||
// //}
|
||||
|
||||
// EditorGUI.BeginProperty(rect, label, property);
|
||||
|
||||
// int baseWidth = (int)(rect.width / 4);
|
||||
// Rect nameRect = new Rect(rect.x, rect.y, baseWidth, rect.height);
|
||||
// //Rect mirrorRect = new Rect(rect.x + baseWidth, rect.y, baseWidth, rect.height);
|
||||
// //Rect typeRect = new Rect(rect.x + baseWidth * 2, rect.y, baseWidth, rect.height);
|
||||
// //Rect valueRect = new Rect(rect.x + baseWidth * 3, rect.y, baseWidth, rect.height);
|
||||
// //Rect typeValueRect = new Rect(rect.x + baseWidth * 2, rect.y, baseWidth * 2, rect.height);
|
||||
|
||||
// EditorGUI.PropertyField(nameRect, nameProperty, GUIContent.none);
|
||||
// //EditorGUI.PropertyField(mirrorRect, mirrorProperty, GUIContent.none);
|
||||
|
||||
// //switch (mirrorProperty.enumValueIndex)
|
||||
// //{
|
||||
// // case (int)VRC_DataStorage.VrcDataMirror.None:
|
||||
// // if (valueProperty == null)
|
||||
// // VRC_EditorTools.FilteredEnumPopup<VRC_DataStorage.VrcDataType>(typeValueRect, typeProperty, t => true);
|
||||
// // else
|
||||
// // {
|
||||
// // VRC_EditorTools.FilteredEnumPopup<VRC_DataStorage.VrcDataType>(typeRect, typeProperty, t => true);
|
||||
// // EditorGUI.PropertyField(valueRect, valueProperty, GUIContent.none);
|
||||
// // }
|
||||
// // break;
|
||||
// // case (int)VRC_DataStorage.VrcDataMirror.SerializeComponent:
|
||||
// // typeProperty.enumValueIndex = (int)VRC_DataStorage.VrcDataType.SerializeObject;
|
||||
// // EditorGUI.PropertyField(typeValueRect, valueProperty, GUIContent.none);
|
||||
// // break;
|
||||
// // default:
|
||||
// // VRC_EditorTools.FilteredEnumPopup<VRC_DataStorage.VrcDataType>(typeValueRect, typeProperty, t => true);
|
||||
// // break;
|
||||
// //}
|
||||
|
||||
// EditorGUI.EndProperty();
|
||||
// }
|
||||
//}
|
||||
|
||||
//[CustomEditor(typeof(VRC_AvatarVariations))]
|
||||
//public class VRC_AvatarVariationsEditor : Editor
|
||||
//{
|
||||
// SerializedProperty categories;
|
||||
|
||||
// void OnEnable()
|
||||
// {
|
||||
// categories = serializedObject.FindProperty("categories");
|
||||
// }
|
||||
|
||||
// public override void OnInspectorGUI()
|
||||
// {
|
||||
// //serializedObject.Update();
|
||||
// // EditorGUILayout.PropertyField(categories);
|
||||
// //serializedObject.ApplyModifiedProperties();
|
||||
|
||||
|
||||
|
||||
// //if (target == null)
|
||||
// // return;
|
||||
|
||||
// ////var prop = serializedObject.FindProperty("root");
|
||||
// ////EditorGUILayout.PropertyField(prop, new GUIContent("Show Help"));
|
||||
// //VRCSDK2.VRC_AvatarVariations variations = target as VRCSDK2.VRC_AvatarVariations;
|
||||
// //if (variations.categories == null)
|
||||
// // variations.categories = new VRC_AvatarVariations.VariationCategory[0];
|
||||
|
||||
// //foreach ( var vc in variations.categories )
|
||||
// //{
|
||||
// // vc.name = EditorGUILayout.TextField("Variation Name", vc.name);
|
||||
// //// SerializedProperty triggers = triggersProperty.Copy();
|
||||
// //// int triggersLength = triggers.arraySize;
|
||||
|
||||
// //// List<int> to_remove = new List<int>();
|
||||
// //// for (int idx = 0; idx < triggersLength; ++idx)
|
||||
// //// {
|
||||
// //// SerializedProperty triggerProperty = triggers.GetArrayElementAtIndex(idx);
|
||||
// //// }
|
||||
|
||||
// //// EditorGUILayout.LabelField("");
|
||||
// ////// helpProperty = serializedObject.FindProperty("ShowHelp");
|
||||
// ////// EditorGUILayout.PropertyField(helpProperty, new GUIContent("Show Help"));
|
||||
// //}
|
||||
|
||||
// ////EditorGUILayout.
|
||||
|
||||
// DrawDefaultInspector();
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eeda995d0ceac6443a54716996eab52e
|
||||
timeCreated: 1511373338
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,91 @@
|
||||
#if VRC_SDK_VRCSDK2 && UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using VRC.SDKBase;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(VRCSDK2.VRC_DataStorage.VrcDataElement))]
|
||||
public class CustomDataElementDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
if (property == null)
|
||||
return;
|
||||
|
||||
SerializedProperty nameProperty = property.FindPropertyRelative("name");
|
||||
SerializedProperty mirrorProperty = property.FindPropertyRelative("mirror");
|
||||
SerializedProperty typeProperty = property.FindPropertyRelative("type");
|
||||
SerializedProperty valueProperty = null;
|
||||
switch (typeProperty.enumValueIndex)
|
||||
{
|
||||
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.Bool:
|
||||
valueProperty = property.FindPropertyRelative("valueBool");
|
||||
break;
|
||||
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.Float:
|
||||
valueProperty = property.FindPropertyRelative("valueFloat");
|
||||
break;
|
||||
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.Int:
|
||||
valueProperty = property.FindPropertyRelative("valueInt");
|
||||
break;
|
||||
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.String:
|
||||
valueProperty = property.FindPropertyRelative("valueString");
|
||||
break;
|
||||
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.SerializeObject:
|
||||
valueProperty = property.FindPropertyRelative("serializeComponent");
|
||||
break;
|
||||
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.None:
|
||||
case (int)VRCSDK2.VRC_DataStorage.VrcDataType.SerializeBytes:
|
||||
break;
|
||||
}
|
||||
|
||||
EditorGUI.BeginProperty(rect, label, property);
|
||||
|
||||
int baseWidth = (int)(rect.width / 4);
|
||||
Rect nameRect = new Rect(rect.x, rect.y, baseWidth, rect.height);
|
||||
Rect mirrorRect = new Rect(rect.x + baseWidth, rect.y, baseWidth, rect.height);
|
||||
Rect typeRect = new Rect(rect.x + baseWidth * 2, rect.y, baseWidth, rect.height);
|
||||
Rect valueRect = new Rect(rect.x + baseWidth * 3, rect.y, baseWidth, rect.height);
|
||||
Rect typeValueRect = new Rect(rect.x + baseWidth * 2, rect.y, baseWidth * 2, rect.height);
|
||||
|
||||
EditorGUI.PropertyField(nameRect, nameProperty, GUIContent.none);
|
||||
EditorGUI.PropertyField(mirrorRect, mirrorProperty, GUIContent.none);
|
||||
|
||||
switch (mirrorProperty.enumValueIndex)
|
||||
{
|
||||
case (int)VRCSDK2.VRC_DataStorage.VrcDataMirror.None:
|
||||
if (valueProperty == null)
|
||||
VRC_EditorTools.FilteredEnumPopup<VRCSDK2.VRC_DataStorage.VrcDataType>(typeValueRect, typeProperty, t => true);
|
||||
else
|
||||
{
|
||||
VRC_EditorTools.FilteredEnumPopup<VRCSDK2.VRC_DataStorage.VrcDataType>(typeRect, typeProperty, t => true);
|
||||
EditorGUI.PropertyField(valueRect, valueProperty, GUIContent.none);
|
||||
}
|
||||
break;
|
||||
case (int)VRCSDK2.VRC_DataStorage.VrcDataMirror.SerializeComponent:
|
||||
typeProperty.enumValueIndex = (int)VRCSDK2.VRC_DataStorage.VrcDataType.SerializeObject;
|
||||
EditorGUI.PropertyField(typeValueRect, valueProperty, GUIContent.none);
|
||||
break;
|
||||
default:
|
||||
VRC_EditorTools.FilteredEnumPopup<VRCSDK2.VRC_DataStorage.VrcDataType>(typeValueRect, typeProperty, t => true);
|
||||
break;
|
||||
}
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_DataStorage)), CanEditMultipleObjects]
|
||||
public class VRC_DataStorageEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
VRCSDK2.VRC_ObjectSync os = ((VRCSDK2.VRC_DataStorage)target).GetComponent<VRCSDK2.VRC_ObjectSync>();
|
||||
if (os != null && os.SynchronizePhysics)
|
||||
EditorGUILayout.HelpBox("Consider either removing the VRC_ObjectSync or disabling SynchronizePhysics.", MessageType.Warning);
|
||||
|
||||
DrawDefaultInspector();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ac7998a36f085844847acbc046d4e27
|
||||
timeCreated: 1478191469
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,57 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using VRC_DestructibleStandard = VRC.SDKBase.VRC_DestructibleStandard;
|
||||
using VRC.SDKBase;
|
||||
|
||||
[CustomEditor(typeof(VRC_DestructibleStandard))]
|
||||
[CanEditMultipleObjects]
|
||||
public class VRC_DestructibleStandardEditor : Editor
|
||||
{
|
||||
VRC_DestructibleStandard ds;
|
||||
|
||||
SerializedProperty maxHealth;
|
||||
SerializedProperty currentHealth;
|
||||
SerializedProperty healable;
|
||||
SerializedProperty onDamagedTrigger;
|
||||
SerializedProperty onDestroyedTrigger;
|
||||
SerializedProperty onHealedTrigger;
|
||||
SerializedProperty onFullHealedTrigger;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
maxHealth = serializedObject.FindProperty("maxHealth");
|
||||
currentHealth = serializedObject.FindProperty("currentHealth");
|
||||
healable = serializedObject.FindProperty("healable");
|
||||
onDamagedTrigger = serializedObject.FindProperty("onDamagedTrigger");
|
||||
onDestroyedTrigger = serializedObject.FindProperty("onDestructedTrigger");
|
||||
onHealedTrigger = serializedObject.FindProperty("onHealedTrigger");
|
||||
onFullHealedTrigger = serializedObject.FindProperty("onFullHealedTrigger");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
ds = (VRC_DestructibleStandard)target;
|
||||
|
||||
// Update the serializedProperty - always do this in the beginning of OnInspectorGUI.
|
||||
serializedObject.Update ();
|
||||
|
||||
EditorGUILayout.PropertyField(maxHealth, new GUIContent("Max Health"));
|
||||
EditorGUILayout.PropertyField(currentHealth, new GUIContent("Current Health"));
|
||||
EditorGUILayout.PropertyField(healable, new GUIContent("Is Healable"));
|
||||
|
||||
EditorGUILayout.PropertyField(onDamagedTrigger, new GUIContent("On Damaged Trigger"));
|
||||
VRC_EditorTools.DrawTriggerActionCallback("On Damaged Action", ds.onDamagedTrigger, ds.onDamagedEvent);
|
||||
|
||||
EditorGUILayout.PropertyField(onDestroyedTrigger, new GUIContent("On Destructed Trigger"));
|
||||
VRC_EditorTools.DrawTriggerActionCallback("On Destructed Action", ds.onDestructedTrigger, ds.onDestructedEvent);
|
||||
|
||||
EditorGUILayout.PropertyField(onHealedTrigger, new GUIContent("On Healed Trigger"));
|
||||
VRC_EditorTools.DrawTriggerActionCallback("On Healed Action", ds.onHealedTrigger, ds.onHealedEvent);
|
||||
|
||||
EditorGUILayout.PropertyField(onFullHealedTrigger, new GUIContent("On Full Healed Trigger"));
|
||||
VRC_EditorTools.DrawTriggerActionCallback("On Full Healed Action", ds.onFullHealedTrigger, ds.onFullHealedEvent);
|
||||
|
||||
// Apply changes to the serializedProperty - always do this in the end of OnInspectorGUI.
|
||||
serializedObject.ApplyModifiedProperties ();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b63b118c0591b548ba1797e6be4292e
|
||||
timeCreated: 1477161996
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,19 @@
|
||||
#if VRC_SDK_VRCSDK2
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_ObjectSync))]
|
||||
public class VRC_ObjectSyncEditor : Editor {
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
VRCSDK2.VRC_ObjectSync c = ((VRCSDK2.VRC_ObjectSync)target);
|
||||
if ((c.gameObject.GetComponent<Animator>() != null || c.gameObject.GetComponent<Animation>() != null) && c.SynchronizePhysics)
|
||||
EditorGUILayout.HelpBox("If the Animator or Animation moves the root position of this object then it will conflict with physics synchronization.", MessageType.Warning);
|
||||
if (c.GetComponent<VRCSDK2.VRC_DataStorage>() != null && c.SynchronizePhysics)
|
||||
EditorGUILayout.HelpBox("Consider either removing the VRC_DataStorage or disabling SynchronizePhysics.", MessageType.Warning);
|
||||
DrawDefaultInspector();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e19a7147a2386554a8e4d6e414f190a2
|
||||
timeCreated: 1504908295
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,70 @@
|
||||
#if VRC_SDK_VRCSDK2 && UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_Pickup))]
|
||||
public class VRC_PickupEditor : UnityEditor.Editor
|
||||
{
|
||||
private void InspectorField(string propertyName, string humanName)
|
||||
{
|
||||
SerializedProperty propertyField = serializedObject.FindProperty(propertyName);
|
||||
EditorGUILayout.PropertyField(propertyField, new GUIContent(humanName), true);
|
||||
}
|
||||
|
||||
private SerializedProperty momentumTransferMethodProperty;
|
||||
private SerializedProperty disallowTheftProperty;
|
||||
private SerializedProperty exactGunProperty;
|
||||
private SerializedProperty exactGripProperty;
|
||||
private SerializedProperty allowManipulationWhenEquippedProperty;
|
||||
private SerializedProperty orientationProperty;
|
||||
private SerializedProperty autoHoldProperty;
|
||||
private SerializedProperty interactionTextProperty;
|
||||
private SerializedProperty useTextProperty;
|
||||
private SerializedProperty throwVelocityBoostMinSpeedProperty;
|
||||
private SerializedProperty throwVelocityBoostScaleProperty;
|
||||
private SerializedProperty pickupableProperty;
|
||||
private SerializedProperty proximityProperty;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
momentumTransferMethodProperty = serializedObject.FindProperty("MomentumTransferMethod");
|
||||
disallowTheftProperty = serializedObject.FindProperty("DisallowTheft");
|
||||
exactGunProperty = serializedObject.FindProperty("ExactGun");
|
||||
exactGripProperty = serializedObject.FindProperty("ExactGrip");
|
||||
allowManipulationWhenEquippedProperty = serializedObject.FindProperty("allowManipulationWhenEquipped");
|
||||
orientationProperty = serializedObject.FindProperty("orientation");
|
||||
autoHoldProperty = serializedObject.FindProperty("AutoHold");
|
||||
interactionTextProperty = serializedObject.FindProperty("InteractionText");
|
||||
useTextProperty = serializedObject.FindProperty("UseText");
|
||||
throwVelocityBoostMinSpeedProperty = serializedObject.FindProperty("ThrowVelocityBoostMinSpeed");
|
||||
throwVelocityBoostScaleProperty = serializedObject.FindProperty("ThrowVelocityBoostScale");
|
||||
pickupableProperty = serializedObject.FindProperty("pickupable");
|
||||
proximityProperty = serializedObject.FindProperty("proximity");
|
||||
|
||||
EditorGUILayout.BeginVertical(GUILayout.MaxWidth(EditorGUIUtility.currentViewWidth - 30));
|
||||
|
||||
EditorGUILayout.PropertyField(momentumTransferMethodProperty, new GUIContent("Momentum Transfer Method"));
|
||||
EditorGUILayout.PropertyField(disallowTheftProperty, new GUIContent("Disallow Theft"));
|
||||
EditorGUILayout.PropertyField(exactGunProperty, new GUIContent("Exact Gun"));
|
||||
EditorGUILayout.PropertyField(exactGripProperty, new GUIContent("Exact Grip"));
|
||||
EditorGUILayout.PropertyField(allowManipulationWhenEquippedProperty, new GUIContent("Allow Manipulation When Equipped"));
|
||||
EditorGUILayout.PropertyField(orientationProperty, new GUIContent("Orientation"));
|
||||
EditorGUILayout.PropertyField(autoHoldProperty, new GUIContent("AutoHold", "If the pickup is supposed to be aligned to the hand (i.e. orientation field is set to Gun or Grip), auto-detect means that it will be Equipped(not dropped when they release trigger), otherwise just hold as a normal pickup."));
|
||||
EditorGUILayout.PropertyField(interactionTextProperty, new GUIContent("Interaction Text","Text displayed when user hovers over the pickup."));
|
||||
if (autoHoldProperty.enumValueIndex != (int)VRCSDK2.VRC_Pickup.AutoHoldMode.No)
|
||||
EditorGUILayout.PropertyField(useTextProperty, new GUIContent("Use Text", "Text to display describing action for clicking button, when this pickup is already being held."));
|
||||
EditorGUILayout.PropertyField(throwVelocityBoostMinSpeedProperty, new GUIContent("Throw Velocity Boost Min Speed"));
|
||||
EditorGUILayout.PropertyField(throwVelocityBoostScaleProperty, new GUIContent("Throw Velocity Boost Scale"));
|
||||
EditorGUILayout.PropertyField(pickupableProperty, new GUIContent("Pickupable"));
|
||||
EditorGUILayout.PropertyField(proximityProperty, new GUIContent("Proximity"));
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4aff4e5c0d600c845b29d7b8b7965d68
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,106 @@
|
||||
#if VRC_SDK_VRCSDK2
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_PlayerAudioOverride))]
|
||||
public class VRC_PlayerAudioOverrideEditor : UnityEditor.Editor
|
||||
{
|
||||
private bool voShow = true;
|
||||
private bool voAdv = false;
|
||||
private bool avShow = true;
|
||||
private bool avAdv = false;
|
||||
private SerializedProperty prioProperty;
|
||||
private SerializedProperty globalProperty;
|
||||
private SerializedProperty regionProperty;
|
||||
private SerializedProperty voGainProperty;
|
||||
private SerializedProperty voNearProperty;
|
||||
private SerializedProperty voFarProperty;
|
||||
private SerializedProperty voRadiusProperty;
|
||||
private SerializedProperty voDisableLpProperty;
|
||||
private SerializedProperty avGainProperty;
|
||||
private SerializedProperty avNearProperty;
|
||||
private SerializedProperty avFarProperty;
|
||||
private SerializedProperty avRadiusProperty;
|
||||
private SerializedProperty avForceSpatialProperty;
|
||||
private SerializedProperty avAllowCustomProperty;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
globalProperty = serializedObject.FindProperty("global");
|
||||
regionProperty = serializedObject.FindProperty("region");
|
||||
prioProperty = serializedObject.FindProperty("regionPriority");
|
||||
|
||||
voGainProperty = serializedObject.FindProperty("VoiceGain");
|
||||
voNearProperty = serializedObject.FindProperty("VoiceNear");
|
||||
voFarProperty = serializedObject.FindProperty("VoiceFar");
|
||||
voRadiusProperty = serializedObject.FindProperty("VoiceVolumetricRadius");
|
||||
voDisableLpProperty = serializedObject.FindProperty("VoiceDisableLowpass");
|
||||
|
||||
avGainProperty = serializedObject.FindProperty("AvatarGainLimit");
|
||||
avNearProperty = serializedObject.FindProperty("AvatarNearLimit");
|
||||
avFarProperty = serializedObject.FindProperty("AvatarFarLimit");
|
||||
avRadiusProperty = serializedObject.FindProperty("AvatarVolumetricRadiusLimit");
|
||||
avForceSpatialProperty = serializedObject.FindProperty("AvatarForceSpatial");
|
||||
avAllowCustomProperty = serializedObject.FindProperty("AvatarAllowCustomCurve");
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
var ovr = serializedObject.targetObject as VRCSDK2.VRC_PlayerAudioOverride;
|
||||
|
||||
EditorGUILayout.PropertyField(globalProperty, new GUIContent("Global"));
|
||||
if (!ovr.global)
|
||||
{
|
||||
EditorGUILayout.PropertyField(regionProperty, new GUIContent("Region"));
|
||||
EditorGUILayout.PropertyField(prioProperty, new GUIContent("Priority"));
|
||||
}
|
||||
|
||||
voShow = EditorGUILayout.Foldout(voShow, "Voice Settings");
|
||||
|
||||
if (voShow)
|
||||
{
|
||||
EditorGUILayout.PropertyField(voGainProperty, new GUIContent("Gain"));
|
||||
EditorGUILayout.PropertyField(voFarProperty, new GUIContent("Far"));
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
voAdv = EditorGUILayout.Foldout(voAdv, "Advanced Options");
|
||||
if (voAdv)
|
||||
{
|
||||
EditorGUILayout.PropertyField(voNearProperty, new GUIContent("Near"));
|
||||
EditorGUILayout.PropertyField(voRadiusProperty, new GUIContent("Volumetric Radius"));
|
||||
EditorGUILayout.PropertyField(voDisableLpProperty, new GUIContent("Disable Lowpass Filter"));
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
avShow = EditorGUILayout.Foldout(avShow, "Avatar Audio Limits");
|
||||
|
||||
if (avShow)
|
||||
{
|
||||
EditorGUILayout.PropertyField(avGainProperty, new GUIContent("Gain Limit"));
|
||||
EditorGUILayout.PropertyField(avFarProperty, new GUIContent("Far Limit"));
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
avAdv = EditorGUILayout.Foldout(avAdv, "Advanced Options");
|
||||
if (avAdv)
|
||||
{
|
||||
EditorGUILayout.PropertyField(avNearProperty, new GUIContent("Near Limit"));
|
||||
EditorGUILayout.PropertyField(avRadiusProperty, new GUIContent("Volumetric Radius Limit"));
|
||||
EditorGUILayout.PropertyField(avForceSpatialProperty, new GUIContent("Force Spatial"));
|
||||
EditorGUILayout.PropertyField(avAllowCustomProperty, new GUIContent("Allow Custom Curve"));
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c545625e0bf93045ac1c5864141c5c1
|
||||
timeCreated: 1474315179
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,58 @@
|
||||
#if VRC_SDK_VRCSDK2 && UNITY_EDITOR
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_SpatialAudioSource))]
|
||||
public class VRC_SpatialAudioSourceEditor : UnityEditor.Editor
|
||||
{
|
||||
private bool showAdvancedOptions = false;
|
||||
private SerializedProperty gainProperty;
|
||||
private SerializedProperty nearProperty;
|
||||
private SerializedProperty farProperty;
|
||||
private SerializedProperty volRadiusProperty;
|
||||
private SerializedProperty enableSpatialProperty;
|
||||
private SerializedProperty useCurveProperty;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
gainProperty = serializedObject.FindProperty("Gain");
|
||||
nearProperty = serializedObject.FindProperty("Near");
|
||||
farProperty = serializedObject.FindProperty("Far");
|
||||
volRadiusProperty = serializedObject.FindProperty("VolumetricRadius");
|
||||
enableSpatialProperty = serializedObject.FindProperty("EnableSpatialization");
|
||||
useCurveProperty = serializedObject.FindProperty("UseAudioSourceVolumeCurve");
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
VRCSDK2.VRC_SpatialAudioSource target = serializedObject.targetObject as VRCSDK2.VRC_SpatialAudioSource;
|
||||
AudioSource source = target.GetComponent<AudioSource>();
|
||||
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
EditorGUILayout.PropertyField(gainProperty, new GUIContent("Gain"));
|
||||
EditorGUILayout.PropertyField(farProperty, new GUIContent("Far"));
|
||||
showAdvancedOptions = EditorGUILayout.Foldout(showAdvancedOptions, "Advanced Options");
|
||||
bool enableSp = enableSpatialProperty.boolValue;
|
||||
if (showAdvancedOptions)
|
||||
{
|
||||
EditorGUILayout.PropertyField(nearProperty, new GUIContent("Near"));
|
||||
EditorGUILayout.PropertyField(volRadiusProperty, new GUIContent("Volumetric Radius"));
|
||||
EditorGUILayout.PropertyField(enableSpatialProperty, new GUIContent("Enable Spatialization"));
|
||||
if (enableSp)
|
||||
EditorGUILayout.PropertyField(useCurveProperty, new GUIContent("Use AudioSource Volume Curve"));
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
if (source != null)
|
||||
source.spatialize = enableSp;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d2d4cba733f5eb4ba170368e67710d2
|
||||
timeCreated: 1474315179
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,105 @@
|
||||
#if VRC_SDK_VRCSDK2
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using VRC.SDKBase;
|
||||
|
||||
[CustomPropertyDrawer(typeof(VRCSDK2.VRC_SyncVideoPlayer.VideoEntry))]
|
||||
public class CustomVideoEntryDrawer : PropertyDrawer
|
||||
{
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
SerializedProperty source = property.FindPropertyRelative("Source");
|
||||
SerializedProperty ratio = property.FindPropertyRelative("AspectRatio");
|
||||
SerializedProperty speed = property.FindPropertyRelative("PlaybackSpeed");
|
||||
SerializedProperty clip = property.FindPropertyRelative("VideoClip");
|
||||
SerializedProperty url = property.FindPropertyRelative("URL");
|
||||
|
||||
return EditorGUI.GetPropertyHeight(source, new GUIContent("Source"), true) + EditorGUIUtility.standardVerticalSpacing
|
||||
+ EditorGUI.GetPropertyHeight(ratio, new GUIContent("Aspect Ratio"), true) + EditorGUIUtility.standardVerticalSpacing
|
||||
+ EditorGUI.GetPropertyHeight(speed, new GUIContent("Playback Speed"), true) + EditorGUIUtility.standardVerticalSpacing
|
||||
+ Mathf.Max(EditorGUI.GetPropertyHeight(clip, new GUIContent("VideoClip"), true), EditorGUI.GetPropertyHeight(url, new GUIContent("URL"), true)) + EditorGUIUtility.standardVerticalSpacing;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
SerializedProperty source = property.FindPropertyRelative("Source");
|
||||
SerializedProperty ratio = property.FindPropertyRelative("AspectRatio");
|
||||
SerializedProperty speed = property.FindPropertyRelative("PlaybackSpeed");
|
||||
SerializedProperty clip = property.FindPropertyRelative("VideoClip");
|
||||
SerializedProperty url = property.FindPropertyRelative("URL");
|
||||
|
||||
EditorGUI.BeginProperty(rect, label, property);
|
||||
float x = rect.x;
|
||||
float y = rect.y;
|
||||
float w = rect.width;
|
||||
float h = EditorGUI.GetPropertyHeight(source, new GUIContent("Source"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
VRC_EditorTools.FilteredEnumPopup<UnityEngine.Video.VideoSource>(new Rect(x, y, w, h), source, (e) => e == UnityEngine.Video.VideoSource.Url);
|
||||
y += h;
|
||||
|
||||
if (source.enumValueIndex == (int)UnityEngine.Video.VideoSource.Url)
|
||||
{
|
||||
h = EditorGUI.GetPropertyHeight(url, new GUIContent("URL"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
EditorGUI.PropertyField(new Rect(x, y, w, h), url);
|
||||
y += h;
|
||||
}
|
||||
else
|
||||
{
|
||||
h = EditorGUI.GetPropertyHeight(clip, new GUIContent("VideoClip"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
EditorGUI.PropertyField(new Rect(x, y, w, h), clip);
|
||||
y += h;
|
||||
}
|
||||
|
||||
h = EditorGUI.GetPropertyHeight(ratio, new GUIContent("AspectRatio"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
EditorGUI.PropertyField(new Rect(x, y, w, h), ratio);
|
||||
y += h;
|
||||
|
||||
h = EditorGUI.GetPropertyHeight(ratio, new GUIContent("Playback Speed"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
EditorGUI.PropertyField(new Rect(x, y, w, h), speed);
|
||||
if (speed.floatValue == 0f)
|
||||
speed.floatValue = 1f;
|
||||
y += h;
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_SyncVideoPlayer))]
|
||||
public class SyncVideoPlayerEditor : Editor
|
||||
{
|
||||
ReorderableList sourceList;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
SerializedProperty searchRoot = serializedObject.FindProperty("VideoSearchRoot");
|
||||
EditorGUILayout.PropertyField(searchRoot);
|
||||
SerializedProperty maxQual = serializedObject.FindProperty("MaxStreamQuality");
|
||||
EditorGUILayout.PropertyField(maxQual);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
sourceList.DoLayoutList();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
SerializedProperty videos = serializedObject.FindProperty("Videos");
|
||||
sourceList = new ReorderableList(serializedObject, videos);
|
||||
sourceList.drawElementCallback += (Rect rect, int index, bool active, bool focused) =>
|
||||
{
|
||||
EditorGUI.PropertyField(rect, serializedObject.FindProperty("Videos").GetArrayElementAtIndex(index));
|
||||
};
|
||||
sourceList.elementHeightCallback += (int index) =>
|
||||
{
|
||||
SerializedProperty element = serializedObject.FindProperty("Videos").GetArrayElementAtIndex(index);
|
||||
return EditorGUI.GetPropertyHeight(element);
|
||||
};
|
||||
sourceList.drawHeaderCallback = (Rect rect) => EditorGUI.LabelField(rect, "Videos");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae0e74693b7899f47bd98864f94b9311
|
||||
timeCreated: 1499468412
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,117 @@
|
||||
#if VRC_SDK_VRCSDK2
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using VRC.SDKBase;
|
||||
|
||||
[CustomPropertyDrawer(typeof(VRCSDK2.VRC_SyncVideoStream.VideoEntry))]
|
||||
public class CustomVideoStreamEntryDrawer : PropertyDrawer
|
||||
{
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
||||
{
|
||||
SerializedProperty source = property.FindPropertyRelative("Source");
|
||||
SerializedProperty speed = property.FindPropertyRelative("PlaybackSpeed");
|
||||
SerializedProperty clip = property.FindPropertyRelative("VideoClip");
|
||||
SerializedProperty url = property.FindPropertyRelative("URL");
|
||||
SerializedProperty live = property.FindPropertyRelative("SyncType");
|
||||
SerializedProperty sync = property.FindPropertyRelative("SyncMinutes");
|
||||
|
||||
return EditorGUI.GetPropertyHeight(source, new GUIContent("Source"), true) + EditorGUIUtility.standardVerticalSpacing
|
||||
+ EditorGUI.GetPropertyHeight(speed, new GUIContent("Playback Speed"), true) + EditorGUIUtility.standardVerticalSpacing
|
||||
+ Mathf.Max(EditorGUI.GetPropertyHeight(clip, new GUIContent("VideoClip"), true), EditorGUI.GetPropertyHeight(url, new GUIContent("URL"), true)) + EditorGUIUtility.standardVerticalSpacing
|
||||
+ EditorGUI.GetPropertyHeight(live, new GUIContent("SyncType"), true) + EditorGUIUtility.standardVerticalSpacing
|
||||
+ EditorGUI.GetPropertyHeight(sync, new GUIContent("SyncMinutes"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
SerializedProperty source = property.FindPropertyRelative("Source");
|
||||
SerializedProperty speed = property.FindPropertyRelative("PlaybackSpeed");
|
||||
SerializedProperty clip = property.FindPropertyRelative("VideoClip");
|
||||
SerializedProperty url = property.FindPropertyRelative("URL");
|
||||
SerializedProperty live = property.FindPropertyRelative("SyncType");
|
||||
SerializedProperty sync = property.FindPropertyRelative("SyncMinutes");
|
||||
|
||||
EditorGUI.BeginProperty(rect, label, property);
|
||||
float x = rect.x;
|
||||
float y = rect.y;
|
||||
float w = rect.width;
|
||||
float h = EditorGUI.GetPropertyHeight(source, new GUIContent("Source"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
VRC_EditorTools.FilteredEnumPopup<UnityEngine.Video.VideoSource>(new Rect(x, y, w, h), source, (e) => e == UnityEngine.Video.VideoSource.Url);
|
||||
y += h;
|
||||
|
||||
if (source.enumValueIndex == (int)UnityEngine.Video.VideoSource.Url)
|
||||
{
|
||||
h = EditorGUI.GetPropertyHeight(url, new GUIContent("URL"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
EditorGUI.PropertyField(new Rect(x, y, w, h), url);
|
||||
y += h;
|
||||
}
|
||||
else
|
||||
{
|
||||
h = EditorGUI.GetPropertyHeight(clip, new GUIContent("VideoClip"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
EditorGUI.PropertyField(new Rect(x, y, w, h), clip);
|
||||
y += h;
|
||||
}
|
||||
|
||||
h = EditorGUI.GetPropertyHeight(speed, new GUIContent("Playback Speed"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
EditorGUI.PropertyField(new Rect(x, y, w, h), speed);
|
||||
if (speed.floatValue == 0f)
|
||||
speed.floatValue = 1f;
|
||||
y += h;
|
||||
|
||||
h = EditorGUI.GetPropertyHeight(live, new GUIContent("SyncType"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
EditorGUI.PropertyField(new Rect(x, y, w, h), live);
|
||||
y += h;
|
||||
|
||||
h = EditorGUI.GetPropertyHeight(sync, new GUIContent("SyncMinutes"), true) + EditorGUIUtility.standardVerticalSpacing;
|
||||
EditorGUI.PropertyField(new Rect(x, y, w, h), sync);
|
||||
if (sync.floatValue < 1f)
|
||||
sync.floatValue = 0;
|
||||
y += h;
|
||||
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_SyncVideoStream))]
|
||||
public class SyncVideoStreamEditor : Editor
|
||||
{
|
||||
ReorderableList sourceList;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
SerializedProperty searchRoot = serializedObject.FindProperty("VideoSearchRoot");
|
||||
EditorGUILayout.PropertyField(searchRoot);
|
||||
SerializedProperty maxQual = serializedObject.FindProperty("MaxStreamQuality");
|
||||
EditorGUILayout.PropertyField(maxQual);
|
||||
SerializedProperty autoStart = serializedObject.FindProperty("AutoStart");
|
||||
EditorGUILayout.PropertyField(autoStart);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
sourceList.DoLayoutList();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
SerializedProperty videos = serializedObject.FindProperty("Videos");
|
||||
sourceList = new ReorderableList(serializedObject, videos);
|
||||
sourceList.drawElementCallback += (Rect rect, int index, bool active, bool focused) =>
|
||||
{
|
||||
EditorGUI.PropertyField(rect, serializedObject.FindProperty("Videos").GetArrayElementAtIndex(index));
|
||||
};
|
||||
sourceList.elementHeightCallback += (int index) =>
|
||||
{
|
||||
SerializedProperty element = serializedObject.FindProperty("Videos").GetArrayElementAtIndex(index);
|
||||
return EditorGUI.GetPropertyHeight(element);
|
||||
};
|
||||
sourceList.drawHeaderCallback = (Rect rect) => EditorGUI.LabelField(rect, "Videos");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f9dccfed0b072f49a307b3f20a7e768
|
||||
timeCreated: 1528745185
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3aecd666943878944a811acb9db2ace7
|
||||
timeCreated: 1474315179
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,215 @@
|
||||
#if VRC_SDK_VRCSDK2 && UNITY_EDITOR
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Build;
|
||||
using System;
|
||||
using ZLinq;
|
||||
using VRC.SDKBase.Editor;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
[CustomEditor(typeof(VRCSDK2.VRC_WebPanel))]
|
||||
public class VRC_WebPanelEditor : UnityEditor.Editor
|
||||
{
|
||||
private void InspectorField(string propertyName, string humanName)
|
||||
{
|
||||
SerializedProperty propertyField = serializedObject.FindProperty(propertyName);
|
||||
EditorGUILayout.PropertyField(propertyField, new GUIContent(humanName), true);
|
||||
}
|
||||
|
||||
bool showFiles = false;
|
||||
System.Collections.Generic.List<string> directories = null;
|
||||
System.Collections.Generic.List<string> files = null;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
EditorGUILayout.HelpBox("Do not play any videos with Web Panels, use VRC_SyncVideoPlayer instead!", MessageType.Error);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
InspectorField("proximity", "Proximity for Interactivity");
|
||||
EditorGUILayout.Space();
|
||||
|
||||
VRCSDK2.VRC_WebPanel web = (VRCSDK2.VRC_WebPanel)target;
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
InspectorField("webRoot", "Web Root");
|
||||
InspectorField("defaultUrl", "URI");
|
||||
|
||||
showFiles = web.webData != null && EditorGUILayout.Foldout(showFiles, web.webData.Count.ToString() + " files imported");
|
||||
if (showFiles)
|
||||
foreach (var file in web.webData)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.PrefixLabel(file.path);
|
||||
EditorGUILayout.LabelField(file.data.Length.ToString());
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SerializedProperty webRoot = serializedObject.FindProperty("webRoot");
|
||||
RenderDirectoryList(serializedObject, "webRoot", "Path To Web Content");
|
||||
|
||||
if (string.IsNullOrEmpty(webRoot.stringValue))
|
||||
{
|
||||
InspectorField("defaultUrl", "Start URI");
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderWebRootSelector(serializedObject, "defaultUrl", "Start Page");
|
||||
|
||||
if (VRCSettings.DisplayHelpBoxes)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Javascript API bindings are called with engine.call('methodName', ...), which returns a promise-like object.", MessageType.Info);
|
||||
EditorGUILayout.HelpBox("Javascript may call ListBindings() to discover available API bindings.", MessageType.Info);
|
||||
EditorGUILayout.HelpBox("Javascript may listen for the 'onBindingsReady' event to execute script when the page is fully loaded and API bindings are available.", MessageType.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
InspectorField("cookiesEnabled", "Enable Cookies");
|
||||
|
||||
InspectorField("interactive", "Is Interactive");
|
||||
|
||||
InspectorField("localOnly", "Only Visible Locally");
|
||||
|
||||
if (!web.localOnly)
|
||||
{
|
||||
InspectorField("syncURI", "Synchronize URI");
|
||||
InspectorField("syncInput", "Synchronize Mouse Position");
|
||||
}
|
||||
|
||||
InspectorField("transparent", "Transparent Background");
|
||||
|
||||
InspectorField("autoFormSubmit", "Input should Submit Forms");
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
InspectorField("station", "Interaction Station");
|
||||
EditorGUILayout.Space();
|
||||
|
||||
InspectorField("cursor", "Mouse Cursor Object");
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
InspectorField("resolutionWidth", "Resolution Width");
|
||||
InspectorField("resolutionHeight", "Resolution Height");
|
||||
InspectorField("displayRegion", "Display Region");
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
InspectorField("extraVideoScreens", "Duplicate Screens");
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void AddSubDirectories(ref System.Collections.Generic.List<string> l, string root)
|
||||
{
|
||||
if (!Directory.Exists(root))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!root.StartsWith(Application.dataPath + Path.DirectorySeparatorChar + "VRCSDK")
|
||||
|| root == Application.dataPath + Path.DirectorySeparatorChar + "VRCSDK" + Path.DirectorySeparatorChar + "Examples" + Path.DirectorySeparatorChar + "Sample Assets" + Path.DirectorySeparatorChar + "WebRoot")
|
||||
l.Add(root.Substring(Application.dataPath.Length));
|
||||
|
||||
string[] subdirectories = Directory.GetDirectories(root);
|
||||
foreach (string dir in subdirectories)
|
||||
AddSubDirectories(ref l, dir);
|
||||
}
|
||||
|
||||
private void RenderDirectoryList(SerializedObject obj, string propertyName, string humanName)
|
||||
{
|
||||
if (directories == null)
|
||||
{
|
||||
directories = new System.Collections.Generic.List<string>();
|
||||
directories.Add("No Web Content Directory");
|
||||
|
||||
AddSubDirectories(ref directories, Application.dataPath + Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
SerializedProperty target = serializedObject.FindProperty(propertyName);
|
||||
|
||||
int selectedIdx = target.stringValue == null ? 0 : directories.IndexOf(target.stringValue);
|
||||
if (selectedIdx < 0 || selectedIdx >= directories.Count)
|
||||
selectedIdx = 0;
|
||||
|
||||
selectedIdx = EditorGUILayout.Popup(humanName, selectedIdx, directories.ToArray());
|
||||
if (selectedIdx > 0 && selectedIdx < directories.Count)
|
||||
target.stringValue = directories[selectedIdx];
|
||||
else
|
||||
target.stringValue = null;
|
||||
}
|
||||
|
||||
private void AddSubDirectoryFiles(ref System.Collections.Generic.List<string> l, string root)
|
||||
{
|
||||
if (!Directory.Exists(root))
|
||||
return;
|
||||
|
||||
string[] files = Directory.GetFiles(root);
|
||||
foreach (string file in files.Where(f => f.ToLower().EndsWith(".html") || f.ToLower().EndsWith(".htm")))
|
||||
l.Add(file.Substring(Application.dataPath.Length));
|
||||
|
||||
string[] subdirectories = Directory.GetDirectories(root);
|
||||
foreach (string dir in subdirectories)
|
||||
AddSubDirectoryFiles(ref l, dir);
|
||||
}
|
||||
|
||||
private void RenderWebRootSelector(SerializedObject obj, string propertyName, string humanName)
|
||||
{
|
||||
SerializedProperty webRoot = obj.FindProperty("webRoot");
|
||||
SerializedProperty target = serializedObject.FindProperty(propertyName);
|
||||
|
||||
if (files == null)
|
||||
{
|
||||
files = new System.Collections.Generic.List<string>();
|
||||
|
||||
AddSubDirectoryFiles(ref files, Application.dataPath + webRoot.stringValue);
|
||||
if (files.Count == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("No suitable html files found in Web Content path.", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int selectedIdx = 0;
|
||||
|
||||
try
|
||||
{
|
||||
System.Uri uri = string.IsNullOrEmpty(target.stringValue) ? null : new Uri(target.stringValue);
|
||||
|
||||
selectedIdx = uri == null ? 0 : files.IndexOf(uri.AbsolutePath.Replace('/', System.IO.Path.DirectorySeparatorChar));
|
||||
if (selectedIdx < 0 || selectedIdx >= files.Count)
|
||||
selectedIdx = 0;
|
||||
}
|
||||
catch { }
|
||||
|
||||
selectedIdx = EditorGUILayout.Popup(humanName, selectedIdx, files.ToArray());
|
||||
if (selectedIdx >= 0 && selectedIdx < files.Count)
|
||||
{
|
||||
System.UriBuilder builder = new UriBuilder()
|
||||
{
|
||||
Scheme = "file",
|
||||
Path = files[selectedIdx].Replace(System.IO.Path.DirectorySeparatorChar, '/'),
|
||||
Host = ""
|
||||
};
|
||||
target.stringValue = builder.Uri.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d09b36020f697be4d9a0f5a6a48cfa83
|
||||
timeCreated: 1457992191
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,20 @@
|
||||
#if UNITY_EDITOR && VRC_SDK_VRCSDK2
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
|
||||
namespace VRCSDK2
|
||||
{
|
||||
[CustomEditor(typeof(VRC_YouTubeSync))]
|
||||
public class VRC_YouTubeSyncEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.HelpBox("This component is deprecated, please use the VRC_SyncVideoPlayer component instead.", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 764e26c1ca28e2e45a30c778c1955a47
|
||||
timeCreated: 1474675311
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e751dcaa3656a324f91244e7b795d83a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,36 @@
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
// Common interface used by all builder panels inside their respective packages
|
||||
|
||||
/// <summary>
|
||||
/// This interface is reserved for SDK use, refer to Interfaces inside the Public SDK API folder for public APIs
|
||||
/// </summary>
|
||||
public interface IVRCSdkControlPanelBuilder
|
||||
{
|
||||
void Initialize();
|
||||
void ShowSettingsOptions();
|
||||
bool IsValidBuilder(out string message);
|
||||
void CreateBuilderErrorGUI(VisualElement root);
|
||||
|
||||
void CreateValidationsGUI(VisualElement root);
|
||||
|
||||
EventHandler OnContentChanged { get; set; }
|
||||
EventHandler OnShouldRevalidate { get; set; }
|
||||
|
||||
void RegisterBuilder(VRCSdkControlPanel baseBuilder);
|
||||
void SelectAllComponents();
|
||||
void CreateContentInfoGUI(VisualElement root);
|
||||
void CreateBuildGUI(VisualElement root);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the image to show within the Builder tab. If no image is provided - the default image is used
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Texture2D GetHeaderImage()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 310a760e312f2984e85eece367bab19a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,511 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using VRC.Core;
|
||||
using VRC.Editor;
|
||||
using VRC.SDKBase.Editor;
|
||||
|
||||
[assembly:InternalsVisibleTo("VRC.ExampleCentral.Editor")]
|
||||
|
||||
/// This class sets up the basic panel layout and draws the main tabs
|
||||
/// Implementation of each tab is handled within other files extending this partial class
|
||||
|
||||
|
||||
[ExecuteInEditMode]
|
||||
public partial class VRCSdkControlPanel : EditorWindow, IVRCSdkPanelApi
|
||||
{
|
||||
public static VRCSdkControlPanel window;
|
||||
|
||||
[MenuItem("VRChat SDK/Show Control Panel", false, 600)]
|
||||
internal static void ShowControlPanel()
|
||||
{
|
||||
if (!ConfigManager.RemoteConfig.IsInitialized())
|
||||
{
|
||||
VRC.Core.API.SetOnlineMode(true);
|
||||
ConfigManager.RemoteConfig.Init(() => ShowControlPanel());
|
||||
return;
|
||||
}
|
||||
|
||||
GetWindow(typeof(VRCSdkControlPanel));
|
||||
window.titleContent.text = "VRChat SDK";
|
||||
window.minSize = new Vector2(SdkWindowWidth + 4, 450);
|
||||
window.maxSize = new Vector2(SdkWindowWidth + 4, 2000);
|
||||
window.Init();
|
||||
window.Show();
|
||||
}
|
||||
|
||||
public VRCSdkControlPanel()
|
||||
{
|
||||
window = this;
|
||||
}
|
||||
|
||||
#region IMGUI Init
|
||||
|
||||
public static GUIStyle titleGuiStyle;
|
||||
public static GUIStyle boxGuiStyle;
|
||||
public static GUIStyle infoGuiStyle;
|
||||
public static GUIStyle listButtonStyleEven;
|
||||
public static GUIStyle listButtonStyleOdd;
|
||||
public static GUIStyle listButtonStyleSelected;
|
||||
public static GUIStyle scrollViewSeparatorStyle;
|
||||
public static GUIStyle searchBarStyle;
|
||||
public static GUIStyle accountWindowStyle;
|
||||
public static GUIStyle centeredLabelStyle;
|
||||
public static GUIStyle contentDescriptionStyle;
|
||||
public static GUIStyle contentTitleStyle;
|
||||
public static GUIStyle unityUpgradeBannerStyle;
|
||||
|
||||
void InitializeStyles()
|
||||
{
|
||||
titleGuiStyle = new GUIStyle();
|
||||
titleGuiStyle.fontSize = 15;
|
||||
titleGuiStyle.fontStyle = FontStyle.BoldAndItalic;
|
||||
titleGuiStyle.alignment = TextAnchor.MiddleCenter;
|
||||
titleGuiStyle.wordWrap = true;
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
titleGuiStyle.normal.textColor = Color.white;
|
||||
else
|
||||
titleGuiStyle.normal.textColor = Color.black;
|
||||
|
||||
boxGuiStyle = new GUIStyle
|
||||
{
|
||||
padding = new RectOffset(5,5,5,5)
|
||||
};
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
{
|
||||
boxGuiStyle.normal.background = CreateBackgroundColorImage(new Color(0.3f, 0.3f, 0.3f));
|
||||
boxGuiStyle.normal.textColor = Color.white;
|
||||
}
|
||||
else
|
||||
{
|
||||
boxGuiStyle.normal.background = CreateBackgroundColorImage(new Color(0.85f, 0.85f, 0.85f));
|
||||
boxGuiStyle.normal.textColor = Color.black;
|
||||
}
|
||||
|
||||
infoGuiStyle = new GUIStyle();
|
||||
infoGuiStyle.wordWrap = true;
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
infoGuiStyle.normal.textColor = Color.white;
|
||||
else
|
||||
infoGuiStyle.normal.textColor = Color.black;
|
||||
infoGuiStyle.margin = new RectOffset(10, 10, 10, 10);
|
||||
|
||||
listButtonStyleEven = new GUIStyle();
|
||||
listButtonStyleEven.margin = new RectOffset(0, 0, 0, 0);
|
||||
listButtonStyleEven.border = new RectOffset(0, 0, 0, 0);
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
{
|
||||
listButtonStyleEven.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
|
||||
listButtonStyleEven.normal.background = CreateBackgroundColorImage(new Color(0.540f, 0.540f, 0.54f));
|
||||
}
|
||||
else
|
||||
{
|
||||
listButtonStyleEven.normal.textColor = Color.black;
|
||||
listButtonStyleEven.normal.background = CreateBackgroundColorImage(new Color(0.85f, 0.85f, 0.85f));
|
||||
}
|
||||
|
||||
listButtonStyleOdd = new GUIStyle();
|
||||
listButtonStyleOdd.margin = new RectOffset(0, 0, 0, 0);
|
||||
listButtonStyleOdd.border = new RectOffset(0, 0, 0, 0);
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
{
|
||||
listButtonStyleOdd.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
|
||||
//listButtonStyleOdd.normal.background = CreateBackgroundColorImage(new Color(0.50f, 0.50f, 0.50f));
|
||||
}
|
||||
else
|
||||
{
|
||||
listButtonStyleOdd.normal.textColor = Color.black;
|
||||
listButtonStyleOdd.normal.background = CreateBackgroundColorImage(new Color(0.90f, 0.90f, 0.90f));
|
||||
}
|
||||
|
||||
listButtonStyleSelected = new GUIStyle();
|
||||
listButtonStyleSelected.normal.textColor = Color.white;
|
||||
listButtonStyleSelected.margin = new RectOffset(0, 0, 0, 0);
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
{
|
||||
listButtonStyleSelected.normal.textColor = new Color(0.8f, 0.8f, 0.8f);
|
||||
listButtonStyleSelected.normal.background = CreateBackgroundColorImage(new Color(0.4f, 0.4f, 0.4f));
|
||||
}
|
||||
else
|
||||
{
|
||||
listButtonStyleSelected.normal.textColor = Color.black;
|
||||
listButtonStyleSelected.normal.background = CreateBackgroundColorImage(new Color(0.75f, 0.75f, 0.75f));
|
||||
}
|
||||
|
||||
scrollViewSeparatorStyle = new GUIStyle("Toolbar");
|
||||
scrollViewSeparatorStyle.fixedWidth = SdkWindowWidth + 10;
|
||||
scrollViewSeparatorStyle.fixedHeight = 4;
|
||||
scrollViewSeparatorStyle.margin.top = 1;
|
||||
|
||||
searchBarStyle = new GUIStyle("Toolbar");
|
||||
searchBarStyle.fixedWidth = SdkWindowWidth - 8;
|
||||
searchBarStyle.fixedHeight = 23;
|
||||
searchBarStyle.padding.top = 3;
|
||||
|
||||
accountWindowStyle = new GUIStyle("window")
|
||||
{
|
||||
padding = new RectOffset(10, 10, 10, 10),
|
||||
margin = new RectOffset(0,0,30,30)
|
||||
};
|
||||
|
||||
centeredLabelStyle = new GUIStyle(EditorStyles.boldLabel)
|
||||
{
|
||||
alignment = TextAnchor.UpperCenter,
|
||||
margin = new RectOffset(0,0,0,10)
|
||||
};
|
||||
|
||||
contentDescriptionStyle = new GUIStyle(EditorStyles.wordWrappedLabel)
|
||||
{
|
||||
wordWrap = true
|
||||
};
|
||||
|
||||
contentTitleStyle = new GUIStyle(EditorStyles.boldLabel)
|
||||
{
|
||||
wordWrap = true
|
||||
};
|
||||
|
||||
unityUpgradeBannerStyle = new GUIStyle
|
||||
{
|
||||
normal = new GUIStyleState
|
||||
{
|
||||
background = Resources.Load<Texture2D>("vrcSdkMigrateTo2022Splash")
|
||||
},
|
||||
alignment = TextAnchor.LowerCenter,
|
||||
margin = new RectOffset(0,0,20,0),
|
||||
fixedWidth = 506,
|
||||
fixedHeight = 148
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void Init()
|
||||
{
|
||||
InitializeStyles();
|
||||
ResetIssues();
|
||||
InitAccount();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
OnEnableAccount();
|
||||
_stylesInitialized = false;
|
||||
OnPanelLoggedIn -= RestoreTab;
|
||||
OnPanelLoggedIn += RestoreTab;
|
||||
OnUserPlatformsFetched -= RefreshPlatformSwitcher;
|
||||
OnUserPlatformsFetched += RefreshPlatformSwitcher;
|
||||
AssemblyReloadEvents.afterAssemblyReload -= BuilderAssemblyReload;
|
||||
AssemblyReloadEvents.afterAssemblyReload += BuilderAssemblyReload;
|
||||
OnSdkPanelEnable?.Invoke(this, null);
|
||||
_panelState = SdkPanelState.Idle;
|
||||
OnSdkPanelStateChange?.Invoke(this, _panelState);
|
||||
TabsEnabled = true;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
OnPanelLoggedIn -= RestoreTab;
|
||||
OnUserPlatformsFetched -= RefreshPlatformSwitcher;
|
||||
AssemblyReloadEvents.afterAssemblyReload -= BuilderAssemblyReload;
|
||||
OnSdkPanelDisable?.Invoke(this, null);
|
||||
_panelState = SdkPanelState.Idle;
|
||||
OnSdkPanelStateChange?.Invoke(this, _panelState);
|
||||
}
|
||||
|
||||
private void RestoreTab(object sender, APIUser e)
|
||||
{
|
||||
rootVisualElement.schedule.Execute(() =>
|
||||
{
|
||||
SelectTab(PanelTab.Builder);
|
||||
}).ExecuteLater(200);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
AccountDestroy();
|
||||
}
|
||||
|
||||
public const int SdkWindowWidth = 512;
|
||||
|
||||
private readonly bool[] _toolbarOptionsLoggedIn = new bool[4] {true, true, true, true};
|
||||
private readonly bool[] _toolbarOptionsNotLoggedIn = new bool[4] {true, false, false, true};
|
||||
private bool _stylesInitialized;
|
||||
|
||||
private SdkPanelState _panelState = SdkPanelState.Idle;
|
||||
|
||||
private Button _authenticationTabBtn;
|
||||
private Button _buildTabBtn;
|
||||
private Button _contentManagerTabBtn;
|
||||
private Button _settingsTabBtn;
|
||||
private Button[] _tabButtons;
|
||||
private VisualElement _sdkPanel;
|
||||
private VisualElement _builderPanel;
|
||||
private VisualTreeAsset _builderPanelLayout;
|
||||
private StyleSheet _builderPanelStyles;
|
||||
private float _windowOpenTime;
|
||||
|
||||
private bool _tabsEnabled;
|
||||
internal bool TabsEnabled
|
||||
{
|
||||
get => _tabsEnabled;
|
||||
set
|
||||
{
|
||||
_tabsEnabled = value;
|
||||
if (_tabButtons != null)
|
||||
{
|
||||
foreach (var button in _tabButtons)
|
||||
{
|
||||
button.SetEnabled(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal enum PanelTab
|
||||
{
|
||||
Account,
|
||||
Builder,
|
||||
ContentManager,
|
||||
Settings
|
||||
}
|
||||
|
||||
private void CreateGUI()
|
||||
{
|
||||
if (window == null)
|
||||
{
|
||||
window = (VRCSdkControlPanel)EditorWindow.GetWindow(typeof(VRCSdkControlPanel));
|
||||
}
|
||||
|
||||
_windowOpenTime = Time.realtimeSinceStartup;
|
||||
|
||||
var visualTree = Resources.Load<VisualTreeAsset>("VRCSdkPanelLayout");
|
||||
visualTree.CloneTree(rootVisualElement);
|
||||
var styles = Resources.Load<StyleSheet>("VRCSdkPanelStyles");
|
||||
rootVisualElement.styleSheets.Add(styles);
|
||||
rootVisualElement.AddToClassList(EditorGUIUtility.isProSkin ? "dark" : "light");
|
||||
|
||||
_sdkPanel = rootVisualElement.Q("sdk-container");
|
||||
_builderPanel = rootVisualElement.Q("builder-panel");
|
||||
|
||||
CreateTabs();
|
||||
RenderTabs();
|
||||
|
||||
rootVisualElement.schedule.Execute(() =>
|
||||
{
|
||||
var currentPanel = VRCSettings.ActiveWindowPanel;
|
||||
if (EditorApplication.isPlaying && currentPanel != 0)
|
||||
{
|
||||
SelectTab(PanelTab.Account);
|
||||
return;
|
||||
}
|
||||
// Check that the tabs are enabled, if not - we must re-render tabs
|
||||
if (APIUser.IsLoggedIn && (!_tabButtons[1].enabledSelf || !_tabButtons[2].enabledSelf))
|
||||
{
|
||||
RenderTabs();
|
||||
return;
|
||||
}
|
||||
// When the user isn't logged in - we only allow Settings and Authentication tabs to be viewed
|
||||
if (APIUser.IsLoggedIn || currentPanel == 0 || currentPanel == 3) return;
|
||||
SelectTab(PanelTab.Account);
|
||||
}).Every(500);
|
||||
|
||||
var sdkContainer = rootVisualElement.Q("sdk-container");
|
||||
sdkContainer.Add(new IMGUIContainer(() =>
|
||||
{
|
||||
if (!_stylesInitialized)
|
||||
{
|
||||
InitializeStyles();
|
||||
_stylesInitialized = true;
|
||||
}
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.BeginVertical();
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
GUI.enabled = false;
|
||||
GUILayout.Space(20);
|
||||
EditorGUILayout.LabelField("Unity Application is running ...\nStop it to access the Control Panel", titleGuiStyle, GUILayout.Width(SdkWindowWidth));
|
||||
GUI.enabled = true;
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.EndHorizontal();
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.EndHorizontal();
|
||||
|
||||
switch ((PanelTab) VRCSettings.ActiveWindowPanel)
|
||||
{
|
||||
case PanelTab.Builder:
|
||||
break;
|
||||
case PanelTab.ContentManager:
|
||||
ShowContent();
|
||||
break;
|
||||
case PanelTab.Settings:
|
||||
ShowSettings();
|
||||
break;
|
||||
case PanelTab.Account:
|
||||
default:
|
||||
ShowAccount();
|
||||
break;
|
||||
}
|
||||
}));
|
||||
|
||||
EnvConfig.SetActiveSDKDefines();
|
||||
}
|
||||
|
||||
private void CreateTabs()
|
||||
{
|
||||
_authenticationTabBtn = rootVisualElement.Q<Button>("tab-authentication");
|
||||
_buildTabBtn = rootVisualElement.Q<Button>("tab-builder");
|
||||
_contentManagerTabBtn = rootVisualElement.Q<Button>("tab-content-manager");
|
||||
_settingsTabBtn = rootVisualElement.Q<Button>("tab-settings");
|
||||
|
||||
_tabButtons = new[]
|
||||
{
|
||||
_authenticationTabBtn,
|
||||
_buildTabBtn,
|
||||
_contentManagerTabBtn,
|
||||
_settingsTabBtn
|
||||
};
|
||||
|
||||
var currentPanel = VRCSettings.ActiveWindowPanel;
|
||||
|
||||
for (int i = 0; i < _tabButtons.Length; i++)
|
||||
{
|
||||
var btnIndex = i;
|
||||
_tabButtons[i].EnableInClassList("active", currentPanel == btnIndex);
|
||||
_tabButtons[i].SetEnabled(APIUser.IsLoggedIn ? _toolbarOptionsLoggedIn[i] : _toolbarOptionsNotLoggedIn[i]);
|
||||
|
||||
_tabButtons[i].clicked += () => SelectTab((PanelTab) btnIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectTab(PanelTab tab)
|
||||
{
|
||||
if (_tabButtons == null) return;
|
||||
if (EditorApplication.isPlaying) return;
|
||||
if (VRCSettings.ActiveWindowPanel == (int) tab)
|
||||
{
|
||||
return;
|
||||
}
|
||||
VRCSettings.ActiveWindowPanel = (int) tab;
|
||||
RenderTabs();
|
||||
}
|
||||
|
||||
internal PanelTab CurrentTab => (PanelTab) VRCSettings.ActiveWindowPanel;
|
||||
|
||||
private void RenderTabs()
|
||||
{
|
||||
var currentPanel = (PanelTab) VRCSettings.ActiveWindowPanel;
|
||||
|
||||
if (currentPanel != PanelTab.Builder)
|
||||
{
|
||||
if (_defaultHeaderImage == null)
|
||||
{
|
||||
_defaultHeaderImage = Resources.Load<Texture2D>("SDK_Panel_Banner");
|
||||
}
|
||||
rootVisualElement.Q("banner").style.backgroundImage = _defaultHeaderImage;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _tabButtons.Length; i++)
|
||||
{
|
||||
_tabButtons[i].EnableInClassList("active", currentPanel == (PanelTab) i);
|
||||
if (!TabsEnabled) continue;
|
||||
_tabButtons[i].SetEnabled(APIUser.IsLoggedIn ? _toolbarOptionsLoggedIn[i] : _toolbarOptionsNotLoggedIn[i]);
|
||||
}
|
||||
|
||||
if (currentPanel == PanelTab.Builder)
|
||||
{
|
||||
if (_builderPanel.childCount != 0) return;
|
||||
ShowBuilders();
|
||||
_builderPanel.RemoveFromClassList("d-none");
|
||||
_sdkPanel.AddToClassList("d-none");
|
||||
}
|
||||
else if (_builderPanel.childCount == 1)
|
||||
{
|
||||
_builderPanel.AddToClassList("d-none");
|
||||
_sdkPanel.RemoveFromClassList("d-none");
|
||||
_builderPanel.Remove(_builderPanel.Children().First());
|
||||
_builderPanel.styleSheets.Remove(_builderPanelStyles);
|
||||
}
|
||||
}
|
||||
|
||||
[UnityEditor.Callbacks.PostProcessScene]
|
||||
static void OnPostProcessScene()
|
||||
{
|
||||
if (window != null)
|
||||
window.Reset();
|
||||
}
|
||||
|
||||
private void OnFocus()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ResetIssues();
|
||||
// style backgrounds may be nulled on scene load. detect if so has happened
|
||||
if((boxGuiStyle != null) && (boxGuiStyle.normal.background == null))
|
||||
InitializeStyles();
|
||||
}
|
||||
|
||||
[UnityEditor.Callbacks.DidReloadScripts(int.MaxValue)]
|
||||
static void DidReloadScripts()
|
||||
{
|
||||
try
|
||||
{
|
||||
RefreshApiUrlSetting();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//Unity's Mono is trash and randomly fails to assemblies types.
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
#region Internal API
|
||||
|
||||
[UsedImplicitly]
|
||||
internal void SetPanelIdle()
|
||||
{
|
||||
_panelState = SdkPanelState.Idle;
|
||||
OnSdkPanelStateChange?.Invoke(this, _panelState);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
internal void SetPanelBuilding()
|
||||
{
|
||||
_panelState = SdkPanelState.Building;
|
||||
OnSdkPanelStateChange?.Invoke(this, _panelState);
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
internal void SetPanelUploading()
|
||||
{
|
||||
_panelState = SdkPanelState.Uploading;
|
||||
OnSdkPanelStateChange?.Invoke(this, _panelState);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
|
||||
public static event EventHandler OnSdkPanelEnable;
|
||||
public static event EventHandler OnSdkPanelDisable;
|
||||
public static event EventHandler<SdkPanelState> OnSdkPanelStateChange;
|
||||
public SdkPanelState PanelState => _panelState;
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20b4cdbdda9655947aab6f8f2c90690f
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,686 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using VRC.Core;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine.UIElements;
|
||||
using VRC.SDKBase.Editor;
|
||||
|
||||
public enum TwoFactorType
|
||||
{
|
||||
None,
|
||||
TOTP,
|
||||
Email,
|
||||
}
|
||||
|
||||
// This file handles the Account tab of the SDK Panel
|
||||
|
||||
public partial class VRCSdkControlPanel : EditorWindow
|
||||
{
|
||||
static bool isInitialized = false;
|
||||
static string clientInstallPath;
|
||||
static bool signingIn = false;
|
||||
static double latestSignInTime = -1.0d;
|
||||
static string error = null;
|
||||
|
||||
private const string UNITY_UPGRADE_PROMPT_URL = "https://creators.vrchat.com/sdk/upgrade/unity-2022";
|
||||
private const double LogoutCooldownAfterLogin = 0.75d;
|
||||
|
||||
public static bool FutureProofPublishEnabled { get { return UnityEditor.EditorPrefs.GetBool("futureProofPublish", DefaultFutureProofPublishEnabled); } }
|
||||
//public static bool DefaultFutureProofPublishEnabled { get { return !SDKClientUtilities.IsInternalSDK(); } }
|
||||
public static bool DefaultFutureProofPublishEnabled { get { return false; } }
|
||||
|
||||
internal static event EventHandler<APIUser> OnPanelLoggedIn;
|
||||
internal static event EventHandler OnPanelLoggedOut;
|
||||
internal static event EventHandler<ApiUserPlatforms> OnUserPlatformsFetched;
|
||||
|
||||
static string storedUsername
|
||||
{
|
||||
get
|
||||
{
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
EditorPrefs.DeleteKey("sdk#username");
|
||||
}
|
||||
}
|
||||
|
||||
static string storedPassword
|
||||
{
|
||||
get
|
||||
{
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
EditorPrefs.DeleteKey("sdk#password");
|
||||
}
|
||||
}
|
||||
|
||||
static string username { get; set; } = null;
|
||||
static string password { get; set; } = null;
|
||||
|
||||
public static ApiServerEnvironment ApiEnvironment => serverEnvironment;
|
||||
static ApiServerEnvironment serverEnvironment
|
||||
{
|
||||
get
|
||||
{
|
||||
ApiServerEnvironment env = ApiServerEnvironment.Release;
|
||||
try
|
||||
{
|
||||
env = (ApiServerEnvironment)System.Enum.Parse(typeof(ApiServerEnvironment), UnityEditor.EditorPrefs.GetString("VRC_ApiServerEnvironment", env.ToString()));
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogError("Invalid server environment name - " + e.ToString());
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
set
|
||||
{
|
||||
UnityEditor.EditorPrefs.SetString("VRC_ApiServerEnvironment", value.ToString());
|
||||
|
||||
API.SetApiUrlFromEnvironment(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnableAccount()
|
||||
{
|
||||
entered2faCodeIsInvalid = false;
|
||||
warningIconGraphic = Resources.Load("2FAIcons/SDK_Warning_Triangle_icon") as Texture2D;
|
||||
}
|
||||
|
||||
public static void RefreshApiUrlSetting()
|
||||
{
|
||||
// this forces the static api url variable to be reset from the server environment set in editor prefs.
|
||||
// needed because the static variable states get cleared when entering / exiting play mode
|
||||
ApiServerEnvironment env = serverEnvironment;
|
||||
serverEnvironment = env;
|
||||
}
|
||||
|
||||
public static void InitAccount()
|
||||
{
|
||||
if (isInitialized)
|
||||
return;
|
||||
|
||||
if (!APIUser.IsLoggedIn && ApiCredentials.Load())
|
||||
{
|
||||
APIUser.InitialFetchCurrentUser(c =>
|
||||
{
|
||||
window.rootVisualElement.Q<IMGUIContainer>().MarkDirtyRepaint();
|
||||
if (c.Model is not APIUser apiUser)
|
||||
{
|
||||
VRC.Core.Logger.LogError("Failed to load user information, please log in again");
|
||||
return;
|
||||
}
|
||||
AnalyticsSDK.LoggedInUserChanged(apiUser);
|
||||
|
||||
OnPanelLoggedIn?.Invoke(window, apiUser);
|
||||
ApiUserPlatforms.Fetch(apiUser.id, userPlatforms =>
|
||||
{
|
||||
OnUserPlatformsFetched?.Invoke(window, userPlatforms);
|
||||
}, null);
|
||||
}, null);
|
||||
}
|
||||
|
||||
// This code proceeds without waiting for the user fetch above to complete
|
||||
clientInstallPath = SDKClientUtilities.GetSavedVRCInstallPath();
|
||||
if (string.IsNullOrEmpty(clientInstallPath))
|
||||
clientInstallPath = SDKClientUtilities.LoadRegistryVRCInstallPath();
|
||||
|
||||
signingIn = false;
|
||||
isInitialized = true;
|
||||
|
||||
ClearContent();
|
||||
}
|
||||
|
||||
void OnAccountGUI()
|
||||
{
|
||||
using (new GUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
AccountWindowGUI();
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
}
|
||||
|
||||
void AccountWindowGUI()
|
||||
{
|
||||
using (new EditorGUILayout.VerticalScope(accountWindowStyle, GUILayout.Width(340)))
|
||||
{
|
||||
EditorGUILayout.LabelField("Account", centeredLabelStyle);
|
||||
if (signingIn)
|
||||
{
|
||||
if (twoFactorAuthenticationEntryType == TwoFactorType.None)
|
||||
{
|
||||
EditorGUILayout.LabelField("Signing in as " + username + ".");
|
||||
}
|
||||
OnTwoFactorAuthenticationGUI(twoFactorAuthenticationEntryType);
|
||||
return;
|
||||
}
|
||||
|
||||
if (APIUser.IsLoggedIn)
|
||||
{
|
||||
if (Status != "Connected")
|
||||
{
|
||||
EditorGUILayout.LabelField(Status);
|
||||
}
|
||||
|
||||
OnCreatorStatusGUI();
|
||||
|
||||
// Add a space, pushing this away from the line that contained "Verify" on the previous page.
|
||||
EditorGUILayout.Space(EditorGUIUtility.singleLineHeight);
|
||||
|
||||
// Disable the logout button for a short amount of time after logging in.
|
||||
// This attempts to patch UX difficulties where users try to click "Verify" on the previous screen as
|
||||
// 2FA automatically submits so they end up accidentally clicking Logout instead.
|
||||
bool wasRecentLogin = latestSignInTime >= 0.0d && EditorApplication.timeSinceStartup < latestSignInTime + LogoutCooldownAfterLogin;
|
||||
using (new EditorGUI.DisabledScope(wasRecentLogin))
|
||||
{
|
||||
if (GUILayout.Button("Logout"))
|
||||
{
|
||||
storedUsername = username = null;
|
||||
storedPassword = password = null;
|
||||
|
||||
VRC.Tools.ClearCookies();
|
||||
APIUser.Logout();
|
||||
ClearContent();
|
||||
OnPanelLoggedOut?.Invoke(window, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
InitAccount();
|
||||
|
||||
ApiServerEnvironment newEnv = ApiServerEnvironment.Release;
|
||||
if (VRCSettings.DisplayAdvancedSettings)
|
||||
newEnv = (ApiServerEnvironment)EditorGUILayout.EnumPopup("Use API", serverEnvironment);
|
||||
if (serverEnvironment != newEnv)
|
||||
serverEnvironment = newEnv;
|
||||
|
||||
const string controlNameUser = "input_user";
|
||||
const string controlNamePass = "input_pass";
|
||||
|
||||
GUI.SetNextControlName(controlNameUser);
|
||||
username = EditorGUILayout.TextField("Username/Email", username);
|
||||
GUI.SetNextControlName(controlNamePass);
|
||||
password = EditorGUILayout.PasswordField("Password", password);
|
||||
|
||||
bool attemptKeyboardSignIn = false;
|
||||
if (
|
||||
Event.current != null &&
|
||||
Event.current.type == EventType.KeyUp &&
|
||||
(Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.KeypadEnter))
|
||||
{
|
||||
switch (GUI.GetNameOfFocusedControl())
|
||||
{
|
||||
case controlNameUser:
|
||||
GUI.FocusControl(controlNamePass);
|
||||
break;
|
||||
case controlNamePass:
|
||||
attemptKeyboardSignIn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Sign In") || attemptKeyboardSignIn)
|
||||
{
|
||||
SignIn(true);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Sign up"))
|
||||
{
|
||||
Application.OpenURL("https://vrchat.com/register");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnCreatorStatusGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("Logged in as:", APIUser.CurrentUser.displayName);
|
||||
|
||||
//if (SDKClientUtilities.IsInternalSDK())
|
||||
// EditorGUILayout.LabelField("Developer Status: ", APIUser.CurrentUser.developerType.ToString());
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
EditorGUILayout.BeginVertical();
|
||||
EditorGUILayout.LabelField("World Creator Status: ", APIUser.CurrentUser.canPublishWorlds ? "Allowed to publish worlds" : "Not yet allowed to publish worlds");
|
||||
EditorGUILayout.LabelField("Avatar Creator Status: ", APIUser.CurrentUser.canPublishAvatars ? "Allowed to publish avatars" : "Not yet allowed to publish avatars");
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
if (!APIUser.CurrentUser.canPublishWorldsAndAvatars)
|
||||
{
|
||||
if (GUILayout.Button("More Info..."))
|
||||
{
|
||||
ShowContentPublishPermissionsDialog();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
void ShowAccount()
|
||||
{
|
||||
if (ConfigManager.RemoteConfig.IsInitialized())
|
||||
{
|
||||
if (ConfigManager.RemoteConfig.HasKey("sdkUnityVersion"))
|
||||
{
|
||||
string sdkUnityVersion = ConfigManager.RemoteConfig.GetString("sdkUnityVersion");
|
||||
if (string.IsNullOrEmpty(sdkUnityVersion))
|
||||
EditorGUILayout.LabelField("Could not fetch remote config.");
|
||||
else if (Application.unityVersion != sdkUnityVersion)
|
||||
{
|
||||
var currentVersion = UnityVersion.Parse(Application.unityVersion).major;
|
||||
var sdkVersion = UnityVersion.Parse(sdkUnityVersion).major;
|
||||
if (currentVersion < sdkVersion && sdkVersion == 2022)
|
||||
{
|
||||
using (new EditorGUILayout.VerticalScope(unityUpgradeBannerStyle))
|
||||
{
|
||||
EditorGUILayout.Space(115);
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
if (GUILayout.Button("Learn More"))
|
||||
{
|
||||
Application.OpenURL(UNITY_UPGRADE_PROMPT_URL);
|
||||
}
|
||||
EditorGUILayout.Space();
|
||||
}
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField("Unity Version", EditorStyles.boldLabel);
|
||||
EditorGUILayout.LabelField("Wrong Unity version. Please use " + sdkUnityVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
API.SetOnlineMode(true);
|
||||
ConfigManager.RemoteConfig.Init();
|
||||
}
|
||||
|
||||
OnAccountGUI();
|
||||
}
|
||||
|
||||
|
||||
private const string TWO_FACTOR_AUTHENTICATION_HELP_URL = "https://docs.vrchat.com/docs/setup-2fa";
|
||||
|
||||
private const string ENTER_2FA_CODE_TITLE_STRING = "Enter a numeric code from your authenticator app.";
|
||||
private const string ENTER_2FA_CODE_LABEL_STRING = "Code:";
|
||||
|
||||
private const string ENTER_2FA_CODE_GUI_EVENT = "Authentication Code Field";
|
||||
|
||||
private const string ENTER_EMAIL_2FA_CODE_TITLE_STRING = "Check your email for a numeric code.";
|
||||
|
||||
private const string CHECKING_2FA_CODE_STRING = "Checking code...";
|
||||
private const string ENTER_2FA_CODE_INVALID_CODE_STRING = "Oops, that code didn't work.\nTry again!";
|
||||
|
||||
private const string ENTER_2FA_CODE_VERIFY_STRING = "Verify";
|
||||
private const string ENTER_2FA_CODE_CANCEL_STRING = "Cancel";
|
||||
private const string ENTER_2FA_CODE_HELP_STRING = "Help";
|
||||
|
||||
private const int WARNING_ICON_SIZE = 60;
|
||||
private const int WARNING_FONT_HEIGHT = 14;
|
||||
|
||||
static private Texture2D warningIconGraphic;
|
||||
|
||||
static bool entered2faCodeIsInvalid;
|
||||
static bool authorizationCodeWasVerified;
|
||||
|
||||
static private int previousAuthenticationCodeLength = 0;
|
||||
static bool checkingCode;
|
||||
static string authenticationCode = "";
|
||||
static string lastCheckedAuthenticationCode = "";
|
||||
|
||||
static System.Action onAuthenticationVerifiedAction;
|
||||
|
||||
static TwoFactorType _twoFactorAuthenticationEntryType = TwoFactorType.None;
|
||||
|
||||
static TwoFactorType twoFactorAuthenticationEntryType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _twoFactorAuthenticationEntryType;
|
||||
}
|
||||
set
|
||||
{
|
||||
_twoFactorAuthenticationEntryType = value;
|
||||
authenticationCode = "";
|
||||
lastCheckedAuthenticationCode = "";
|
||||
if (_twoFactorAuthenticationEntryType == TwoFactorType.None && !authorizationCodeWasVerified)
|
||||
Logout();
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsValidAuthenticationCodeFormat()
|
||||
{
|
||||
bool isValid2faAuthenticationCode = false;
|
||||
|
||||
if (!string.IsNullOrEmpty(authenticationCode))
|
||||
{
|
||||
// check if the input is a valid 6-digit numberic code (ignoring spaces)
|
||||
Regex rx = new Regex(@"^(\s*\d\s*){6}$", RegexOptions.Compiled);
|
||||
MatchCollection matches6DigitCode = rx.Matches(authenticationCode);
|
||||
isValid2faAuthenticationCode = (matches6DigitCode.Count == 1);
|
||||
}
|
||||
|
||||
return isValid2faAuthenticationCode;
|
||||
}
|
||||
|
||||
static bool IsValidRecoveryCodeFormat()
|
||||
{
|
||||
bool isValid2faRecoveryCode = false;
|
||||
|
||||
if (!string.IsNullOrEmpty(authenticationCode))
|
||||
{
|
||||
// check if the input is a valid 8-digit alpha-numberic code (format xxxx-xxxx) "-" is optional & ignore any spaces
|
||||
// OTP codes also exclude the letters i,l,o and the digit 1 to prevent any confusion
|
||||
Regex rx = new Regex(@"^(\s*[a-hj-km-np-zA-HJ-KM-NP-Z02-9]\s*){4}-?(\s*[a-hj-km-np-zA-HJ-KM-NP-Z02-9]\s*){4}$", RegexOptions.Compiled);
|
||||
MatchCollection matchesRecoveryCode = rx.Matches(authenticationCode);
|
||||
isValid2faRecoveryCode = (matchesRecoveryCode.Count == 1);
|
||||
}
|
||||
|
||||
return isValid2faRecoveryCode;
|
||||
}
|
||||
|
||||
static void OnTwoFactorAuthenticationGUI(TwoFactorType twoFactorType)
|
||||
{
|
||||
if (twoFactorType == TwoFactorType.None)
|
||||
return;
|
||||
|
||||
const int ENTER_2FA_CODE_BORDER_SIZE = 20;
|
||||
const int ENTER_2FA_CODE_BUTTON_WIDTH = 260;
|
||||
const int ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH = ENTER_2FA_CODE_BUTTON_WIDTH / 2;
|
||||
const int ENTER_2FA_CODE_ENTRY_REGION_WIDTH = 130;
|
||||
const int ENTER_2FA_CODE_MIN_WINDOW_WIDTH = ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH + ENTER_2FA_CODE_ENTRY_REGION_WIDTH + (ENTER_2FA_CODE_BORDER_SIZE * 3);
|
||||
|
||||
bool isValidAuthenticationCode = IsValidAuthenticationCodeFormat();
|
||||
|
||||
|
||||
// Invalid code text
|
||||
if (entered2faCodeIsInvalid)
|
||||
{
|
||||
GUIStyle s = new GUIStyle(EditorStyles.label)
|
||||
{
|
||||
normal =
|
||||
{
|
||||
textColor = new Color(1,0.3f,0.3f)
|
||||
},
|
||||
fontSize = WARNING_FONT_HEIGHT,
|
||||
fixedHeight = WARNING_ICON_SIZE
|
||||
};
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
using (new EditorGUILayout.VerticalScope())
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.Label(new GUIContent(warningIconGraphic), GUILayout.Width(WARNING_ICON_SIZE), GUILayout.Height(WARNING_ICON_SIZE));
|
||||
EditorGUILayout.LabelField(ENTER_2FA_CODE_INVALID_CODE_STRING, s);
|
||||
}
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
}
|
||||
else if (checkingCode)
|
||||
{
|
||||
// Display checking code message
|
||||
EditorGUILayout.BeginVertical();
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUIStyle s = new GUIStyle(EditorStyles.label);
|
||||
s.alignment = TextAnchor.MiddleCenter;
|
||||
s.fixedHeight = WARNING_ICON_SIZE;
|
||||
EditorGUILayout.LabelField(CHECKING_2FA_CODE_STRING, s, GUILayout.Height(WARNING_ICON_SIZE));
|
||||
EditorGUILayout.EndHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
|
||||
GUILayout.FlexibleSpace();
|
||||
GUIStyle titleStyle = new GUIStyle(EditorStyles.label);
|
||||
titleStyle.alignment = TextAnchor.MiddleCenter;
|
||||
titleStyle.wordWrap = true;
|
||||
string twofactorTitle = "Enter Code";
|
||||
switch (twoFactorType)
|
||||
{
|
||||
case TwoFactorType.TOTP:
|
||||
twofactorTitle = ENTER_2FA_CODE_TITLE_STRING;
|
||||
break;
|
||||
case TwoFactorType.Email:
|
||||
twofactorTitle = ENTER_EMAIL_2FA_CODE_TITLE_STRING;
|
||||
break;
|
||||
}
|
||||
EditorGUILayout.LabelField(twofactorTitle, titleStyle, GUILayout.Width(ENTER_2FA_CODE_MIN_WINDOW_WIDTH - (2 * ENTER_2FA_CODE_BORDER_SIZE)), GUILayout.Height(WARNING_ICON_SIZE), GUILayout.ExpandHeight(true));
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
|
||||
GUILayout.FlexibleSpace();
|
||||
Vector2 size = EditorStyles.boldLabel.CalcSize(new GUIContent(ENTER_2FA_CODE_LABEL_STRING));
|
||||
|
||||
EditorGUILayout.LabelField(ENTER_2FA_CODE_LABEL_STRING, EditorStyles.boldLabel, GUILayout.MaxWidth(size.x));
|
||||
authenticationCode = EditorGUILayout.TextField(authenticationCode);
|
||||
|
||||
|
||||
// Verify 2FA code button
|
||||
if (lastCheckedAuthenticationCode != authenticationCode && IsValidAuthenticationCodeFormat()
|
||||
|| GUILayout.Button(ENTER_2FA_CODE_VERIFY_STRING, GUILayout.Width(ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH)))
|
||||
{
|
||||
lastCheckedAuthenticationCode = authenticationCode;
|
||||
checkingCode = true;
|
||||
string authCodeType = API2FA.TIME_BASED_ONE_TIME_PASSWORD_AUTHENTICATION;
|
||||
switch (twoFactorType)
|
||||
{
|
||||
case TwoFactorType.TOTP:
|
||||
authCodeType = API2FA.TIME_BASED_ONE_TIME_PASSWORD_AUTHENTICATION;
|
||||
break;
|
||||
case TwoFactorType.Email:
|
||||
authCodeType = API2FA.EMAIL_BASED_ONE_TIME_PASSWORD_AUTHENTICATION;
|
||||
break;
|
||||
}
|
||||
APIUser.VerifyTwoFactorAuthCode(authenticationCode, authCodeType, username, password,
|
||||
delegate
|
||||
{
|
||||
// valid 2FA code submitted
|
||||
entered2faCodeIsInvalid = false;
|
||||
authorizationCodeWasVerified = true;
|
||||
checkingCode = false;
|
||||
twoFactorAuthenticationEntryType = TwoFactorType.None;
|
||||
if (null != onAuthenticationVerifiedAction)
|
||||
onAuthenticationVerifiedAction();
|
||||
},
|
||||
delegate
|
||||
{
|
||||
entered2faCodeIsInvalid = true;
|
||||
checkingCode = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
// after user has entered an invalid code causing the invalid code message to be displayed,
|
||||
// edit the code will change it's length meaning it is invalid format, so we can clear the invalid code setting until they resubmit
|
||||
if (previousAuthenticationCodeLength != authenticationCode.Length)
|
||||
{
|
||||
previousAuthenticationCodeLength = authenticationCode.Length;
|
||||
entered2faCodeIsInvalid = false;
|
||||
}
|
||||
|
||||
GUI.enabled = true;
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
// Two-Factor Authentication Help button
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button(ENTER_2FA_CODE_HELP_STRING))
|
||||
{
|
||||
Application.OpenURL(TWO_FACTOR_AUTHENTICATION_HELP_URL);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// Cancel button
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button(ENTER_2FA_CODE_CANCEL_STRING))
|
||||
{
|
||||
twoFactorAuthenticationEntryType = TwoFactorType.None;
|
||||
Logout();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private static string Status
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!APIUser.IsLoggedIn)
|
||||
return error == null ? "Please log in." : "Error in authenticating: " + error;
|
||||
if (signingIn)
|
||||
return "Logging in.";
|
||||
else
|
||||
{
|
||||
if( serverEnvironment == ApiServerEnvironment.Dev )
|
||||
return "Connected to " + serverEnvironment.ToString();
|
||||
return "Connected";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnAuthenticationCompleted()
|
||||
{
|
||||
AttemptLogin();
|
||||
}
|
||||
|
||||
private static void AttemptLogin()
|
||||
{
|
||||
APIUser.Login(username, password,
|
||||
delegate (ApiModelContainer<APIUser> c)
|
||||
{
|
||||
APIUser user = c.Model as APIUser;
|
||||
if (c.Cookies.ContainsKey("twoFactorAuth"))
|
||||
ApiCredentials.Set(user.username, username, "vrchat", c.Cookies["auth"], c.Cookies["twoFactorAuth"]);
|
||||
else if (c.Cookies.ContainsKey("auth"))
|
||||
ApiCredentials.Set(user.username, username, "vrchat", c.Cookies["auth"]);
|
||||
else
|
||||
ApiCredentials.SetHumanName(user.username);
|
||||
signingIn = false;
|
||||
latestSignInTime = EditorApplication.timeSinceStartup;
|
||||
error = null;
|
||||
storedUsername = null;
|
||||
storedPassword = null;
|
||||
AnalyticsSDK.LoggedInUserChanged(user);
|
||||
|
||||
OnPanelLoggedIn?.Invoke(window, user);
|
||||
|
||||
if (!APIUser.CurrentUser.canPublishWorldsAndAvatars)
|
||||
{
|
||||
if (UnityEditor.SessionState.GetString("HasShownContentPublishPermissionsDialogForUser", "") != user.id)
|
||||
{
|
||||
UnityEditor.SessionState.SetString("HasShownContentPublishPermissionsDialogForUser", user.id);
|
||||
VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch platforms that the user can publish to
|
||||
ApiUserPlatforms.Fetch(user.id, userPlatforms => {
|
||||
OnUserPlatformsFetched?.Invoke(window, userPlatforms);
|
||||
}, null);
|
||||
},
|
||||
delegate (ApiModelContainer<APIUser> c)
|
||||
{
|
||||
Logout();
|
||||
error = c.Error;
|
||||
EditorUtility.DisplayDialog("Error logging in", error, "OK");
|
||||
VRC.Core.Logger.Log("Error logging in: " + error);
|
||||
},
|
||||
delegate (ApiModelContainer<API2FA> c)
|
||||
{
|
||||
window.rootVisualElement.Q<IMGUIContainer>().MarkDirtyRepaint();
|
||||
if (c.Cookies.ContainsKey("auth"))
|
||||
ApiCredentials.Set(username, username, "vrchat", c.Cookies["auth"]);
|
||||
API2FA model2FA = c.Model as API2FA;
|
||||
if (model2FA.requiresTwoFactorAuth.Contains(API2FA.TIME_BASED_ONE_TIME_PASSWORD_AUTHENTICATION))
|
||||
twoFactorAuthenticationEntryType = TwoFactorType.TOTP;
|
||||
else if (model2FA.requiresTwoFactorAuth.Contains(API2FA.EMAIL_BASED_ONE_TIME_PASSWORD_AUTHENTICATION))
|
||||
twoFactorAuthenticationEntryType = TwoFactorType.Email;
|
||||
else
|
||||
twoFactorAuthenticationEntryType = TwoFactorType.None;
|
||||
onAuthenticationVerifiedAction = OnAuthenticationCompleted;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private static object syncObject = new object();
|
||||
private void SignIn(bool explicitAttempt)
|
||||
{
|
||||
lock (syncObject)
|
||||
{
|
||||
if (signingIn
|
||||
|| APIUser.IsLoggedIn
|
||||
|| (!explicitAttempt && string.IsNullOrEmpty(storedUsername)))
|
||||
return;
|
||||
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
|
||||
{
|
||||
EditorUtility.DisplayDialog("Error logging in", "Please enter a valid username and password.", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
signingIn = true;
|
||||
}
|
||||
|
||||
InitAccount();
|
||||
|
||||
AttemptLogin();
|
||||
|
||||
// Show login status immediately without needing an automatic repaint (cursor movement).
|
||||
Repaint();
|
||||
}
|
||||
|
||||
public static void Logout()
|
||||
{
|
||||
signingIn = false;
|
||||
storedUsername = null;
|
||||
storedPassword = null;
|
||||
VRC.Tools.ClearCookies();
|
||||
APIUser.Logout();
|
||||
OnPanelLoggedOut?.Invoke(window, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void AccountDestroy()
|
||||
{
|
||||
signingIn = false;
|
||||
isInitialized = false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5066cd5c1cc208143a1253cac821714a
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user