using System;
using UnityEngine;
using UnityEngine.XR.ARSubsystems;
using UnityEngine.Rendering;
using Object = UnityEngine.Object;
namespace UnityEngine.XR.ARFoundation
{
///
/// Container that pairs a wrapping a native texture
/// object with a Texture that is created for the native texture object.
///
internal struct ARTextureInfo : IEquatable, IDisposable
{
///
/// Constant for whether the texture is in a linear color space.
///
///
/// Constant for whether the texture is in a linear color space.
///
const bool k_TextureHasLinearColorSpace = false;
///
/// The texture descriptor describing the metadata for the native texture object.
///
///
/// The texture descriptor describing the metadata for the native texture object.
///
public XRTextureDescriptor descriptor
{
get { return m_Descriptor; }
}
XRTextureDescriptor m_Descriptor;
///
/// The Unity Texture object for the native texture.
///
///
/// The Unity Texture object for the native texture.
///
public Texture texture
{
get { return m_Texture; }
}
Texture m_Texture;
///
/// Constructs the texture info with the given descriptor and material.
///
/// The texture descriptor wrapping a native texture object.
public ARTextureInfo(XRTextureDescriptor descriptor)
{
m_Descriptor = descriptor;
m_Texture = CreateTexture(m_Descriptor);
}
///
/// Resets the texture info back to the default state destroying the texture GameObject, if one exists.
///
public void Reset()
{
m_Descriptor.Reset();
DestroyTexture();
}
///
/// Destroys the texture and sets the property to null.
///
void DestroyTexture()
{
if (m_Texture != null)
{
UnityEngine.Object.Destroy(m_Texture);
m_Texture = null;
}
}
///
/// Sets the current descriptor and creates/updates the associated texture as appropriate.
///
/// The texture info to update.
/// The texture descriptor wrapping a native texture object.
///
/// The updated texture information.
///
public static ARTextureInfo GetUpdatedTextureInfo(ARTextureInfo textureInfo, XRTextureDescriptor descriptor)
{
// If the current and given descriptors are equal, exit early from this method.
if (textureInfo.m_Descriptor.Equals(descriptor))
{
return textureInfo;
}
// If the given descriptor is invalid, destroy any existing texture, and return the default texture
// info.
if (!descriptor.valid)
{
textureInfo.DestroyTexture();
return default(ARTextureInfo);
}
DebugWarn.WhenFalse(textureInfo.m_Descriptor.dimension == TextureDimension.None || textureInfo.m_Descriptor.dimension == descriptor.dimension)?.
WithMessage($"Texture descriptor dimension should not change from {textureInfo.m_Descriptor.dimension} to {descriptor.dimension}.");
// If there is a texture already and if the descriptors have identical texture metadata, we only need
// to update the existing texture with the given native texture object.
if ((textureInfo.m_Texture != null) && textureInfo.m_Descriptor.hasIdenticalTextureMetadata(descriptor))
{
// Update the current descriptor with the given descriptor.
textureInfo.m_Descriptor = descriptor;
// Update the current texture with the native texture object.
switch(descriptor.dimension)
{
case TextureDimension.Tex3D:
((Texture3D)textureInfo.m_Texture).UpdateExternalTexture(textureInfo.m_Descriptor.nativeTexture);
break;
case TextureDimension.Tex2D:
((Texture2D)textureInfo.m_Texture).UpdateExternalTexture(textureInfo.m_Descriptor.nativeTexture);
break;
case TextureDimension.Cube:
((Cubemap)textureInfo.m_Texture).UpdateExternalTexture(textureInfo.m_Descriptor.nativeTexture);
break;
default:
throw new NotSupportedException($"'{descriptor.dimension.ToString()}' is not a supported texture type.");
}
}
// Else, we need to destroy the existing texture object and create a new texture object.
else
{
// Update the current descriptor with the given descriptor.
textureInfo.m_Descriptor = descriptor;
// Replace the current texture with a newly created texture, and update the material.
textureInfo.DestroyTexture();
textureInfo.m_Texture = CreateTexture(textureInfo.m_Descriptor);
}
return textureInfo;
}
///
/// Create the texture object for the native texture wrapped by the valid descriptor.
///
/// The texture descriptor wrapping a native texture object.
///
/// If the descriptor is valid, the Texture object created from the texture descriptor. Otherwise,
/// null.
///
static Texture CreateTexture(XRTextureDescriptor descriptor)
{
if (!descriptor.valid)
{
return null;
}
switch(descriptor.dimension)
{
case TextureDimension.Tex3D:
return Texture3D.CreateExternalTexture(descriptor.width, descriptor.height,
descriptor.depth, descriptor.format,
(descriptor.mipmapCount != 0), descriptor.nativeTexture);
case TextureDimension.Tex2D:
var texture = Texture2D.CreateExternalTexture(descriptor.width, descriptor.height,
descriptor.format, (descriptor.mipmapCount != 0),
k_TextureHasLinearColorSpace,
descriptor.nativeTexture);
// NB: SetWrapMode needs to be the first call here, and the value passed
// needs to be kTexWrapClamp - this is due to limitations of what
// wrap modes are allowed for external textures in OpenGL (which are
// used for ARCore), as Texture::ApplySettings will eventually hit
// an assert about an invalid enum (see calls to glTexParameteri
// towards the top of ApiGLES::TextureSampler)
// reference: "3.7.14 External Textures" section of
// https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt
// (it shouldn't ever matter what the wrap mode is set to normally, since
// this is for a pass-through video texture, so we shouldn't ever need to
// worry about the wrap mode as textures should never "wrap")
texture.wrapMode = TextureWrapMode.Clamp;
texture.filterMode = FilterMode.Bilinear;
texture.hideFlags = HideFlags.HideAndDontSave;
return texture;
case TextureDimension.Cube:
return Cubemap.CreateExternalTexture(descriptor.width,
descriptor.format,
(descriptor.mipmapCount != 0),
descriptor.nativeTexture);
default:
return null;
}
}
public static bool IsSupported(XRTextureDescriptor descriptor)
{
if(descriptor.dimension == TextureDimension.Tex3D)
{
return true;
}
else if(descriptor.dimension == TextureDimension.Tex2D)
{
return true;
}
else if(descriptor.dimension == TextureDimension.Cube)
{
return true;
}
else
{
return false;
}
}
public void Dispose()
{
DestroyTexture();
}
public override int GetHashCode()
{
int hash = 486187739;
unchecked
{
hash = hash * 486187739 + m_Descriptor.GetHashCode();
hash = hash * 486187739 + ((m_Texture == null) ? 0 : m_Texture.GetHashCode());
}
return hash;
}
public bool Equals(ARTextureInfo other)
{
return m_Descriptor.Equals(other) && (m_Texture == other.m_Texture);
}
public override bool Equals(System.Object obj)
{
return ((obj is ARTextureInfo) && Equals((ARTextureInfo)obj));
}
public static bool operator ==(ARTextureInfo lhs, ARTextureInfo rhs)
{
return lhs.Equals(rhs);
}
public static bool operator !=(ARTextureInfo lhs, ARTextureInfo rhs)
{
return !lhs.Equals(rhs);
}
}
}