OSDN Git Service

qq
[shooting3/shootinggame.git] / ShootingGame / BasicLoader.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 "BasicLoader.h"
10 #include "BasicShapes.h"
11 #include "DDSTextureLoader.h"
12 #include "DirectXSample.h"
13 #include <memory>
14
15 using namespace Microsoft::WRL;
16 using namespace Windows::Storage;
17 using namespace Windows::Storage::Streams;
18 using namespace Windows::Foundation;
19 using namespace Windows::ApplicationModel;
20
21 BasicLoader::BasicLoader(
22     _In_ ID3D11Device* d3dDevice,
23     _In_opt_ IWICImagingFactory2* wicFactory
24     ) : 
25     m_d3dDevice(d3dDevice),
26     m_wicFactory(wicFactory)
27 {
28     // Create a new BasicReaderWriter to do raw file I/O.
29     m_basicReaderWriter = ref new BasicReaderWriter();
30 }
31
32 template<class DeviceChildType>
33 inline void BasicLoader::SetDebugName(
34     _In_ DeviceChildType* object,
35     _In_ Platform::String^ name
36     )
37 {
38 #if defined(_DEBUG)
39     // Only assign debug names in debug builds.
40
41     char nameString[1024];
42     int nameStringLength = WideCharToMultiByte(
43         CP_ACP,
44         0,
45         name->Data(),
46         -1, 
47         nameString,
48         1024,
49         nullptr,
50         nullptr
51         );
52
53     if (nameStringLength == 0 )
54     {
55         char defaultNameString[] = "BasicLoaderObject";
56         DX::ThrowIfFailed(
57             object->SetPrivateData(
58                 WKPDID_D3DDebugObjectName,
59                 sizeof(defaultNameString) - 1,
60                 defaultNameString
61                 )
62             );
63     }
64     else
65     {
66         DX::ThrowIfFailed(
67             object->SetPrivateData(
68                 WKPDID_D3DDebugObjectName,
69                 nameStringLength - 1,
70                 nameString
71                 )
72             );
73     }
74 #endif
75 }
76
77 Platform::String^ BasicLoader::GetExtension(
78     _In_ Platform::String^ filename
79     )
80 {
81     int lastDotIndex = -1;
82     for (int i = filename->Length() - 1; i >= 0 && lastDotIndex == -1; i--)
83     {
84         if (*(filename->Data() + i) == '.')
85         {
86             lastDotIndex = i;
87         }
88     }
89     if (lastDotIndex != -1)
90     {
91         std::unique_ptr<wchar_t[]> extension(new wchar_t[filename->Length() - lastDotIndex]);
92         for (unsigned int i = 0; i < filename->Length() - lastDotIndex; i++)
93         {
94             extension[i] = tolower(*(filename->Data() + lastDotIndex + 1 + i));
95         }
96         return ref new Platform::String(extension.get());
97     }
98     return "";
99 }
100
101 void BasicLoader::CreateTexture(
102     _In_ bool decodeAsDDS,
103     _In_reads_bytes_(dataSize) byte* data,
104     _In_ uint32 dataSize,
105     _Out_opt_ ID3D11Texture2D** texture,
106     _Out_opt_ ID3D11ShaderResourceView** textureView,
107     _In_opt_ Platform::String^ debugName
108     )
109 {
110     ComPtr<ID3D11ShaderResourceView> shaderResourceView;
111     ComPtr<ID3D11Texture2D> texture2D;
112
113     if (decodeAsDDS)
114     {
115         ComPtr<ID3D11Resource> resource;
116
117         if (textureView == nullptr)
118         {
119             CreateDDSTextureFromMemory(
120                 m_d3dDevice.Get(),
121                 data,
122                 dataSize,
123                 &resource,
124                 nullptr
125                 );
126         }
127         else
128         {
129             CreateDDSTextureFromMemory(
130                 m_d3dDevice.Get(),
131                 data,
132                 dataSize,
133                 &resource,
134                 &shaderResourceView
135                 );
136         }
137     
138         DX::ThrowIfFailed(
139             resource.As(&texture2D)
140             );
141     }
142     else
143     {
144         if (m_wicFactory.Get() == nullptr)
145         {
146             // A WIC factory object is required in order to load texture
147             // assets stored in non-DDS formats.  If BasicLoader was not
148             // initialized with one, create one as needed.
149             DX::ThrowIfFailed(
150                 CoCreateInstance(
151                     CLSID_WICImagingFactory,
152                     nullptr,
153                     CLSCTX_INPROC_SERVER,
154                     IID_PPV_ARGS(&m_wicFactory)
155                     )
156                 );
157         }
158
159         ComPtr<IWICStream> stream;
160         DX::ThrowIfFailed(
161             m_wicFactory->CreateStream(&stream)
162             );
163
164         DX::ThrowIfFailed(
165             stream->InitializeFromMemory(
166                 data,
167                 dataSize
168                 )
169             );
170
171         ComPtr<IWICBitmapDecoder> bitmapDecoder;
172         DX::ThrowIfFailed(
173             m_wicFactory->CreateDecoderFromStream(
174                 stream.Get(),
175                 nullptr,
176                 WICDecodeMetadataCacheOnDemand,
177                 &bitmapDecoder
178                 )
179             );
180
181         ComPtr<IWICBitmapFrameDecode> bitmapFrame;
182         DX::ThrowIfFailed(
183             bitmapDecoder->GetFrame(0, &bitmapFrame)
184             );
185
186         ComPtr<IWICFormatConverter> formatConverter;
187         DX::ThrowIfFailed(
188             m_wicFactory->CreateFormatConverter(&formatConverter)
189             );
190
191         DX::ThrowIfFailed(
192             formatConverter->Initialize(
193                 bitmapFrame.Get(),
194                 GUID_WICPixelFormat32bppPBGRA,
195                 WICBitmapDitherTypeNone,
196                 nullptr,
197                 0.0,
198                 WICBitmapPaletteTypeCustom
199                 )
200             );
201
202         uint32 width;
203         uint32 height;
204         DX::ThrowIfFailed(
205             bitmapFrame->GetSize(&width, &height)
206             );
207
208         std::unique_ptr<byte[]> bitmapPixels(new byte[width * height * 4]);
209         DX::ThrowIfFailed(
210             formatConverter->CopyPixels(
211                 nullptr,
212                 width * 4,
213                 width * height * 4,
214                 bitmapPixels.get()
215                 )
216             );
217
218         D3D11_SUBRESOURCE_DATA initialData;
219         ZeroMemory(&initialData, sizeof(initialData));
220         initialData.pSysMem = bitmapPixels.get();
221         initialData.SysMemPitch = width * 4;
222         initialData.SysMemSlicePitch = 0;
223
224         DX::ThrowIfFailed(
225             m_d3dDevice->CreateTexture2D(
226                 &CD3D11_TEXTURE2D_DESC(
227                     DXGI_FORMAT_B8G8R8A8_UNORM,
228                     width,
229                     height,
230                     1,
231                     1
232                     ),
233                 &initialData,
234                 &texture2D
235                 )
236             );
237
238         if (textureView != nullptr)
239         {
240             DX::ThrowIfFailed(
241                 m_d3dDevice->CreateShaderResourceView(
242                     texture2D.Get(),
243                     &CD3D11_SHADER_RESOURCE_VIEW_DESC(
244                         texture2D.Get(),
245                         D3D11_SRV_DIMENSION_TEXTURE2D
246                         ),
247                     &shaderResourceView
248                     )
249                 );
250         }
251     }
252
253     SetDebugName(texture2D.Get(), debugName);
254
255     if (texture != nullptr)
256     {
257         *texture = texture2D.Detach();
258     }
259     if (textureView != nullptr)
260     {
261         *textureView = shaderResourceView.Detach();
262     }
263 }
264
265 void BasicLoader::CreateInputLayout(
266     _In_reads_bytes_(bytecodeSize) byte* bytecode,
267     _In_ uint32 bytecodeSize,
268     _In_reads_opt_(layoutDescNumElements) D3D11_INPUT_ELEMENT_DESC* layoutDesc,
269     _In_ uint32 layoutDescNumElements,
270     _Out_ ID3D11InputLayout** layout
271     )
272 {
273     if (layoutDesc == nullptr)
274     {
275         // If no input layout is specified, use the BasicVertex layout.
276         const D3D11_INPUT_ELEMENT_DESC basicVertexLayoutDesc[] = 
277         {
278             { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
279             { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
280             { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
281         };
282
283         DX::ThrowIfFailed(
284             m_d3dDevice->CreateInputLayout(
285                 basicVertexLayoutDesc,
286                 ARRAYSIZE(basicVertexLayoutDesc),
287                 bytecode,
288                 bytecodeSize,
289                 layout
290                 )
291             );
292     }
293     else
294     {
295         DX::ThrowIfFailed(
296             m_d3dDevice->CreateInputLayout(
297                 layoutDesc,
298                 layoutDescNumElements,
299                 bytecode,
300                 bytecodeSize,
301                 layout
302                 )
303             );
304     }
305 }
306
307 void BasicLoader::CreateMesh(
308     _In_ byte* meshData,
309     _Out_ ID3D11Buffer** vertexBuffer,
310     _Out_ ID3D11Buffer** indexBuffer,
311     _Out_opt_ uint32* vertexCount,
312     _Out_opt_ uint32* indexCount,
313     _In_opt_ Platform::String^ debugName
314     )
315 {
316     // The first 4 bytes of the BasicMesh format define the number of vertices in the mesh.
317     uint32 numVertices = *reinterpret_cast<uint32*>(meshData);
318
319     // The following 4 bytes define the number of indices in the mesh.
320     uint32 numIndices = *reinterpret_cast<uint32*>(meshData + sizeof(uint32));
321
322     // The next segment of the BasicMesh format contains the vertices of the mesh.
323     BasicVertex* vertices = reinterpret_cast<BasicVertex*>(meshData + sizeof(uint32) * 2);
324
325     // The last segment of the BasicMesh format contains the indices of the mesh.
326     uint16* indices = reinterpret_cast<uint16*>(meshData + sizeof(uint32) * 2 + sizeof(BasicVertex) * numVertices);
327
328     // Create the vertex and index buffers with the mesh data.
329
330     D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
331     vertexBufferData.pSysMem = vertices;
332     vertexBufferData.SysMemPitch = 0;
333     vertexBufferData.SysMemSlicePitch = 0;
334     DX::ThrowIfFailed(
335         m_d3dDevice->CreateBuffer(
336             &CD3D11_BUFFER_DESC(numVertices * sizeof(BasicVertex), D3D11_BIND_VERTEX_BUFFER),
337             &vertexBufferData,
338             vertexBuffer
339             )
340         );
341
342     D3D11_SUBRESOURCE_DATA indexBufferData = {0};
343     indexBufferData.pSysMem = indices;
344     indexBufferData.SysMemPitch = 0;
345     indexBufferData.SysMemSlicePitch = 0;
346     DX::ThrowIfFailed(
347         m_d3dDevice->CreateBuffer(
348             &CD3D11_BUFFER_DESC(numIndices * sizeof(uint16), D3D11_BIND_INDEX_BUFFER),
349             &indexBufferData,
350             indexBuffer
351             )
352         );
353
354     SetDebugName(*vertexBuffer, Platform::String::Concat(debugName, "_VertexBuffer"));
355     SetDebugName(*indexBuffer, Platform::String::Concat(debugName, "_IndexBuffer"));
356
357     if (vertexCount != nullptr)
358     {
359         *vertexCount = numVertices;
360     }
361     if (indexCount != nullptr)
362     {
363         *indexCount = numIndices;
364     }
365 }
366
367 void BasicLoader::LoadTexture(
368     _In_ Platform::String^ filename,
369     _Out_opt_ ID3D11Texture2D** texture,
370     _Out_opt_ ID3D11ShaderResourceView** textureView
371     )
372 {
373     Platform::Array<byte>^ textureData = m_basicReaderWriter->ReadData(filename);
374
375     CreateTexture(
376         GetExtension(filename) == "dds",
377         textureData->Data,
378         textureData->Length,
379         texture,
380         textureView,
381         filename
382         );
383 }
384
385 void BasicLoader::LoadTextureAsync(
386     _In_ Platform::String^ filename,
387     _Out_opt_ ID3D11Texture2D** texture,
388     _Out_opt_ ID3D11ShaderResourceView** textureView
389     )
390 {
391     m_basicReaderWriter->ReadDataAsync(filename, ref new ReadDataAsyncCallback([=](Platform::Array<byte>^ textureData, AsyncStatus /*status*/)
392     {
393         CreateTexture(
394             GetExtension(filename) == "dds",
395             textureData->Data,
396             textureData->Length,
397             texture,
398             textureView,
399             filename
400             );
401     }));
402 }
403
404 void BasicLoader::LoadShader(
405     _In_ Platform::String^ filename,
406     _In_reads_opt_(layoutDescNumElements) D3D11_INPUT_ELEMENT_DESC layoutDesc[],
407     _In_ uint32 layoutDescNumElements,
408     _Out_ ID3D11VertexShader** shader,
409     _Out_opt_ ID3D11InputLayout** layout
410     )
411 {
412     Platform::Array<byte>^ bytecode = m_basicReaderWriter->ReadData(filename);
413
414     DX::ThrowIfFailed(
415         m_d3dDevice->CreateVertexShader(
416             bytecode->Data,
417             bytecode->Length,
418             nullptr,
419             shader
420             )
421         );
422
423     SetDebugName(*shader, filename);
424
425     if (layout != nullptr)
426     {
427         CreateInputLayout(
428             bytecode->Data,
429             bytecode->Length,
430             layoutDesc,
431             layoutDescNumElements,
432             layout
433             );
434
435         SetDebugName(*layout, filename);
436     }
437 }
438
439 void BasicLoader::LoadShaderAsync(
440     _In_ Platform::String^ filename,
441     _In_reads_opt_(layoutDescNumElements) D3D11_INPUT_ELEMENT_DESC layoutDesc[],
442     _In_ uint32 layoutDescNumElements,
443     _Out_ ID3D11VertexShader** shader,
444     _Out_opt_ ID3D11InputLayout** layout
445     )
446 {
447     m_basicReaderWriter->ReadDataAsync(filename, ref new ReadDataAsyncCallback([=](Platform::Array<byte>^ bytecode, AsyncStatus /*status*/)
448     {
449         DX::ThrowIfFailed(
450             m_d3dDevice->CreateVertexShader(
451                 bytecode->Data,
452                 bytecode->Length,
453                 nullptr,
454                 shader
455                 )
456             );
457
458         SetDebugName(*shader, filename);
459
460         if (layout != nullptr)
461         {
462             CreateInputLayout(
463                 bytecode->Data,
464                 bytecode->Length,
465                 layoutDesc,
466                 layoutDescNumElements,
467                 layout
468                 );
469
470             SetDebugName(*layout, filename);
471         }
472     }));
473 }
474
475 void BasicLoader::LoadShader(
476     _In_ Platform::String^ filename,
477     _Out_ ID3D11PixelShader** shader
478     )
479 {
480     Platform::Array<byte>^ bytecode = m_basicReaderWriter->ReadData(filename);
481
482     DX::ThrowIfFailed(
483         m_d3dDevice->CreatePixelShader(
484             bytecode->Data,
485             bytecode->Length,
486             nullptr,
487             shader
488             )
489         );
490
491     SetDebugName(*shader, filename);
492 }
493
494 void BasicLoader::LoadShaderAsync(
495     _In_ Platform::String^ filename,
496     _Out_ ID3D11PixelShader** shader
497     )
498 {
499     m_basicReaderWriter->ReadDataAsync(filename, ref new ReadDataAsyncCallback([=](Platform::Array<byte>^ bytecode, AsyncStatus /*status*/)
500     {
501         DX::ThrowIfFailed(
502             m_d3dDevice->CreatePixelShader(
503                 bytecode->Data,
504                 bytecode->Length,
505                 nullptr,
506                 shader
507                 )
508             );
509
510         SetDebugName(*shader, filename);
511     }));
512 }
513
514 void BasicLoader::LoadShader(
515     _In_ Platform::String^ filename,
516     _Out_ ID3D11ComputeShader** shader
517     )
518 {
519     Platform::Array<byte>^ bytecode = m_basicReaderWriter->ReadData(filename);
520
521     DX::ThrowIfFailed(
522         m_d3dDevice->CreateComputeShader(
523             bytecode->Data,
524             bytecode->Length,
525             nullptr,
526             shader
527             )
528         );
529
530     SetDebugName(*shader, filename);
531 }
532
533 void BasicLoader::LoadShaderAsync(
534     _In_ Platform::String^ filename,
535     _Out_ ID3D11ComputeShader** shader
536     )
537 {
538     m_basicReaderWriter->ReadDataAsync(filename, ref new ReadDataAsyncCallback([=](Platform::Array<byte>^ bytecode, AsyncStatus /*status*/)
539     {
540         DX::ThrowIfFailed(
541             m_d3dDevice->CreateComputeShader(
542                 bytecode->Data,
543                 bytecode->Length,
544                 nullptr,
545                 shader
546                 )
547             );
548
549         SetDebugName(*shader, filename);
550     }));
551 }
552
553 void BasicLoader::LoadShader(
554     _In_ Platform::String^ filename,
555     _Out_ ID3D11GeometryShader** shader
556     )
557 {
558     Platform::Array<byte>^ bytecode = m_basicReaderWriter->ReadData(filename);
559
560     DX::ThrowIfFailed(
561         m_d3dDevice->CreateGeometryShader(
562             bytecode->Data,
563             bytecode->Length,
564             nullptr,
565             shader
566             )
567         );
568
569     SetDebugName(*shader, filename);
570 }
571
572 void BasicLoader::LoadShaderAsync(
573     _In_ Platform::String^ filename,
574     _Out_ ID3D11GeometryShader** shader
575     )
576 {
577     m_basicReaderWriter->ReadDataAsync(filename, ref new ReadDataAsyncCallback([=](Platform::Array<byte>^ bytecode, AsyncStatus /*status*/)
578     {
579         DX::ThrowIfFailed(
580             m_d3dDevice->CreateGeometryShader(
581                 bytecode->Data,
582                 bytecode->Length,
583                 nullptr,
584                 shader
585                 )
586             );
587
588         SetDebugName(*shader, filename);
589     }));
590 }
591
592 void BasicLoader::LoadShader(
593     _In_ Platform::String^ filename,
594     _In_reads_opt_(numEntries) const D3D11_SO_DECLARATION_ENTRY* streamOutDeclaration,
595     _In_ uint32 numEntries,
596     _In_reads_opt_(numStrides) const uint32* bufferStrides,
597     _In_ uint32 numStrides,
598     _In_ uint32 rasterizedStream,
599     _Out_ ID3D11GeometryShader** shader
600     )
601 {
602     Platform::Array<byte>^ bytecode = m_basicReaderWriter->ReadData(filename);
603
604     DX::ThrowIfFailed(
605         m_d3dDevice->CreateGeometryShaderWithStreamOutput(
606             bytecode->Data,
607             bytecode->Length,
608             streamOutDeclaration,
609             numEntries,
610             bufferStrides,
611             numStrides,
612             rasterizedStream,
613             nullptr,
614             shader
615             )
616         );
617
618     SetDebugName(*shader, filename);
619 }
620
621 void BasicLoader::LoadShaderAsync(
622     _In_ Platform::String^ filename,
623     _In_reads_opt_(numEntries) const D3D11_SO_DECLARATION_ENTRY* streamOutDeclaration,
624     _In_ uint32 numEntries,
625     _In_reads_opt_(numStrides) const uint32* bufferStrides,
626     _In_ uint32 numStrides,
627     _In_ uint32 rasterizedStream,
628     _Out_ ID3D11GeometryShader** shader
629     )
630 {
631     m_basicReaderWriter->ReadDataAsync(filename, ref new ReadDataAsyncCallback([=](Platform::Array<byte>^ bytecode, AsyncStatus /*status*/)
632     {
633         DX::ThrowIfFailed(
634             m_d3dDevice->CreateGeometryShaderWithStreamOutput(
635                 bytecode->Data,
636                 bytecode->Length,
637                 streamOutDeclaration,
638                 numEntries,
639                 bufferStrides,
640                 numStrides,
641                 rasterizedStream,
642                 nullptr,
643                 shader
644                 )
645             );
646
647         SetDebugName(*shader, filename);
648     }));
649 }
650
651 void BasicLoader::LoadShader(
652     _In_ Platform::String^ filename,
653     _Out_ ID3D11HullShader** shader
654     )
655 {
656     Platform::Array<byte>^ bytecode = m_basicReaderWriter->ReadData(filename);
657
658     DX::ThrowIfFailed(
659         m_d3dDevice->CreateHullShader(
660             bytecode->Data,
661             bytecode->Length,
662             nullptr,
663             shader
664             )
665         );
666
667     SetDebugName(*shader, filename);
668 }
669
670 void BasicLoader::LoadShaderAsync(
671     _In_ Platform::String^ filename,
672     _Out_ ID3D11HullShader** shader
673     )
674 {
675     m_basicReaderWriter->ReadDataAsync(filename, ref new ReadDataAsyncCallback([=](Platform::Array<byte>^ bytecode, AsyncStatus /*status*/)
676     {
677         DX::ThrowIfFailed(
678             m_d3dDevice->CreateHullShader(
679                 bytecode->Data,
680                 bytecode->Length,
681                 nullptr,
682                 shader
683                 )
684             );
685
686         SetDebugName(*shader, filename);
687     }));
688 }
689
690 void BasicLoader::LoadShader(
691     _In_ Platform::String^ filename,
692     _Out_ ID3D11DomainShader** shader
693     )
694 {
695     Platform::Array<byte>^ bytecode = m_basicReaderWriter->ReadData(filename);
696
697     DX::ThrowIfFailed(
698         m_d3dDevice->CreateDomainShader(
699             bytecode->Data,
700             bytecode->Length,
701             nullptr,
702             shader
703             )
704         );
705
706     SetDebugName(*shader, filename);
707 }
708
709 void BasicLoader::LoadShaderAsync(
710     _In_ Platform::String^ filename,
711     _Out_ ID3D11DomainShader** shader
712     )
713 {
714     m_basicReaderWriter->ReadDataAsync(filename, ref new ReadDataAsyncCallback([=](Platform::Array<byte>^ bytecode, AsyncStatus /*status*/)
715     {
716         DX::ThrowIfFailed(
717             m_d3dDevice->CreateDomainShader(
718                 bytecode->Data,
719                 bytecode->Length,
720                 nullptr,
721                 shader
722                 )
723             );
724
725         SetDebugName(*shader, filename);
726     }));
727 }
728
729 void BasicLoader::LoadMesh(
730     _In_ Platform::String^ filename,
731     _Out_ ID3D11Buffer** vertexBuffer,
732     _Out_ ID3D11Buffer** indexBuffer,
733     _Out_opt_ uint32* vertexCount,
734     _Out_opt_ uint32* indexCount
735     )
736 {
737     Platform::Array<byte>^ meshData = m_basicReaderWriter->ReadData(filename);
738
739     CreateMesh(
740         meshData->Data,
741         vertexBuffer,
742         indexBuffer,
743         vertexCount,
744         indexCount,
745         filename
746         );
747 }
748
749 void BasicLoader::LoadMeshAsync(
750     _In_ Platform::String^ filename,
751     _Out_ ID3D11Buffer** vertexBuffer,
752     _Out_ ID3D11Buffer** indexBuffer,
753     _Out_opt_ uint32* vertexCount,
754     _Out_opt_ uint32* indexCount
755     )
756 {
757     m_basicReaderWriter->ReadDataAsync(filename, ref new ReadDataAsyncCallback([=](Platform::Array<byte>^ meshData, AsyncStatus /*status*/)
758     {
759         CreateMesh(
760             meshData->Data,
761             vertexBuffer,
762             indexBuffer,
763             vertexCount,
764             indexCount,
765             filename
766             );
767     }));
768 }