From daf00e092bd479e89cbb8961d0e06dadc9fdbcec Mon Sep 17 00:00:00 2001 From: Crown <38720085+x-Crown@users.noreply.github.com> Date: Fri, 26 Sep 2025 02:24:12 +0300 Subject: [PATCH 1/6] Update main.lua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR introduces an additional hover highlight effect in the MTA:SA Editor. When the player hovers over an object in the map editor: The object becomes semi-transparent. A 3D animated bounding box is drawn around it with rainbow cycling colors (HSV → RGB). The effect resets automatically when the cursor leaves the object --- [editor]/editor_main/client/main.lua | 122 +++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/[editor]/editor_main/client/main.lua b/[editor]/editor_main/client/main.lua index de1f55782..30e9cf827 100644 --- a/[editor]/editor_main/client/main.lua +++ b/[editor]/editor_main/client/main.lua @@ -1405,3 +1405,125 @@ function disableCharacterSounds() -- CJ stealth breathing, fall screaming etc. setWorldSoundEnabled ( 25, false ) end + +----------------------------------------------------------- +-- Extra: Highlight on hover (Transparency + Animated Color Box) +----------------------------------------------------------- + +local screenW, screenH = guiGetScreenSize() +local highlightedObject = nil + +-- HSV to RGB conversion +local function hsvToRgb(h, s, v) + local r, g, b + + local i = math.floor(h * 6) + local f = h * 6 - i + local p = v * (1 - s) + local q = v * (1 - f * s) + local t = v * (1 - (1 - f * s)) + + i = i % 6 + + if i == 0 then r, g, b = v, t, p + elseif i == 1 then r, g, b = q, v, p + elseif i == 2 then r, g, b = p, v, t + elseif i == 3 then r, g, b = p, q, v + elseif i == 4 then r, g, b = t, p, v + elseif i == 5 then r, g, b = v, p, q + end + + return r*255, g*255, b*255 +end + +-- Get the object under the cursor +local function getObjectFromCursor() + local cx, cy = getCursorPosition() + if not cx or not cy then return false end + + local camX, camY, camZ = getCameraMatrix() + local worldX, worldY, worldZ = getWorldFromScreenPosition(cx*screenW, cy*screenH, 1000) + + local hit, _, _, _, hitElement = processLineOfSight( + camX, camY, camZ, + worldX, worldY, worldZ, + true,true,true,true,true,true,true,true + ) + + if hit and hitElement and getElementType(hitElement) == "object" then + return hitElement + end + return false +end + +-- Get object bounding box corners +local function getObjectBoundingBox(obj) + if not isElement(obj) then return false end + local minX, minY, minZ, maxX, maxY, maxZ = getElementBoundingBox(obj) + if not minX then return false end + + local x, y, z = getElementPosition(obj) + + return { + {x+minX, y+minY, z+minZ}, + {x+maxX, y+minY, z+minZ}, + {x+maxX, y+maxY, z+minZ}, + {x+minX, y+maxY, z+minZ}, + {x+minX, y+minY, z+maxZ}, + {x+maxX, y+minY, z+maxZ}, + {x+maxX, y+maxY, z+maxZ}, + {x+minX, y+maxY, z+maxZ} + } +end + +-- Draw 3D bounding box lines +local function drawBoundingBox(corners, r,g,b,a) + if not corners then return end + -- base + dxDrawLine3D(corners[1][1],corners[1][2],corners[1][3], corners[2][1],corners[2][2],corners[2][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[2][1],corners[2][2],corners[2][3], corners[3][1],corners[3][2],corners[3][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[3][1],corners[3][2],corners[3][3], corners[4][1],corners[4][2],corners[4][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[4][1],corners[4][2],corners[4][3], corners[1][1],corners[1][2],corners[1][3], tocolor(r,g,b,a), 2) + -- top + dxDrawLine3D(corners[5][1],corners[5][2],corners[5][3], corners[6][1],corners[6][2],corners[6][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[6][1],corners[6][2],corners[6][3], corners[7][1],corners[7][2],corners[7][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[7][1],corners[7][2],corners[7][3], corners[8][1],corners[8][2],corners[8][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[8][1],corners[8][2],corners[8][3], corners[5][1],corners[5][2],corners[5][3], tocolor(r,g,b,a), 2) + -- vertical + dxDrawLine3D(corners[1][1],corners[1][2],corners[1][3], corners[5][1],corners[5][2],corners[5][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[2][1],corners[2][2],corners[2][3], corners[6][1],corners[6][2],corners[6][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[3][1],corners[3][2],corners[3][3], corners[7][1],corners[7][2],corners[7][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[4][1],corners[4][2],corners[4][3], corners[8][1],corners[8][2],corners[8][3], tocolor(r,g,b,a), 2) +end + +-- Main render event +local function onRenderHighlight() + local obj = getObjectFromCursor() + + if obj and obj ~= highlightedObject then + -- Reset previous object + if highlightedObject and isElement(highlightedObject) then + setElementAlpha(highlightedObject, 255) + end + + highlightedObject = obj + setElementAlpha(highlightedObject, 150) -- semi-transparent + + elseif not obj and highlightedObject then + if isElement(highlightedObject) then + setElementAlpha(highlightedObject, 255) + end + highlightedObject = nil + end + + -- Draw animated bounding box + if highlightedObject and isElement(highlightedObject) then + local box = getObjectBoundingBox(highlightedObject) + if box then + local tick = getTickCount() / 2000 -- animation speed + local r,g,b = hsvToRgb(tick % 1, 1, 1) -- rainbow color cycle + drawBoundingBox(box, r, g, b, 255) + end + end +end +addEventHandler("onClientRender", root, onRenderHighlight) From b898261b1deb6bf720cc4205088d47f13122be1c Mon Sep 17 00:00:00 2001 From: Arran Date: Sat, 11 Apr 2026 13:45:55 +0100 Subject: [PATCH 2/6] Remove alpha changes because it conflicts. It doesn't take into account the objects alpha before or after hovering over it, also the properties says the alpha is 150 of any object you select. --- [editor]/editor_main/client/main.lua | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/[editor]/editor_main/client/main.lua b/[editor]/editor_main/client/main.lua index 30e9cf827..27781d9a5 100644 --- a/[editor]/editor_main/client/main.lua +++ b/[editor]/editor_main/client/main.lua @@ -1501,18 +1501,8 @@ local function onRenderHighlight() local obj = getObjectFromCursor() if obj and obj ~= highlightedObject then - -- Reset previous object - if highlightedObject and isElement(highlightedObject) then - setElementAlpha(highlightedObject, 255) - end - highlightedObject = obj - setElementAlpha(highlightedObject, 150) -- semi-transparent - elseif not obj and highlightedObject then - if isElement(highlightedObject) then - setElementAlpha(highlightedObject, 255) - end highlightedObject = nil end From f2f68f6ab6ab6036dba101be6066b5924ccb15f7 Mon Sep 17 00:00:00 2001 From: Arran Date: Sat, 11 Apr 2026 14:08:56 +0100 Subject: [PATCH 3/6] Remove unneeded function --- [editor]/editor_main/client/main.lua | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/[editor]/editor_main/client/main.lua b/[editor]/editor_main/client/main.lua index 27781d9a5..05b7add82 100644 --- a/[editor]/editor_main/client/main.lua +++ b/[editor]/editor_main/client/main.lua @@ -1410,7 +1410,6 @@ end -- Extra: Highlight on hover (Transparency + Animated Color Box) ----------------------------------------------------------- -local screenW, screenH = guiGetScreenSize() local highlightedObject = nil -- HSV to RGB conversion @@ -1436,26 +1435,6 @@ local function hsvToRgb(h, s, v) return r*255, g*255, b*255 end --- Get the object under the cursor -local function getObjectFromCursor() - local cx, cy = getCursorPosition() - if not cx or not cy then return false end - - local camX, camY, camZ = getCameraMatrix() - local worldX, worldY, worldZ = getWorldFromScreenPosition(cx*screenW, cy*screenH, 1000) - - local hit, _, _, _, hitElement = processLineOfSight( - camX, camY, camZ, - worldX, worldY, worldZ, - true,true,true,true,true,true,true,true - ) - - if hit and hitElement and getElementType(hitElement) == "object" then - return hitElement - end - return false -end - -- Get object bounding box corners local function getObjectBoundingBox(obj) if not isElement(obj) then return false end @@ -1477,7 +1456,7 @@ local function getObjectBoundingBox(obj) end -- Draw 3D bounding box lines -local function drawBoundingBox(corners, r,g,b,a) +local function drawBoundingBox(corners, r, g, b, a) if not corners then return end -- base dxDrawLine3D(corners[1][1],corners[1][2],corners[1][3], corners[2][1],corners[2][2],corners[2][3], tocolor(r,g,b,a), 2) @@ -1498,7 +1477,10 @@ end -- Main render event local function onRenderHighlight() - local obj = getObjectFromCursor() + local obj = getTargetedElement() + if obj and getElementType(obj) ~= "object" then + obj = false + end if obj and obj ~= highlightedObject then highlightedObject = obj From 9212615de17a0da36ad5a82bc1b7ebed7d433a1f Mon Sep 17 00:00:00 2001 From: Arran Date: Sat, 11 Apr 2026 14:22:26 +0100 Subject: [PATCH 4/6] Add it as a setting disabled by default --- [editor]/editor_gui/client/options_action.lua | 18 +++++++++------- .../editor_gui/client/options_backend.lua | 21 +++++++++++-------- [editor]/editor_gui/client/options_gui.lua | 5 +++-- [editor]/editor_main/client/main.lua | 15 ++++++++----- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/[editor]/editor_gui/client/options_action.lua b/[editor]/editor_gui/client/options_action.lua index 07149e110..85594fe36 100644 --- a/[editor]/editor_gui/client/options_action.lua +++ b/[editor]/editor_gui/client/options_action.lua @@ -7,13 +7,17 @@ function optionsActions.enableSounds (value) enableSound = value end -function optionsActions.enableBox(value) - optionsData.enableBox = value -end - -function optionsActions.enableXYZlines(value) - optionsData.enableXYZlines = value -end +function optionsActions.enableBox(value) + optionsData.enableBox = value +end + +function optionsActions.enableHoverBoundingBox(value) + optionsData.enableHoverBoundingBox = value +end + +function optionsActions.enableXYZlines(value) + optionsData.enableXYZlines = value +end function optionsActions.enablePrecisionRotation(value) optionsData.enablePrecisionRotation = value diff --git a/[editor]/editor_gui/client/options_backend.lua b/[editor]/editor_gui/client/options_backend.lua index 4dbd9a00a..604034ec9 100644 --- a/[editor]/editor_gui/client/options_backend.lua +++ b/[editor]/editor_gui/client/options_backend.lua @@ -22,9 +22,10 @@ local xmlVariants = { ["slowElemScale"]="scaling_slow_speed", ["lockToAxes"]="movement_lock_to_axes", ["autosnap"]="currentbrowser_autosnap", -["tutorialOnStart"]="tutorial_on_start", -["enableBox"]="enablebox", -["enableXYZlines"]="enablexyzlines", +["tutorialOnStart"]="tutorial_on_start", +["enableBox"]="enablebox", +["enableHoverBoundingBox"]="enablehoverboundingbox", +["enableXYZlines"]="enablexyzlines", ["precisionLevel"]="precisionlevel", ["precisionRotLevel"]="precisionrotlevel", ["elemScalingSnap"]="elemscalingsnap", @@ -59,9 +60,10 @@ local nodeTypes = { ["lockToAxes"]="bool", ["autosnap"]="bool", ["tutorialOnStart"]="bool", -["enableDumpSave"]="bool", -["enableBox"]="bool", -["precisionLevel"]={"10","5","2","1","0.1","0.01","0.001","0.0001"}, +["enableDumpSave"]="bool", +["enableBox"]="bool", +["enableHoverBoundingBox"]="bool", +["precisionLevel"]={"10","5","2","1","0.1","0.01","0.001","0.0001"}, ["precisionRotLevel"]={"180","90","45","30","20","10","5","1"}, ["elemScalingSnap"]={"1","0.1","0.01","0.001","0.0001"}, ["enablePrecisionSnap"]="bool", @@ -95,9 +97,10 @@ local defaults = { ["slowElemScale"]=0.01, ["lockToAxes"]=false, ["autosnap"]=true, -["tutorialOnStart"]=true, -["enableBox"]=true, -["precisionLevel"]="0.1", +["tutorialOnStart"]=true, +["enableBox"]=true, +["enableHoverBoundingBox"]=false, +["precisionLevel"]="0.1", ["precisionRotLevel"]="30", ["elemScalingSnap"]="0.0001", ["enablePrecisionSnap"]=true, diff --git a/[editor]/editor_gui/client/options_gui.lua b/[editor]/editor_gui/client/options_gui.lua index e259c0521..d2ef17c1d 100644 --- a/[editor]/editor_gui/client/options_gui.lua +++ b/[editor]/editor_gui/client/options_gui.lua @@ -32,8 +32,9 @@ function createOptionsDialog() dialog.tutorialOnStart = editingControl.boolean:create{["x"]=0.02,["y"]=0.8,["width"]=0.35,["height"]=0.1,["relative"]=true,["parent"]=dialog.generalTab,["label"]="Query for tutorial on start"} --------------------------------- - dialog.enableBox = editingControl.boolean:create{["x"]=0.60,["y"]=0.02,["width"]=0.35,["height"]=0.1,["relative"]=true,["parent"]=dialog.generalTab,["label"]="Enable Bounding Box"} - dialog.enableXYZlines = editingControl.boolean:create{["x"]=0.60,["y"]=0.12,["width"]=0.35,["height"]=0.1,["relative"]=true,["parent"]=dialog.generalTab,["label"]="Enable XYZ Lines"} + dialog.enableBox = editingControl.boolean:create{["x"]=0.43,["y"]=0.02,["width"]=0.25,["height"]=0.1,["relative"]=true,["parent"]=dialog.generalTab,["label"]="Enable Bounding Box"} + dialog.enableHoverBoundingBox = editingControl.boolean:create{["x"]=0.68,["y"]=0.02,["width"]=0.31,["height"]=0.1,["relative"]=true,["parent"]=dialog.generalTab,["label"]="Bounding box on hover over"} + dialog.enableXYZlines = editingControl.boolean:create{["x"]=0.60,["y"]=0.12,["width"]=0.35,["height"]=0.1,["relative"]=true,["parent"]=dialog.generalTab,["label"]="Enable XYZ Lines"} --------------------------------- dialog.enablePrecisionSnap = editingControl.boolean:create{["x"]=0.60,["y"]=0.22,["width"]=0.35,["height"]=0.1,["relative"]=true,["parent"]=dialog.generalTab,["label"]="Enable Snap - Precise Position"} guiCreateLabel ( 0.47, 0.34, 70, 17, "Position Snap Level:", true, dialog.generalTab ) diff --git a/[editor]/editor_main/client/main.lua b/[editor]/editor_main/client/main.lua index 05b7add82..b5a9fe345 100644 --- a/[editor]/editor_main/client/main.lua +++ b/[editor]/editor_main/client/main.lua @@ -1475,11 +1475,16 @@ local function drawBoundingBox(corners, r, g, b, a) dxDrawLine3D(corners[4][1],corners[4][2],corners[4][3], corners[8][1],corners[8][2],corners[8][3], tocolor(r,g,b,a), 2) end --- Main render event -local function onRenderHighlight() - local obj = getTargetedElement() - if obj and getElementType(obj) ~= "object" then - obj = false +-- Main render event +local function onRenderHighlight() + if not exports["editor_gui"]:sx_getOptionData("enableHoverBoundingBox") then + highlightedObject = nil + return + end + + local obj = getTargetedElement() + if obj and getElementType(obj) ~= "object" then + obj = false end if obj and obj ~= highlightedObject then From 518649a8d6efba403b39d582bae826a9bfbe5512 Mon Sep 17 00:00:00 2001 From: Arran Date: Sat, 11 Apr 2026 14:27:05 +0100 Subject: [PATCH 5/6] Make it work with all elements --- [editor]/editor_main/client/main.lua | 112 ++++++++++++++++++--------- 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/[editor]/editor_main/client/main.lua b/[editor]/editor_main/client/main.lua index b5a9fe345..1df3038e1 100644 --- a/[editor]/editor_main/client/main.lua +++ b/[editor]/editor_main/client/main.lua @@ -1410,7 +1410,10 @@ end -- Extra: Highlight on hover (Transparency + Animated Color Box) ----------------------------------------------------------- -local highlightedObject = nil +local highlightedElement = nil +local hoverBoundingBoxIgnoreMatrix = { + pickup = true +} -- HSV to RGB conversion local function hsvToRgb(h, s, v) @@ -1435,25 +1438,62 @@ local function hsvToRgb(h, s, v) return r*255, g*255, b*255 end --- Get object bounding box corners -local function getObjectBoundingBox(obj) - if not isElement(obj) then return false end - local minX, minY, minZ, maxX, maxY, maxZ = getElementBoundingBox(obj) - if not minX then return false end - - local x, y, z = getElementPosition(obj) - - return { - {x+minX, y+minY, z+minZ}, - {x+maxX, y+minY, z+minZ}, - {x+maxX, y+maxY, z+minZ}, - {x+minX, y+maxY, z+minZ}, - {x+minX, y+minY, z+maxZ}, - {x+maxX, y+minY, z+maxZ}, - {x+maxX, y+maxY, z+maxZ}, - {x+minX, y+maxY, z+maxZ} - } -end +local function getElementBoundingBoxCorners(element) + if not isElement(element) then return false end + if getElementDimension(element) ~= getElementDimension(localPlayer) then return false end + + local x, y, z = edf.edfGetElementPosition(element) + if not x then return false end + + local minX, minY, minZ, maxX, maxY, maxZ = edf.edfGetElementBoundingBox(element) + if not minX then + local radius = edf.edfGetElementRadius(element) + if radius then + minX, minY, minZ, maxX, maxY, maxZ = -radius, -radius, -radius, radius, radius, radius + else + return false + end + end + + local halfCenterX = (minX + maxX) * 0.25 + local halfCenterY = (minY + maxY) * 0.25 + local halfCenterZ = (minZ + maxZ) * 0.25 + + minX = minX - halfCenterX + minY = minY - halfCenterY + minZ = minZ - halfCenterZ + maxX = maxX - halfCenterX + maxY = maxY - halfCenterY + maxZ = maxZ - halfCenterZ + + local relativeCorners = { + {minX, minY, minZ}, + {maxX, minY, minZ}, + {maxX, maxY, minZ}, + {minX, maxY, minZ}, + {minX, minY, maxZ}, + {maxX, minY, maxZ}, + {maxX, maxY, maxZ}, + {minX, maxY, maxZ} + } + + local elementMatrix = getElementMatrix(element) + if elementMatrix and not hoverBoundingBoxIgnoreMatrix[getElementType(element)] then + for i, corner in ipairs(relativeCorners) do + relativeCorners[i] = { + corner[1] * elementMatrix[1][1] + corner[2] * elementMatrix[2][1] + corner[3] * elementMatrix[3][1] + elementMatrix[4][1], + corner[1] * elementMatrix[1][2] + corner[2] * elementMatrix[2][2] + corner[3] * elementMatrix[3][2] + elementMatrix[4][2], + corner[1] * elementMatrix[1][3] + corner[2] * elementMatrix[2][3] + corner[3] * elementMatrix[3][3] + elementMatrix[4][3] + } + end + else + for i, corner in ipairs(relativeCorners) do + relativeCorners[i] = {x + corner[1], y + corner[2], z + corner[3]} + end + end + + return relativeCorners +end -- Draw 3D bounding box lines local function drawBoundingBox(corners, r, g, b, a) @@ -1478,27 +1518,23 @@ end -- Main render event local function onRenderHighlight() if not exports["editor_gui"]:sx_getOptionData("enableHoverBoundingBox") then - highlightedObject = nil + highlightedElement = nil return end - local obj = getTargetedElement() - if obj and getElementType(obj) ~= "object" then - obj = false - end - - if obj and obj ~= highlightedObject then - highlightedObject = obj - elseif not obj and highlightedObject then - highlightedObject = nil - end - - -- Draw animated bounding box - if highlightedObject and isElement(highlightedObject) then - local box = getObjectBoundingBox(highlightedObject) - if box then - local tick = getTickCount() / 2000 -- animation speed - local r,g,b = hsvToRgb(tick % 1, 1, 1) -- rainbow color cycle + local element = getTargetedElement() + if element and element ~= highlightedElement then + highlightedElement = element + elseif not element and highlightedElement then + highlightedElement = nil + end + + -- Draw animated bounding box + if highlightedElement and isElement(highlightedElement) then + local box = getElementBoundingBoxCorners(highlightedElement) + if box then + local tick = getTickCount() / 2000 -- animation speed + local r,g,b = hsvToRgb(tick % 1, 1, 1) -- rainbow color cycle drawBoundingBox(box, r, g, b, 255) end end From 5a80f606e2e6103596df1b00fcdc0537e6f4a4a6 Mon Sep 17 00:00:00 2001 From: Arran Date: Sat, 11 Apr 2026 14:30:21 +0100 Subject: [PATCH 6/6] Move it to a more appropriate file --- [editor]/editor_main/client/gridlines.lua | 134 +++++++++++++++++++++ [editor]/editor_main/client/main.lua | 137 +--------------------- 2 files changed, 135 insertions(+), 136 deletions(-) diff --git a/[editor]/editor_main/client/gridlines.lua b/[editor]/editor_main/client/gridlines.lua index 9f4a1e8f0..b57c23a9b 100644 --- a/[editor]/editor_main/client/gridlines.lua +++ b/[editor]/editor_main/client/gridlines.lua @@ -274,6 +274,7 @@ function drawObjectMoveLines() end function doBasicElementRenders() + renderHoverOverGridLines() if not isElement(attachedToElement) then return end if exports["editor_gui"]:sx_getOptionData("enableBox") then renderGridlines() end if exports["editor_gui"]:sx_getOptionData("enableXYZlines") then drawXYZLines() end @@ -314,3 +315,136 @@ function getOffsetRelativeToElement ( element, x, y, z ) elementMatrix = matrix{x,y,z} * elementMatrix return elementMatrix end + +----------------------------------------------------------- +-- Extra: Highlight on hover (Transparency + Animated Color Box) +----------------------------------------------------------- + +local highlightedElement = nil +local hoverBoundingBoxIgnoreMatrix = { + pickup = true +} + +-- HSV to RGB conversion +local function hsvToRgb(h, s, v) + local r, g, b + + local i = math.floor(h * 6) + local f = h * 6 - i + local p = v * (1 - s) + local q = v * (1 - f * s) + local t = v * (1 - (1 - f * s)) + + i = i % 6 + + if i == 0 then r, g, b = v, t, p + elseif i == 1 then r, g, b = q, v, p + elseif i == 2 then r, g, b = p, v, t + elseif i == 3 then r, g, b = p, q, v + elseif i == 4 then r, g, b = t, p, v + elseif i == 5 then r, g, b = v, p, q + end + + return r*255, g*255, b*255 +end + +local function getElementBoundingBoxCorners(element) + if not isElement(element) then return false end + if getElementDimension(element) ~= getElementDimension(localPlayer) then return false end + + local x, y, z = edf.edfGetElementPosition(element) + if not x then return false end + + local minX, minY, minZ, maxX, maxY, maxZ = edf.edfGetElementBoundingBox(element) + if not minX then + local radius = edf.edfGetElementRadius(element) + if radius then + minX, minY, minZ, maxX, maxY, maxZ = -radius, -radius, -radius, radius, radius, radius + else + return false + end + end + + local halfCenterX = (minX + maxX) * 0.25 + local halfCenterY = (minY + maxY) * 0.25 + local halfCenterZ = (minZ + maxZ) * 0.25 + + minX = minX - halfCenterX + minY = minY - halfCenterY + minZ = minZ - halfCenterZ + maxX = maxX - halfCenterX + maxY = maxY - halfCenterY + maxZ = maxZ - halfCenterZ + + local relativeCorners = { + {minX, minY, minZ}, + {maxX, minY, minZ}, + {maxX, maxY, minZ}, + {minX, maxY, minZ}, + {minX, minY, maxZ}, + {maxX, minY, maxZ}, + {maxX, maxY, maxZ}, + {minX, maxY, maxZ} + } + + local elementMatrix = getElementMatrix(element) + if elementMatrix and not hoverBoundingBoxIgnoreMatrix[getElementType(element)] then + for i, corner in ipairs(relativeCorners) do + relativeCorners[i] = { + corner[1] * elementMatrix[1][1] + corner[2] * elementMatrix[2][1] + corner[3] * elementMatrix[3][1] + elementMatrix[4][1], + corner[1] * elementMatrix[1][2] + corner[2] * elementMatrix[2][2] + corner[3] * elementMatrix[3][2] + elementMatrix[4][2], + corner[1] * elementMatrix[1][3] + corner[2] * elementMatrix[2][3] + corner[3] * elementMatrix[3][3] + elementMatrix[4][3] + } + end + else + for i, corner in ipairs(relativeCorners) do + relativeCorners[i] = {x + corner[1], y + corner[2], z + corner[3]} + end + end + + return relativeCorners +end + +-- Draw 3D bounding box lines +local function drawBoundingBox(corners, r, g, b, a) + if not corners then return end + -- base + dxDrawLine3D(corners[1][1],corners[1][2],corners[1][3], corners[2][1],corners[2][2],corners[2][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[2][1],corners[2][2],corners[2][3], corners[3][1],corners[3][2],corners[3][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[3][1],corners[3][2],corners[3][3], corners[4][1],corners[4][2],corners[4][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[4][1],corners[4][2],corners[4][3], corners[1][1],corners[1][2],corners[1][3], tocolor(r,g,b,a), 2) + -- top + dxDrawLine3D(corners[5][1],corners[5][2],corners[5][3], corners[6][1],corners[6][2],corners[6][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[6][1],corners[6][2],corners[6][3], corners[7][1],corners[7][2],corners[7][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[7][1],corners[7][2],corners[7][3], corners[8][1],corners[8][2],corners[8][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[8][1],corners[8][2],corners[8][3], corners[5][1],corners[5][2],corners[5][3], tocolor(r,g,b,a), 2) + -- vertical + dxDrawLine3D(corners[1][1],corners[1][2],corners[1][3], corners[5][1],corners[5][2],corners[5][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[2][1],corners[2][2],corners[2][3], corners[6][1],corners[6][2],corners[6][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[3][1],corners[3][2],corners[3][3], corners[7][1],corners[7][2],corners[7][3], tocolor(r,g,b,a), 2) + dxDrawLine3D(corners[4][1],corners[4][2],corners[4][3], corners[8][1],corners[8][2],corners[8][3], tocolor(r,g,b,a), 2) +end + +function renderHoverOverGridLines() + if not exports["editor_gui"]:sx_getOptionData("enableHoverBoundingBox") then + highlightedElement = nil + return + end + + local element = getTargetedElement() + if element and element ~= highlightedElement then + highlightedElement = element + elseif not element and highlightedElement then + highlightedElement = nil + end + + -- Draw animated bounding box + if highlightedElement and isElement(highlightedElement) then + local box = getElementBoundingBoxCorners(highlightedElement) + if box then + local tick = getTickCount() / 2000 -- animation speed + local r,g,b = hsvToRgb(tick % 1, 1, 1) -- rainbow color cycle + drawBoundingBox(box, r, g, b, 255) + end + end +end \ No newline at end of file diff --git a/[editor]/editor_main/client/main.lua b/[editor]/editor_main/client/main.lua index 1df3038e1..3f36f99cd 100644 --- a/[editor]/editor_main/client/main.lua +++ b/[editor]/editor_main/client/main.lua @@ -1404,139 +1404,4 @@ end function disableCharacterSounds() -- CJ stealth breathing, fall screaming etc. setWorldSoundEnabled ( 25, false ) -end - ------------------------------------------------------------ --- Extra: Highlight on hover (Transparency + Animated Color Box) ------------------------------------------------------------ - -local highlightedElement = nil -local hoverBoundingBoxIgnoreMatrix = { - pickup = true -} - --- HSV to RGB conversion -local function hsvToRgb(h, s, v) - local r, g, b - - local i = math.floor(h * 6) - local f = h * 6 - i - local p = v * (1 - s) - local q = v * (1 - f * s) - local t = v * (1 - (1 - f * s)) - - i = i % 6 - - if i == 0 then r, g, b = v, t, p - elseif i == 1 then r, g, b = q, v, p - elseif i == 2 then r, g, b = p, v, t - elseif i == 3 then r, g, b = p, q, v - elseif i == 4 then r, g, b = t, p, v - elseif i == 5 then r, g, b = v, p, q - end - - return r*255, g*255, b*255 -end - -local function getElementBoundingBoxCorners(element) - if not isElement(element) then return false end - if getElementDimension(element) ~= getElementDimension(localPlayer) then return false end - - local x, y, z = edf.edfGetElementPosition(element) - if not x then return false end - - local minX, minY, minZ, maxX, maxY, maxZ = edf.edfGetElementBoundingBox(element) - if not minX then - local radius = edf.edfGetElementRadius(element) - if radius then - minX, minY, minZ, maxX, maxY, maxZ = -radius, -radius, -radius, radius, radius, radius - else - return false - end - end - - local halfCenterX = (minX + maxX) * 0.25 - local halfCenterY = (minY + maxY) * 0.25 - local halfCenterZ = (minZ + maxZ) * 0.25 - - minX = minX - halfCenterX - minY = minY - halfCenterY - minZ = minZ - halfCenterZ - maxX = maxX - halfCenterX - maxY = maxY - halfCenterY - maxZ = maxZ - halfCenterZ - - local relativeCorners = { - {minX, minY, minZ}, - {maxX, minY, minZ}, - {maxX, maxY, minZ}, - {minX, maxY, minZ}, - {minX, minY, maxZ}, - {maxX, minY, maxZ}, - {maxX, maxY, maxZ}, - {minX, maxY, maxZ} - } - - local elementMatrix = getElementMatrix(element) - if elementMatrix and not hoverBoundingBoxIgnoreMatrix[getElementType(element)] then - for i, corner in ipairs(relativeCorners) do - relativeCorners[i] = { - corner[1] * elementMatrix[1][1] + corner[2] * elementMatrix[2][1] + corner[3] * elementMatrix[3][1] + elementMatrix[4][1], - corner[1] * elementMatrix[1][2] + corner[2] * elementMatrix[2][2] + corner[3] * elementMatrix[3][2] + elementMatrix[4][2], - corner[1] * elementMatrix[1][3] + corner[2] * elementMatrix[2][3] + corner[3] * elementMatrix[3][3] + elementMatrix[4][3] - } - end - else - for i, corner in ipairs(relativeCorners) do - relativeCorners[i] = {x + corner[1], y + corner[2], z + corner[3]} - end - end - - return relativeCorners -end - --- Draw 3D bounding box lines -local function drawBoundingBox(corners, r, g, b, a) - if not corners then return end - -- base - dxDrawLine3D(corners[1][1],corners[1][2],corners[1][3], corners[2][1],corners[2][2],corners[2][3], tocolor(r,g,b,a), 2) - dxDrawLine3D(corners[2][1],corners[2][2],corners[2][3], corners[3][1],corners[3][2],corners[3][3], tocolor(r,g,b,a), 2) - dxDrawLine3D(corners[3][1],corners[3][2],corners[3][3], corners[4][1],corners[4][2],corners[4][3], tocolor(r,g,b,a), 2) - dxDrawLine3D(corners[4][1],corners[4][2],corners[4][3], corners[1][1],corners[1][2],corners[1][3], tocolor(r,g,b,a), 2) - -- top - dxDrawLine3D(corners[5][1],corners[5][2],corners[5][3], corners[6][1],corners[6][2],corners[6][3], tocolor(r,g,b,a), 2) - dxDrawLine3D(corners[6][1],corners[6][2],corners[6][3], corners[7][1],corners[7][2],corners[7][3], tocolor(r,g,b,a), 2) - dxDrawLine3D(corners[7][1],corners[7][2],corners[7][3], corners[8][1],corners[8][2],corners[8][3], tocolor(r,g,b,a), 2) - dxDrawLine3D(corners[8][1],corners[8][2],corners[8][3], corners[5][1],corners[5][2],corners[5][3], tocolor(r,g,b,a), 2) - -- vertical - dxDrawLine3D(corners[1][1],corners[1][2],corners[1][3], corners[5][1],corners[5][2],corners[5][3], tocolor(r,g,b,a), 2) - dxDrawLine3D(corners[2][1],corners[2][2],corners[2][3], corners[6][1],corners[6][2],corners[6][3], tocolor(r,g,b,a), 2) - dxDrawLine3D(corners[3][1],corners[3][2],corners[3][3], corners[7][1],corners[7][2],corners[7][3], tocolor(r,g,b,a), 2) - dxDrawLine3D(corners[4][1],corners[4][2],corners[4][3], corners[8][1],corners[8][2],corners[8][3], tocolor(r,g,b,a), 2) -end - --- Main render event -local function onRenderHighlight() - if not exports["editor_gui"]:sx_getOptionData("enableHoverBoundingBox") then - highlightedElement = nil - return - end - - local element = getTargetedElement() - if element and element ~= highlightedElement then - highlightedElement = element - elseif not element and highlightedElement then - highlightedElement = nil - end - - -- Draw animated bounding box - if highlightedElement and isElement(highlightedElement) then - local box = getElementBoundingBoxCorners(highlightedElement) - if box then - local tick = getTickCount() / 2000 -- animation speed - local r,g,b = hsvToRgb(tick % 1, 1, 1) -- rainbow color cycle - drawBoundingBox(box, r, g, b, 255) - end - end -end -addEventHandler("onClientRender", root, onRenderHighlight) +end \ No newline at end of file