Allow `DrawGizmo` to be used outside of FxDK

Currently DrawGizmo is limited to only being used in FxDK, but there are many use cases where using this native outside of the development kit would make sense, like when moving decorations around in a players home, figuring out the proper rotation of a prop in a peds hands, etc.

While currently the community has make its own Gizmos with three.js and such, it would be better & nicer to be able to use them in base game code without having to use the CEF/UI to handle it

I believe it’s currently possible to use the gizmo outside of FxDK, but that info is gatekept and I was only able to draw the gizmo, could not figure out how to actually move it since i believe it involves NUI stuff

It’s possible to use DrawGizmo outside of fxdk, although not sure if possible in lua.
Best way to do it is to copy code from fxdk in js using Float32Array.
Note : it’s not related to NUI, and it’s not gatekept as fxdk is open sourced since day one

You also need to enable the gizmo layer cursor, it makes it difficult to use as it’s not the game cursor, neither the NUI cursor.

Example of a player using it : Twitch

Gatekept is probably not the best word, what I meant to say was that some people already looked into the fxdk code and converted it to a fivem resource but it has not been shared with the community

More likely then not this is one they made themselves in NUI (see [FREE][tool] object controller gizmo and GitHub - dolutattoo/dolu_tool: This is a dev tool for FiveM. Includes interior debuggin tools and many other features)

The FxDK Gizmo doesn’t look like its usable in base game because you cannot set m_gizmoMode and im3d wont work without the m_gizmoMode set.

I’ve already tried getting it to work, but as said in the reasons above the Gizmo isn’t able to be used at all in base game.

// Taken from FxDK
function makeEntityMatrix(entity: Entity): Float32Array {
    const [f, r, u, a] = GetEntityMatrix(entity.Handle);

    return new Float32Array([
        r[0], r[1], r[2], 0,
        f[0], f[1], f[2], 0,
        u[0], u[1], u[2], 0,
        a[0], a[1], a[2], 1,
    ]);
}

// Taken from FxDK
function applyEntityMatrix(entity: Entity, mat: Float32Array | number[]) {
    const ent = entity.Handle;
    SetEntityMatrix(
        ent,
        mat[4], mat[5], mat[6], // right
        mat[0], mat[1], mat[2], // forward
        mat[8], mat[9], mat[10], // up
        mat[12], mat[13], mat[14], // at
    );
}

if (editorMode) {
    const matrixBuff = makeEntityMatrix(hitEnt as Entity) as any
    // Always false because m_gizmoMode isn't set
    const changed = DrawGizmo(matrixBuff, 'Editor1')
    if (changed) {
        applyEntityMatrix(hitEnt, matrixBuff);
    }
}
1 Like

Actually it seems I’m wrong, you can use these in base game, I’ll post a resource to do it later :slight_smile: I don’t currently have the time :frowning_face:

If you want to use the gizmo for base game all you have to do is actually register the key mappings
You can find the keys here fivem/GamePrimitives_Im3D.cpp at cf859b338ebe362ebeede242b3bb84a8bc4f2d76 · citizenfx/fivem · GitHub

RegisterKeyMapping("+gizmoSelect", "Selects the gizmo", "MOUSE_BUTTON", "MOUSE_LEFT")
RegisterKeyMapping("+gizmoTranslation", "Sets mode to gizmo translation", "keyboard", "F6")
1 Like

That is pretty much where im stuck, I can hover and switch modes but cant actually move the entity using the gizmo

function makeEntityMatrix(entity: number): Float32Array {
    const [f, r, u, a] = GetEntityMatrix(entity);

    return new Float32Array([
        r[0], r[1], r[2], 0,
        f[0], f[1], f[2], 0,
        u[0], u[1], u[2], 0,
        a[0], a[1], a[2], 1,
    ]);
}

function applyEntityMatrix(entity: number, mat: Float32Array | number[]) {
    SetEntityMatrix(
        entity,
        mat[4], mat[5], mat[6], // right
        mat[0], mat[1], mat[2], // forward
        mat[8], mat[9], mat[10], // up
        mat[12], mat[13], mat[14], // at
    );
}

function clearEntityDraw(entity: number) {
    if (!DoesEntityExist(entity)) return;
    SetEntityDrawOutline(entity, false);
}

let lastDebugEnt: number | null = null;
let gizmoToggle = false;
export const handleEditorMode = (editorMode: boolean) => {
    let hitEntDebug = lastDebugEnt || 0;

    // If editorMode was enabled and is now disabled we want to clear our last draw ent
    if (!editorMode && lastDebugEnt) {
        clearEntityDraw(lastDebugEnt);
        lastDebugEnt = 0;
    }

    if (!editorMode) return;

    const isPressed = IsDisabledControlPressed(0, 24);
    const wasReleased = IsDisabledControlJustReleased(0, 24);
    if (wasReleased && lastDebugEnt) {
        clearEntityDraw(lastDebugEnt);
        lastDebugEnt = 0;
    }
    // We don't want to switch entities if we're currently moving one.
    if (!isPressed && !gizmoToggle) {
        hitEntDebug = SelectEntityAtCursor(6 | (1 << 5), true);
        // For some reason SetEntityMatrix doesn't work with the entity guid SelectEntityAtCursor gives, so we have to get the closest object instead
        const [x, y, z] = GetEntityCoords(hitEntDebug, true);
        const model = GetEntityModel(hitEntDebug);
        hitEntDebug = GetClosestObjectOfType(x, y, z, 2.0, model, false, true, true);
    }

    // If we don't have an entity then we don't want to display
    if (hitEntDebug === 0 && !lastDebugEnt) return;

    const matrixBuff = makeEntityMatrix(hitEntDebug) as any
    const changed = DrawGizmo(matrixBuff, 'Editor1')
    if (changed) {
        // Apply the changes from the buff
        applyEntityMatrix(hitEntDebug, matrixBuff);
    }

    if (hitEntDebug) {
        SetEntityDrawOutline(hitEntDebug, true);
    }

    if (hitEntDebug !== lastDebugEnt) {
        if (lastDebugEnt) {
            clearEntityDraw(lastDebugEnt);
        }
        lastDebugEnt = hitEntDebug;
    }

}

RegisterCommand("gizmoToggleSelection", () => {
    if (lastDebugEnt || gizmoToggle) {
        gizmoToggle = !gizmoToggle;
    }
}, false)


RegisterKeyMapping("gizmoToggleSelection", "Toggles the gizmo to lock for the current entity", "MOUSE_BUTTON", "MOUSE_RIGHT")
RegisterKeyMapping("+gizmoSelect", "Selects the currently highlighted gizmo", "MOUSE_BUTTON", "MOUSE_LEFT")
RegisterKeyMapping("+gizmoTranslation", "Sets mode of the gizmo to translation", "keyboard", "T")
RegisterKeyMapping("+gizmoRotation", "Sets mode for the gizmo to rotation", "keyboard", "R")
RegisterKeyMapping("+gizmoScale", "Sets mode for the gizmo to scale", "keyboard", "S")
RegisterKeyMapping("+gizmoLocal", "Sets gizmo to be local to the entity instead of world", "keyboard", "L")
4 Likes

Thank you for this, i see know what was missing

OP request