[FREE RELEASE] cr-3dnui - Interactive 3D DUI panels for FiveM —( real web apps rendered on in-game surfaces)

You’re gonna love what I’m releasing soon with this as a dependency :slight_smile:

1 Like

OH I’M GONNA BE USING THAT! Great Job!

3 Likes

update 2.7

cr-3dnui native mouse support using UV grid…plus new focus UV helpers and broader focus/render improvements for smoother and more reliable 3D UI behavior in-world.

What “UV Grid with Native Mouse” Means

  • UV coordinates (u, v) are still the interaction basis (0.0 to 1.0), same as before.
  • The new part is that UV interaction can now follow actual native mouse position instead of only camera-center aiming.
  • You can read live UV hit position and forward that to your panel UI logic.

Core Exports You Use

  • SetPanelInteractionMode(panelId, mode)
  • BeginFocus(panelId, opts)
  • FocusTick()
  • GetFocusUV()
  • SendMouseMove(panelId, u, v, opts)
  • SendMouseDown(panelId, button)
  • SendMouseUp(panelId, button)
  • SendMouseWheel(panelId, delta)

Minimal Setup Example

local panelId = exports['cr-3dnui']:CreatePanel({
  url = 'https://your-ui/index.html',
  pos = vector3(0.0, 0.0, 0.0),
  normal = vector3(0.0, 1.0, 0.0),
  width = 1.0,
  height = 0.6,
  resW = 1280,
  resH = 720
})

-- Enable native cursor-driven interaction
exports['cr-3dnui']:SetPanelInteractionMode(panelId, 'native_mouse')

-- Start focus flow (optional lib cursor overlay + optional focus enter/leave messages)
exports['cr-3dnui']:BeginFocus(panelId, {
  drawCursor = true,
  sendFocusMessages = true,
  strict = true,
  maxDist = 7.0
})

Reading Live UV Hits

CreateThread(function()
  while true do
    Wait(0)
    local hasHit, u, v, focusedPanelId, hitPos = exports['cr-3dnui']:GetFocusUV()
    if hasHit and focusedPanelId == panelId then
      -- Example: send UV to your DUI app for debug overlays/tooltips
      exports['cr-3dnui']:SendMessage(panelId, {
        type = 'uv_debug',
        u = u,
        v = v
      })
    end
  end
end)

Manual Mouse Forwarding Example

CreateThread(function()
  while true do
    Wait(0)
    local hasHit, u, v, focusedPanelId = exports['cr-3dnui']:GetFocusUV()
    if hasHit and focusedPanelId == panelId then
      exports['cr-3dnui']:SendMouseMove(panelId, u, v)

      if IsControlJustPressed(0, 24) then -- LMB
        exports['cr-3dnui']:SendMouseDown(panelId, 'left')
      end
      if IsControlJustReleased(0, 24) then
        exports['cr-3dnui']:SendMouseUp(panelId, 'left')
      end

      if IsControlJustPressed(0, 15) then -- wheel up
        exports['cr-3dnui']:SendMouseWheel(panelId, 1)
      elseif IsControlJustPressed(0, 14) then -- wheel down
        exports['cr-3dnui']:SendMouseWheel(panelId, -1)
      end
    end
  end
end)

Example: Native Mouse UV Flow

This example shows a practical pattern for real use: enable native mouse interaction on a panel, start focus with sane guardrails, continuously forward UV/mouse input, and force-release mouse state if focus or hit is lost.

-- 1) Create panel + enable native mouse UV mode
local panelId = exports['cr-3dnui']:CreatePanel({
  url = 'https://your-ui/index.html',
  pos = vector3(100.0, 100.0, 30.0),
  normal = vector3(0.0, 1.0, 0.0),
  width = 1.4,
  height = 0.9,
  resW = 2048,
  resH = 2048
})
exports['cr-3dnui']:SetPanelInteractionMode(panelId, 'native_mouse')

-- 2) Enter focus with strict controls and no lib cursor overlay
exports['cr-3dnui']:BeginFocus(panelId, {
  strict = true,
  drawCursor = false,
  sendFocusMessages = true,
  maxDist = 7.0,
  missGraceMs = 300
})

-- 3) Forward UV/mouse while focused, with safe release on loss
local mouseDown = false
CreateThread(function()
  while true do
    Wait(0)
    local focused = exports['cr-3dnui']:IsFocused()
    local hasHit, u, v, pid = exports['cr-3dnui']:GetFocusUV()

    -- Clamp UV to avoid edge spikes
    if hasHit and pid == panelId then
      if u < 0.0 then u = 0.0 elseif u > 1.0 then u = 1.0 end
      if v < 0.0 then v = 0.0 elseif v > 1.0 then v = 1.0 end
      exports['cr-3dnui']:SendMouseMove(panelId, u, v)

      if IsControlJustPressed(0, 24) then -- LMB down
        exports['cr-3dnui']:SendMouseDown(panelId, 'left')
        mouseDown = true
      end
      if IsControlJustReleased(0, 24) then -- LMB up
        exports['cr-3dnui']:SendMouseUp(panelId, 'left')
        mouseDown = false
      end
    else
      -- Fail-safe: always release if hit/focus is lost
      if mouseDown then
        exports['cr-3dnui']:SendMouseUp(panelId, 'left')
        mouseDown = false
      end
      if not focused then
        break
      end
    end
  end
end)
1 Like