-- -- c_main.lua -- local orderPriority = "+2.5" local alphaMult = 0.75 -- water alpha multiplier local normalStrength = {0.05,0.05} -- refraction distortion intensity local detailRefRect = {100,100} -- refraction material size local lowRefRect = {700,700} -- low detail refraction material size -- don't mess local scx,scy = guiGetScreenSize () local isDynamicSkyStarted = false local forceOnIfNoDB = false --------------------------------- -- shader model version --------------------------------- function vCardPSVer() local smVersion = tostring(dxGetStatus().VideoCardPSVersion) return smVersion end --------------------------------- -- DepthBuffer access --------------------------------- function isDepthBufferAccessible() local depthStatus = tostring(dxGetStatus().DepthBufferFormat) if depthStatus == 'unknown' then depthStatus = false end return depthStatus end --------------------------------- -- Version check --------------------------------- function isMTAUpToDate() local mtaVer = getVersion().sortable if getVersion ().sortable < "1.4.1-9.07181" then return false else return true end end ---------------------------------------------------------------- -- enableWaterRef ---------------------------------------------------------------- function enableWaterRef() if wrEffectEnabled then return end -- Create things myScreenSource = dxCreateScreenSource( scx, scy ) imgRefractShader, tecName = dxCreateShader( "fx/img_refract.fx" ) texWaterShader, tecName = dxCreateShader( "fx/tex_water.fx" ) textureVol = dxCreateTexture ( "images/wavemap.png", 'dxt5' ) textureVol2 = dxCreateTexture ( "images/enb-water.dds", 'dxt5') textureCube = dxCreateTexture ( "images/cube_env256.dds", 'dxt5') -- Get list of all elements used effectParts = { myScreenSource, imgRefractShader, texWaterShader, textureVol, textureVol2, textureCube } -- Check list of all elements used bAllValid = true for _,part in ipairs(effectParts) do bAllValid = part and bAllValid end if not bAllValid then return end dxSetShaderValue( imgRefractShader, "sElementRotation", 0,0,1 ) dxSetShaderValue( imgRefractShader, "sElementSize", detailRefRect[1],detailRefRect[2] ) dxSetShaderValue( imgRefractShader, "sElementLow", lowRefRect[1], lowRefRect[2] ) dxSetShaderValue( imgRefractShader, "gNormalStrength", normalStrength[1],normalStrength[2] ) dxSetShaderValue( imgRefractShader, "sRandomTexture", textureVol ) dxSetShaderValue( imgRefractShader, "sRandomTexture", textureVol2 ) dxSetShaderValue( imgRefractShader, "sScrSize", scx, scy) local sFov = dxGetStatus().SettingFOV dxSetShaderValue( imgRefractShader, "sFov", math.rad(sFov)) dxSetShaderValue( texWaterShader, "sRandomTexture", textureVol ) dxSetShaderValue( texWaterShader, "sRandomTexture", textureVol2 ) dxSetShaderValue( texWaterShader, "sReflectionTexture", textureCube ) engineApplyShaderToWorldTexture( texWaterShader, "waterclear256" ) dBuffAccess = isDepthBufferAccessible() wrEffectEnabled = true if dBuffAccess or forceOnIfNoDB then startCameraMatrixTimer() end if isDynamicSkyStarted then applyDynamicSky() else startShineTimer() end addEventHandler( "onClientPreRender", root, updateVisibility ) -- Update water color incase it gets changed by persons unknown watTimer = setTimer( function() if texWaterShader and wrEffectEnabled then local r, g, b, a = getWaterColor() r, g, b, a = r/255, g/255, b/255, a/255 dxSetShaderValue ( texWaterShader, "sWaterColor", r, g, b, a * alphaMult ) end end ,100,0 ) destroyElement(textureVol) destroyElement(textureVol2) destroyElement(textureCube) end ---------------------------------------------------------------- -- disableWaterRef ---------------------------------------------------------------- function disableWaterRef() if not wrEffectEnabled then return end if texWaterShader then engineRemoveShaderFromWorldTexture( texWaterShader, "waterclear256" ) end -- Destroy all shaders for _,part in ipairs(effectParts) do if part then if isElement(part) then destroyElement( part ) end part = nil end end effectParts = {} bAllValid = false -- Flag effect as stopped wrEffectEnabled = false if dBuffAccess or forceOnIfNoDB then killCameraMatrixTimer() end if isDynamicSkyStarted then removeDynamicSky() else killShineTimer() end removeEventHandler( "onClientPreRender", root, updateVisibility ) end ----------------------------------------------------------------------------------- -- faulty cameraMatrix manage .. is quite faulty ----------------------------------------------------------------------------------- local camMatIsProper = false local cutPos = { { xMin = -294, xMax = 1729, yMax = 2341, yMin = 696 }, { xMin = 1388, xMax = 2129, yMax = -2117, yMin = -2705 }, { xMin = -2647, xMax = -1847.05, yMax = 1258, yMin = -776.47 }, { xMin = 329.411, xMax = 2694.117, yMax = -529.411, yMin = -1800 }, { xMin = -1200, xMax = -376.47, yMax = -882.35, yMin = -1611.7 }, { xMin = -294.117, xMax = 847.058, yMax = 2341.17, yMin = 752.94 } } function startCameraMatrixTimer() camMatTimer = setTimer( isProperCameraMatrix, 200, 0 ) end function killCameraMatrixTimer() killTimer( camMatTimer ) end function getElementSpeed(thisElement) local velX, velY, velZ = getElementVelocity(thisElement) velX = velX or 0 velY = velY or 0 velZ = velZ or 0 return math.sqrt(velX * velX) + math.sqrt(velY * velY) + math.sqrt(velZ * velZ) end function isProperCameraMatrix() local localPlayer = getLocalPlayer() if getPedControlState( "aim_weapon" ) then if getPedWeapon( localPlayer )==34 then camMatIsProper = false return end end local vehicle = getPedOccupiedVehicle( localPlayer) if vehicle then if getElementSpeed(vehicle) > 0.75 then camMatIsProper = false return end end local posX, posY, posZ = getElementPosition( localPlayer ) for i,v in ipairs(cutPos) do if (posX > v.xMin and posX < v.xMax) and (posY < v.yMax and posY > v.yMin) then camMatIsProper = false return end end camMatIsProper = true return end ----------------------------------------------------------------------------------- -- onClientHUDRender ----------------------------------------------------------------------------------- function hudrender() if not bAllValid or not wrEffectEnabled then return end if not camMatIsProper or (not dBuffAccess and not forceOnIfNoDB) then return end local camMat = getElementMatrix( getCamera() ) local posX,posY,posZ = camMat[4][1],camMat[4][2],camMat[4][3] local wLevel = getWaterLevel( posX,posY,posZ ) if type(wLevel) == "number" then if (posZ < wLevel) then dxSetShaderValue( texWaterShader, "sFogEnable", false) else dxSetShaderValue( texWaterShader, "sFogEnable", true) end end wLevel = wLevel or 0 if (posZ - wLevel) < (math.min(detailRefRect[1],detailRefRect[2])) then posZ = wLevel local roll = math.atan2(camMat[1][3], camMat[3][3]) dxSetShaderValue( imgRefractShader, "sCameraPosition", camMat[4]) dxSetShaderValue( imgRefractShader, "sCameraDirection", camMat[2]) dxSetShaderValue( imgRefractShader, "sCameraRoll", -roll) local fFogEnable = getFogDistance() and true dxSetShaderValue( imgRefractShader, "fFogEnable", fFogEnable) -- Update screen dxUpdateScreenSource( myScreenSource, true ) dxSetShaderValue( imgRefractShader, "sProjectiveTexture", myScreenSource ) dxSetShaderValue( imgRefractShader, "sElementPosition", posX,posY,posZ) dxSetShaderValue( imgRefractShader, "bIsDetailed", true ) dxDrawImage( 0, 0, scx, scy, imgRefractShader, 0, 0, 0, tocolor(255,255,255,255) ) dxSetShaderValue( imgRefractShader, "bIsDetailed", false ) dxDrawImage( 0, 0, scx, scy, imgRefractShader, 0, 0, 0, tocolor(255,255,255,255) ) dxSetShaderValue( texWaterShader, "sRefract", false ) else dxSetShaderValue( texWaterShader, "sRefract", true ) end end function createElementMatrix(ElementPosition, ElementRotation) posX, posY, posZ = unpack(ElementPosition) rotX, rotY, rotZ = unpack(ElementRotation) rx, ry, rz = math.rad(rotX), math.rad(rotY), math.rad(rotZ) local rx, ry, rz = math.rad(rotX), math.rad(rotY), math.rad(rotZ) return {{ math.cos(rz) * math.cos(ry) - math.sin(rz) * math.sin(rx) * math.sin(ry), math.cos(ry) * math.sin(rz) + math.cos(rz) * math.sin(rx) * math.sin(ry), -math.cos(rx) * math.sin(ry) }, { -math.cos(rx) * math.sin(rz), math.cos(rz) * math.cos(rx), math.sin(rx) }, {math.cos(rz) * math.sin(ry) + math.cos(ry) * math.sin(rz) * math.sin(rx), math.sin(rz) * math.sin(ry) - math.cos(rz) * math.cos(ry) * math.sin(rx), math.cos(rx) * math.cos(ry)}, {posX, posY, posZ, 1 }} end ----------------------------------------------------------------------------------- -- dynamicSky exports manage ----------------------------------------------------------------------------------- addEventHandler("onClientResourceStart", root, function(startedResName) local resource_name = tostring(getResourceName(startedResName)) if resource_name == "nc_antilag_sky" then if not isDynamicSkyStarted then isDynamicSkyStarted = exports.nc_antilag_sky:isDynamicSkyEnabled() applyDynamicSky() end end end ) addEventHandler("onClientResourceStop", root, function(startedResName) local resource_name = tostring(getResourceName(startedResName)) if resource_name == "nc_antilag_sky" then if isDynamicSkyStarted then isDynamicSkyStarted = false removeDynamicSky() end end end ) addEventHandler("onClientResourceStart", resourceRoot, function() local this_resource = getResourceFromName ("nc_antilag_sky") if this_resource then if getResourceState (this_resource)=="running" then if not isDynamicSkyStarted then isDynamicSkyStarted = exports.nc_antilag_sky:isDynamicSkyEnabled() applyDynamicSky() end end end end ) local getLastTick = 0 removeWeather = {8,9,16,19,30,31,32,118} function renderSun() if getTickCount() - 120 < getLastTick then return end local thisWeather = getWeather() local weatherImpact = 1 for index, nr in ipairs(removeWeather) do if nr == thisWeather then weatherImpact = 0 end end if texWaterShader and wrEffectEnabled then local vecX, vecY, vecZ = exports.nc_antilag_sky:getDynamicSunVector() local prevImpact = 1 - math.clamp(0, math.unlerp(0.4, vecZ, 0.15), 1) if vecZ < 0 then local angImpact = math.clamp(0, math.unlerp(-0.05, vecZ, -0.2), 1) dxSetShaderValue( texWaterShader, "sLightDir", vecX, vecY, vecZ) local cr, cg, cb, dr, dg, db = getSunColor() cr, cg, cb, dr, dg, db = cr/255, cg/255, cb/255, dr/255, dg/255, db/255 dxSetShaderValue( texWaterShader, "sSunColorTop", cr, cg, cb) dxSetShaderValue( texWaterShader, "sSunColorBott", dr, dg, db) dxSetShaderValue( texWaterShader, "sSpecularBrightness", 1 * weatherImpact * angImpact) else vecX, vecY, vecZ = exports.nc_antilag_sky:getDynamicMoonVector() if vecZ < 0 then local angImpact = math.clamp(0, math.unlerp(-0.05, vecZ, -0.2), 1) local hr, mn = getTime() if hr > 18 then angImpact = angImpact * ((( hr + mn / 60) - 18) / 6) end dxSetShaderValue( texWaterShader, "sLightDir", vecX, vecY, vecZ) dxSetShaderValue( texWaterShader, "sSunColorTop", 1, 1, 1) dxSetShaderValue( texWaterShader, "sSunColorBott", 1, 1, 1) local mPhase = exports.nc_antilag_sky:getMoonPhaseValue() mPhase = math.sin(mPhase * math.pi) dxSetShaderValue( texWaterShader, "sSpecularBrightness", mPhase * weatherImpact * angImpact * 0.5 * prevImpact) end end end getLastTick = getTickCount() end local isDynamicSkyEnabled = false function applyDynamicSky() if isDynamicSkyEnabled then return end if texWaterShader and wrEffectEnabled then dxSetShaderValue( texWaterShader, "sVisibility", 1) dxSetShaderValue( texWaterShader, "sSpecularPower", 4 ) if isTimer(shineTimer) then killShineTimer() end addEventHandler("onClientPreRender", root, renderSun ) isDynamicSkyEnabled = true end end function removeDynamicSky() if not isDynamicSkyEnabled then return end if texWaterShader and wrEffectEnabled then dxSetShaderValue( texWaterShader, "sVisibility", 0) startShineTimer() removeEventHandler("onClientPreRender", root, renderSun ) isDynamicSkyEnabled = false end end ----------------------------------------------------------------------------------- -- dynamicSky effect on or off ----------------------------------------------------------------------------------- function switchDynamicSky( dsOn ) if dsOn then applyDynamicSky() else removeDynamicSky() end end addEvent( "switchDynamicSky", true ) addEventHandler( "switchDynamicSky", root, switchDynamicSky ) ----------------------------------------------------------------------------------- -- Light source visiblility detector ----------------------------------------------------------------------------------- local lightDirection = {0,0,1} local dectectorPos = 1 local dectectorScore = 0 local detectorList = { { x = -1, y = -1, status = 0 }, { x = 0, y = -1, status = 0 }, { x = 1, y = -1, status = 0 }, { x = -1, y = 0, status = 0 }, { x = 0, y = 0, status = 0 }, { x = 1, y = 0, status = 0 }, { x = -1, y = 1, status = 0 }, { x = 0, y = 1, status = 0 }, { x = 1, y = 1, status = 0 }, } function detectNext () -- Step through detectorList - one item per call dectectorPos = ( dectectorPos + 1 ) % #detectorList dectector = detectorList[dectectorPos+1] local lightDirX, lightDirY, lightDirZ if isDynamicSkyEnabled then local vecX, vecY, vecZ = exports.nc_antilag_sky:getDynamicSunVector() if vecZ < 0 then lightDirX, lightDirY, lightDirZ = vecX, vecY, vecZ else vecX, vecY, vecZ = exports.nc_antilag_sky:getDynamicMoonVector() if vecZ < 0 then lightDirX, lightDirY, lightDirZ = vecX, vecY, vecZ else lightDirX, lightDirY, lightDirZ = 0, 0, 1 end end else lightDirX, lightDirY, lightDirZ = unpack(lightDirection) end local x, y, z = getElementPosition(localPlayer) x = x + dectector.x y = y + dectector.y local endX = x - lightDirX * 200 local endY = y - lightDirY * 200 local endZ = z - lightDirZ * 200 if dectector.status == 1 then dectectorScore = dectectorScore - 1 end dectector.status = isLineOfSightClear ( x,y,z, endX, endY, endZ, true, false, false ) and 1 or 0 if dectector.status == 1 then dectectorScore = dectectorScore + 1 end -- Enable this to see the 'line of sight' checks if false then local color = tocolor(255,255,0) if dectector.status == 1 then color = tocolor(255,0,255) end dxDrawLine3D ( x,y,z, endX, endY, endZ, color ) end end ----------------------------------------------------------------------------------- -- updateVisibility ----------------------------------------------------------------------------------- local fadeTarget = 0 local fadeCurrent = 0 function updateVisibility ( deltaTicks ) if not wrEffectEnabled then return end detectNext () if dectectorScore > 0 then fadeTarget = 1 else fadeTarget = 0 end local dif = fadeTarget - fadeCurrent local maxChange = deltaTicks / 1000 dif = math.clamp(-maxChange,dif,maxChange) fadeCurrent = fadeCurrent + dif -- Update shaders if texWaterShader then dxSetShaderValue( texWaterShader, "sVisibility", fadeCurrent ) end end ---------------------------------------------------------------- -- updateShineDirection ---------------------------------------------------------------- -- Big list describing light direction at a particular game time shineDirectionList = { -- H M Direction x, y, z, sharpness, brightness, nightness { 0, 0, -0.019183, 0.994869, -0.099336, 4, 0.0, 1 }, -- Moon fade in start { 0, 30, -0.019183, 0.994869, -0.099336, 4, 0.25, 1 }, -- Moon fade in end { 3, 00, -0.019183, 0.994869, -0.099336, 4, 0.5, 1 }, -- Moon bright { 5, 00, -0.019183, 0.994869, -0.099336, 4, 0.5, 1 }, -- Moon fade out start { 5, 10, -0.019183, 0.994869, -0.099336, 4, 0.0, 0 }, -- Moon fade out end { 5, 20, -0.914400, 0.377530, -0.146093, 4, 0.0, 0 }, -- Sun fade in start { 5, 30, -0.914400, 0.377530, -0.146093, 4, 1.0, 0 }, -- Sun fade in end { 7, 0, -0.891344, 0.377265, -0.251386, 4, 1.0, 0 }, -- Sun { 10, 0, -0.678627, 0.405156, -0.612628, 4, 0.5, 0 }, -- Sun { 13, 0, -0.303948, 0.490790, -0.816542, 4, 0.5, 0 }, -- Sun { 16, 0, 0.169642, 0.707262, -0.686296, 4, 0.5, 0 }, -- Sun { 18, 0, 0.380167, 0.893543, -0.238859, 4, 0.5, 0 }, -- Sun { 18, 30, 0.398043, 0.911378, -0.238859, 4, 1.0, 0 }, -- Sun { 18, 40, 0.390288, 0.932817, -0.138859, 4, 1.5, 0 }, -- Sun fade out start { 18, 50, 0.390288, 0.932817, -0.118859, 4, 0.0, 0 }, -- Sun fade out end { 19, 01, 0.390288, 0.932817, -0.612628, 4, 0.0, 0 }, -- General fade in start { 19, 30, 0.390288, 0.932817, -0.612628, 4, 0.0, 0 }, -- General fade in end { 21, 00, 0.390288, 0.932817, -0.612628, 4, 0.0, 0 }, -- General fade out start { 22, 09, 0.390288, 0.932817, -0.612628, 4, 0.0, 0 }, -- General fade out end { 23, 59, -0.394331, 0.663288, -0.077591, 4, 0.0, 1 }, -- Nothing } function updateShineDirection () if not wrEffectEnabled or isDynamicSkyEnabled then return end -- Get game time local h, m, s = getTimeHMS () local fhoursNow = h + m / 60 + s / 3600 -- Find which two lines in the shineDirectionList to blend between for idx,v in ipairs( shineDirectionList ) do local fhoursTo = v[1] + v[2] / 60 if fhoursNow <= fhoursTo then -- Work out blend from line local vFrom = shineDirectionList[ math.max( idx-1, 1 ) ] local fhoursFrom = vFrom[1] + vFrom[2] / 60 -- Calc blend factor local f = math.unlerp( fhoursFrom, fhoursNow, fhoursTo ) -- Calc final direction, sharpness and brightness local x = math.lerp( vFrom[3], f, v[3] ) local y = math.lerp( vFrom[4], f, v[4] ) local z = math.lerp( vFrom[5], f, v[5] ) local sharpness = math.lerp( vFrom[6], f, v[6] ) local brightness = math.lerp( vFrom[7], f, v[7] ) local nightness = math.lerp( vFrom[8], f, v[8] ) -- Modify depending upon the weather sharpness, brightness = applyWeatherInfluence ( sharpness, brightness, nightness ) lightDirection = { x, y, z } -- Update shaders if texWaterShader then local cr, cg, cb, dr, dg, db = getSunColor() cr, cg, cb, dr, dg, db = cr/255, cg/255, cb/255, dr/255, dg/255, db/255 if fhoursNow > 5.2 and fhoursNow < 22.1 then dxSetShaderValue( texWaterShader, "sSunColorTop", cr, cg, cb) dxSetShaderValue( texWaterShader, "sSunColorBott", dr, dg, db) else dxSetShaderValue( texWaterShader, "sSunColorTop", 1, 1, 1) dxSetShaderValue( texWaterShader, "sSunColorBott", 1, 1, 1) end dxSetShaderValue( texWaterShader, "sLightDir", x, y, z ) dxSetShaderValue( texWaterShader, "sSpecularPower", sharpness ) dxSetShaderValue( texWaterShader, "sSpecularBrightness", brightness ) end break end end end function startShineTimer() -- Update direction all the time shineTimer = setTimer( updateShineDirection, 100, 0 ) end function killShineTimer() killTimer( shineTimer ) end ---------------------------------------------------------------- -- getWeatherInfluence ---------------------------------------------------------------- weatherInfluenceList = { -- id sun:size :translucency :bright night:bright { 0, 1, 0, 1, 1 }, -- Hot, Sunny, Clear { 1, 0.8, 0, 1, 1 }, -- Sunny, Low Clouds { 2, 0.8, 0, 1, 1 }, -- Sunny, Clear { 3, 0.8, 0, 0.8, 1 }, -- Sunny, Cloudy { 4, 1, 0, 0.2, 0 }, -- Dark Clouds { 5, 1.5, 0, 0.5, 1 }, -- Sunny, More Low Clouds { 6, 1.5, 1, 0.5, 1 }, -- Sunny, Even More Low Clouds { 7, 1, 0, 0.01, 0 }, -- Cloudy Skies { 8, 1, 0, 0, 0 }, -- Thunderstorm { 9, 1, 0, 0, 0 }, -- Foggy { 10, 1, 0, 1, 1 }, -- Sunny, Cloudy (2) { 11, 1.5, 0, 1, 1 }, -- Hot, Sunny, Clear (2) { 12, 1.5, 1, 0.5, 0 }, -- White, Cloudy { 13, 1, 0, 0.8, 1 }, -- Sunny, Clear (2) { 14, 1, 0, 0.7, 1 }, -- Sunny, Low Clouds (2) { 15, 1, 0, 0.1, 0 }, -- Dark Clouds (2) { 16, 1, 0, 0, 0 }, -- Thunderstorm (2) { 17, 1.5, 1, 0.8, 1 }, -- Hot, Cloudy { 18, 1.5, 1, 0.8, 1 }, -- Hot, Cloudy (2) { 19, 1, 0, 0, 0 }, -- Sandstorm } function applyWeatherInfluence ( sharpness, brightness, nightness ) -- Get info from table local id = getWeather() id = math.min ( id, #weatherInfluenceList - 1 ) local item = weatherInfluenceList[ id + 1 ] local sunSize = item[2] local sunTranslucency = item[3] local sunBright = item[4] local nightBright = item[5] -- Hack for clouds bug if bHasCloudsBug and not getCloudsEnabled() then nightBright = 0 end -- Modify depending on nightness local useSize = math.lerp( sunSize, nightness, 1 ) local useTranslucency = math.lerp( sunTranslucency, nightness, 0 ) local useBright = math.lerp( sunBright, nightness, nightBright ) -- Apply brightness = brightness * useBright sharpness = sharpness / useSize -- Return result return sharpness, brightness end ---------------------------------------------------------------- -- getTimeHMS -- Returns game time including seconds ---------------------------------------------------------------- local timeHMS = {0,0,0} local minuteStartTickCount local minuteEndTickCount function getTimeHMS() return unpack(timeHMS) end function render() if not wrEffectEnabled then return end local h, m = getTime () local s = 0 if m ~= timeHMS[2] then minuteStartTickCount = getTickCount () local gameSpeed = math.clamp( 0.01, getGameSpeed(), 10 ) minuteEndTickCount = minuteStartTickCount + 1000 / gameSpeed end if minuteStartTickCount then local minFraction = math.unlerpclamped( minuteStartTickCount, getTickCount(), minuteEndTickCount ) s = math.min ( 59, math.floor ( minFraction * 60 ) ) end timeHMS = {h, m, s} --dxDrawText( string.format("%02d:%02d:%02d",h,m,s), 200, 200 ) end ---------------------------------------------------------------- -- Math helper functions ---------------------------------------------------------------- function math.lerp(from,alpha,to) return from + (to-from) * alpha end function math.unlerp(from,pos,to) if ( to == from ) then return 1 end return ( pos - from ) / ( to - from ) end function math.clamp(low,value,high) return math.max(low,math.min(value,high)) end function math.unlerpclamped(from,pos,to) return math.clamp(0,math.unlerp(from,pos,to),1) end