Files
community_bridge/lib/client/gizmo.lua
T
gononono64 99389dcd2d itroducing placeable object + gizmo mix + raycasting
NOTE: Did not change Placeable. Should not impact current resources

rebound_entities are still WIP
2025-02-12 08:58:36 -06:00

347 lines
11 KiB
Lua

-- CREDITS
-- Andyyy7666: https://github.com/overextended/ox_lib/pull/453
-- AvarianKnight: https://forum.cfx.re/t/allow-drawgizmo-to-be-used-outside-of-fxdk/5091845/8?u=demi-automatic
local dataview = Require("lib/client/dataview.lua")
local enableScale = false -- allow scaling mode. doesnt scale collisions and resets when physics are applied it seems
local gizmoEnabled = false
local currentMode = 'translate'
local isRelative = false
local currentEntity
-- FUNCTIONS
local function normalize(x, y, z)
local length = math.sqrt(x * x + y * y + z * z)
if length == 0 then
return 0, 0, 0
end
return x / length, y / length, z / length
end
local function makeEntityMatrix(entity)
local f, r, u, a = GetEntityMatrix(entity)
local view = dataview.ArrayBuffer(60)
view:SetFloat32(0, r[1])
:SetFloat32(4, r[2])
:SetFloat32(8, r[3])
:SetFloat32(12, 0)
:SetFloat32(16, f[1])
:SetFloat32(20, f[2])
:SetFloat32(24, f[3])
:SetFloat32(28, 0)
:SetFloat32(32, u[1])
:SetFloat32(36, u[2])
:SetFloat32(40, u[3])
:SetFloat32(44, 0)
:SetFloat32(48, a[1])
:SetFloat32(52, a[2])
:SetFloat32(56, a[3])
:SetFloat32(60, 1)
return view
end
local function applyEntityMatrix(entity, view)
local x1, y1, z1 = view:GetFloat32(16), view:GetFloat32(20), view:GetFloat32(24)
local x2, y2, z2 = view:GetFloat32(0), view:GetFloat32(4), view:GetFloat32(8)
local x3, y3, z3 = view:GetFloat32(32), view:GetFloat32(36), view:GetFloat32(40)
local tx, ty, tz = view:GetFloat32(48), view:GetFloat32(52), view:GetFloat32(56)
if not enableScale then
x1, y1, z1 = normalize(x1, y1, z1)
x2, y2, z2 = normalize(x2, y2, z2)
x3, y3, z3 = normalize(x3, y3, z3)
end
SetEntityMatrix(entity,
x1, y1, z1,
x2, y2, z2,
x3, y3, z3,
tx, ty, tz
)
end
function InBoundary(pos, boundary)
if not boundary then return false end
local x, y, z = table.unpack(pos)
local minX, minY, minZ = table.unpack(boundary.min)
local maxX, maxY, maxZ = table.unpack(boundary.max)
return x >= minX and x <= maxX and y >= minY and y <= maxY and z >= minZ and z <= maxZ
end
-- LOOPS
function RegisterButtonHandler(button, onPressed, onReleased)
onPressed = onPressed and type(onPressed) == 'function' and onPressed
onReleased = onReleased and type(onReleased) == 'function' and onReleased
DisableControlAction(0, button, true)
local pressed = IsDisabledControlJustPressed(0, button)
local released = IsDisabledControlJustReleased(0, button)
return (pressed and onPressed and onPressed()) or (released and onReleased and onReleased())
end
local function gizmoLoop(entity, onConfirm, onUpdate, onCancel, settings)
assert(entity, 'entity is required')
entity = tonumber(entity)
EnterCursorMode()
settings = settings or {}
local allowedMats = settings.allowedMats
local maxDepth = settings.maxDepth or 10
local outLine = false
if IsEntityAPed(entity) then
SetEntityAlpha(entity, 200)
end
if allowedMats or boundary then
CreateThread(function()
while gizmoEnabled do
local coords = GetEntityCoords(entity)
local destination = coords - vector3(0, 0, maxDepth)
local hit, _, _, _, materialHash = Raycast.ToCoords(coords, destination, 1, 4)
if hit and hit ~=1 and (allowedMats?[materialHash] or InBoundary(GetEntityCoords(entity), boundary)) then
if not inBounds then
inBounds = true
SetEntityDrawOutlineColor(0, 255, 0, 255)
SetEntityDrawOutline(entity, true)
end
elseif inBounds then
inBounds = false
SetEntityDrawOutline(entity, false)
end
Wait(500)
end
end)
end
while gizmoEnabled and DoesEntityExist(entity) do
Wait(0)
DisableControlAction(0, 24, true) -- lmb
DisableControlAction(0, 25, true) -- rmb
DisableControlAction(0, 140, true) -- r
DisableControlAction(0, 245, true) --t
DisablePlayerFiring(PlayerId(), true)
SetEntityCollision(entity, false, false)
FreezeEntityPosition(entity, false)
local matrixBuffer = makeEntityMatrix(entity)
local changed = Citizen.InvokeNative(0xEB2EDCA2, matrixBuffer:Buffer(), 'Editor1', Citizen.ReturnResultAnyway())
if changed then
applyEntityMatrix(entity, matrixBuffer)
local coords = GetEntityCoords(entity)
SetEntityCoords(entity, coords.x, coords.y, coords.z, false, false, false, false)
end
if onUpdate and type(onUpdate) == 'function' then onUpdate(entity, GetEntityCoords(entity), GetEntityRotation(entity)) end
--controles
--frontend confirm 18 [ENTER]
RegisterButtonHandler(191, function()
if not gizmoEnabled then return end
gizmoEnabled = false
if onConfirm and type(onConfirm) == 'function' then onConfirm(entity, GetEntityCoords(entity), GetEntityRotation(entity)) end
end)
--frontend cancel 202 [BACKSPACE]
RegisterButtonHandler(202, function()
if not gizmoEnabled then return end
gizmoEnabled = false
if onCancel and type(onCancel) == 'function' then onCancel(entity, GetEntityCoords(entity), GetEntityRotation(entity)) end
CreateThread(function()
local timeout = 100
while timeout > 0 do
DisableFrontendThisFrame()
timeout = timeout - 1
Wait(1)
end
end)
end)
--register e
RegisterButtonHandler(38, function()
if not gizmoEnabled then return end
gizmoEnabled = false
local playerPos = GetEntityCoords(PlayerPedId())
local entityPos = GetFinalRenderedCamCoord()
local depth = #(playerPos - entityPos) -- Vector3 distance
settings.depth = depth
PlaceableObject.Create({spawned = entity}, nil, onConfirm, onUpdate, onCancel, settings)
end)
--ctrl
RegisterButtonHandler(36, function()
if not gizmoEnabled then return end
LeaveCursorMode()
end, function()
EnterCursorMode()
end)
RegisterButtonHandler(19, function()
if not gizmoEnabled then return end
PlaceObjectOnGroundProperly_2(currentEntity)
end)
FreezeEntityPosition(entity, false)
end
LeaveCursorMode()
if DoesEntityExist(entity) then
if IsEntityAPed(entity) then SetEntityAlpha(entity, 255) end
SetEntityDrawOutline(entity, false)
end
gizmoEnabled = false
currentEntity = nil
return true
end
--===================================================
-- █▀ █ █ ▄▀▀ █▄▀ ▀▄▀ ▄▀▄ █ █ █▄ █ ██▀ █ █ ██▄
-- █▀ ▀▄█ ▀▄▄ █ █ █ ▀▄▀ ▀▄█ █ ▀█ █▄▄ ▀▄▀▄▀ █▄█
--===================================================
-- .--------.
-- | taktak |
-- |/-------'
-- .-. ,-.
-- ;oo oo;
-- / \| |/ \
-- |. `. .' .|
-- `;.;' `;.;'
-- .-^-. .-^-. ko1
-- local function textUILoop()
-- CreateThread(function()
-- while gizmoEnabled do
-- Wait(100)
-- local scaleText = (enableScale and '[S] - Scale Mode \n') or ''
-- lib.showTextUI(
-- ('Current Mode: %s | %s \n'):format(currentMode, (isRelative and 'Relative') or 'World') ..
-- '[W] - Translate Mode \n' ..
-- '[R] - Rotate Mode \n' ..
-- scaleText ..
-- '[Q] - Relative/World \n' ..
-- '[LALT] - Snap To Ground \n' ..
-- '[ENTER] - Done Editing \n'
-- )
-- end
-- lib.hideTextUI()
-- end)
-- end
-- EXPORTS
local function useGizmo(entity, onConfirm, onUpdate, onCancel, settings)
gizmoEnabled = true
currentEntity = entity
-- textUILoop()
gizmoLoop(entity, onConfirm, onUpdate, onCancel, settings)
return {
handle = entity,
position = GetEntityCoords(entity),
rotation = GetEntityRotation(entity)
}
end
exports("useGizmo", useGizmo)
Gizmo = {}
local running = false
function Gizmo.UseGizmo(obj, onConfirm, onUpdate, onCancel, settings)
if running then return end
running = true
--instructional buttons
local scaleform = Scaleform.SetupInstructionalButtons({
{type = "CLEAR_ALL"},
{type = "SET_CLEAR_SPACE", int = 200},
{type = "SET_DATA_SLOT", name = 'Translate:', keyIndex = {245}, int = 1},
{type = "SET_DATA_SLOT", name = 'Scale:', keyIndex = {33}, int = 2},
{type = "SET_DATA_SLOT", name = 'Rotate:', keyIndex = {45}, int = 3},
{type = "SET_DATA_SLOT", name = 'Snap to Ground:', keyIndex = {19}, int = 4},
{type = "SET_DATA_SLOT", name = 'Toggle Camera:', keyIndex = {36}, int = 5},
{type = "SET_DATA_SLOT", name = 'Cancel Placement:', keyIndex = {202}, int = 6},
{type = "SET_DATA_SLOT", name = 'Toggle Gizmo:', keyIndex = {38}, int = 7},
{type = "DRAW_INSTRUCTIONAL_BUTTONS"},
{type = "SET_BACKGROUND_COLOUR"},
})
local quit = false
CreateThread(function()
while not quit do
DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0)
Wait(0)
end
running = false
end)
quit = useGizmo(obj, onConfirm, onUpdate, onCancel, settings)
running = false
end
exports("UseGizmo", Gizmo.UseGizmo)
--got rid of the cancer
-- lib.addKeybind({
-- name = '_gizmoLocal',
-- description = 'toggle gizmo to be local to the entity instead of world',
-- defaultKey = 'Q',
-- onPressed = function(self)
-- if not gizmoEnabled then return end
-- isRelative = not isRelative
-- ExecuteCommand('+gizmoLocal')
-- end,
-- onReleased = function (self)
-- ExecuteCommand('-gizmoLocal')
-- end
-- })
-- lib.addKeybind({
-- name = 'gizmoclose',
-- description = 'close gizmo',
-- defaultKey = 'RETURN',
-- onPressed = function(self)
-- if not gizmoEnabled then return end
-- gizmoEnabled = false
-- end,
-- })
-- lib.addKeybind({
-- name = 'gizmoSnapToGround',
-- description = 'snap current gizmo object to floor/surface',
-- defaultKey = 'LMENU',
-- onPressed = function(self)
-- if not gizmoEnabled then return end
-- PlaceObjectOnGroundProperly_2(currentEntity)
-- end,
-- })
-- if enableScale then
-- lib.addKeybind({
-- name = '_gizmoScale',
-- description = 'Sets mode for the gizmo to scale',
-- defaultKey = 'S',
-- onPressed = function(self)
-- if not gizmoEnabled then return end
-- currentMode = 'Scale'
-- ExecuteCommand('+gizmoScale')
-- end,
-- onReleased = function (self)
-- ExecuteCommand('-gizmoScale')
-- end
-- })
-- end
--onreousrcestop
AddEventHandler('onResourceStop', function(resource)
if resource == GetCurrentResourceName() then
if not gizmoEnabled then return end
gizmoEnabled = false
LeaveCursorMode()
end
end)
return Gizmo