/// <summary> /// Function to load the associated sprite texture based on the project metadata. /// </summary> /// <param name="graphics">The graphics interface to use when looking up or creating the texture.</param> /// <param name="fileSystem">The file system containing the file to load.</param> /// <param name="metadata">The metadata for the project.</param> /// <param name="imagePath">The path to the image.</param> /// <param name="usage">The intended usage for the texture.</param> /// <param name="codecs">The list of available codecs.</param> /// <returns>A new texture containing the sprite texture data.</returns> private static GorgonTexture2D GetTexture(GorgonGraphics graphics, IGorgonFileSystem fileSystem, IProjectMetadata metadata, string imagePath, ResourceUsage usage, IReadOnlyDictionary <string, IGorgonImageCodec> codecs) { bool shouldConvertToPremultiplied = false; // First, check to see if this texture isn't already loaded into memory. GorgonTexture2D texture = graphics.LocateResourcesByName <GorgonTexture2D>(imagePath).FirstOrDefault(); if (texture != null) { return(texture); } if (!metadata.ProjectItems.TryGetValue(imagePath, out ProjectItemMetadata textureMetadata)) { return(null); } if (textureMetadata.Attributes.TryGetValue("PremultipliedAlpha", out string isPremultiplied)) { #pragma warning disable CA1806 // Do not ignore method results bool.TryParse(isPremultiplied, out shouldConvertToPremultiplied); #pragma warning restore CA1806 // Do not ignore method results } if ((!textureMetadata.Attributes.TryGetValue(CommonEditorConstants.ContentTypeAttr, out string contentType)) || (!string.Equals(contentType, CommonEditorContentTypes.ImageType, StringComparison.OrdinalIgnoreCase)) || (!textureMetadata.Attributes.TryGetValue("ImageCodec", out string imageCodecTypeName)) || (!codecs.TryGetValue(imageCodecTypeName, out IGorgonImageCodec codec))) { return(null); } IGorgonVirtualFile file = fileSystem.GetFile(imagePath); if (file == null) { return(null); } using (Stream fileStream = file.OpenStream()) { texture = GorgonTexture2D.FromStream(graphics, fileStream, codec, file.Size, new GorgonTexture2DLoadOptions { ConvertToPremultipliedAlpha = shouldConvertToPremultiplied, IsTextureCube = false, Name = file.FullPath, Binding = TextureBinding.ShaderResource, Usage = (((usage & ResourceUsage.None) == ResourceUsage.None) || ((usage & ResourceUsage.Staging) == ResourceUsage.Staging)) ? ResourceUsage.Default : usage }); } return(texture); }
/// <summary> /// Function to load a version 1.x Gorgon sprite. /// </summary> /// <param name="graphics">The graphics interface used to create states.</param> /// <param name="reader">Binary reader to use to read in the data.</param> /// <param name="overrideTexture">The texture to assign to the sprite instead of the texture associated with the name stored in the file.</param> /// <returns>The sprite from the stream data.</returns> private static GorgonSprite LoadSprite(GorgonGraphics graphics, GorgonBinaryReader reader, GorgonTexture2DView overrideTexture) { Version version; string imageName = string.Empty; string headerVersion = reader.ReadString(); if ((!headerVersion.StartsWith("GORSPR", StringComparison.OrdinalIgnoreCase)) || (headerVersion.Length < 7) || (headerVersion.Length > 9)) { throw new GorgonException(GorgonResult.CannotRead, Resources.GOR2DIO_ERR_INVALID_HEADER); } var sprite = new GorgonSprite(); // Get the version information. switch (headerVersion.ToUpperInvariant()) { case "GORSPR1": version = new Version(1, 0); break; case "GORSPR1.1": version = new Version(1, 1); break; case "GORSPR1.2": version = new Version(1, 2); break; default: throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOR2DIO_ERR_VERSION_MISMATCH, headerVersion)); } // We don't need the sprite name. reader.ReadString(); // Find out if we have an image. if (reader.ReadBoolean()) { bool isRenderTarget = reader.ReadBoolean(); imageName = reader.ReadString(); // We won't be supporting reading render targets from sprites in this version. if (isRenderTarget) { // Skip the target data. reader.ReadInt32(); reader.ReadInt32(); reader.ReadInt32(); reader.ReadBoolean(); reader.ReadBoolean(); } } // We don't use "inherited" values anymore. But we need them because // the file doesn't include inherited data. bool InheritAlphaMaskFunction = reader.ReadBoolean(); bool InheritAlphaMaskValue = reader.ReadBoolean(); bool InheritBlending = reader.ReadBoolean(); bool InheritHorizontalWrapping = reader.ReadBoolean(); bool InheritSmoothing = reader.ReadBoolean(); bool InheritStencilCompare = reader.ReadBoolean(); bool InheritStencilEnabled = reader.ReadBoolean(); bool InheritStencilFailOperation = reader.ReadBoolean(); bool InheritStencilMask = reader.ReadBoolean(); bool InheritStencilPassOperation = reader.ReadBoolean(); bool InheritStencilReference = reader.ReadBoolean(); bool InheritStencilZFailOperation = reader.ReadBoolean(); bool InheritVerticalWrapping = reader.ReadBoolean(); bool InheritDepthBias = true; bool InheritDepthTestFunction = true; bool InheritDepthWriteEnabled = true; // Get version 1.1 fields. if ((version.Major == 1) && (version.Minor >= 1)) { InheritDepthBias = reader.ReadBoolean(); InheritDepthTestFunction = reader.ReadBoolean(); InheritDepthWriteEnabled = reader.ReadBoolean(); } // Get the size of the sprite. sprite.Size = new DX.Size2F(reader.ReadSingle(), reader.ReadSingle()); // Older versions of the sprite object used pixel space for their texture coordinates. We will have to // fix up these coordinates into texture space once we have a texture loaded. At this point, there's no guarantee // that the texture was loaded safely, so we'll have to defer it until later. // Also, older versions used the size the determine the area on the texture to cover. So use the size to // get the texture bounds. var textureOffset = new DX.Vector2(reader.ReadSingle(), reader.ReadSingle()); // Read the anchor. // Gorgon v3 anchors are relative, so we need to convert them based on our sprite size. sprite.Anchor = new DX.Vector2(reader.ReadSingle() / sprite.Size.Width, reader.ReadSingle() / sprite.Size.Height); // Get vertex offsets. sprite.CornerOffsets.UpperLeft = new DX.Vector3(reader.ReadSingle(), reader.ReadSingle(), 0); sprite.CornerOffsets.UpperRight = new DX.Vector3(reader.ReadSingle(), reader.ReadSingle(), 0); sprite.CornerOffsets.LowerRight = new DX.Vector3(reader.ReadSingle(), reader.ReadSingle(), 0); sprite.CornerOffsets.LowerLeft = new DX.Vector3(reader.ReadSingle(), reader.ReadSingle(), 0); // Get vertex colors. sprite.CornerColors.UpperLeft = new GorgonColor(reader.ReadInt32()); sprite.CornerColors.UpperRight = new GorgonColor(reader.ReadInt32()); sprite.CornerColors.LowerLeft = new GorgonColor(reader.ReadInt32()); sprite.CornerColors.LowerRight = new GorgonColor(reader.ReadInt32()); // Skip shader information. Version 1.0 had shader information attached to the sprite. if ((version.Major == 1) && (version.Minor < 1)) { if (reader.ReadBoolean()) { reader.ReadString(); reader.ReadBoolean(); if (reader.ReadBoolean()) { reader.ReadString(); } } } // We no longer have an alpha mask function. if (!InheritAlphaMaskFunction) { reader.ReadInt32(); } if (!InheritAlphaMaskValue) { // Direct 3D 9 used a value from 0..255 for alpha masking, we use // a scalar value so convert to a scalar. sprite.AlphaTest = new GorgonRangeF(0.0f, reader.ReadInt32() / 255.0f); } // Set the blending mode. if (!InheritBlending) { // Skip the blending mode. We don't use it per-sprite. reader.ReadInt32(); reader.ReadInt32(); reader.ReadInt32(); } // Get alpha blending mode. if ((version.Major == 1) && (version.Minor >= 2)) { // Skip the blending mode information. We don't use it per-sprite. reader.ReadInt32(); reader.ReadInt32(); } TextureWrap hWrap = TextureWrap.Clamp; TextureWrap vWrap = TextureWrap.Clamp; SampleFilter filter = SampleFilter.MinMagMipLinear; GorgonColor samplerBorder = GorgonColor.White; // Get horizontal wrapping mode. if (!InheritHorizontalWrapping) { hWrap = ConvertImageAddressToTextureAddress(reader.ReadInt32()); } // Get smoothing mode. if (!InheritSmoothing) { filter = ConvertSmoothingToFilter(reader.ReadInt32()); } // Get stencil stuff. if (!InheritStencilCompare) { // We don't use depth/stencil info per sprite anymore. reader.ReadInt32(); } if (!InheritStencilEnabled) { // We don't enable stencil in the same way anymore, so skip this value. reader.ReadBoolean(); } if (!InheritStencilFailOperation) { // We don't use depth/stencil info per sprite anymore. reader.ReadInt32(); } if (!InheritStencilMask) { // We don't use depth/stencil info per sprite anymore. reader.ReadInt32(); } if (!InheritStencilPassOperation) { // We don't use depth/stencil info per sprite anymore. reader.ReadInt32(); } if (!InheritStencilReference) { // We don't use depth/stencil info per sprite anymore. reader.ReadInt32(); } if (!InheritStencilZFailOperation) { // We don't use depth/stencil info per sprite anymore. reader.ReadInt32(); } // Get vertical wrapping mode. if (!InheritVerticalWrapping) { vWrap = ConvertImageAddressToTextureAddress(reader.ReadInt32()); } // Get depth info. if ((version.Major == 1) && (version.Minor >= 1)) { if (!InheritDepthBias) { // Depth bias values are quite different on D3D9 than they are on D3D11, so skip this. reader.ReadSingle(); } if (!InheritDepthTestFunction) { // We don't use depth/stencil info per sprite anymore. reader.ReadInt32(); } if (!InheritDepthWriteEnabled) { // We don't use depth/stencil info per sprite anymore. reader.ReadBoolean(); } samplerBorder = new GorgonColor(reader.ReadInt32()); // The border in the older version defaults to black. To make it more performant, reverse this value to white. if (samplerBorder == GorgonColor.Black) { samplerBorder = GorgonColor.White; } } // Get flipped flags. sprite.HorizontalFlip = reader.ReadBoolean(); sprite.VerticalFlip = reader.ReadBoolean(); GorgonTexture2DView textureView; // Bind the texture (if we have one bound to this sprite) if it's already loaded, otherwise defer it. if ((!string.IsNullOrEmpty(imageName)) && (overrideTexture == null)) { GorgonTexture2D texture = graphics.LocateResourcesByName <GorgonTexture2D>(imageName).FirstOrDefault(); textureView = texture?.GetShaderResourceView(); } else { textureView = overrideTexture; } // If we cannot load the image, then fall back to the standard coordinates. if (textureView == null) { sprite.TextureRegion = new DX.RectangleF(0, 0, 1, 1); } else { sprite.TextureRegion = new DX.RectangleF(textureOffset.X / textureView.Width, textureOffset.Y / textureView.Height, sprite.Size.Width / textureView.Width, sprite.Size.Height / textureView.Height); sprite.TextureSampler = CreateSamplerState(graphics, filter, samplerBorder, hWrap, vWrap); } sprite.Texture = textureView; return(sprite); }
/// <summary> /// Function to read a texture from the JSON data. /// </summary> /// <param name="reader">The JSON reader to use.</param> /// <param name="textureName">The name of the texture.</param> /// <returns></returns> public GorgonTexture2DView ReadTexture(JsonReader reader, out string textureName) { textureName = string.Empty; if ((reader.TokenType != JsonToken.StartObject) || (_graphics == null)) { return(null); } int? texWidth = null; int? texHeight = null; int? texMipCount = null; int? texArrayCount = null; BufferFormat?texFormat = null; int? viewMipStart = null; int? viewMipCount = null; int? viewArrayStart = null; int? viewArrayCount = null; BufferFormat?viewFormat = null; while ((reader.Read()) && (reader.TokenType != JsonToken.EndObject)) { if (reader.TokenType != JsonToken.PropertyName) { continue; } switch (reader.Value.ToString()) { case "name": textureName = reader.ReadAsString(); break; case "texWidth": texWidth = reader.ReadAsInt32(); break; case "texHeight": texHeight = reader.ReadAsInt32(); break; case "texFormat": texFormat = (BufferFormat?)reader.ReadAsInt32(); break; case "texArrayCount": texArrayCount = reader.ReadAsInt32(); break; case "texMipCount": texMipCount = reader.ReadAsInt32(); break; case "arrayStart": viewArrayStart = reader.ReadAsInt32(); break; case "arrayCount": viewArrayCount = reader.ReadAsInt32(); break; case "mipStart": viewMipStart = reader.ReadAsInt32(); break; case "mipCount": viewMipCount = reader.ReadAsInt32(); break; case "format": viewFormat = (BufferFormat?)reader.ReadAsInt32(); break; } } if ((string.IsNullOrWhiteSpace(textureName)) || (texWidth == null) || (texHeight == null) || (texFormat == null) || (texArrayCount == null) || (texMipCount == null) || (viewArrayStart == null) || (viewArrayCount == null) || (viewMipStart == null) || (viewMipCount == null) || (viewFormat == null)) { if (string.IsNullOrWhiteSpace(textureName)) { _graphics?.Log.Print("Attempted to load a texture from JSON data, but texture was not in memory and name is unknown, or JSON texture data is not in the correct format.", LoggingLevel.Verbose); } return(null); } if (_override != null) { return(_override); } GorgonTexture2D texture = _graphics?.LocateResourcesByName <GorgonTexture2D>(textureName) .FirstOrDefault(item => (item.Width == texWidth) && (item.Height == texHeight) && (item.Format == texFormat) && (item.MipLevels == texMipCount) && (item.ArrayCount == texArrayCount)); return(texture?.GetShaderResourceView(viewFormat.Value, viewMipStart.Value, viewMipCount.Value, viewArrayStart.Value, viewArrayCount.Value)); }
/// <summary> /// Function to load a version 1.x Gorgon sprite. /// </summary> /// <param name="graphics">The graphics interface used to create states.</param> /// <param name="reader">Binary reader to use to read in the data.</param> /// <param name="overrideTexture">The texture to assign to the sprite instead of the texture associated with the name stored in the file.</param> /// <returns>The sprite from the stream data.</returns> private static GorgonSprite LoadSprite(GorgonGraphics graphics, GorgonChunkReader reader, GorgonTexture2DView overrideTexture) { var sprite = new GorgonSprite(); if (!reader.HasChunk(FileHeader)) { throw new GorgonException(GorgonResult.CannotRead, Resources.GOR2DIO_ERR_INVALID_HEADER); } reader.Begin(FileHeader); reader.Begin(SpriteDataChunk); sprite.Anchor = reader.Read <DX.Vector2>(); sprite.Size = reader.Read <DX.Size2F>(); sprite.Anchor = new DX.Vector2(sprite.Anchor.X / sprite.Size.Width, sprite.Anchor.Y / sprite.Size.Height); sprite.HorizontalFlip = reader.ReadBoolean(); sprite.VerticalFlip = reader.ReadBoolean(); // Read vertex colors. sprite.CornerColors.UpperLeft = reader.Read <GorgonColor>(); sprite.CornerColors.UpperRight = reader.Read <GorgonColor>(); sprite.CornerColors.LowerLeft = reader.Read <GorgonColor>(); sprite.CornerColors.LowerRight = reader.Read <GorgonColor>(); // Write vertex offsets. sprite.CornerOffsets.UpperLeft = new DX.Vector3(reader.Read <DX.Vector2>(), 0); sprite.CornerOffsets.UpperRight = new DX.Vector3(reader.Read <DX.Vector2>(), 0); sprite.CornerOffsets.LowerLeft = new DX.Vector3(reader.Read <DX.Vector2>(), 0); sprite.CornerOffsets.LowerRight = new DX.Vector3(reader.Read <DX.Vector2>(), 0); reader.End(); // Read rendering information. reader.Begin(RenderDataChunk); // Culling mode is not per-sprite anymore. reader.SkipBytes(Unsafe.SizeOf <CullingMode>()); sprite.AlphaTest = reader.Read <GorgonRangeF>(); // Blending values are not per-sprite anymore. // Depth/stencil values are not per-sprite anymore. reader.SkipBytes(91); reader.End(); // Read texture information. reader.Begin(TextureDataChunk); GorgonColor borderColor = reader.Read <GorgonColor>(); TextureWrap hWrap = ConvertV2TextureWrapToTextureAddress(reader.Read <int>()); TextureWrap vWrap = ConvertV2TextureWrapToTextureAddress(reader.Read <int>()); SampleFilter filter = ConvertV2TextureFilterToFilter(reader.Read <TextureFilter>()); string textureName = reader.ReadString(); GorgonTexture2DView textureView; // Bind the texture (if we have one bound to this sprite) if it's already loaded, otherwise defer it. if ((!string.IsNullOrEmpty(textureName)) && (overrideTexture == null)) { GorgonTexture2D texture = graphics.LocateResourcesByName <GorgonTexture2D>(textureName).FirstOrDefault(); // If we used the editor build to sprite, the path to the texture is stored in the name instead of just the name. // So let's try and strip out the path information and extension and try again. if (texture == null) { textureName = Path.GetFileNameWithoutExtension(textureName); texture = graphics.LocateResourcesByName <GorgonTexture2D>(textureName).FirstOrDefault(); } textureView = texture?.GetShaderResourceView(); } else { textureView = overrideTexture; } sprite.TextureRegion = reader.ReadRectangleF(); if (textureView != null) { // V2 used black transparent by default, so convert it to our default so we can keep from creating unnecessary states. if (borderColor == GorgonColor.BlackTransparent) { borderColor = GorgonColor.White; } sprite.Texture = textureView; sprite.TextureSampler = CreateSamplerState(graphics, filter, borderColor, hWrap, vWrap); } reader.End(); reader.End(); return(sprite); }