![]()
this is just to share my progress in using this. this is in fact the coolest script ive played with
![]()
this is just to share my progress in using this. this is in fact the coolest script ive played with
That looks really good, the camera detach is excellent!
This is literally what I envisioned when I thought of this… walk up to ATM and actually press the buttons kind of thing
It’s also makes me very happy that you’re making this and using my library!
But more importantly you’re helping me shape it by giving me these little edge cases so I could put up guard rails making it much easier for devs to strap in.
Tonight when I’m done with my day job I will crack at the new mouse input interactions.
I’ve been iterating hard based on real-world use cases + edge cases.
Many GTA/FiveM monitor models are not perfectly flat (slightly tilted back), which can cause bezel clipping even when your normal looks “correct”.
New options to make panels behave more like a real screen:
depthCompensation = "screen" — safer depth bias for tilted props / bezel clippingfrontOnly = true — only render + accept input when viewing the front (prevents backside interaction)If a panel looks “stuck” when attached to an entity/bone, it’s usually not updating transforms.
To make it behave like a real attachment (like the vehicle demo):
updateInterval = 0 — per-frame transform updatesupdateMaxDistance = 120 — optional distance cullingThe whiteboard demo now supports both:
KEY2DUI is especially useful for constrained camera scenarios (vehicles / tight desks) because it avoids the “UV fighting the camera” feeling.
Core client code was split into smaller modules for maintainability. Exports remain backwards compatible.
been loving following the quick progress on this. keep up the good work!
panel → prop → bone
prop → bone + panel → bone
-- Keep the PROP as the bone-attached visual object (true entity parenting),
-- but keep the PANEL attached to the VEHICLE using a baked offset/normal derived from the prop.
-- This removes jitter while still looking like the panel is mounted to the prop.
-- Helper: normalize a vector3
local function vNorm(v)
local l = #(vector3(v.x, v.y, v.z))
if l < 0.000001 then return vector3(0.0, 0.0, 1.0) end
return vector3(v.x / l, v.y / l, v.z / l)
end
-- 1) Spawn prop and attach it to a vehicle bone (inherits full bone animation)
local function attachPropToBone(veh, boneName, model, off, rot)
local bone = GetEntityBoneIndexByName(veh, boneName)
if bone == -1 then return nil end
RequestModel(model)
while not HasModelLoaded(model) do Wait(0) end
local prop = CreateObject(model, 0.0, 0.0, 0.0, false, false, false)
-- make it non-physical (no collision, no damage)
SetEntityCollision(prop, false, false)
SetEntityCompletelyDisableCollision(prop, true, true)
SetEntityHasGravity(prop, false)
AttachEntityToEntity(
prop, veh, bone,
off.x, off.y, off.z,
rot.x, rot.y, rot.z,
false, false, false, true, 2, true
)
SetModelAsNoLongerNeeded(model)
return prop
end
-- 2) Bake the panel transform from the prop into VEHICLE LOCAL space
local function bakePanelFromProp(veh, prop, panelLocalOffsetOnProp, panelLocalNormalOnProp)
-- where the panel should be in WORLD space (prop-local offset)
local worldPos = GetOffsetFromEntityInWorldCoords(
prop,
panelLocalOffsetOnProp.x,
panelLocalOffsetOnProp.y,
panelLocalOffsetOnProp.z
)
-- convert that WORLD position into VEHICLE LOCAL offset (for stable attach)
local bakedOffset = GetOffsetFromEntityGivenWorldCoords(veh, worldPos.x, worldPos.y, worldPos.z)
-- compute WORLD normal direction from prop-local "normal"
local propOrigin = GetEntityCoords(prop)
local worldDirPoint = GetOffsetFromEntityInWorldCoords(
prop,
panelLocalNormalOnProp.x,
panelLocalNormalOnProp.y,
panelLocalNormalOnProp.z
)
local worldDir = vNorm(vector3(worldDirPoint.x - propOrigin.x, worldDirPoint.y - propOrigin.y, worldDirPoint.z - propOrigin.z))
-- convert that WORLD direction into VEHICLE LOCAL direction (baked normal)
local vehOrigin = GetEntityCoords(veh)
local vehLocalDirPoint = GetOffsetFromEntityGivenWorldCoords(
veh,
vehOrigin.x + worldDir.x,
vehOrigin.y + worldDir.y,
vehOrigin.z + worldDir.z
)
local bakedNormal = vNorm(vehLocalDirPoint)
return bakedOffset, bakedNormal
end
-- 3) Stable setup: prop -> bone, panel -> vehicle (baked from prop)
local function mountDashPanelStable(veh)
local prop = attachPropToBone(veh, DASH_BONE, DASH_PROP_MODEL, DASH_PROP_OFFSET, DASH_PROP_ROT)
if not prop then return nil, nil end
local bakedOffset, bakedNormal = bakePanelFromProp(veh, prop, MONITOR_LOCAL_OFFSET, MONITOR_LOCAL_NORMAL)
local panelId = exports["cr-3dnui"]:AttachPanelToEntity({
entity = veh,
localOffset = bakedOffset, -- stable position
localNormal = bakedNormal, -- correct facing (vehicle-local)
width = MONITOR_W,
height = MONITOR_H,
rotateNormal = true, -- keep it vehicle-relative
})
return panelId, prop
end
attaching a panel to a prop then attaching that prop to a bone creates limitations with updating the position of the panel fast enough with the position of the car
its basically extra math for no reason since your no calculating car/prop/panel
meaning more steps
but if we instead use the prop to calulate the offset then attach panel to bone from that offset we could fake it being attached to the prop but have no jitter when moving fast or driving
the only limitation this really presents is that each vehicle will have unique offsets for the position of the panel
so ive created a simple tool to move the prop XYZ and ROT
and you simply use your mouse wheel to modify values
this way (if needed) you can modify the positions and use a prop based panel in any car – *instead of 8 hours of hair pulling–
1 /nuidash
this is the command to attach the panel and the prop to your car.
(dont worry if they arent lined up even with your offset)
2 /nuidashprop
to bind the panel to prop ( this is REQUIRED to be used BEFORE /dashprint)
3 /dashtune
this will enter the mode that allows you to modify the X/Y/Z/rX/rY/rZ
simply use your scroll wheel to adjust the location + or -
for small adjustments hold CTRL
for big adjustments hold SHIFT
4/dashaxis <x|y|z|rx|ry|rz>
use commands like /dashaxis x or /dashaxis rz to modify individual parameters
rx/ry/rz are rotational variables
5/dashprint
once your happy with the positions this command will print to the F8 console for easy copy pasta
MUST USE
/nuidashpropBEFORE/dashprintto have the COMPLETE output like image below:
Note:
(The debug tools default settings use theElegy2as the car andprop_monitor_01aas the prop but you can change them but each car and prop must be tuned individually)
3D-NUI panels are not real entities, so they do not automatically inherit full parent rotation; their orientation must be explicitly recomputed from the parent’s live transform if you want true roll/pitch/yaw behavior.
lol - seriouslyThe solution is to stop baking the panel’s normal – and instead recompute it continuously from the vehicle’s live right/forward/up vectors, using the panel’s vehicle-space rest orientation as input.
Almost acting like gravity or magnets where it pulls / pushes where it needs to in order to match the car.
-- panel's REST orientation in VEHICLE SPACE (never changes)
-- this is what you tuned
local PANEL_REST_NORMAL = vector3(nx, ny, nz)
-- every update (frame or throttled loop)
local function updatePanelNormal(panelId, vehicle)
-- get the vehicle’s live axes (already include roll/pitch/yaw)
local right, forward, up, _ = GetEntityMatrix(vehicle)
-- rebuild the panel normal from the vehicle’s current orientation
local dynamicNormal =
right * PANEL_REST_NORMAL.x +
forward * PANEL_REST_NORMAL.y +
up * PANEL_REST_NORMAL.z
-- normalize (important)
dynamicNormal = dynamicNormal / #(dynamicNormal)
-- apply to the panel
exports['cr-3dnui']:SetPanelNormal(panelId, dynamicNormal)
end
In theory this should work-- or something similar.
TL:DR
i did the annoying part and made a debug tool to make it easy to get the locations/offsets/normals/rots… i also solved the prop > bone + panel > bone nightmare solving the jittery panels issue on moving cars and i came up with a plan to tackle panels to follow cars rotations.
The library is updated to 2.5 (only the car demo has changed)
edit: currently debugging and fixing the panel ROT to allgin with car
How ROT (Roll / Pitch / Yaw) Is Actually Solved
3D-NUI panels are not entities.
That means:
ROT requires two vectors:
localNormal → facinglocalUp → roll referenceThe library must support an explicit up vector:
panelUp
Internally, the panel basis is built from:
normal
panelUp
right = cross(panelUp, normal)
up = cross(normal, right)
Without panelUp, true roll is impossible.
This was the library-side limitation that had to be fixed.
ROT comes from rebuilding the panel orientation from a live transform.
That source is the prop attached to the bone.
You do not attach the panel to the prop.
Instead:
prop → bone (real rotation)
panel → vehicle (stable)
Then you bake the prop’s orientation into vehicle-local space.
From demo (simplified):
-- prop local → world
local worldNormal = pr * propNormal.x + pf * propNormal.y + pu * propNormal.z
local worldUp = pr * propUp.x + pf * propUp.y + pu * propUp.z
-- world → vehicle local
local localNormal = normalize(worldNormal • vehicleAxes)
local localUp = normalize(worldUp • vehicleAxes)
This gives you:
That’s ROT.
In the demo, Originally was:
localNormal = vNeg(bakedNormal)
localUp = vNeg(bakedUp)
That mirrors the basis.
Result:
localNormal = vNeg(bakedNormal)
localUp = bakedUp
That’s it.
After this:
TL:DR
ROT is solved by:
- Library supports
panelUp- ROT is baked from a real entity (prop → bone)
- Panel attaches to vehicle using baked normal + up
- Only the normal is negated — never the up
(If any one of those is missing, ROT breaks)
WORK IN PROGESS
New panel creation method
(MUCH EASIER)
Instead of calculating all the fancy math to place a panel on a prop…
This replaces the texture inside the prop with our 3d-NUI
Simply open code walker and search the model and look up the textures in that model
over ride the texture!
still working on focus mode and a few things but should be pushed tonight
Laptop with 3dnui injected via texture override
Animated 3dnui injected via texture override into Yankton plate
(that means WORKING registration tags for cars and animated plates)