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); } } }