Advertisement
alien_fx_fiend

LongBow & Arrow Wireframe Animation (Claude 3.7 (O3 AI Initial))

May 19th, 2025 (edited)
520
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 26.17 KB | Source Code | 0 0
  1. I want to draw the 'fletching' for the bottom side of the arrow as well (should be fletching on top and bottom), mirroring the top, and also make the fletching swept backward instead of front as it is presently (make it point the correct side).
  2.  
  3. Here's the modified code for the fletching section in diff-like format:
  4.  
  5. ```diff
  6. // Fletching (3 short diagonal lines)
  7. const float fletch = 20.0f;
  8. const float step = 5.0f;
  9. // Top fletching
  10. for (int i = 0; i < 3; ++i)
  11. {
  12.    float x = g_arrowPosX - arrowLen + i * step;
  13.    g_pRenderTarget->DrawLine(
  14.        D2D1::Point2F(x, arrowY),
  15. -       D2D1::Point2F(x + fletch, arrowY - fletch * 0.4f + i * 3.0f),
  16. +       D2D1::Point2F(x - fletch, arrowY - fletch * 0.4f + i * 3.0f),
  17.        g_pBrush, 2.0f);
  18. }
  19. + // Bottom fletching
  20. + for (int i = 0; i < 3; ++i)
  21. + {
  22. +     float x = g_arrowPosX - arrowLen + i * step;
  23. +     g_pRenderTarget->DrawLine(
  24. +         D2D1::Point2F(x, arrowY),
  25. +         D2D1::Point2F(x - fletch, arrowY + fletch * 0.4f - i * 3.0f),
  26. +         g_pBrush, 2.0f);
  27. + }
  28. ```
  29.  
  30. Would you like me to explain the changes I made to the code?
  31.  
  32. ######################################################################################
  33. ######################################################################################
  34. ##  endroughxfer-urldump #1#
  35. ##  <EOF><EOF>
  36. ######################################################################################
  37. ######################################################################################
  38.  
  39. New Code /w Fixes:::
  40. //  BowAndArrow.cpp
  41. //  Simple Win32 / Direct2D demo - "wireframe longbow"
  42. //  Build: cl /EHsc /DUNICODE /D_UNICODE BowAndArrow.cpp d2d1.lib user32.lib gdi32.lib
  43.  
  44. #include <windows.h>
  45. #include <d2d1.h>
  46. #include <chrono>
  47. #include <cmath>
  48. #include <algorithm>
  49. #pragma comment(lib, "d2d1")
  50.  
  51. // -------------------------------------------------
  52. // Small RAII helper to release COM objects
  53. // -------------------------------------------------
  54. template<class T> void SafeRelease(T** pp)
  55. {
  56.    if (*pp) { (*pp)->Release(); *pp = nullptr; }
  57. }
  58.  
  59. // -------------------------------------------------
  60. // Animation constants
  61. // -------------------------------------------------
  62. constexpr float  COCK_TIME = 1.0f;   // seconds
  63. constexpr float  HOLD_TIME = 0.5f;   // seconds
  64. constexpr float  FIRE_TIME = 1.0f;   // seconds
  65. constexpr UINT   TIMER_ID = 1;
  66. constexpr UINT   TIMER_MS = 16;     // ~60 FPS
  67.  
  68. enum class AnimStage { Cocking, Hold, Firing, Done };
  69. constexpr float ARROW_REST_OFFSET = 500.0f;   // pixels in-front of bow ( > 0 )
  70.  
  71. // String pull distance (pixels) and bow X center
  72. constexpr float STRING_MAX_PULL = 180.0f;
  73. constexpr float CX_BOW = 220.0f;
  74.  
  75. // -------------------------------------------------
  76. // Global state (kept simple for a demo)
  77. // -------------------------------------------------
  78. HWND                    g_hWnd = nullptr;
  79. ID2D1Factory* g_pFactory = nullptr;
  80. ID2D1HwndRenderTarget* g_pRenderTarget = nullptr;
  81. ID2D1SolidColorBrush* g_pBrush = nullptr;
  82.  
  83. float g_windowW = 1280.0f;
  84. float g_windowH = 720.0f;
  85. float g_arrowPosX = CX_BOW + ARROW_REST_OFFSET;   // head starts free-floating
  86.  
  87. // Geometry helpers (depend on window height, so they are updated in WM_SIZE)
  88. float cx = CX_BOW;
  89. float topY = 120.0f;
  90. float bottomY = 600.0f;   // will be overwritten in WM_SIZE
  91. float midY = 360.0f;
  92. float halfHeight = 240.0f;
  93.  
  94. // Animation variables
  95. AnimStage   g_stage = AnimStage::Cocking;
  96. float       g_timeInStage = 0.0f;        // seconds
  97. auto        g_prevTick = std::chrono::high_resolution_clock::now();
  98. float       g_stringOffset = 0.0f;        // 0 = relaxed, 1 = fully drawn
  99. //float       g_arrowPosX = 0.0f;        // head position
  100.  
  101. // -------------------------------------------------
  102. // Helpers
  103. // -------------------------------------------------
  104. float Lerp(float a, float b, float t) { return a + (b - a) * t; }
  105.  
  106. // Create or re-create render target
  107. HRESULT CreateDeviceResources()
  108. {
  109.    if (g_pRenderTarget) return S_OK;
  110.  
  111.    RECT rc; GetClientRect(g_hWnd, &rc);
  112.  
  113.    D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left,
  114.        rc.bottom - rc.top);
  115.  
  116.    HRESULT hr = g_pFactory->CreateHwndRenderTarget(
  117.        D2D1::RenderTargetProperties(),
  118.        D2D1::HwndRenderTargetProperties(g_hWnd, size),
  119.        &g_pRenderTarget);
  120.  
  121.    if (SUCCEEDED(hr))
  122.        hr = g_pRenderTarget->CreateSolidColorBrush(
  123.            D2D1::ColorF(D2D1::ColorF::White), &g_pBrush);
  124.  
  125.    return hr;
  126. }
  127.  
  128. void DiscardDeviceResources()
  129. {
  130.    SafeRelease(&g_pBrush);
  131.    SafeRelease(&g_pRenderTarget);
  132. }
  133.  
  134. // -------------------------------------------------
  135. // Core rendering
  136. // -------------------------------------------------
  137. void OnRender()
  138. {
  139.    if (FAILED(CreateDeviceResources()))
  140.        return;
  141.  
  142.    g_pRenderTarget->BeginDraw();
  143.    g_pRenderTarget->Clear(D2D1::ColorF(0x202020)); // dark bg
  144.  
  145.    // -------------------------------------------------
  146.    // 1. Draw Bow limbs (two quadratic curves)
  147.    // -------------------------------------------------
  148.    ID2D1PathGeometry* pGeom = nullptr;
  149.    ID2D1GeometrySink* pSink = nullptr;
  150.    if (SUCCEEDED(g_pFactory->CreatePathGeometry(&pGeom)))
  151.    {
  152.        pGeom->Open(&pSink);
  153.  
  154.        //--------------------------------------------------------------
  155.        //  One continuous ")"-shaped limb with a slightly pointy tip.
  156.        //  End-points stay on the string ( (cx, topY) & (cx, bottomY) )
  157.        //--------------------------------------------------------------
  158.        constexpr float NOSE = 35.0f;               // how far the tips stick out
  159.        const     float BULGE = halfHeight * 0.5f;  // max belly distance (x)
  160.  
  161.        pSink->BeginFigure(D2D1::Point2F(cx, topY), D2D1_FIGURE_BEGIN_HOLLOW);
  162.  
  163.        // Draw semicircle bow with pointy tip at middle
  164.        // First quarter - top to middle-top
  165.        pSink->AddBezier(
  166.            D2D1::BezierSegment(
  167.                D2D1::Point2F(cx + NOSE, topY + halfHeight * 0.1f),      // slight curve at top
  168.                D2D1::Point2F(cx + BULGE, topY + halfHeight * 0.5f),     // first quarter curve
  169.                D2D1::Point2F(cx + BULGE + NOSE, midY - NOSE)));         // approaching tip
  170.  
  171.        // Middle part with pointy tip
  172.        pSink->AddBezier(
  173.            D2D1::BezierSegment(
  174.                D2D1::Point2F(cx + BULGE + NOSE * 1.2f, midY - NOSE * 0.5f),  // curve to tip
  175.                D2D1::Point2F(cx + BULGE + NOSE * 1.5f, midY),               // pointy tip
  176.                D2D1::Point2F(cx + BULGE + NOSE * 1.2f, midY + NOSE * 0.5f)));  // curve from tip
  177.  
  178.        // Last quarter - middle-bottom to bottom
  179.        pSink->AddBezier(
  180.            D2D1::BezierSegment(
  181.                D2D1::Point2F(cx + BULGE + NOSE, midY + NOSE),            // after tip
  182.                D2D1::Point2F(cx + BULGE, bottomY - halfHeight * 0.5f),   // last quarter curve
  183.                D2D1::Point2F(cx + NOSE, bottomY - halfHeight * 0.1f)));  // approaching bottom
  184.  
  185.        // Connect back to string
  186.        pSink->AddBezier(
  187.            D2D1::BezierSegment(
  188.                D2D1::Point2F(cx + NOSE * 0.5f, bottomY - NOSE * 0.25f),  // curve to bottom
  189.                D2D1::Point2F(cx, bottomY),                              // bottom endpoint
  190.                D2D1::Point2F(cx, bottomY)));                            // dummy point (required)
  191.  
  192.        pSink->EndFigure(D2D1_FIGURE_END_OPEN);
  193.  
  194.        pSink->Close();
  195.        g_pRenderTarget->DrawGeometry(pGeom, g_pBrush, 2.0f);
  196.  
  197.        SafeRelease(&pSink);
  198.        SafeRelease(&pGeom);
  199.    }
  200.  
  201.    // -------------------------------------------------
  202.    // 2. Draw Bow string
  203.    // -------------------------------------------------
  204.    float pull = g_stringOffset * STRING_MAX_PULL;
  205.    D2D1_POINT_2F p1 = D2D1::Point2F(cx, topY);
  206.    D2D1_POINT_2F pm = D2D1::Point2F(cx - pull, midY);
  207.    D2D1_POINT_2F p2 = D2D1::Point2F(cx, bottomY);
  208.  
  209.    g_pRenderTarget->DrawLine(p1, pm, g_pBrush, 2.0f);
  210.    g_pRenderTarget->DrawLine(pm, p2, g_pBrush, 2.0f);
  211.  
  212.    // -------------------------------------------------
  213.    // 3. Draw Arrow (simple shaft + head + tail)
  214.    // -------------------------------------------------
  215.    float arrowY = midY;
  216.    float arrowLen = 240.0f;
  217.    float arrowHead = 18.0f;
  218.  
  219.    // Shaft
  220.    g_pRenderTarget->DrawLine(
  221.        D2D1::Point2F(g_arrowPosX, arrowY),
  222.        D2D1::Point2F(g_arrowPosX - arrowLen, arrowY),
  223.        g_pBrush, 2.0f);
  224.  
  225.    // Arrow head (small triangle)
  226.    D2D1_POINT_2F headPts[3] = {
  227.        D2D1::Point2F(g_arrowPosX,                 arrowY),
  228.        D2D1::Point2F(g_arrowPosX - arrowHead,     arrowY - arrowHead * 0.5f),
  229.        D2D1::Point2F(g_arrowPosX - arrowHead,     arrowY + arrowHead * 0.5f)
  230.    };
  231.    g_pRenderTarget->DrawLine(headPts[0], headPts[1], g_pBrush, 2.0f);
  232.    g_pRenderTarget->DrawLine(headPts[0], headPts[2], g_pBrush, 2.0f);
  233.  
  234.    // Fletching (3 short diagonal lines)
  235.    const float fletch = 20.0f;
  236.    const float step = 5.0f;
  237.    // Top fletching
  238.    for (int i = 0; i < 6; ++i)
  239.    {
  240.        float x = g_arrowPosX - arrowLen + i * step;
  241.        g_pRenderTarget->DrawLine(
  242.            D2D1::Point2F(x, arrowY),
  243.            D2D1::Point2F(x - fletch, arrowY - fletch * 0.4f + i * 3.0f),
  244.            g_pBrush, 2.0f);
  245.    }
  246.    // Bottom fletching
  247.    for (int i = 0; i < 6; ++i)
  248.    {
  249.        float x = g_arrowPosX - arrowLen + i * step;
  250.        g_pRenderTarget->DrawLine(
  251.            D2D1::Point2F(x, arrowY),
  252.            D2D1::Point2F(x - fletch, arrowY + fletch * 0.4f - i * 3.0f),
  253.            g_pBrush, 2.0f);
  254.    }
  255.  
  256.    // Present
  257.    HRESULT hr = g_pRenderTarget->EndDraw();
  258.    if (hr == D2DERR_RECREATE_TARGET)
  259.        DiscardDeviceResources();
  260. }
  261.  
  262. // -------------------------------------------------
  263. // Animation tick (called from WM_TIMER)
  264. // -------------------------------------------------
  265. void UpdateAnimation()
  266. {
  267.    // Compute delta time
  268.    auto  now = std::chrono::high_resolution_clock::now();
  269.    float dt = std::chrono::duration<float>(now - g_prevTick).count();
  270.    g_prevTick = now;
  271.    g_timeInStage += dt;
  272.  
  273.    switch (g_stage)
  274.    {
  275.    case AnimStage::Cocking:
  276.    {
  277.        float t = min(g_timeInStage / COCK_TIME, 1.0f);
  278.        g_stringOffset = t;                       // 0 -> 1  (string pulled left)
  279.  
  280.        float startX = cx + ARROW_REST_OFFSET;          // <<< NEW
  281.        float cockedX = cx - STRING_MAX_PULL + 50.0f;    // same as before
  282.        g_arrowPosX = Lerp(startX, cockedX, t);
  283.  
  284.        if (t >= 1.0f) { g_stage = AnimStage::Hold; g_timeInStage = 0.f; }
  285.    } break;
  286.  
  287.    case AnimStage::Hold:
  288.        if (g_timeInStage >= HOLD_TIME)
  289.        {
  290.            g_stage = AnimStage::Firing; g_timeInStage = 0.f;
  291.        }
  292.        break;
  293.  
  294.    case AnimStage::Firing:
  295.    {
  296.        float t = min(g_timeInStage / FIRE_TIME, 1.0f);
  297.        g_stringOffset = 1.0f - t;             // 1 -> 0  (same)
  298.  
  299.        float startX = cx - STRING_MAX_PULL + 50.0f;  // << same value as cockedX
  300.        float endX = g_windowW + 100.0f;            // fly off screen right
  301.        g_arrowPosX = Lerp(startX, endX, t);
  302.  
  303.        if (t >= 1.0f)
  304.        {
  305.            g_stage = AnimStage::Cocking; g_timeInStage = 0.f;
  306.        } // loop
  307.    } break;
  308.  
  309.    case AnimStage::Done:
  310.        // Not used anymore, but kept for completeness
  311.        break;
  312.    }
  313.  
  314.    // Request redraw
  315.    InvalidateRect(g_hWnd, nullptr, FALSE);
  316. }
  317.  
  318. // -------------------------------------------------
  319. // Window procedure
  320. // -------------------------------------------------
  321. LRESULT CALLBACK WndProc(HWND hWnd, UINT msg,
  322.    WPARAM wParam, LPARAM lParam)
  323. {
  324.    switch (msg)
  325.    {
  326.    case WM_CREATE:
  327.        if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
  328.            &g_pFactory)))
  329.            return -1; // abort window creation
  330.        SetTimer(hWnd, TIMER_ID, TIMER_MS, nullptr);
  331.        g_prevTick = std::chrono::high_resolution_clock::now();
  332.        return 0;
  333.  
  334.    case WM_SIZE:
  335.    {
  336.        g_windowW = static_cast<float>(LOWORD(lParam));
  337.        g_windowH = static_cast<float>(HIWORD(lParam));
  338.  
  339.        // Update dependent geometry values
  340.        topY = 120.0f;
  341.        bottomY = g_windowH - 120.0f;
  342.        midY = (topY + bottomY) * 0.5f;
  343.        halfHeight = (bottomY - topY) * 0.5f;
  344.  
  345.        if (g_pRenderTarget)
  346.            g_pRenderTarget->Resize(
  347.                D2D1::SizeU(static_cast<UINT>(g_windowW),
  348.                    static_cast<UINT>(g_windowH)));
  349.    }
  350.    return 0;
  351.  
  352.    case WM_TIMER:
  353.        if (wParam == TIMER_ID)
  354.            UpdateAnimation();
  355.        return 0;
  356.  
  357.    case WM_PAINT:
  358.    {
  359.        PAINTSTRUCT ps;
  360.        BeginPaint(hWnd, &ps);
  361.        OnRender();
  362.        EndPaint(hWnd, &ps);
  363.    } return 0;
  364.  
  365.    case WM_DESTROY:
  366.        KillTimer(hWnd, TIMER_ID);
  367.        DiscardDeviceResources();
  368.        SafeRelease(&g_pFactory);
  369.        PostQuitMessage(0);
  370.        return 0;
  371.    }
  372.    return DefWindowProc(hWnd, msg, wParam, lParam);
  373. }
  374.  
  375. // -------------------------------------------------
  376. // WinMain - entry point
  377. // -------------------------------------------------
  378. int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, PWSTR, int nCmdShow)
  379. {
  380.    const wchar_t CLASS_NAME[] = L"BowWindow";
  381.  
  382.    WNDCLASS wc = {};
  383.    wc.lpfnWndProc = WndProc;
  384.    wc.hInstance = hInst;
  385.    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
  386.    wc.lpszClassName = CLASS_NAME;
  387.    RegisterClass(&wc);
  388.  
  389.    g_hWnd = CreateWindowEx(0, CLASS_NAME, L"Direct2D - Longbow Demo",
  390.        WS_OVERLAPPEDWINDOW,
  391.        CW_USEDEFAULT, CW_USEDEFAULT,
  392.        static_cast<int>(g_windowW),
  393.        static_cast<int>(g_windowH),
  394.        nullptr, nullptr, hInst, nullptr);
  395.  
  396.    ShowWindow(g_hWnd, nCmdShow);
  397.  
  398.    // Main message loop
  399.    MSG msg;
  400.    while (GetMessage(&msg, nullptr, 0, 0))
  401.    {
  402.        TranslateMessage(&msg);
  403.        DispatchMessage(&msg);
  404.    }
  405.    return 0;
  406. }
  407.  
  408. ######################################################################################
  409. ######################################################################################
  410. ##  endroughxfer-urldump #1#
  411. ##  <EOF><EOF>
  412. ######################################################################################
  413. ######################################################################################
  414.  
  415. Original Code:::
  416. //  BowAndArrow.cpp
  417. //  Simple Win32 / Direct2D demo - "wireframe longbow"
  418. //  Build: cl /EHsc /DUNICODE /D_UNICODE BowAndArrow.cpp d2d1.lib user32.lib gdi32.lib
  419.  
  420. #include <windows.h>
  421. #include <d2d1.h>
  422. #include <chrono>
  423. #include <cmath>
  424. #include <algorithm>
  425. #pragma comment(lib, "d2d1")
  426.  
  427. // -------------------------------------------------
  428. // Small RAII helper to release COM objects
  429. // -------------------------------------------------
  430. template<class T> void SafeRelease(T** pp)
  431. {
  432.    if (*pp) { (*pp)->Release(); *pp = nullptr; }
  433. }
  434.  
  435. // -------------------------------------------------
  436. // Animation constants
  437. // -------------------------------------------------
  438. constexpr float  COCK_TIME = 1.0f;   // seconds
  439. constexpr float  HOLD_TIME = 0.5f;   // seconds
  440. constexpr float  FIRE_TIME = 1.0f;   // seconds
  441. constexpr UINT   TIMER_ID = 1;
  442. constexpr UINT   TIMER_MS = 16;     // ~60 FPS
  443.  
  444. enum class AnimStage { Cocking, Hold, Firing, Done };
  445. constexpr float ARROW_REST_OFFSET = 500.0f;   // pixels in-front of bow ( > 0 )
  446.  
  447. // String pull distance (pixels) and bow X center
  448. constexpr float STRING_MAX_PULL = 180.0f;
  449. constexpr float CX_BOW = 220.0f;
  450.  
  451. // -------------------------------------------------
  452. // Global state (kept simple for a demo)
  453. // -------------------------------------------------
  454. HWND                    g_hWnd = nullptr;
  455. ID2D1Factory* g_pFactory = nullptr;
  456. ID2D1HwndRenderTarget* g_pRenderTarget = nullptr;
  457. ID2D1SolidColorBrush* g_pBrush = nullptr;
  458.  
  459. float g_windowW = 1280.0f;
  460. float g_windowH = 720.0f;
  461. float g_arrowPosX = CX_BOW + ARROW_REST_OFFSET;   // head starts free-floating
  462.  
  463. // Geometry helpers (depend on window height, so they are updated in WM_SIZE)
  464. float cx = CX_BOW;
  465. float topY = 120.0f;
  466. float bottomY = 600.0f;   // will be overwritten in WM_SIZE
  467. float midY = 360.0f;
  468. float halfHeight = 240.0f;
  469.  
  470. // Animation variables
  471. AnimStage   g_stage = AnimStage::Cocking;
  472. float       g_timeInStage = 0.0f;        // seconds
  473. auto        g_prevTick = std::chrono::high_resolution_clock::now();
  474. float       g_stringOffset = 0.0f;        // 0 = relaxed, 1 = fully drawn
  475. //float       g_arrowPosX = 0.0f;        // head position
  476.  
  477. // -------------------------------------------------
  478. // Helpers
  479. // -------------------------------------------------
  480. float Lerp(float a, float b, float t) { return a + (b - a) * t; }
  481.  
  482. // Create or re-create render target
  483. HRESULT CreateDeviceResources()
  484. {
  485.    if (g_pRenderTarget) return S_OK;
  486.  
  487.    RECT rc; GetClientRect(g_hWnd, &rc);
  488.  
  489.    D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left,
  490.        rc.bottom - rc.top);
  491.  
  492.    HRESULT hr = g_pFactory->CreateHwndRenderTarget(
  493.        D2D1::RenderTargetProperties(),
  494.        D2D1::HwndRenderTargetProperties(g_hWnd, size),
  495.        &g_pRenderTarget);
  496.  
  497.    if (SUCCEEDED(hr))
  498.        hr = g_pRenderTarget->CreateSolidColorBrush(
  499.            D2D1::ColorF(D2D1::ColorF::White), &g_pBrush);
  500.  
  501.    return hr;
  502. }
  503.  
  504. void DiscardDeviceResources()
  505. {
  506.    SafeRelease(&g_pBrush);
  507.    SafeRelease(&g_pRenderTarget);
  508. }
  509.  
  510. // -------------------------------------------------
  511. // Core rendering
  512. // -------------------------------------------------
  513. void OnRender()
  514. {
  515.    if (FAILED(CreateDeviceResources()))
  516.        return;
  517.  
  518.    g_pRenderTarget->BeginDraw();
  519.    g_pRenderTarget->Clear(D2D1::ColorF(0x202020)); // dark bg
  520.  
  521.    // -------------------------------------------------
  522.    // 1. Draw Bow limbs (two quadratic curves)
  523.    // -------------------------------------------------
  524.    ID2D1PathGeometry* pGeom = nullptr;
  525.    ID2D1GeometrySink* pSink = nullptr;
  526.    if (SUCCEEDED(g_pFactory->CreatePathGeometry(&pGeom)))
  527.    {
  528.        pGeom->Open(&pSink);
  529.  
  530.        //--------------------------------------------------------------
  531.        //  One continuous ")"-shaped limb with a slightly pointy tip.
  532.        //  End-points stay on the string ( (cx, topY) & (cx, bottomY) )
  533.        //--------------------------------------------------------------
  534.        constexpr float NOSE = 35.0f;               // how far the tips stick out
  535.        const     float BULGE = halfHeight * 0.5f;  // max belly distance (x)
  536.  
  537.        pSink->BeginFigure(D2D1::Point2F(cx, topY), D2D1_FIGURE_BEGIN_HOLLOW);
  538.  
  539.        // Draw semicircle bow with pointy tip at middle
  540.        // First quarter - top to middle-top
  541.        pSink->AddBezier(
  542.            D2D1::BezierSegment(
  543.                D2D1::Point2F(cx + NOSE, topY + halfHeight * 0.1f),      // slight curve at top
  544.                D2D1::Point2F(cx + BULGE, topY + halfHeight * 0.5f),     // first quarter curve
  545.                D2D1::Point2F(cx + BULGE + NOSE, midY - NOSE)));         // approaching tip
  546.  
  547.        // Middle part with pointy tip
  548.        pSink->AddBezier(
  549.            D2D1::BezierSegment(
  550.                D2D1::Point2F(cx + BULGE + NOSE * 1.2f, midY - NOSE * 0.5f),  // curve to tip
  551.                D2D1::Point2F(cx + BULGE + NOSE * 1.5f, midY),               // pointy tip
  552.                D2D1::Point2F(cx + BULGE + NOSE * 1.2f, midY + NOSE * 0.5f)));  // curve from tip
  553.  
  554.        // Last quarter - middle-bottom to bottom
  555.        pSink->AddBezier(
  556.            D2D1::BezierSegment(
  557.                D2D1::Point2F(cx + BULGE + NOSE, midY + NOSE),            // after tip
  558.                D2D1::Point2F(cx + BULGE, bottomY - halfHeight * 0.5f),   // last quarter curve
  559.                D2D1::Point2F(cx + NOSE, bottomY - halfHeight * 0.1f)));  // approaching bottom
  560.  
  561.        // Connect back to string
  562.        pSink->AddBezier(
  563.            D2D1::BezierSegment(
  564.                D2D1::Point2F(cx + NOSE * 0.5f, bottomY - NOSE * 0.25f),  // curve to bottom
  565.                D2D1::Point2F(cx, bottomY),                              // bottom endpoint
  566.                D2D1::Point2F(cx, bottomY)));                            // dummy point (required)
  567.  
  568.        pSink->EndFigure(D2D1_FIGURE_END_OPEN);
  569.  
  570.        pSink->Close();
  571.        g_pRenderTarget->DrawGeometry(pGeom, g_pBrush, 2.0f);
  572.  
  573.        SafeRelease(&pSink);
  574.        SafeRelease(&pGeom);
  575.    }
  576.  
  577.    // -------------------------------------------------
  578.    // 2. Draw Bow string
  579.    // -------------------------------------------------
  580.    float pull = g_stringOffset * STRING_MAX_PULL;
  581.    D2D1_POINT_2F p1 = D2D1::Point2F(cx, topY);
  582.    D2D1_POINT_2F pm = D2D1::Point2F(cx - pull, midY);
  583.    D2D1_POINT_2F p2 = D2D1::Point2F(cx, bottomY);
  584.  
  585.    g_pRenderTarget->DrawLine(p1, pm, g_pBrush, 2.0f);
  586.    g_pRenderTarget->DrawLine(pm, p2, g_pBrush, 2.0f);
  587.  
  588.    // -------------------------------------------------
  589.    // 3. Draw Arrow (simple shaft + head + tail)
  590.    // -------------------------------------------------
  591.    float arrowY = midY;
  592.    float arrowLen = 240.0f;
  593.    float arrowHead = 18.0f;
  594.  
  595.    // Shaft
  596.    g_pRenderTarget->DrawLine(
  597.        D2D1::Point2F(g_arrowPosX, arrowY),
  598.        D2D1::Point2F(g_arrowPosX - arrowLen, arrowY),
  599.        g_pBrush, 2.0f);
  600.  
  601.    // Arrow head (small triangle)
  602.    D2D1_POINT_2F headPts[3] = {
  603.        D2D1::Point2F(g_arrowPosX,                 arrowY),
  604.        D2D1::Point2F(g_arrowPosX - arrowHead,     arrowY - arrowHead * 0.5f),
  605.        D2D1::Point2F(g_arrowPosX - arrowHead,     arrowY + arrowHead * 0.5f)
  606.    };
  607.    g_pRenderTarget->DrawLine(headPts[0], headPts[1], g_pBrush, 2.0f);
  608.    g_pRenderTarget->DrawLine(headPts[0], headPts[2], g_pBrush, 2.0f);
  609.  
  610.    // Fletching (3 short diagonal lines)
  611.    const float fletch = 20.0f;
  612.    const float step = 5.0f;
  613.    for (int i = 0; i < 3; ++i)
  614.    {
  615.        float x = g_arrowPosX - arrowLen + i * step;
  616.        g_pRenderTarget->DrawLine(
  617.            D2D1::Point2F(x, arrowY),
  618.            D2D1::Point2F(x + fletch, arrowY - fletch * 0.4f + i * 3.0f),
  619.            g_pBrush, 2.0f);
  620.    }
  621.  
  622.    // Present
  623.    HRESULT hr = g_pRenderTarget->EndDraw();
  624.    if (hr == D2DERR_RECREATE_TARGET)
  625.        DiscardDeviceResources();
  626. }
  627.  
  628. // -------------------------------------------------
  629. // Animation tick (called from WM_TIMER)
  630. // -------------------------------------------------
  631. void UpdateAnimation()
  632. {
  633.    // Compute delta time
  634.    auto  now = std::chrono::high_resolution_clock::now();
  635.    float dt = std::chrono::duration<float>(now - g_prevTick).count();
  636.    g_prevTick = now;
  637.    g_timeInStage += dt;
  638.  
  639.    switch (g_stage)
  640.    {
  641.    case AnimStage::Cocking:
  642.    {
  643.        float t = min(g_timeInStage / COCK_TIME, 1.0f);
  644.        g_stringOffset = t;                       // 0 -> 1  (string pulled left)
  645.  
  646.        float startX = cx + ARROW_REST_OFFSET;          // <<< NEW
  647.        float cockedX = cx - STRING_MAX_PULL + 50.0f;    // same as before
  648.        g_arrowPosX = Lerp(startX, cockedX, t);
  649.  
  650.        if (t >= 1.0f) { g_stage = AnimStage::Hold; g_timeInStage = 0.f; }
  651.    } break;
  652.  
  653.    case AnimStage::Hold:
  654.        if (g_timeInStage >= HOLD_TIME)
  655.        {
  656.            g_stage = AnimStage::Firing; g_timeInStage = 0.f;
  657.        }
  658.        break;
  659.  
  660.    case AnimStage::Firing:
  661.    {
  662.        float t = min(g_timeInStage / FIRE_TIME, 1.0f);
  663.        g_stringOffset = 1.0f - t;             // 1 -> 0  (same)
  664.  
  665.        float startX = cx - STRING_MAX_PULL + 50.0f;  // << same value as cockedX
  666.        float endX = g_windowW + 100.0f;            // fly off screen right
  667.        g_arrowPosX = Lerp(startX, endX, t);
  668.  
  669.        if (t >= 1.0f)
  670.        {
  671.            g_stage = AnimStage::Cocking; g_timeInStage = 0.f;
  672.        } // loop
  673.    } break;
  674.  
  675.    case AnimStage::Done:
  676.        // Not used anymore, but kept for completeness
  677.        break;
  678.    }
  679.  
  680.    // Request redraw
  681.    InvalidateRect(g_hWnd, nullptr, FALSE);
  682. }
  683.  
  684. // -------------------------------------------------
  685. // Window procedure
  686. // -------------------------------------------------
  687. LRESULT CALLBACK WndProc(HWND hWnd, UINT msg,
  688.    WPARAM wParam, LPARAM lParam)
  689. {
  690.    switch (msg)
  691.    {
  692.    case WM_CREATE:
  693.        if (FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
  694.            &g_pFactory)))
  695.            return -1; // abort window creation
  696.        SetTimer(hWnd, TIMER_ID, TIMER_MS, nullptr);
  697.        g_prevTick = std::chrono::high_resolution_clock::now();
  698.        return 0;
  699.  
  700.    case WM_SIZE:
  701.    {
  702.        g_windowW = static_cast<float>(LOWORD(lParam));
  703.        g_windowH = static_cast<float>(HIWORD(lParam));
  704.  
  705.        // Update dependent geometry values
  706.        topY = 120.0f;
  707.        bottomY = g_windowH - 120.0f;
  708.        midY = (topY + bottomY) * 0.5f;
  709.        halfHeight = (bottomY - topY) * 0.5f;
  710.  
  711.        if (g_pRenderTarget)
  712.            g_pRenderTarget->Resize(
  713.                D2D1::SizeU(static_cast<UINT>(g_windowW),
  714.                    static_cast<UINT>(g_windowH)));
  715.    }
  716.    return 0;
  717.  
  718.    case WM_TIMER:
  719.        if (wParam == TIMER_ID)
  720.            UpdateAnimation();
  721.        return 0;
  722.  
  723.    case WM_PAINT:
  724.    {
  725.        PAINTSTRUCT ps;
  726.        BeginPaint(hWnd, &ps);
  727.        OnRender();
  728.        EndPaint(hWnd, &ps);
  729.    } return 0;
  730.  
  731.    case WM_DESTROY:
  732.        KillTimer(hWnd, TIMER_ID);
  733.        DiscardDeviceResources();
  734.        SafeRelease(&g_pFactory);
  735.        PostQuitMessage(0);
  736.        return 0;
  737.    }
  738.    return DefWindowProc(hWnd, msg, wParam, lParam);
  739. }
  740.  
  741. // -------------------------------------------------
  742. // WinMain - entry point
  743. // -------------------------------------------------
  744. int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, PWSTR, int nCmdShow)
  745. {
  746.    const wchar_t CLASS_NAME[] = L"BowWindow";
  747.  
  748.    WNDCLASS wc = {};
  749.    wc.lpfnWndProc = WndProc;
  750.    wc.hInstance = hInst;
  751.    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
  752.    wc.lpszClassName = CLASS_NAME;
  753.    RegisterClass(&wc);
  754.  
  755.    g_hWnd = CreateWindowEx(0, CLASS_NAME, L"Direct2D - Longbow Demo",
  756.        WS_OVERLAPPEDWINDOW,
  757.        CW_USEDEFAULT, CW_USEDEFAULT,
  758.        static_cast<int>(g_windowW),
  759.        static_cast<int>(g_windowH),
  760.        nullptr, nullptr, hInst, nullptr);
  761.  
  762.    ShowWindow(g_hWnd, nCmdShow);
  763.  
  764.    // Main message loop
  765.    MSG msg;
  766.    while (GetMessage(&msg, nullptr, 0, 0))
  767.    {
  768.        TranslateMessage(&msg);
  769.        DispatchMessage(&msg);
  770.    }
  771.    return 0;
  772. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement