Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

425 строки
16 KiB

  1. // Adapted from Frank Luna's "Sprites and Text" example here: http://www.d3dcoder.net/resources.htm
  2. // checkout his books here: http://www.d3dcoder.net/default.htm
  3. using System;
  4. using System.Collections.Generic;
  5. using SharpDX.Direct3D11;
  6. using SharpDX;
  7. using System.Diagnostics;
  8. using SharpDX.D3DCompiler;
  9. using System.Runtime.InteropServices;
  10. namespace RenderHookAPI.Hook.DX11
  11. {
  12. public class DXSprite : Component
  13. {
  14. Device _device;
  15. DeviceContext _deviceContext;
  16. public DXSprite(Device device, DeviceContext deviceContext)
  17. {
  18. _device = device;
  19. _deviceContext = deviceContext;
  20. }
  21. [StructLayout(LayoutKind.Sequential)]
  22. internal struct SpriteVertex
  23. {
  24. public Vector3 Pos;
  25. public Vector2 Tex;
  26. public Color4 Color;
  27. }
  28. [StructLayout(LayoutKind.Sequential)]
  29. internal struct Sprite
  30. {
  31. public Rectangle SrcRect;
  32. public Rectangle DestRect;
  33. public Color4 Color;
  34. public float Z;
  35. public float Angle;
  36. public float Scale;
  37. public Sprite(Rectangle sourceRect, Rectangle destRect, Color4 color)
  38. {
  39. SrcRect = sourceRect;
  40. DestRect = destRect;
  41. Color = color;
  42. Z = 0.0f;
  43. Angle = 0.0f;
  44. Scale = 1.0f;
  45. }
  46. }
  47. bool _initialized;
  48. BlendState _transparentBS;
  49. EffectTechnique _spriteTech;
  50. EffectShaderResourceVariable _spriteMap;
  51. ShaderResourceView _batchTexSRV;
  52. InputLayout _inputLayout;
  53. SharpDX.Direct3D11.Buffer _VB;
  54. SharpDX.Direct3D11.Buffer _IB;
  55. int _texWidth;
  56. int _texHeight;
  57. List<Sprite> _spriteList = new List<Sprite>(128);
  58. float _screenWidth;
  59. float _screenHeight;
  60. CompilationResult _compiledFX;
  61. Effect _effect;
  62. SafeHGlobal _indexBuffer = null;
  63. public bool Initialize()
  64. {
  65. Debug.Assert(!_initialized);
  66. #region Shaders
  67. string SpriteFX = @"Texture2D SpriteTex;
  68. SamplerState samLinear {
  69. Filter = MIN_MAG_MIP_LINEAR;
  70. AddressU = WRAP;
  71. AddressV = WRAP;
  72. };
  73. struct VertexIn {
  74. float3 PosNdc : POSITION;
  75. float2 Tex : TEXCOORD;
  76. float4 Color : COLOR;
  77. };
  78. struct VertexOut {
  79. float4 PosNdc : SV_POSITION;
  80. float2 Tex : TEXCOORD;
  81. float4 Color : COLOR;
  82. };
  83. VertexOut VS(VertexIn vin) {
  84. VertexOut vout;
  85. vout.PosNdc = float4(vin.PosNdc, 1.0f);
  86. vout.Tex = vin.Tex;
  87. vout.Color = vin.Color;
  88. return vout;
  89. };
  90. float4 PS(VertexOut pin) : SV_Target {
  91. return pin.Color*SpriteTex.Sample(samLinear, pin.Tex);
  92. };
  93. technique11 SpriteTech {
  94. pass P0 {
  95. SetVertexShader( CompileShader( vs_5_0, VS() ) );
  96. SetHullShader( NULL );
  97. SetDomainShader( NULL );
  98. SetGeometryShader( NULL );
  99. SetPixelShader( CompileShader( ps_5_0, PS() ) );
  100. }
  101. };";
  102. #endregion
  103. _compiledFX = ToDispose(ShaderBytecode.Compile(SpriteFX, "SpriteTech", "fx_5_0"));
  104. {
  105. if (_compiledFX.HasErrors)
  106. return false;
  107. _effect = ToDispose(new Effect(_device, _compiledFX));
  108. {
  109. _spriteTech = ToDispose(_effect.GetTechniqueByName("SpriteTech"));
  110. _spriteMap = ToDispose(_effect.GetVariableByName("SpriteTex").AsShaderResource());
  111. using (var pass = _spriteTech.GetPassByIndex(0))
  112. {
  113. InputElement[] layoutDesc = {
  114. new InputElement("POSITION", 0, SharpDX.DXGI.Format.R32G32B32_Float, 0, 0, InputClassification.PerVertexData, 0),
  115. new InputElement("TEXCOORD", 0, SharpDX.DXGI.Format.R32G32_Float, 12, 0, InputClassification.PerVertexData, 0),
  116. new InputElement("COLOR", 0, SharpDX.DXGI.Format.R32G32B32A32_Float, 20, 0, InputClassification.PerVertexData, 0)
  117. };
  118. _inputLayout = ToDispose(new InputLayout(_device, pass.Description.Signature, layoutDesc));
  119. }
  120. // Create Vertex Buffer
  121. BufferDescription vbd = new BufferDescription
  122. {
  123. SizeInBytes = 2048 * Marshal.SizeOf(typeof(SpriteVertex)),
  124. Usage = ResourceUsage.Dynamic,
  125. BindFlags = BindFlags.VertexBuffer,
  126. CpuAccessFlags = CpuAccessFlags.Write,
  127. OptionFlags = ResourceOptionFlags.None,
  128. StructureByteStride = 0
  129. };
  130. _VB = ToDispose(new SharpDX.Direct3D11.Buffer(_device, vbd));
  131. // Create and initialise Index Buffer
  132. short[] indices = new short[3072];
  133. for (ushort i = 0; i < 512; ++i)
  134. {
  135. indices[i * 6] = (short)(i * 4);
  136. indices[i * 6 + 1] = (short)(i * 4 + 1);
  137. indices[i * 6 + 2] = (short)(i * 4 + 2);
  138. indices[i * 6 + 3] = (short)(i * 4);
  139. indices[i * 6 + 4] = (short)(i * 4 + 2);
  140. indices[i * 6 + 5] = (short)(i * 4 + 3);
  141. }
  142. _indexBuffer = ToDispose(new SafeHGlobal(indices.Length * Marshal.SizeOf(indices[0])));
  143. Marshal.Copy(indices, 0, _indexBuffer.DangerousGetHandle(), indices.Length);
  144. BufferDescription ibd = new BufferDescription
  145. {
  146. SizeInBytes = 3072 * Marshal.SizeOf(typeof(short)),
  147. Usage = ResourceUsage.Immutable,
  148. BindFlags = BindFlags.IndexBuffer,
  149. CpuAccessFlags = CpuAccessFlags.None,
  150. OptionFlags = ResourceOptionFlags.None,
  151. StructureByteStride = 0
  152. };
  153. _IB = ToDispose(new SharpDX.Direct3D11.Buffer(_device, _indexBuffer.DangerousGetHandle(), ibd));
  154. BlendStateDescription transparentDesc = new BlendStateDescription()
  155. {
  156. AlphaToCoverageEnable = false,
  157. IndependentBlendEnable = false,
  158. };
  159. transparentDesc.RenderTarget[0].IsBlendEnabled = true;
  160. transparentDesc.RenderTarget[0].SourceBlend = BlendOption.SourceAlpha;
  161. transparentDesc.RenderTarget[0].DestinationBlend = BlendOption.InverseSourceAlpha;
  162. transparentDesc.RenderTarget[0].BlendOperation = BlendOperation.Add;
  163. transparentDesc.RenderTarget[0].SourceAlphaBlend = BlendOption.One;
  164. transparentDesc.RenderTarget[0].DestinationAlphaBlend = BlendOption.Zero;
  165. transparentDesc.RenderTarget[0].AlphaBlendOperation = BlendOperation.Add;
  166. transparentDesc.RenderTarget[0].RenderTargetWriteMask = ColorWriteMaskFlags.All;
  167. _transparentBS = ToDispose(new BlendState(_device, transparentDesc));
  168. }
  169. }
  170. _initialized = true;
  171. return true;
  172. }
  173. internal static Color4 ToColor4(System.Drawing.Color color)
  174. {
  175. Vector4 Vec = new Vector4(color.R > 0 ? (float)(color.R / 255.0f) : 0.0f, color.G > 0 ? (float)(color.G / 255.0f) : 0.0f, color.B > 0 ? (float)(color.B / 255.0f) : 0.0f, color.A > 0 ? (float)(color.A / 255.0f) : 0.0f);
  176. return new Color4(Vec);
  177. }
  178. public void DrawImage(int x, int y, float scale, float angle, System.Drawing.Color? color, DXImage image)
  179. {
  180. Debug.Assert(_initialized);
  181. Color4 blendFactor = new Color4(1.0f);
  182. SharpDX.Mathematics.Interop.RawColor4 backupBlendFactor;
  183. int backupMask;
  184. using (var backupBlendState = _deviceContext.OutputMerger.GetBlendState(out backupBlendFactor, out backupMask))
  185. {
  186. _deviceContext.OutputMerger.SetBlendState(_transparentBS, blendFactor);
  187. BeginBatch(image.GetSRV());
  188. Draw(new Rectangle(x, y, (int)(scale * image.Width), (int)(scale * image.Height)), new Rectangle(0, 0, image.Width, image.Height), color.HasValue ? ToColor4(color.Value) : Color4.White, 1.0f, angle);
  189. EndBatch();
  190. _deviceContext.OutputMerger.SetBlendState(backupBlendState, backupBlendFactor, backupMask);
  191. }
  192. }
  193. public void DrawString(int X, int Y, string text, System.Drawing.Color color, DXFont F)
  194. {
  195. Color4 blendFactor = new Color4(1.0f);
  196. SharpDX.Mathematics.Interop.RawColor4 backupBlendFactor;
  197. int backupMask;
  198. using (var backupBlendState = _deviceContext.OutputMerger.GetBlendState(out backupBlendFactor, out backupMask))
  199. {
  200. _deviceContext.OutputMerger.SetBlendState(_transparentBS, blendFactor);
  201. BeginBatch(F.GetFontSheetSRV());
  202. int length = text.Length;
  203. int posX = X;
  204. int posY = Y;
  205. Color4 color4 = ToColor4(color);
  206. for (int i = 0; i < length; ++i)
  207. {
  208. char character = text[i];
  209. if (character == ' ')
  210. posX += F.GetSpaceWidth();
  211. else if (character == '\n')
  212. {
  213. posX = X;
  214. posY += F.GetCharHeight();
  215. }
  216. else
  217. {
  218. Rectangle charRect = F.GetCharRect(character);
  219. int width = charRect.Right - charRect.Left;
  220. int height = charRect.Bottom - charRect.Top;
  221. Draw(new Rectangle(posX, posY, width, height), charRect, color4);
  222. posX += width + 1;
  223. }
  224. }
  225. EndBatch();
  226. _deviceContext.OutputMerger.SetBlendState(backupBlendState, backupBlendFactor, backupMask);
  227. }
  228. }
  229. public void BeginBatch(ShaderResourceView texSRV)
  230. {
  231. Debug.Assert(_initialized);
  232. _batchTexSRV = texSRV;
  233. using (Texture2D tex = _batchTexSRV.ResourceAs<Texture2D>())
  234. {
  235. Texture2DDescription texDesc = tex.Description;
  236. _texWidth = texDesc.Width;
  237. _texHeight = texDesc.Height;
  238. }
  239. _spriteList.Clear();
  240. }
  241. public void EndBatch()
  242. {
  243. Debug.Assert(_initialized);
  244. ViewportF[] vp = _deviceContext.Rasterizer.GetViewports<ViewportF>();
  245. _screenWidth = vp[0].Width;
  246. _screenHeight = vp[0].Height;
  247. int stride = Marshal.SizeOf(typeof(SpriteVertex));
  248. int offset = 0;
  249. _deviceContext.InputAssembler.InputLayout = _inputLayout;
  250. _deviceContext.InputAssembler.SetIndexBuffer(_IB, SharpDX.DXGI.Format.R16_UInt, 0);
  251. _deviceContext.InputAssembler.SetVertexBuffers(0, new[] { _VB }, new[] { stride }, new[] { offset });
  252. _deviceContext.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;
  253. _spriteMap.SetResource(_batchTexSRV);
  254. using (EffectPass pass = _spriteTech.GetPassByIndex(0))
  255. {
  256. pass.Apply(_deviceContext);
  257. var spritesToDraw = _spriteList.Count;
  258. int startIndex = 0;
  259. while (spritesToDraw > 0)
  260. {
  261. if (spritesToDraw <= 512)
  262. {
  263. DrawBatch(startIndex, spritesToDraw);
  264. spritesToDraw = 0;
  265. }
  266. else
  267. {
  268. DrawBatch(startIndex, 512);
  269. startIndex += 512;
  270. spritesToDraw -= 512;
  271. }
  272. }
  273. }
  274. _batchTexSRV = null;
  275. }
  276. public void Draw(Rectangle destinationRect, Rectangle sourceRect, Color4 color, float scale = 1.0f, float angle = 0f, float z = 0f)
  277. {
  278. Sprite sprite = new Sprite(
  279. sourceRect,
  280. destinationRect,
  281. color
  282. )
  283. {
  284. Scale = scale,
  285. Angle = angle,
  286. Z = z
  287. };
  288. _spriteList.Add(sprite);
  289. }
  290. void DrawBatch(int startSpriteIndex, int spriteCount)
  291. {
  292. DataBox mappedData = _deviceContext.MapSubresource(_VB, 0, MapMode.WriteDiscard, MapFlags.None);
  293. // Update the vertices
  294. unsafe
  295. {
  296. SpriteVertex* v = (SpriteVertex*)mappedData.DataPointer.ToPointer();
  297. for (int i = 0; i < spriteCount; ++i)
  298. {
  299. Sprite sprite = _spriteList[startSpriteIndex + i];
  300. SpriteVertex[] quad = new SpriteVertex[4];
  301. BuildSpriteQuad(sprite, ref quad);
  302. v[i * 4] = quad[0];
  303. v[i * 4 + 1] = quad[1];
  304. v[i * 4 + 2] = quad[2];
  305. v[i * 4 + 3] = quad[3];
  306. }
  307. }
  308. _deviceContext.UnmapSubresource(_VB, 0);
  309. _deviceContext.DrawIndexed(spriteCount * 6, 0, 0);
  310. }
  311. Vector3 PointToNdc(int x, int y, float z)
  312. {
  313. Vector3 p;
  314. p.X = 2.0f * (float)x / _screenWidth - 1.0f;
  315. p.Y = 1.0f - 2.0f * (float)y / _screenHeight;
  316. p.Z = z;
  317. return p;
  318. }
  319. void BuildSpriteQuad(Sprite sprite, ref SpriteVertex[] v)
  320. {
  321. if (v.Length < 4)
  322. throw new ArgumentException("must have 4 sprite vertices", "v");
  323. Rectangle dest = sprite.DestRect;
  324. Rectangle src = sprite.SrcRect;
  325. v[0].Pos = PointToNdc(dest.Left, dest.Bottom, sprite.Z);
  326. v[1].Pos = PointToNdc(dest.Left, dest.Top, sprite.Z);
  327. v[2].Pos = PointToNdc(dest.Right, dest.Top, sprite.Z);
  328. v[3].Pos = PointToNdc(dest.Right, dest.Bottom, sprite.Z);
  329. v[0].Tex = new Vector2((float)src.Left / _texWidth, (float)src.Bottom / _texHeight);
  330. v[1].Tex = new Vector2((float)src.Left / _texWidth, (float)src.Top / _texHeight);
  331. v[2].Tex = new Vector2((float)src.Right / _texWidth, (float)src.Top / _texHeight);
  332. v[3].Tex = new Vector2((float)src.Right / _texWidth, (float)src.Bottom / _texHeight);
  333. v[0].Color = sprite.Color;
  334. v[1].Color = sprite.Color;
  335. v[2].Color = sprite.Color;
  336. v[3].Color = sprite.Color;
  337. float tx = 0.5f * (v[0].Pos.X + v[3].Pos.X);
  338. float ty = 0.5f * (v[0].Pos.Y + v[1].Pos.Y);
  339. Vector2 origin = new Vector2(tx, ty);
  340. Vector2 translation = new Vector2(0.0f, 0.0f);
  341. Matrix T = Matrix.AffineTransformation2D(sprite.Scale, origin, sprite.Angle, translation);
  342. for (int i = 0; i < 4; ++i)
  343. {
  344. Vector3 p = v[i].Pos;
  345. p = Vector3.TransformCoordinate(p, T);
  346. v[i].Pos = p;
  347. }
  348. }
  349. }
  350. }