OSDN Git Service

最初のコミット
[shooting3/shootinggame.git] / ShootingGame / BasicSprites.cpp
1 //// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
2 //// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
3 //// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
4 //// PARTICULAR PURPOSE.
5 ////
6 //// Copyright (c) Microsoft Corporation. All rights reserved
7
8 #include "pch.h"
9 #include "BasicSprites.h"
10 #include "DirectXSample.h"
11 #include "BasicLoader.h"
12
13 // Display a performance warning message in Debug builds.
14 #if defined(_DEBUG)
15 #pragma message("Warning: SpriteBatch performance is significantly decreased in the Debug configuration.  Switch to the Release configuration for better performance.")
16 #endif
17
18 using namespace Microsoft::WRL;
19 using namespace BasicSprites;
20
21 SpriteBatch::SpriteBatch() :
22     m_capacity(0)
23 {
24 }
25
26 void SpriteBatch::Initialize(
27     _In_ ID3D11Device1* d3dDevice,
28     _In_ int capacity = 1024
29     )
30 {
31     m_d3dDevice = d3dDevice;
32     m_d3dDevice->GetImmediateContext1(&m_d3dContext);
33     m_capacity = capacity;
34
35     // Determine the technique that will be used to render the sprites.
36     auto featureLevel = m_d3dDevice->GetFeatureLevel();
37
38     if (featureLevel >= D3D_FEATURE_LEVEL_10_0)
39     {
40         // On DirectX 10+ devices, the Geometry Shader allows the sprite vertices to be 
41         // generated on the GPU, significantly reducing memory bandwidth requirements.
42         m_technique = RenderTechnique::GeometryShader;
43     }
44     else if (featureLevel >= D3D_FEATURE_LEVEL_9_3)
45     {
46         // On DirectX 9.3+ devices, instancing allows shared sprite geometry with unique
47         // per-sprite instance parameters, eliminating redundant data transfer.
48         m_technique = RenderTechnique::Instancing;
49     }
50     else
51     {
52         // On devices that do not support Instancing, sprite vertex data must be replicated
53         // in order to achieve the desired effect.
54         m_technique = RenderTechnique::Replication;
55
56         if (capacity > static_cast<int>(Parameters::MaximumCapacityCompatible))
57         {
58             // The index buffer format for feature-level 9.1 devices may only be 16 bits.
59             // With 4 vertices per sprite, this allows a maximum of (1 << 16) / 4 sprites.
60             throw ref new Platform::InvalidArgumentException();
61         }
62     }
63
64     // Create the texture sampler.
65
66     D3D11_SAMPLER_DESC samplerDesc;
67     ZeroMemory(&samplerDesc, sizeof(samplerDesc));
68     samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
69     samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
70     samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
71     samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
72     samplerDesc.MipLODBias = 0.0f;
73     samplerDesc.MaxAnisotropy = 0;
74     samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
75     samplerDesc.BorderColor[0] = 0.0f;
76     samplerDesc.BorderColor[1] = 0.0f;
77     samplerDesc.BorderColor[2] = 0.0f;
78     samplerDesc.BorderColor[3] = 0.0f;
79     samplerDesc.MinLOD = 0.0f;
80     samplerDesc.MaxLOD = FLT_MAX;
81
82     DX::ThrowIfFailed(
83         m_d3dDevice->CreateSamplerState(
84             &samplerDesc,
85             &m_linearSampler
86             )
87         );
88
89     // Create the blend states.
90
91     D3D11_BLEND_DESC1 blendDesc;
92     ZeroMemory(&blendDesc, sizeof(blendDesc));
93     blendDesc.AlphaToCoverageEnable = false;
94     blendDesc.IndependentBlendEnable = false;
95     blendDesc.RenderTarget[0].BlendEnable = true;
96     blendDesc.RenderTarget[0].LogicOpEnable = false;
97     blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
98     blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
99     blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
100     blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
101     blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
102     blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
103     blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
104
105     DX::ThrowIfFailed(
106         m_d3dDevice->CreateBlendState1(
107             &blendDesc,
108             &m_blendStateAlpha
109             )
110         );
111
112     blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
113
114     DX::ThrowIfFailed(
115         m_d3dDevice->CreateBlendState1(
116             &blendDesc,
117             &m_blendStateAdditive
118             )
119         );
120
121     BasicLoader^ loader = ref new BasicLoader(m_d3dDevice.Get());
122
123     if (m_technique == RenderTechnique::GeometryShader)
124     {
125         D3D11_INPUT_ELEMENT_DESC layoutDesc[] = 
126         {
127             { "TRANSFORM", 0, DXGI_FORMAT_R32G32_FLOAT,   0, 0,  D3D11_INPUT_PER_INSTANCE_DATA, 1 },
128             { "TRANSFORM", 1, DXGI_FORMAT_R32G32_FLOAT,   0, 8,  D3D11_INPUT_PER_INSTANCE_DATA, 1 },
129             { "TRANSFORM", 2, DXGI_FORMAT_R32_FLOAT,      0, 16, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
130             { "COLOR",     0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 20, D3D11_INPUT_PER_INSTANCE_DATA, 1 }
131         };
132         loader->LoadShader(
133             "BasicSprites.GeometryShader.vs.cso",
134             layoutDesc,
135             ARRAYSIZE(layoutDesc),
136             &m_vertexShader,
137             &m_inputLayout
138             );
139         loader->LoadShader(
140             "BasicSprites.GeometryShader.gs.cso",
141             &m_geometryShader
142             );
143     }
144     else if (m_technique == RenderTechnique::Instancing)
145     {
146         D3D11_INPUT_ELEMENT_DESC layoutDesc[] = 
147         {
148             { "POSITION",  0, DXGI_FORMAT_R32G32_FLOAT,   0, 0,  D3D11_INPUT_PER_VERTEX_DATA,   0 },
149             { "TEXCOORD",  0, DXGI_FORMAT_R32G32_FLOAT,   0, 8,  D3D11_INPUT_PER_VERTEX_DATA,   0 },
150             { "TRANSFORM", 0, DXGI_FORMAT_R32G32_FLOAT,   1, 0,  D3D11_INPUT_PER_INSTANCE_DATA, 1 },
151             { "TRANSFORM", 1, DXGI_FORMAT_R32G32_FLOAT,   1, 8,  D3D11_INPUT_PER_INSTANCE_DATA, 1 },
152             { "TRANSFORM", 2, DXGI_FORMAT_R32_FLOAT,      1, 16, D3D11_INPUT_PER_INSTANCE_DATA, 1 },
153             { "COLOR",     0, DXGI_FORMAT_R8G8B8A8_UNORM, 1, 20, D3D11_INPUT_PER_INSTANCE_DATA, 1 }
154         };
155         loader->LoadShader(
156             "BasicSprites.Instancing.vs.cso",
157             layoutDesc,
158             ARRAYSIZE(layoutDesc),
159             &m_vertexShader,
160             &m_inputLayout
161             );
162     }
163     else if (m_technique == RenderTechnique::Replication)
164     {
165         D3D11_INPUT_ELEMENT_DESC layoutDesc[] = 
166         {
167             { "POSITIONT", 0, DXGI_FORMAT_R32G32_FLOAT,   0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
168             { "TEXCOORD",  0, DXGI_FORMAT_R32G32_FLOAT,   0, 8,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
169             { "COLOR",     0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 }
170         };
171         loader->LoadShader(
172             "BasicSprites.Replication.vs.cso",
173             layoutDesc,
174             ARRAYSIZE(layoutDesc),
175             &m_vertexShader,
176             &m_inputLayout
177             );
178     }
179
180     loader->LoadShader(
181         "BasicSprites.ps.cso",
182         &m_pixelShader
183         );
184
185     // Create buffers.
186
187     if (m_technique == RenderTechnique::GeometryShader)
188     {
189         // Create the instance data buffer.
190
191         DX::ThrowIfFailed(
192             m_d3dDevice->CreateBuffer(
193                 &CD3D11_BUFFER_DESC(
194                     m_capacity * sizeof(InstanceData),
195                     D3D11_BIND_VERTEX_BUFFER,
196                     D3D11_USAGE_DEFAULT,
197                     0
198                     ),
199                 nullptr,
200                 &m_instanceDataBuffer
201                 )
202             );
203
204         m_instanceData.reset(new InstanceData[m_capacity]);
205     }
206     else if (m_technique == RenderTechnique::Instancing)
207     {
208         // Create the vertex buffer.
209
210         InstancingVertex vertexBufferData[] = 
211         {
212             { float2(-1.0f,  1.0f), float2(0.0f, 0.0f) },
213             { float2( 1.0f,  1.0f), float2(1.0f, 0.0f) },
214             { float2(-1.0f, -1.0f), float2(0.0f, 1.0f) },
215             { float2( 1.0f, -1.0f), float2(1.0f, 1.0f) }
216         };
217
218         D3D11_SUBRESOURCE_DATA vertexInitialData;
219         vertexInitialData.pSysMem = vertexBufferData;
220         vertexInitialData.SysMemPitch = 0;
221         vertexInitialData.SysMemSlicePitch = 0;
222
223         DX::ThrowIfFailed(
224             m_d3dDevice->CreateBuffer(
225                 &CD3D11_BUFFER_DESC(
226                     sizeof(vertexBufferData),
227                     D3D11_BIND_VERTEX_BUFFER,
228                     D3D11_USAGE_DEFAULT,
229                     0
230                     ),
231                 &vertexInitialData,
232                 &m_vertexBuffer
233                 )
234             );
235
236         // Create the instance data buffer.
237
238         DX::ThrowIfFailed(
239             m_d3dDevice->CreateBuffer(
240                 &CD3D11_BUFFER_DESC(
241                     m_capacity * sizeof(InstanceData),
242                     D3D11_BIND_VERTEX_BUFFER,
243                     D3D11_USAGE_DEFAULT,
244                     0
245                     ),
246                 nullptr,
247                 &m_instanceDataBuffer
248                 )
249             );
250
251         m_instanceData.reset(new InstanceData[m_capacity]);
252
253         // Create the index buffer.
254
255         unsigned int indexBufferData[] = 
256         {
257             0, 1, 2,
258             1, 3, 2
259         };
260
261         D3D11_SUBRESOURCE_DATA indexInitialData;
262         indexInitialData.pSysMem = indexBufferData;
263         indexInitialData.SysMemPitch = 0;
264         indexInitialData.SysMemSlicePitch = 0;
265
266         DX::ThrowIfFailed(
267             m_d3dDevice->CreateBuffer(
268                 &CD3D11_BUFFER_DESC(
269                     sizeof(indexBufferData),
270                     D3D11_BIND_INDEX_BUFFER,
271                     D3D11_USAGE_DEFAULT,
272                     0
273                     ),
274                 &indexInitialData,
275                 &m_indexBuffer
276                 )
277             );
278     }
279     else if (m_technique == RenderTechnique::Replication)
280     {
281         // Create the vertex buffer.
282
283         DX::ThrowIfFailed(
284             m_d3dDevice->CreateBuffer(
285                 &CD3D11_BUFFER_DESC(
286                     m_capacity * 4 * sizeof(ReplicationVertex),
287                     D3D11_BIND_VERTEX_BUFFER,
288                     D3D11_USAGE_DEFAULT,
289                     0
290                     ),
291                 nullptr,
292                 &m_vertexBuffer
293                 )
294             );
295
296         m_vertexData.reset(new ReplicationVertex[m_capacity * 4]);
297
298         // Create the index buffer.
299
300         std::unique_ptr<unsigned short[]> indexBufferData(new unsigned short[m_capacity * 6]);
301
302         for (int i = 0; i < m_capacity; i++)
303         {
304             indexBufferData[i * 6 + 0] = i * 4 + 0;
305             indexBufferData[i * 6 + 1] = i * 4 + 1;
306             indexBufferData[i * 6 + 2] = i * 4 + 2;
307             indexBufferData[i * 6 + 3] = i * 4 + 1;
308             indexBufferData[i * 6 + 4] = i * 4 + 3;
309             indexBufferData[i * 6 + 5] = i * 4 + 2;
310         }
311
312         D3D11_SUBRESOURCE_DATA initialData;
313         initialData.pSysMem = indexBufferData.get();
314         initialData.SysMemPitch = 0;
315         initialData.SysMemSlicePitch = 0;
316
317         DX::ThrowIfFailed(
318             m_d3dDevice->CreateBuffer(
319                 &CD3D11_BUFFER_DESC(
320                     m_capacity * 6 * sizeof(unsigned short),
321                     D3D11_BIND_INDEX_BUFFER,
322                     D3D11_USAGE_DEFAULT,
323                     0
324                     ),
325                 &initialData,
326                 &m_indexBuffer
327                 )
328             );
329     }
330
331     if (m_technique == RenderTechnique::GeometryShader || m_technique == RenderTechnique::Instancing)
332     {
333         // Both the Geometry Shader and Instancing techniques scale geometry in shader code.
334         // As a result, they require information about the render target size.
335         DX::ThrowIfFailed(
336             m_d3dDevice->CreateBuffer(
337                 &CD3D11_BUFFER_DESC(
338                     16, // Constant buffer sizes must be a multiple of 16 bytes.  16 is sufficient for the required float2 data.
339                     D3D11_BIND_CONSTANT_BUFFER,
340                     D3D11_USAGE_DYNAMIC,
341                     D3D11_CPU_ACCESS_WRITE
342                     ),
343                 nullptr,
344                 &m_renderTargetInfoCbuffer
345                 )
346             );
347     }
348 }
349
350 void SpriteBatch::AddTexture(
351     _In_ ID3D11Texture2D* texture
352     )
353 {
354     TextureMapElement mapElement;
355     DX::ThrowIfFailed(
356         m_d3dDevice->CreateShaderResourceView(
357             texture,
358             &CD3D11_SHADER_RESOURCE_VIEW_DESC(
359                 texture,
360                 D3D11_SRV_DIMENSION_TEXTURE2D
361                 ),
362             &mapElement.srv
363             )
364         );
365
366     D3D11_TEXTURE2D_DESC textureDesc;
367     texture->GetDesc(&textureDesc);
368     mapElement.size = float2(
369         static_cast<float>(textureDesc.Width),
370         static_cast<float>(textureDesc.Height)
371         );
372
373     m_textureMap[texture] = mapElement;
374 }
375
376 void SpriteBatch::RemoveTexture(
377     _In_ ID3D11Texture2D* texture
378     )
379 {
380     m_textureMap.erase(texture);
381 }
382
383 void SpriteBatch::Begin()
384 {
385     // Reset internal sprite data.
386
387     m_numSpritesDrawn = 0;
388     m_spritesInRun = 0;
389     m_spriteRuns.clear();
390
391     // Get the current render target dimensions and logical DPI.
392     
393     ComPtr<ID3D11RenderTargetView> renderTargetView;
394     m_d3dContext->OMGetRenderTargets(
395         1,
396         &renderTargetView,
397         nullptr
398         );
399
400     ComPtr<ID3D11Resource> renderTarget;
401     renderTargetView->GetResource(&renderTarget);
402
403     ComPtr<ID3D11Texture2D> renderTargetTexture;
404     renderTarget.As(&renderTargetTexture);
405
406     D3D11_TEXTURE2D_DESC renderTargetTextureDesc;
407     renderTargetTexture->GetDesc(&renderTargetTextureDesc);
408
409     m_renderTargetSize = float2(
410         static_cast<float>(renderTargetTextureDesc.Width),
411         static_cast<float>(renderTargetTextureDesc.Height)
412         );
413
414     m_dpi = Windows::Graphics::Display::DisplayProperties::LogicalDpi;
415 }
416
417 void SpriteBatch::End()
418 {
419     // If no sprites were drawn, do nothing.
420     if (m_numSpritesDrawn == 0)
421     {
422         return;
423     }
424
425     // Save the final sprite run info.
426
427     SpriteRunInfo runInfo;
428     runInfo.textureView = m_currentTextureView;
429     runInfo.blendState = m_currentBlendState;
430     runInfo.numSprites = m_spritesInRun;
431     m_spriteRuns.push_back(runInfo);
432
433     // Update the buffer data.
434
435     if (m_technique == RenderTechnique::GeometryShader || m_technique == RenderTechnique::Instancing)
436     {
437         m_d3dContext->UpdateSubresource(
438             m_instanceDataBuffer.Get(),
439             0,
440             &CD3D11_BOX(0, 0, 0, sizeof(InstanceData) * m_numSpritesDrawn, 1, 1),
441             m_instanceData.get(),
442             0,
443             0
444             );
445     }
446     else if (m_technique == RenderTechnique::Replication)
447     {
448         m_d3dContext->UpdateSubresource(
449             m_vertexBuffer.Get(),
450             0,
451             &CD3D11_BOX(0, 0, 0, sizeof(ReplicationVertex) * m_numSpritesDrawn * 4, 1, 1),
452             m_vertexData.get(),
453             0,
454             0
455             );
456     }
457
458     if (m_technique == RenderTechnique::GeometryShader || m_technique == RenderTechnique::Instancing)
459     {
460         D3D11_MAPPED_SUBRESOURCE mappedSubresource;
461         m_d3dContext->Map(
462             m_renderTargetInfoCbuffer.Get(),
463             0,
464             D3D11_MAP_WRITE_DISCARD,
465             0,
466             &mappedSubresource
467             );
468         *static_cast<float2*>(mappedSubresource.pData) = m_renderTargetSize;
469         m_d3dContext->Unmap(
470             m_renderTargetInfoCbuffer.Get(),
471             0
472             );
473     }
474
475     // Set the pipeline state
476
477     if (m_technique == RenderTechnique::Instancing)
478     {
479         m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
480         m_d3dContext->IASetIndexBuffer(
481             m_indexBuffer.Get(),
482             DXGI_FORMAT_R32_UINT,
483             0
484             );
485     }
486     else if (m_technique == RenderTechnique::Replication)
487     {
488         m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
489         m_d3dContext->IASetIndexBuffer(
490             m_indexBuffer.Get(),
491             DXGI_FORMAT_R16_UINT,
492             0
493             );
494     }
495     else if (m_technique == RenderTechnique::GeometryShader)
496     {
497         m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);
498         m_d3dContext->GSSetShader(
499             m_geometryShader.Get(),
500             nullptr,
501             0
502             );
503     }
504
505     m_d3dContext->IASetInputLayout(m_inputLayout.Get());
506
507     m_d3dContext->VSSetShader(
508         m_vertexShader.Get(),
509         nullptr,
510         0
511         );
512
513     if (m_technique == RenderTechnique::GeometryShader)
514     {
515         m_d3dContext->GSSetConstantBuffers(
516             0,
517             1,
518             m_renderTargetInfoCbuffer.GetAddressOf()
519             );
520     }
521     else if (m_technique == RenderTechnique::Instancing)
522     {
523         m_d3dContext->VSSetConstantBuffers(
524             0,
525             1,
526             m_renderTargetInfoCbuffer.GetAddressOf()
527             );
528     }
529
530     m_d3dContext->PSSetShader(
531         m_pixelShader.Get(),
532         nullptr,
533         0
534         );
535
536     m_d3dContext->PSSetSamplers(
537         0,
538         1,
539         m_linearSampler.GetAddressOf()
540         );
541
542     if (m_technique == RenderTechnique::GeometryShader)
543     {
544         unsigned int stride = sizeof(InstanceData);
545         unsigned int offset = 0;
546         m_d3dContext->IASetVertexBuffers(
547             0,
548             1,
549             m_instanceDataBuffer.GetAddressOf(),
550             &stride,
551             &offset
552             );
553     }
554     else if (m_technique == RenderTechnique::Instancing)
555     {
556         unsigned int stride = sizeof(InstancingVertex);
557         unsigned int offset = 0;
558         m_d3dContext->IASetVertexBuffers(
559             0,
560             1,
561             m_vertexBuffer.GetAddressOf(),
562             &stride,
563             &offset
564             );
565     }
566     else if (m_technique == RenderTechnique::Replication)
567     {
568         unsigned int stride = sizeof(ReplicationVertex);
569         unsigned int offset = 0;
570         m_d3dContext->IASetVertexBuffers(
571             0,
572             1,
573             m_vertexBuffer.GetAddressOf(),
574             &stride,
575             &offset
576             );
577     }
578
579     // Draw each sprite run
580
581     unsigned int indexBase = 0;
582     for (auto runIterator = m_spriteRuns.begin(); runIterator != m_spriteRuns.end(); runIterator++)
583     {
584         m_d3dContext->PSSetShaderResources(
585             0,
586             1,
587             &runIterator->textureView
588             );
589
590         const FLOAT blendFactor[] = {0.0f, 0.0f, 0.0f, 0.0f};
591
592         m_d3dContext->OMSetBlendState(
593             runIterator->blendState,
594             nullptr,
595             0xFFFFFFFF
596             );
597
598         if (m_technique == RenderTechnique::GeometryShader)
599         {
600             unsigned int instancesToDraw = runIterator->numSprites;
601             m_d3dContext->DrawInstanced(
602                 1,
603                 instancesToDraw,
604                 0,
605                 indexBase
606                 );
607             indexBase += instancesToDraw;
608         }
609         else if (m_technique == RenderTechnique::Instancing)
610         {
611             unsigned int instancesToDraw = runIterator->numSprites;
612             unsigned int stride = sizeof(InstanceData);
613             unsigned int offset = indexBase * stride;
614             // Instance data offset must be zero for the draw call on feature level 9.3 and below.
615             // Instead, set the offset in the input assembler.
616             m_d3dContext->IASetVertexBuffers(
617                 1,
618                 1,
619                 m_instanceDataBuffer.GetAddressOf(),
620                 &stride,
621                 &offset
622                 );
623             m_d3dContext->DrawIndexedInstanced(
624                 6,
625                 instancesToDraw,
626                 0,
627                 0,
628                 0
629                 );
630             indexBase += instancesToDraw;
631         }
632         else if (m_technique == RenderTechnique::Replication)
633         {
634             unsigned int indicesToDraw = runIterator->numSprites * 6;
635             m_d3dContext->DrawIndexed(indicesToDraw, indexBase, 0);
636             indexBase += indicesToDraw;
637         }        
638     }
639 }
640
641 unsigned int SpriteBatch::MakeUnorm(float4 color)
642 {
643     unsigned int r = max(0, min(255, static_cast<unsigned int>(color.r * 255.0f)));
644     unsigned int g = max(0, min(255, static_cast<unsigned int>(color.g * 255.0f)));
645     unsigned int b = max(0, min(255, static_cast<unsigned int>(color.b * 255.0f)));
646     unsigned int a = max(0, min(255, static_cast<unsigned int>(color.a * 255.0f)));
647     return
648         (a << 24) |
649         (b << 16) |
650         (g << 8) |
651         r;
652 }
653
654 float2 SpriteBatch::StandardOrigin(float2 position, PositionUnits positionUnits, float2 renderTargetSize, float dpi)
655 {
656     float2 origin;
657     if (positionUnits == PositionUnits::Pixels)
658     {
659         origin.x = (position.x / renderTargetSize.x) * 2.0f - 1.0f;
660         origin.y = 1.0f - (position.y / renderTargetSize.y) * 2.0f;
661     }
662     else if (positionUnits == PositionUnits::DIPs)
663     {
664         origin.x = ((position.x * dpi / 96.0f) / renderTargetSize.x) * 2.0f - 1.0f;
665         origin.y = 1.0f - ((position.y * dpi / 96.0f) / renderTargetSize.y) * 2.0f;
666     }
667     else if (positionUnits == PositionUnits::Normalized)
668     {
669         origin.x = position.x * 2.0f - 1.0f;
670         origin.y = 1.0f - position.y * 2.0f;
671     }
672     else if (positionUnits == PositionUnits::UniformWidth)
673     {
674         origin.x = position.x * 2.0f - 1.0f;
675         origin.y = 1.0f - position.y * (renderTargetSize.x / renderTargetSize.y) * 2.0f;
676     }
677     else if (positionUnits == PositionUnits::UniformHeight)
678     {
679         origin.x = position.x * (renderTargetSize.y / renderTargetSize.x) * 2.0f - 1.0f;
680         origin.y = 1.0f - position.y * 2.0f;
681     }
682     return origin;
683 }
684
685 float2 SpriteBatch::StandardOffset(float2 size, SizeUnits sizeUnits, float2 spriteSize, float dpi)
686 {
687     float2 offset;
688     if (sizeUnits == SizeUnits::Pixels)
689     {
690         offset = size;
691     }
692     else if (sizeUnits == SizeUnits::DIPs)
693     {
694         offset = size * dpi / 96.0f;
695     }
696     else if (sizeUnits == SizeUnits::Normalized)
697     {
698         offset = spriteSize * size;
699     }
700     return offset;
701 }
702
703 void SpriteBatch::Draw(
704     _In_ ID3D11Texture2D* texture,
705     _In_ float2 position,
706     _In_ PositionUnits positionUnits = PositionUnits::DIPs
707     )
708 {
709     Draw(
710         texture,
711         position,
712         positionUnits,
713         float2(1.0f, 1.0f),
714         SizeUnits::Normalized
715         );
716 }
717
718 void SpriteBatch::Draw(
719     _In_ ID3D11Texture2D* texture,
720     _In_ float2 position,
721     _In_ PositionUnits positionUnits,
722     _In_ float2 size,
723     _In_ SizeUnits sizeUnits
724     )
725 {
726     Draw(
727         texture,
728         position,
729         positionUnits,
730         size,
731         sizeUnits,
732         float4(1.0f, 1.0f, 1.0f, 1.0f)
733         );
734 }
735
736 void SpriteBatch::Draw(
737     _In_ ID3D11Texture2D* texture,
738     _In_ float2 position,
739     _In_ PositionUnits positionUnits,
740     _In_ float2 size,
741     _In_ SizeUnits sizeUnits,
742     _In_ float4 color
743     )
744 {
745     Draw(
746         texture,
747         position,
748         positionUnits,
749         size,
750         sizeUnits,
751         color,
752         0.0f
753         );
754 }
755
756 void SpriteBatch::Draw(
757     _In_ ID3D11Texture2D* texture,
758     _In_ float2 position,
759     _In_ PositionUnits positionUnits,
760     _In_ float2 size,
761     _In_ SizeUnits sizeUnits,
762     _In_ float4 color,
763     _In_ float rotation
764     )
765 {
766     Draw(
767         texture,
768         position,
769         positionUnits,
770         size,
771         sizeUnits,
772         color,
773         rotation,
774         BlendMode::Alpha
775         );
776 }
777
778 void SpriteBatch::Draw(
779     _In_ ID3D11Texture2D* texture,
780     _In_ float2 position,
781     _In_ PositionUnits positionUnits,
782     _In_ float2 size,
783     _In_ SizeUnits sizeUnits,
784     _In_ float4 color,
785     _In_ float rotation,
786     _In_ BlendMode blendMode
787     )
788 {
789     // Fail if drawing this sprite would exceed the capacity of the sprite batch.
790     if (m_numSpritesDrawn >= m_capacity)
791     {
792         throw ref new Platform::OutOfBoundsException();
793     }
794
795     // Retrieve information about the sprite.
796     TextureMapElement element = m_textureMap[texture];
797     ID3D11ShaderResourceView* textureView = element.srv.Get();
798     float2 textureSize = element.size;
799     ID3D11BlendState1* blendState = blendMode == BlendMode::Additive ? m_blendStateAdditive.Get() : m_blendStateAlpha.Get();
800
801     // Fail if the texture has not previously been added to the sprite batch.
802     if (textureView == nullptr)
803     {
804         throw ref new Platform::NullReferenceException();
805     }
806
807     // Unless this is the first sprite run, save out the previous run info if a new run is required.
808     if (
809         m_numSpritesDrawn > 0 && (
810             textureView != m_currentTextureView ||
811             blendState != m_currentBlendState
812             )
813         )
814     {
815         SpriteRunInfo runInfo;
816         runInfo.textureView = m_currentTextureView;
817         runInfo.blendState = m_currentBlendState;
818         runInfo.numSprites = m_spritesInRun;
819         m_spriteRuns.push_back(runInfo);
820         m_spritesInRun = 0; // Reset for the next sprite run.
821     }
822     m_currentTextureView = textureView;
823     m_currentBlendState = blendState;
824
825     // Add the sprite to the buffer.
826
827     float2 origin = StandardOrigin(position, positionUnits, m_renderTargetSize, m_dpi);
828     float2 offset = StandardOffset(size, sizeUnits, textureSize, m_dpi);
829
830     if (m_technique == RenderTechnique::GeometryShader || m_technique == RenderTechnique::Instancing)
831     {
832         m_instanceData[m_numSpritesDrawn].origin = origin;
833         m_instanceData[m_numSpritesDrawn].offset = offset;
834         m_instanceData[m_numSpritesDrawn].rotation = rotation;
835         m_instanceData[m_numSpritesDrawn].color = MakeUnorm(color);
836     }
837     else if (m_technique == RenderTechnique::Replication)
838     {
839         float2 offsets[4] = 
840         {
841             float2(-offset.x,  offset.y),
842             float2( offset.x,  offset.y),
843             float2(-offset.x, -offset.y),
844             float2( offset.x, -offset.y)
845         };
846
847         float sinRotation = sinf(rotation);
848         float cosRotation = cosf(rotation);
849
850         for (int i = 0; i < 4; i++)
851         {
852             offsets[i] = float2(
853                 offsets[i].x * cosRotation - offsets[i].y * sinRotation,
854                 offsets[i].x * sinRotation + offsets[i].y * cosRotation
855                 );
856             offsets[i].x /= m_renderTargetSize.x;
857             offsets[i].y /= m_renderTargetSize.y;
858         }
859
860         // Write vertex buffer data.
861
862         ReplicationVertex* singleSpriteVertices = &m_vertexData[m_numSpritesDrawn * 4];
863         unsigned int colorUnorm = MakeUnorm(color);
864         singleSpriteVertices[0].pos = origin + offsets[0];
865         singleSpriteVertices[1].pos = origin + offsets[1];
866         singleSpriteVertices[2].pos = origin + offsets[2];
867         singleSpriteVertices[3].pos = origin + offsets[3];
868         singleSpriteVertices[0].color = colorUnorm;
869         singleSpriteVertices[1].color = colorUnorm;
870         singleSpriteVertices[2].color = colorUnorm;
871         singleSpriteVertices[3].color = colorUnorm;
872         singleSpriteVertices[0].tex = float2(0.0f, 0.0f);
873         singleSpriteVertices[1].tex = float2(1.0f, 0.0f);
874         singleSpriteVertices[2].tex = float2(0.0f, 1.0f);
875         singleSpriteVertices[3].tex = float2(1.0f, 1.0f);
876     }
877
878     m_spritesInRun++;
879     m_numSpritesDrawn++;
880 }