//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved

#include "pch.h"
//#include "MainPage.xaml.h"
#include "GameMain.h"

using namespace Microsoft::WRL;
//using namespace Windows::Foundation;
//using namespace Windows::Foundation::Collections;
//using namespace Windows::UI::Core;
//using namespace Windows::UI::ViewManagement;
//
//using namespace Windows::UI::ApplicationSettings;
//using namespace Windows::UI::Popups;
//using namespace Windows::UI::Xaml::Controls;
//using namespace Windows::Graphics::Display;
using namespace DirectX;
using namespace D2D1;
using namespace concurrency;
using namespace BasicSprites;

namespace ShootingGame {

const float GameMain::BACKBUFFER_WIDTH = 320.0f;
const float GameMain::BACKBUFFER_HEIGHT = 240.0f;


GameMain::GameMain() 
  :backBufferViewPort_(0.0f,0.0f,BACKBUFFER_WIDTH,BACKBUFFER_HEIGHT) , isDestroy_(true),state_(this),
  eventHolder_(::CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE))
{
  //  GameStateMachine machine;
  soundDriver_.reset(new sf::SoundDriver());
  soundManager_.reset(new sf::SoundManager(*soundDriver_.get()));
 
  //soundThread_ = std::thread(
  //   [this]() -> void 
  //  {
  //	  ExecuteSoundThread();
  //  }
  //);
}

GameMain::~GameMain()
{
  StopSound();
  eventHolder_.reset();
  //if(soundThread_.joinable())
  //{
  //	soundThread_.join();
  //}
};

void GameMain::CreateDeviceIndependentResources()
{
  DirectXBase::CreateDeviceIndependentResources();

  // Create the performance throttler.

  autoThrottle_ = ref new ::AutoThrottle(1.0f / 60.0f);
}

void GameMain::Initialize(
  _In_ Windows::UI::Core::CoreWindow^ window,
  _In_ Windows::UI::Xaml::Controls::SwapChainBackgroundPanel^ swapChainPanel,
  _In_ float dpi
  )
{
  panel_  = swapChainPanel;
  Initialize(window,dpi);
  state_.Start();
  state_.ProcessEvent(Event::Init());
}

void GameMain::CreateDeviceResources()
{
  DirectXBase::CreateDeviceResources();

  // Create the sprite batch.

  spriteBatch_ = ref new BasicSprites::SpriteBatch();
  unsigned int capacity = 1024;
  if (m_featureLevel < D3D_FEATURE_LEVEL_9_3)
  {
    capacity = min(Parameters::MaximumCapacityCompatible, capacity);
  }
  spriteBatch_->Initialize(
    m_d3dDevice.Get(),
    capacity
    );

  // Create the Sample Overlay.

  sampleOverlay_ = ref new ::SampleOverlay();

  sampleOverlay_->Initialize(
    m_d2dDevice.Get(),
    m_d2dContext.Get(),
    m_wicFactory.Get(),
    m_dwriteFactory.Get(),
    L"シューティングゲーム"
    );



}

void GameMain::CreateWindowSizeDependentResources()
{
  //	DirectXBase::CreateWindowSizeDependentResources();
  // Store the window bounds so the next time we get a SizeChanged event we can
  // avoid rebuilding everything if the size is identical.
  m_windowBounds = m_window->Bounds;

  // If the swap chain already exists, resize it.
  if(m_swapChain != nullptr)
  {
    DX::ThrowIfFailed(
      m_swapChain->ResizeBuffers(2, 0, 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0)
      );
  }
  // Otherwise, create a new one.
  else
  {
    // Allocate a descriptor.
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
    scale_ = floorf(m_window->Bounds.Width * m_dpi / ( 96.0f * BACKBUFFER_WIDTH));
    swapChainDesc.Width = static_cast<UINT>(m_window->Bounds.Width * m_dpi / 96.0f);    // Can not use 0 to get the default on Composition SwapChain
    swapChainDesc.Height = static_cast<UINT>(m_window->Bounds.Height * m_dpi / 96.0f);
    swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;           // this is the most common swapchain format
    swapChainDesc.Stereo = false; 
    swapChainDesc.SampleDesc.Count = 1;                          // don't use multi-sampling
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = 2;                               // use double buffering to enable flip
    swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // all Metro style apps must use this SwapEffect
    swapChainDesc.Flags = 0;

    // Once the desired swap chain description is configured, it must be created on the same adapter as our D3D Device

    // First, retrieve the underlying DXGI Device from the D3D Device.
    ComPtr<IDXGIDevice1>  dxgiDevice;
    DX::ThrowIfFailed(
      m_d3dDevice.As(&dxgiDevice)
      );

    // Identify the physical adapter (GPU or card) this device is running on.
    ComPtr<IDXGIAdapter> dxgiAdapter;
    DX::ThrowIfFailed(
      dxgiDevice->GetAdapter(&dxgiAdapter)
      );

    // And obtain the factory object that created it.
    ComPtr<IDXGIFactory2> dxgiFactory;
    DX::ThrowIfFailed(
      dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
      );

    // Obtain the final swap chain for this window from the DXGI factory.
    DX::ThrowIfFailed(
      dxgiFactory->CreateSwapChainForComposition(
      m_d3dDevice.Get(),
      &swapChainDesc,
      nullptr,    // allow on all displays
      &m_swapChain
      )
      );

    ComPtr<ISwapChainBackgroundPanelNative> dxRootPanelAsNative;

    // set the swap chain on the SwapChainBackgroundPanel
    reinterpret_cast<IUnknown*>(panel_)->QueryInterface(__uuidof(ISwapChainBackgroundPanelNative), (void**)&dxRootPanelAsNative);

    DX::ThrowIfFailed(
      dxRootPanelAsNative->SetSwapChain(m_swapChain.Get())
      );

    // Ensure that DXGI does not queue more than one frame at a time. This both reduces 
    // latency and ensures that the application will only render after each VSync, minimizing 
    // power consumption.
    DX::ThrowIfFailed(
      dxgiDevice->SetMaximumFrameLatency(1)
      );

  }

  // Obtain the backbuffer for this window which will be the final 3D rendertarget.
  ComPtr<ID3D11Texture2D> backBuffer;
  DX::ThrowIfFailed(
    m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
    );

  // Create a view interface on the rendertarget to use on bind.
  DX::ThrowIfFailed(
    m_d3dDevice->CreateRenderTargetView(
    backBuffer.Get(),
    nullptr,
    &m_renderTargetView
    )
    );

  // Cache the rendertarget dimensions in our helper class for convenient use.
  D3D11_TEXTURE2D_DESC backBufferDesc = {0};
  backBuffer->GetDesc(&backBufferDesc);
  m_renderTargetSize.Width  = static_cast<float>(backBufferDesc.Width);
  m_renderTargetSize.Height = static_cast<float>(backBufferDesc.Height);

  // Create a descriptor for the depth/stencil buffer.
  CD3D11_TEXTURE2D_DESC depthStencilDesc(
    DXGI_FORMAT_D24_UNORM_S8_UINT, 
    backBufferDesc.Width,
    backBufferDesc.Height,
    1,
    1,
    D3D11_BIND_DEPTH_STENCIL
    );

  // Allocate a 2-D surface as the depth/stencil buffer.
  ComPtr<ID3D11Texture2D> depthStencil;
  DX::ThrowIfFailed(
    m_d3dDevice->CreateTexture2D(
    &depthStencilDesc,
    nullptr,
    &depthStencil
    )
    );

  // Create a DepthStencil view on this surface to use on bind.
  DX::ThrowIfFailed(
    m_d3dDevice->CreateDepthStencilView(
    depthStencil.Get(),
    &CD3D11_DEPTH_STENCIL_VIEW_DESC(D3D11_DSV_DIMENSION_TEXTURE2D),
    &m_depthStencilView
    )
    );

  // Create a viewport descriptor of the full window size.
  swapChainViewPort_.Width =         static_cast<float>(backBufferDesc.Width);
  swapChainViewPort_.Height =         static_cast<float>(backBufferDesc.Height);

  // Set the current viewport using the descriptor.
  m_d3dContext->RSSetViewports(1, &swapChainViewPort_);

  // Now we set up the Direct2D render target bitmap linked to the swapchain. 
  // Whenever we render to this bitmap, it will be directly rendered to the 
  // swapchain associated with the window.
  D2D1_BITMAP_PROPERTIES1 bitmapProperties = 
    BitmapProperties1(
    D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
    PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
    m_dpi,
    m_dpi
    );

  // Direct2D needs the dxgi version of the backbuffer surface pointer.
  ComPtr<IDXGISurface> dxgiBackBuffer;
  DX::ThrowIfFailed(
    m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
    );

  // Get a D2D surface from the DXGI back buffer to use as the D2D render target.
  DX::ThrowIfFailed(
    m_d2dContext->CreateBitmapFromDxgiSurface(
    dxgiBackBuffer.Get(),
    &bitmapProperties,
    &m_d2dTargetBitmap
    )
    );

  // So now we can set the Direct2D render target.
  m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());

  // Set D2D text anti-alias mode to Grayscale to ensure proper rendering of text on intermediate surfaces.
  m_d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);

  // Randomly generate some non-interactive asteroids to fit the screen.

  sampleOverlay_->UpdateForWindowSizeChange();
  // バックバッファの作成
  //ComPtr<ID3D11Texture2D> backBuffer;
  //DX::ThrowIfFailed(
  //    m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
  //    );
  //D3D11_TEXTURE2D_DESC backBufferDesc = {0};
  //backBuffer->GetDesc(&backBufferDesc);

  D3D11_TEXTURE2D_DESC desc = {0};
  desc.Width = 320;
  desc.Height = 240;
  desc.Format = backBufferDesc.Format;
  desc.MipLevels = 1;
  desc.SampleDesc.Count = 1;
  desc.SampleDesc.Quality = 0;
  desc.ArraySize = 1;
  desc.Usage = D3D11_USAGE_DEFAULT;
  desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
  //    desc.MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE;

  DX::ThrowIfFailed(m_d3dDevice->CreateTexture2D(&desc,NULL,&backBuffer_));

  DX::ThrowIfFailed(m_d3dDevice->CreateRenderTargetView(backBuffer_.Get(),0,&backBufferRenderTargetView_));

  //   // 深度バッファの作成
  //   D3D11_TEXTURE2D_DESC depth = {} ;
  //   depth.Width = desc.Width;//rc.right - rc.left;client_width_;
  //   depth.Height = desc.Height;//rc.bottom - rc.top;client_height_;
  //   depth.MipLevels = 1;
  //   depth.ArraySize = 1;
  //   depth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
  //   depth.SampleDesc.Count = 1;
  //   depth.SampleDesc.Quality = 0;
  //   depth.Usage = D3D11_USAGE_DEFAULT;
  //   depth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
  //   depth.CPUAccessFlags = 0;
  //   depth.MiscFlags = 0;
  //   ComPtr<ID3D11Texture2D> depthT;
  //DX::ThrowIfFailed(m_d3dDevice->CreateTexture2D( &depth, NULL, &depthT ));

  //   D3D11_DEPTH_STENCIL_VIEW_DESC dsv = {};
  //   dsv.Format = depth.Format;
  //   dsv.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
  //   dsv.Texture2D.MipSlice = 0;

  //DX::ThrowIfFailed(m_d3dDevice->CreateDepthStencilView( depthT.Get(), &dsv, &backBufferDepthStencilView_ ));

  spriteBatch_->AddTexture(backBuffer_.Get(),float2());

  // 深度ステンシルステートを作成する
  //D3D11_DEPTH_STENCIL_DESC ddsDesc;
  //::ZeroMemory( &ddsDesc, sizeof( ddsDesc ) );
  //ddsDesc.DepthEnable = TRUE;                                     // 深度テストを使用する
  //ddsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
  //ddsDesc.DepthFunc = D3D11_COMPARISON_LESS;
  //ddsDesc.StencilEnable = FALSE;
  //DX::ThrowIfFailed(m_d3dDevice->CreateDepthStencilState( &ddsDesc, &backBufferDepthStencilState_ ));

}

void GameMain::Update(float timeTotal, float timeDelta)
{
  // Update the performance throttler.

  auto control = autoThrottle_->Update(timeDelta);
  state_.ProcessEvent(Event::Update());
  //if (control == FrameWorkload::Increase)
  //{
  //    m_numParticlesToDraw += SampleSettings::Performance::ParticleCountDelta;
  //}
  //if (control == FrameWorkload::Decrease)
  //{
  //    m_numParticlesToDraw -= SampleSettings::Performance::ParticleCountDelta;
  //}
  //if (control != FrameWorkload::Maintain)
  //{
  //    m_numParticlesToDraw = max(SampleSettings::Performance::ParticleCountMin, min(SampleSettings::Performance::ParticleCountMax, m_numParticlesToDraw));
  //    if (m_featureLevel < D3D_FEATURE_LEVEL_9_3)
  //    {
  //        m_numParticlesToDraw = min(static_cast<int>(Parameters::MaximumCapacityCompatible - SampleSettings::NumAsteroids - 1), m_numParticlesToDraw);
  //    }
  //}

  // Update the non-interactive asteroids.
  // Their behavior is to drift across the window with a fixed translational and rotational
  // velocity.  Upon crossing a boundary outside the window, their position wraps.

  //for (auto asteroid = m_asteroidData.begin(); asteroid != m_asteroidData.end(); asteroid++)
  //{
  //    static const float border = 100.0f;
  //    asteroid->pos = asteroid->pos + asteroid->vel * timeDelta;
  //    if (asteroid->vel.x < 0)
  //    {
  //        if (asteroid->pos.x < -border)
  //        {
  //            asteroid->pos.x = m_windowBounds.Width + border;
  //        }
  //    }
  //    else
  //    {
  //        if (asteroid->pos.x > m_windowBounds.Width + border)
  //        {
  //            asteroid->pos.x = -border;
  //        }
  //    }
  //    if (asteroid->vel.y < 0)
  //    {
  //        if (asteroid->pos.y < -border)
  //        {
  //            asteroid->pos.y = m_windowBounds.Height + border;
  //        }
  //    }
  //    else
  //    {
  //        if (asteroid->pos.y > m_windowBounds.Height + border)
  //        {
  //            asteroid->pos.y = -border;
  //        }
  //    }

  //    asteroid->rot += asteroid->rotVel * timeDelta;
  //    if (asteroid->rot > static_cast<float>(M_PI))
  //    {
  //        asteroid->rot -= 2.0f * static_cast<float>(M_PI);
  //    }
  //    if (asteroid->rot < static_cast<float>(-M_PI))
  //    {
  //        asteroid->rot += 2.0f * static_cast<float>(M_PI);
  //    }
  //}

  //// Update the interactive particles.
  //// Their behavior is to be gravitationally attracted to two oscillating gravity
  //// wells and repelled by any pressed pointer points.  Upon reaching the edge of
  //// the window, the particles bounce.

  //// Add two gravity wells that move throughout the window.
  //float2 wellPositions[] =
  //{
  //    float2(
  //        (1.0f + 0.8f * cosf(timeTotal / (2.0f * static_cast<float>(M_PI)) + 3.0f)) * m_windowBounds.Width / 2.0f,
  //        (1.0f + 0.8f * sinf(timeTotal / 5.0f)) * m_windowBounds.Height / 2.0f
  //        ),
  //    float2(
  //        (1.0f + 0.8f * cosf(timeTotal / static_cast<float>(M_PI * M_PI) + 1.0f)) * m_windowBounds.Width / 2.0f,
  //        (1.0f + 0.8f * sinf(timeTotal / static_cast<float>(M_PI))) * m_windowBounds.Height / 2.0f
  //        )
  //};

  //for (auto particle = m_particleData.begin(); particle != m_particleData.begin() + m_numParticlesToDraw; particle++)
  //{
  //    if (particle->pos.x < 0)
  //    {
  //        particle->vel.x = abs(particle->vel.x);
  //    }
  //    if (particle->pos.x > m_windowBounds.Width)
  //    {
  //        particle->vel.x = -abs(particle->vel.x);
  //    }
  //    if (particle->pos.y < 0)
  //    {
  //        particle->vel.y = abs(particle->vel.y);
  //    }
  //    if (particle->pos.y > m_windowBounds.Height)
  //    {
  //        particle->vel.y = -abs(particle->vel.y);
  //    }

  //    for (auto repulsor = m_repulsors.begin(); repulsor != m_repulsors.end(); repulsor++)
  //    {
  //        float2 delta = particle->pos - repulsor->second;
  //        float deltaLength = length(delta) + 24.0f; // Offset length to avoid division by zero.
  //        float deltaLengthCubed = deltaLength * deltaLength * deltaLength;
  //        particle->vel = particle->vel + SampleSettings::Physics::Gravity * timeDelta * delta / deltaLengthCubed;
  //    }

  //    for (int i = 0; i < ARRAYSIZE(wellPositions); i++)
  //    {
  //        float gravitySign = 1.0f;
  //        if ((static_cast<int>(timeTotal / 2.0f) + 1) % 10 == 0)
  //        {
  //            // Every 20 seconds, "explode" the gravity wells for 2 seconds.
  //            gravitySign = -1.0f;
  //        }
  //        float2 delta = wellPositions[i] - particle->pos;
  //        float deltaLength = length(delta) + 24.0f;
  //        float deltaLengthCubed = deltaLength * deltaLength * deltaLength;
  //        particle->vel = particle->vel + gravitySign * 0.2f * SampleSettings::Physics::Gravity * timeDelta * delta / deltaLengthCubed;
  //    }

  //    particle->vel = particle->vel * (1.0f - SampleSettings::Physics::Damping);

  //    // Add random noise to the velocity to prevent particles from locking together.
  //    
  //    particle->vel.x += RandFloat(-0.5f, 0.5f);
  //    particle->vel.y += RandFloat(-0.5f, 0.5f);

  //    particle->pos = particle->pos + particle->vel * timeDelta;
  //}
}

void GameMain::Render()
{
  state_.ProcessEvent(Event::Render());



  //// Draw the non-interactive asteroids.

  //for (auto asteroid = m_asteroidData.begin(); asteroid != m_asteroidData.end(); asteroid++)
  //{
  //    spriteBatch_->Draw(
  //        m_asteroid.Get(),
  //        asteroid->pos,
  //        PositionUnits::DIPs,
  //        float2(1.0f, 1.0f) * asteroid->scale,
  //        SizeUnits::Normalized,
  //        float4(0.8f, 0.8f, 1.0f, 1.0f),
  //        asteroid->rot
  //        );
  //}

  //// Draw the interactive particles.

  //for (auto particle = m_particleData.begin(); particle != m_particleData.begin() + m_numParticlesToDraw; particle++)
  //{
  //    float alpha = length(particle->vel) / 200.0f;
  //    spriteBatch_->Draw(
  //        m_particle.Get(),
  //        particle->pos,
  //        PositionUnits::DIPs,
  //        float2(32.0f, 32.0f),
  //        SizeUnits::DIPs,
  //        float4(0.1f, 0.02f, 0.0f, alpha),
  //        0.0f,
  //        BlendMode::Additive
  //        );
  //}

}



float GameMain::RandFloat(float min, float max)
{
  return (static_cast<float>(rand() % RAND_MAX) / static_cast<float>(RAND_MAX)) * (max - min) + min;
}

// サウンド再生スレッド
void GameMain::ExecuteSoundThread()
{
  //sf::com_init comInit;
  //  InitSound();
  soundManager_->Sequencer().Play();
  while(!isDestroy_)
  {
    soundDriver_->Render();
    //		Concurrency::wait(800);
    //	soundDriver_->Update();
    //	soundDriver_->Render();
  }
  ::SetEvent(eventHolder_.get());
  //isDestroy_ = false;
}

void GameMain::StartSound()
{
  if(isDestroy_) {
    isDestroy_ = false;
    // サウンド再生スレッドの開始
    soundTask_ = task<void>(create_async([this]()
    {
      ExecuteSoundThread();
    })
      );
  }
}

void GameMain::StopSound()
{
  // サウンド再生スレッドの停止
  if(!isDestroy_){
    isDestroy_ = true;
    WaitForSingleObjectEx(eventHolder_.get(),INFINITE,FALSE);
  }
}

void GameMain::ClearScreen()
{
  m_d3dContext->OMSetRenderTargets(
    1,
    backBufferRenderTargetView_.GetAddressOf(),
    nullptr//backBufferDepthStencilView_.Get()
    );
  //	m_d3dContext->OMSetDepthStencilState(backBufferDepthStencilState_.Get(),0);
  m_d3dContext->RSSetViewports(1, &backBufferViewPort_);
  //m_d3dContext->RSSetState(


  m_d3dContext->ClearRenderTargetView(
    backBufferRenderTargetView_.Get(),
    reinterpret_cast<float*>(&D2D1::ColorF(D2D1::ColorF::Black))
    );

  //m_d3dContext->ClearDepthStencilView(backBufferDepthStencilView_.Get(),D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL,1.0f,0);

}

void GameMain::RenderScreen()
{
  m_d3dContext->OMSetRenderTargets(
    1,
    m_renderTargetView.GetAddressOf(),
    nullptr/*m_depthStencilView.Get()*/
    );

  m_d3dContext->RSSetViewports(1, &swapChainViewPort_);


  m_d3dContext->ClearRenderTargetView(
    m_renderTargetView.Get(),
    reinterpret_cast<float*>(&D2D1::ColorF(D2D1::ColorF::MidnightBlue))
    );

  //m_d3dContext->ClearDepthStencilView(m_depthStencilView.Get(),D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL,1.0f,0);

  spriteBatch_->Begin();

  spriteBatch_->Draw(
    backBuffer_.Get(),
    float4(m_window->Bounds.Width / 2.0f,m_window->Bounds.Height / 2.0f,0.0f,1.0f),
    PositionUnits::DIPs,
    float2(1.0f, 1.0f) * scale_,
    SizeUnits::Normalized,
    float4(1.0f, 1.0f, 1.0f, 1.0f),
    0.0f,
    BlendMode::Alpha
    );

  spriteBatch_->End();

  // Render the Sample Overlay.
  sampleOverlay_->Render();

}

void GameMain::LoadTexture(const std::wstring& filename,ID3D11Texture2D** texPtrAddr)
{
  // Load the sprite textures.

  BasicLoader^ loader = ref new BasicLoader(m_d3dDevice.Get(), m_wicFactory.Get());
  Platform::String^ f = ref new Platform::String(filename.c_str());

  loader->LoadTexture(
    f,
    texPtrAddr,
    nullptr
    );
}


}

