using System.Runtime.InteropServices;
using LoadAction = UnityEngine.Rendering.RenderBufferLoadAction;
using StoreAction = UnityEngine.Rendering.RenderBufferStoreAction;
#if UNITY_2022_2_OR_NEWER
using static UnityEngine.Rendering.CustomMarkerCallbackFlags;
#endif
#if ENABLE_URP_PACKAGE_RENDER_GRAPH
using UnityEngine.Rendering.RenderGraphModule;
#endif
///
/// In Unity, the render thread is only accesible in C++ using IssuePluginEvent(). This is a helper
/// class to communicate a C# view with its C++ renderer.
///
public class NoesisRenderer
{
///
/// Registers a view in the render thread
///
public static void RegisterView(Noesis.View view, UnityEngine.Rendering.CommandBuffer commands)
{
commands.IssuePluginEventAndData(_renderRegisterCallback, (int)EventId.NoRender, view.Renderer.CPtr.Handle);
}
///
/// Sends render tree update commands to native code
///
public static void UpdateRenderTree(Noesis.View view, UnityEngine.Rendering.CommandBuffer commands)
{
// Send information about requested textures to C++ texture provider
NoesisTextureProvider.instance.UpdateTextures();
commands.IssuePluginEventAndData(_updateRenderTreeCallback, (int)EventId.NoRender, view.Renderer.CPtr.Handle);
}
///
/// Sends offscreen render commands to native code
///
public static void RenderOffscreen(Noesis.View view, UnityEngine.Rendering.CommandBuffer commands, bool invalidate)
{
// This a way to force Unity to close the current MTL command encoder
// We need to activate a new encoder in the current command buffer for our Offscreen phase
if (UnityEngine.SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Metal)
{
UnityEngine.RenderTexture surface = UnityEngine.RenderTexture.GetTemporary(1,1);
commands.SetRenderTarget(surface, LoadAction.DontCare, StoreAction.DontCare, LoadAction.DontCare, StoreAction.DontCare);
commands.ClearRenderTarget(false, false, UnityEngine.Color.clear);
UnityEngine.RenderTexture.ReleaseTemporary(surface);
}
#if UNITY_EDITOR
// When a texture is modified and reimported its native pointer changes, so we need to
// send the new texture native pointer to C++ to update texture provider cache
NoesisTextureProvider.instance.UpdateTextures();
#endif
#if UNITY_2022_2_OR_NEWER
commands.IssuePluginEventAndDataWithFlags(_renderOffscreenCallback, (int)EventId.RenderOffscreen,
invalidate ? CustomMarkerCallbackForceInvalidateStateTracking : CustomMarkerCallbackDefault,
view.Renderer.CPtr.Handle);
#else
commands.IssuePluginEventAndData(_renderOffscreenCallback, (int)EventId.RenderOffscreen, view.Renderer.CPtr.Handle);
if (invalidate) InvalidateState(commands);
#endif
}
private static void FixCommandBuffer(UnityEngine.Rendering.CommandBuffer commands)
{
// This is a workaround for a bug in Unity. When rendering nothing Unity sends us an empty command buffer
if (UnityEngine.SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.Metal ||
UnityEngine.SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.PlayStation4 ||
UnityEngine.SystemInfo.graphicsDeviceType == UnityEngine.Rendering.GraphicsDeviceType.PlayStation5)
{
commands.DrawMesh(GetDummyMesh(), new UnityEngine.Matrix4x4(), GetDummyMaterial());
}
}
///
/// Sends render commands to native code
///
public static void RenderOnscreen(Noesis.View view, bool flipY, UnityEngine.Rendering.CommandBuffer commands,
bool invalidate, bool clearStencil)
{
FixCommandBuffer(commands);
if (clearStencil)
{
commands.ClearRenderTarget(UnityEngine.Rendering.RTClearFlags.Stencil, UnityEngine.Color.black, 1.0f, 0);
}
var eventId = flipY ? EventId.RenderOnscreenFlipY : EventId.RenderOnscreen;
#if UNITY_2022_2_OR_NEWER
commands.IssuePluginEventAndDataWithFlags(_renderOnscreenCallback, (int)eventId,
invalidate ? CustomMarkerCallbackForceInvalidateStateTracking : CustomMarkerCallbackDefault,
view.Renderer.CPtr.Handle);
#else
commands.IssuePluginEventAndData(_renderOnscreenCallback, (int)eventId, view.Renderer.CPtr.Handle);
if (invalidate) InvalidateState(commands);
#endif
}
#if ENABLE_URP_PACKAGE_RENDER_GRAPH
public static void RenderOnscreen_(Noesis.View view, bool flipY, UnityEngine.Rendering.RasterCommandBuffer commands,
bool invalidate, bool clearStencil)
{
if (clearStencil)
{
commands.ClearRenderTarget(UnityEngine.Rendering.RTClearFlags.Stencil, UnityEngine.Color.black, 1.0f, 0);
}
var eventId = flipY ? EventId.RenderOnscreenFlipY : EventId.RenderOnscreen;
commands.IssuePluginEventAndData(_renderOnscreenCallback, (int)eventId, view.Renderer.CPtr.Handle);
if (invalidate) InvalidateState(commands);
}
public static void RenderOnscreen_(Noesis.View view, bool flipY, UnityEngine.Rendering.UnsafeCommandBuffer commands,
bool invalidate, bool clearStencil)
{
if (clearStencil)
{
commands.ClearRenderTarget(UnityEngine.Rendering.RTClearFlags.Stencil, UnityEngine.Color.black, 1.0f, 0);
}
var eventId = flipY ? EventId.RenderOnscreenFlipY : EventId.RenderOnscreen;
commands.IssuePluginEventAndData(_renderOnscreenCallback, (int)eventId, view.Renderer.CPtr.Handle);
if (invalidate) InvalidateState(commands);
}
#endif
[StructLayoutAttribute(LayoutKind.Sequential)]
private struct ViewProj
{
public System.IntPtr renderer;
public Noesis.Matrix4 projection;
};
public static void RenderOnscreen(Noesis.View view_, Noesis.Matrix4 projection_, bool flipY,
UnityEngine.Rendering.CommandBuffer commands, bool invalidate, bool clearStencil)
{
FixCommandBuffer(commands);
if (clearStencil)
{
commands.ClearRenderTarget(UnityEngine.Rendering.RTClearFlags.Stencil, UnityEngine.Color.black, 1.0f, 0);
}
var data = new ViewProj() { renderer = view_.Renderer.CPtr.Handle, projection = projection_ };
System.IntPtr ptr = Noesis_AllocateProj();
Marshal.StructureToPtr(data, ptr, false);
var eventId = flipY ? EventId.RenderOnscreenFlipY : EventId.RenderOnscreen;
#if UNITY_2022_2_OR_NEWER
commands.IssuePluginEventAndDataWithFlags(_renderOnscreenMtxCallback, (int)eventId,
invalidate ? CustomMarkerCallbackForceInvalidateStateTracking : CustomMarkerCallbackDefault,
ptr);
#else
commands.IssuePluginEventAndData(_renderOnscreenMtxCallback, (int)eventId, ptr);
if (invalidate) InvalidateState(commands);
#endif
}
#if ENABLE_URP_PACKAGE_RENDER_GRAPH
public static void RenderOnscreen_(Noesis.View view_, Noesis.Matrix4 projection_, bool flipY,
UnityEngine.Rendering.RasterCommandBuffer commands, bool invalidate, bool clearStencil)
{
if (clearStencil)
{
commands.ClearRenderTarget(UnityEngine.Rendering.RTClearFlags.Stencil, UnityEngine.Color.black, 1.0f, 0);
}
var data = new ViewProj() { renderer = view_.Renderer.CPtr.Handle, projection = projection_ };
System.IntPtr ptr = Noesis_AllocateProj();
Marshal.StructureToPtr(data, ptr, false);
var eventId = flipY ? EventId.RenderOnscreenFlipY : EventId.RenderOnscreen;
commands.IssuePluginEventAndData(_renderOnscreenMtxCallback, (int)eventId, ptr);
if (invalidate) InvalidateState(commands);
}
public static void RenderOnscreen_(Noesis.View view_, Noesis.Matrix4 projection_, bool flipY,
UnityEngine.Rendering.UnsafeCommandBuffer commands, bool invalidate, bool clearStencil)
{
if (clearStencil)
{
commands.ClearRenderTarget(UnityEngine.Rendering.RTClearFlags.Stencil, UnityEngine.Color.black, 1.0f, 0);
}
var data = new ViewProj() { renderer = view_.Renderer.CPtr.Handle, projection = projection_ };
System.IntPtr ptr = Noesis_AllocateProj();
Marshal.StructureToPtr(data, ptr, false);
var eventId = flipY ? EventId.RenderOnscreenFlipY : EventId.RenderOnscreen;
commands.IssuePluginEventAndData(_renderOnscreenMtxCallback, (int)eventId, ptr);
if (invalidate) InvalidateState(commands);
}
#endif
[StructLayoutAttribute(LayoutKind.Sequential)]
private struct ViewStereoProj
{
public System.IntPtr renderer;
public Noesis.Matrix4 projection;
public Noesis.Matrix4 leftEyeProjection;
public Noesis.Matrix4 rightEyeProjection;
};
public static void RenderOnscreen(Noesis.View view_, Noesis.Matrix4 projection_, Noesis.Matrix4 leftEyeProjection_,
Noesis.Matrix4 rightEyeProjection_, bool flipY, UnityEngine.Rendering.CommandBuffer commands, bool invalidate,
bool clearStencil)
{
FixCommandBuffer(commands);
if (clearStencil)
{
commands.ClearRenderTarget(UnityEngine.Rendering.RTClearFlags.Stencil, UnityEngine.Color.black, 1.0f, 0);
}
var data = new ViewStereoProj() { renderer = view_.Renderer.CPtr.Handle, projection = projection_, leftEyeProjection = leftEyeProjection_, rightEyeProjection = rightEyeProjection_ };
System.IntPtr ptr = Noesis_AllocateStereoProj();
Marshal.StructureToPtr(data, ptr, false);
var eventId = flipY ? EventId.RenderOnscreenFlipY : EventId.RenderOnscreen;
#if UNITY_2022_2_OR_NEWER
commands.IssuePluginEventAndDataWithFlags(_renderOnscreenMtxStereoCallback, (int)eventId,
invalidate ? CustomMarkerCallbackForceInvalidateStateTracking : CustomMarkerCallbackDefault,
ptr);
#else
commands.IssuePluginEventAndData(_renderOnscreenMtxStereoCallback, (int)eventId, ptr);
if (invalidate) InvalidateState(commands);
#endif
}
#if ENABLE_URP_PACKAGE_RENDER_GRAPH
public static void RenderOnscreen_(Noesis.View view_, Noesis.Matrix4 projection_, Noesis.Matrix4 leftEyeProjection_,
Noesis.Matrix4 rightEyeProjection_, bool flipY, UnityEngine.Rendering.RasterCommandBuffer commands, bool invalidate,
bool clearStencil)
{
if (clearStencil)
{
commands.ClearRenderTarget(UnityEngine.Rendering.RTClearFlags.Stencil, UnityEngine.Color.black, 1.0f, 0);
}
var data = new ViewStereoProj() { renderer = view_.Renderer.CPtr.Handle, projection = projection_, leftEyeProjection = leftEyeProjection_, rightEyeProjection = rightEyeProjection_ };
System.IntPtr ptr = Noesis_AllocateStereoProj();
Marshal.StructureToPtr(data, ptr, false);
var eventId = flipY ? EventId.RenderOnscreenFlipY : EventId.RenderOnscreen;
commands.IssuePluginEventAndData(_renderOnscreenMtxStereoCallback, (int)eventId, ptr);
if (invalidate) InvalidateState(commands);
}
public static void RenderOnscreen_(Noesis.View view_, Noesis.Matrix4 projection_, Noesis.Matrix4 leftEyeProjection_,
Noesis.Matrix4 rightEyeProjection_, bool flipY, UnityEngine.Rendering.UnsafeCommandBuffer commands, bool invalidate,
bool clearStencil)
{
if (clearStencil)
{
commands.ClearRenderTarget(UnityEngine.Rendering.RTClearFlags.Stencil, UnityEngine.Color.black, 1.0f, 0);
}
var data = new ViewStereoProj() { renderer = view_.Renderer.CPtr.Handle, projection = projection_, leftEyeProjection = leftEyeProjection_, rightEyeProjection = rightEyeProjection_ };
System.IntPtr ptr = Noesis_AllocateStereoProj();
Marshal.StructureToPtr(data, ptr, false);
var eventId = flipY ? EventId.RenderOnscreenFlipY : EventId.RenderOnscreen;
commands.IssuePluginEventAndData(_renderOnscreenMtxStereoCallback, (int)eventId, ptr);
if (invalidate) InvalidateState(commands);
}
#endif
///
/// CommandBuffer equivalent to GL.InvalidateState
///
private static void InvalidateState(UnityEngine.Rendering.CommandBuffer commands)
{
// There is nothing equivalent to GL.InvalidateState() for the command buffer
// But as IssuePluginEvent() invalidates the state, invoking an empty C++ callback gives the same effect
// Note that IssuePluginEventAndData() does not invalidate the state
// D3D12 and Vulkan use ConfigureEvent via IUnityGraphics and this is not needed
if (UnityEngine.SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Direct3D12 &&
UnityEngine.SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Vulkan)
{
commands.IssuePluginEvent(_invalidateStateCallback, (int)EventId.NoRender);
}
}
#if ENABLE_URP_PACKAGE_RENDER_GRAPH
private static void InvalidateState(UnityEngine.Rendering.RasterCommandBuffer commands)
{
// There is nothing equivalent to GL.InvalidateState() for the command buffer
// But as IssuePluginEvent() invalidates the state, invoking an empty C++ callback gives the same effect
// Note that IssuePluginEventAndData() does not invalidate the state
// D3D12 and Vulkan use ConfigureEvent via IUnityGraphics and this is not needed
if (UnityEngine.SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Direct3D12 &&
UnityEngine.SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Vulkan)
{
commands.IssuePluginEvent(_invalidateStateCallback, (int)EventId.NoRender);
}
}
private static void InvalidateState(UnityEngine.Rendering.UnsafeCommandBuffer commands)
{
// There is nothing equivalent to GL.InvalidateState() for the command buffer
// But as IssuePluginEvent() invalidates the state, invoking an empty C++ callback gives the same effect
// Note that IssuePluginEventAndData() does not invalidate the state
// D3D12 and Vulkan use ConfigureEvent via IUnityGraphics and this is not needed
if (UnityEngine.SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Direct3D12 &&
UnityEngine.SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Vulkan)
{
commands.IssuePluginEvent(_invalidateStateCallback, (int)EventId.NoRender);
}
}
#endif
private static string ActiveShaderLang()
{
var type = UnityEngine.SystemInfo.graphicsDeviceType;
if (type == UnityEngine.Rendering.GraphicsDeviceType.Direct3D11) return "hlsl";
if (type == UnityEngine.Rendering.GraphicsDeviceType.Direct3D12) return "hlsl";
if (type == UnityEngine.Rendering.GraphicsDeviceType.OpenGLCore) return "glsl";
#if !UNITY_2023_1_OR_NEWER
if (type == UnityEngine.Rendering.GraphicsDeviceType.OpenGLES2) return "essl";
#endif
if (type == UnityEngine.Rendering.GraphicsDeviceType.OpenGLES3) return "essl";
if (type == UnityEngine.Rendering.GraphicsDeviceType.Vulkan) return "spirv";
if (type == UnityEngine.Rendering.GraphicsDeviceType.Metal) return "mtl";
if (type == UnityEngine.Rendering.GraphicsDeviceType.PlayStation4) return "pssl_orbis";
if (type == UnityEngine.Rendering.GraphicsDeviceType.PlayStation5) return "pssl_prospero";
if (type == UnityEngine.Rendering.GraphicsDeviceType.Switch) return "nvn";
if (type == UnityEngine.Rendering.GraphicsDeviceType.GameCoreXboxOne) return "hlsl";
if (type == UnityEngine.Rendering.GraphicsDeviceType.GameCoreXboxSeries) return "hlsl";
return "";
}
private static uint StrHash(string str)
{
uint result = 2166136261;
foreach (char c in str)
{
result = (result * 16777619) ^ c;
}
return result;
}
///
/// Creates a custom pixel shader
///
public static void CreatePixelShader(NoesisShader shader)
{
if (shader.code != null && shader.code.Length > 0)
{
var stream = new System.IO.MemoryStream(shader.code);
var reader = new System.IO.BinaryReader(stream);
while (stream.Position < stream.Length)
{
uint id = (uint)reader.ReadInt32();
int size = reader.ReadInt32();
if (id == StrHash(ActiveShaderLang()))
{
byte[] label = System.Text.Encoding.ASCII.GetBytes(shader.label);
System.IntPtr data = Noesis_AllocateNative(4 * 4 + size + label.Length);
int[] args = new int[] { shader.type, size, label.Length, 0 };
Marshal.Copy(args, 0, data, 4);
Marshal.Copy(shader.code, (int)stream.Position, data + 16, size);
Marshal.Copy(label, 0, data + 16 + size, label.Length);
if (_nextShaderId == 0)
{
// At domain reload _nextShaderId is reset, let's keep in sync with the render thread
_commands.IssuePluginEventAndData(_renderClearShadersCallback, (int)EventId.NoRender, System.IntPtr.Zero);
}
_commands.IssuePluginEventAndData(_renderCreateShaderCallback, (int)EventId.NoRender, data);
UnityEngine.Graphics.ExecuteCommandBuffer(_commands);
_commands.Clear();
if (shader.type == 1)
{
shader.brush_path = (System.IntPtr)(++_nextShaderId);
shader.brush_path_aa = (System.IntPtr)(++_nextShaderId);
shader.brush_sdf = (System.IntPtr)(++_nextShaderId);
shader.brush_opacity = (System.IntPtr)(++_nextShaderId);
}
else
{
shader.effect = (System.IntPtr)(++_nextShaderId);
}
break;
}
stream.Seek(size, System.IO.SeekOrigin.Current);
}
}
}
///
/// Unregisters given renderer
///
public static void UnregisterView(Noesis.View view, UnityEngine.Rendering.CommandBuffer commands)
{
commands.IssuePluginEventAndData(_renderUnregisterCallback, (int)EventId.NoRender, view.Renderer.CPtr.Handle);
}
///
/// Updates render settings
///
public static void SetRenderSettings()
{
NoesisSettings settings = NoesisSettings.Get();
bool linearRendering = false;
switch (settings.linearRendering)
{
case NoesisSettings.LinearRendering._SamesAsUnity:
{
linearRendering = UnityEngine.QualitySettings.activeColorSpace == UnityEngine.ColorSpace.Linear;
break;
}
case NoesisSettings.LinearRendering._Enabled:
{
linearRendering = true;
break;
}
case NoesisSettings.LinearRendering._Disabled:
{
linearRendering = false;
break;
}
}
int sampleCount = 1;
switch (settings.offscreenSampleCount)
{
case NoesisSettings.OffscreenSampleCount._SameAsUnity:
{
sampleCount = UnityEngine.QualitySettings.antiAliasing;
break;
}
case NoesisSettings.OffscreenSampleCount._1x:
{
sampleCount = 1;
break;
}
case NoesisSettings.OffscreenSampleCount._2x:
{
sampleCount = 2;
break;
}
case NoesisSettings.OffscreenSampleCount._4x:
{
sampleCount = 4;
break;
}
case NoesisSettings.OffscreenSampleCount._8x:
{
sampleCount = 8;
break;
}
}
uint offscreenDefaultNumSurfaces = settings.offscreenInitSurfaces;
uint offscreenMaxNumSurfaces = settings.offscreenMaxSurfaces;
int glyphCacheTextureWidth = 1024;
int glyphCacheTextureHeight = 1024;
switch (settings.glyphTextureSize)
{
case NoesisSettings.TextureSize._256x256:
{
glyphCacheTextureWidth = 256;
glyphCacheTextureHeight = 256;
break;
}
case NoesisSettings.TextureSize._512x512:
{
glyphCacheTextureWidth = 512;
glyphCacheTextureHeight = 512;
break;
}
case NoesisSettings.TextureSize._1024x1024:
{
glyphCacheTextureWidth = 1024;
glyphCacheTextureHeight = 1024;
break;
}
case NoesisSettings.TextureSize._2048x2048:
{
glyphCacheTextureWidth = 2048;
glyphCacheTextureHeight = 2048;
break;
}
case NoesisSettings.TextureSize._4096x4096:
{
glyphCacheTextureWidth = 4096;
glyphCacheTextureHeight = 4096;
break;
}
}
Noesis_RendererSettings(linearRendering, sampleCount, offscreenDefaultNumSurfaces,
offscreenMaxNumSurfaces, glyphCacheTextureWidth, glyphCacheTextureHeight);
}
#region Private
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_GetRenderRegisterCallback();
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_GetUpdateRenderTreeCallback();
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_GetRenderOffscreenCallback();
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_GetRenderOnscreenCallback();
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_GetRenderOnscreenMtxCallback();
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_GetRenderOnscreenMtxStereoCallback();
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_GetInvalidateStateCallback();
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_AllocateNative(int size);
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_AllocateProj();
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_AllocateStereoProj();
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_GetRenderClearShadersCallback();
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_GetRenderCreateShaderCallback();
[DllImport(Noesis.Library.Name)]
private static extern System.IntPtr Noesis_GetRenderUnregisterCallback();
[DllImport(Noesis.Library.Name)]
private static extern void Noesis_RendererSettings(bool linearSpaceRendering, int offscreenSampleCount,
uint offscreenDefaultNumSurfaces, uint offscreenMaxNumSurfaces, int glyphCacheTextureWidth, int glyphCacheTextureHeight);
// Keep this in sync with UnityDevice in C++
private enum EventId
{
NoRender = 0x6446,
RenderOffscreen = 0x6447,
RenderOnscreen = 0x6448,
RenderOnscreenFlipY = 0x6449
}
private static uint _nextShaderId;
private static UnityEngine.Rendering.CommandBuffer _commands = new UnityEngine.Rendering.CommandBuffer();
private static System.IntPtr _renderRegisterCallback = Noesis_GetRenderRegisterCallback();
private static System.IntPtr _updateRenderTreeCallback = Noesis_GetUpdateRenderTreeCallback();
private static System.IntPtr _renderOffscreenCallback = Noesis_GetRenderOffscreenCallback();
private static System.IntPtr _renderOnscreenCallback = Noesis_GetRenderOnscreenCallback();
private static System.IntPtr _renderOnscreenMtxCallback = Noesis_GetRenderOnscreenMtxCallback();
private static System.IntPtr _renderOnscreenMtxStereoCallback = Noesis_GetRenderOnscreenMtxStereoCallback();
private static System.IntPtr _invalidateStateCallback = Noesis_GetInvalidateStateCallback();
private static System.IntPtr _renderClearShadersCallback = Noesis_GetRenderClearShadersCallback();
private static System.IntPtr _renderCreateShaderCallback = Noesis_GetRenderCreateShaderCallback();
private static System.IntPtr _renderUnregisterCallback = Noesis_GetRenderUnregisterCallback();
private static UnityEngine.Material _dummyMaterial;
private static UnityEngine.Mesh _dummyMesh;
private static UnityEngine.Material GetDummyMaterial()
{
if (_dummyMaterial == null)
{
_dummyMaterial = new UnityEngine.Material(UnityEngine.Shader.Find("UI/Default"));
}
return _dummyMaterial;
}
private static UnityEngine.Mesh GetDummyMesh()
{
if (_dummyMesh == null)
{
_dummyMesh = new UnityEngine.Mesh();
_dummyMesh.vertices = new UnityEngine.Vector3[3];
_dummyMesh.vertices[0] = new UnityEngine.Vector3(0, 0, 0);
_dummyMesh.vertices[1] = new UnityEngine.Vector3(0, 0, 0);
_dummyMesh.vertices[2] = new UnityEngine.Vector3(0, 0, 0);
_dummyMesh.triangles = new int[3] { 0, 2, 1 };
}
return _dummyMesh;
}
#endregion
}