Last active
October 21, 2022 19:04
-
-
Save CheezusChrust/7ccce5f5196d3adc95ab9573009f735a to your computer and use it in GitHub Desktop.
Spawn on a StarfallEx screen, play with the numbers inside.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--@name tools/Torque Curve Visualizer v1.1 | |
--@author Cheezus | |
--@shared | |
if CLIENT then | |
local Engine = {} | |
Engine.name = "427 V8" | |
--[[ | |
ACF3 curves: | |
GenericPetrol = { 0.4, 0.65, 0.85, 1, 0.9, 0.6 } | |
GenericDiesel = { 0.7, 0.96, 1, 0.97, 0.93, 0.82, 0.7, 0.5 } | |
Radial = { 0.7, 0.96, 1, 0.97, 0.93, 0.82, 0.7, 0.5 } | |
Turbine = { 1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1 } | |
Rotary = { 0.35, 0.7, 0.85, 0.95, 1, 0.9, 0.7 } | |
--]] | |
Engine.curve = { 0.4, 0.65, 0.85, 1, 0.9, 0.6 } -- Torque curve - begins at idle RPM | |
Engine.idle = 1100 -- Idle RPM | |
Engine.redline = 8800 -- Peak RPM | |
Engine.peakTq = 630 -- Peak torque, in Nm | |
local res = 32 -- Resolution of the curve - ACF3 and ACE use a res of 32 | |
local function calcCurve(points, pos) | |
local count = #points | |
if count < 3 then | |
throw("Cannot calculate curve with fewer than 3 points") | |
end | |
if pos <= 0 then | |
return points[1] | |
elseif pos >= 1 then | |
return points[count] | |
end | |
local t = (pos * (count - 1)) % 1 | |
local currentPoint = math.floor(pos * (count - 1) + 1) | |
local p0 = points[math.clamp(currentPoint - 1, 1, count - 2)] | |
local p1 = points[math.clamp(currentPoint, 1, count - 1)] | |
local p2 = points[math.clamp(currentPoint + 1, 2, count)] | |
local p3 = points[math.clamp(currentPoint + 2, 3, count)] | |
return 0.5 * ((2 * p1) + | |
(p2 - p0) * t + | |
(2 * p0 - 5 * p1 + 4 * p2 - p3) * t ^ 2 + | |
(3 * p1 - p0 - 3 * p2 + p3) * t ^ 3) | |
end | |
render.createRenderTarget("rt") | |
local function drawToRT(f) | |
hook.add("renderoffscreen", "drawToRT", function() | |
render.selectRenderTarget("rt") | |
f() | |
render.selectRenderTarget() | |
hook.remove("renderoffscreen", "drawToRT") | |
end) | |
end | |
local function getEngineData(curve, maxTq, idle, redline) | |
local peakTq = 0 | |
local peakPower = 0 | |
local powerTable = {} | |
local peakTqRPM | |
local peakPowerRPM | |
local powerbandMinRPM | |
local powerbandMaxRPM | |
--Calculate peak torque/power rpm | |
for i = 0, res do | |
local rpm = i / res * redline | |
local perc = math.remap(rpm, idle, redline, 0, 1) | |
local curTq = calcCurve(curve, perc) | |
local power = (maxTq * curTq * rpm / 9548.8) | |
powerTable[i] = power | |
if power > peakPower then | |
peakPower = power | |
peakPowerRPM = rpm | |
end | |
if curTq > peakTq then | |
peakTq = curTq | |
peakTqRPM = rpm | |
end | |
end | |
--Find the bounds of the powerband | |
for i = 0, res do | |
local powerFrac = powerTable[i] / peakPower | |
local rpm = i / res * redline | |
if powerFrac > 0.9 and not powerbandMinRPM then | |
powerbandMinRPM = math.round(rpm / 10) * 10 | |
end | |
if (powerbandMinRPM and powerFrac < 0.9 and not powerbandMaxRPM) or (i == res and not powerbandMaxRPM) then | |
powerbandMaxRPM = math.round(rpm / 10) * 10 | |
end | |
end | |
return { | |
peakTq = peakTq * Engine.peakTq, | |
peakTqRPM = math.round(peakTqRPM), | |
peakPower = math.round(peakPower), | |
peakPowerRPM = math.round(peakPowerRPM), | |
powerbandMinRPM = powerbandMinRPM or 0, | |
powerbandMaxRPM = powerbandMaxRPM or 0 | |
} | |
end | |
local bigfont = render.createFont("Trebuchet", 48, nil, true) | |
drawToRT(function() | |
render.setColor(Color(50, 50, 50)) | |
render.drawRect(0, 512, 1024, 512) | |
render.setColor(Color(255, 255, 255)) | |
render.setFont("Trebuchet24") | |
render.drawText(512, 4, "Simulated Dynamometer", 1) | |
render.drawText(512, 28, Engine.name, 1) | |
render.setFont(bigfont) | |
render.drawText(512, 440, "RPM", 1) | |
render.setFont("Trebuchet24") | |
for i = 1, 16 do | |
render.setColor(Color(255, 255, 255)) | |
render.drawText((i / 17) * 1024, 490, tostring(math.floor(i / 17 * (Engine.redline - 0) + 0)), 1) | |
render.setColor(Color(80, 80, 80)) | |
render.drawLine((i / 17) * 1024, 512, (i / 17) * 1024, 1024) | |
end | |
local engineData = getEngineData(Engine.curve, Engine.peakTq, Engine.idle, Engine.redline) | |
local scale = math.max(engineData.peakPower, Engine.peakTq) * 1.1 | |
render.setFont(bigfont) | |
local str = string.format("Redline: %srpm, Idle: %srpm", Engine.redline, Engine.idle) | |
render.setColor(Color(255, 255, 255)) | |
render.drawText(32, 50, str) | |
str = string.format("Peak Torque: %sNm/%sft-lbs @ %srpm", Engine.peakTq, math.round(Engine.peakTq * 0.7376), engineData.peakTqRPM) | |
render.setColor(Color(50, 100, 255)) | |
render.drawText(32, 90, str) | |
str = string.format("Peak Power: %skW/%shp @ %srpm", engineData.peakPower, math.round(engineData.peakPower * 1.341), engineData.peakPowerRPM) | |
render.setColor(Color(255, 0, 0)) | |
render.drawText(32, 130, str) | |
render.setColor(Color(255, 200, 100)) | |
render.drawText(32, 170, "Powerband: " .. engineData.powerbandMinRPM .. " - " .. engineData.powerbandMaxRPM .. "rpm") | |
render.setColor(Color(255, 200, 100, 100)) | |
render.drawRect(engineData.powerbandMinRPM / Engine.redline * 1024, 512, 2, 512) | |
render.drawRect(engineData.powerbandMaxRPM / Engine.redline * 1024 - 1, 512, 2, 512) | |
for i = 1, res do | |
local rpm1 = ((i - 1) / res) * Engine.redline | |
local rpm2 = (i / res) * Engine.redline | |
local perc1 = math.remap(rpm1, Engine.idle, Engine.redline, 0, 1) | |
local perc2 = math.remap(rpm2, Engine.idle, Engine.redline, 0, 1) | |
local tq1 = math.clamp(calcCurve(Engine.curve, perc1), 0, 1) * Engine.peakTq | |
local tq2 = math.clamp(calcCurve(Engine.curve, perc2), 0, 1) * Engine.peakTq | |
local kw1 = tq1 * rpm1 / 9548.8 | |
local kw2 = tq2 * rpm2 / 9548.8 | |
local x1 = (i - 1) / res * 1024 | |
local x2 = i / res * 1024 | |
local tq_y1 = 1024 - (tq1 / scale) * 512 | |
local tq_y2 = 1024 - (tq2 / scale) * 512 | |
local kw_y1 = 1024 - (kw1 / scale) * 512 | |
local kw_y2 = 1024 - (kw2 / scale) * 512 | |
render.setColor(Color(50, 100, 255)) | |
render.drawLine(x1, tq_y1, x2, tq_y2) | |
render.setColor(Color(255, 0, 0)) | |
render.drawLine(x1, kw_y1, x2, kw_y2) | |
end | |
local tq_x = (engineData.peakTqRPM / Engine.redline) * 1024 | |
local tq_y = 1024 - (engineData.peakTq / scale) * 512 | |
local power_x = (engineData.peakPowerRPM / Engine.redline) * 1024 | |
local power_y = 1024 - (engineData.peakPower / scale) * 512 | |
render.setFont("Trebuchet24") | |
render.setColor(Color(50, 100, 255)) | |
render.drawCircle(tq_x, tq_y, 8) | |
local str = string.format("%sNm, %sft-lbs", Engine.peakTq, math.round(Engine.peakTq * 0.7376)) | |
render.drawText(tq_x, tq_y - 32, str, 2) | |
render.setColor(Color(255, 0, 0)) | |
render.drawCircle(power_x, power_y, 8) | |
local str = string.format("%skW, %shp", engineData.peakPower, math.round(engineData.peakPower * 1.341)) | |
render.drawText(power_x, power_y - 32, str, 2) | |
render.setColor(Color(25, 25, 25, 200)) | |
render.drawRect(0, 512, (Engine.idle / Engine.redline) * 1024, 512) | |
end) | |
hook.add("render", "", function() | |
render.setColor(Color(255, 255, 255)) | |
render.setRenderTargetTexture("rt") | |
render.drawTexturedRect(0, 0, 512, 512) | |
render.setRenderTargetTexture() | |
end) | |
else | |
chip():isWeldedTo():linkComponent(chip()) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment