pixel77 Posted October 22 Share Posted October 22 Hi! I want to make a paintjob system, and I found two examples. What do you think, which one is more performance/fps friendly? 1.: availablePaintJobs = { [480] = { {'remap', 'textures/comet/1.dds'}, {'remap', 'textures/comet/2.dds'}, {'remap', 'textures/comet/3.dds'}, {'remap', 'textures/comet/4.dds'}, {'remap', 'textures/r8/r8_4.dds'}, {'remap', 'textures/lada4.dds'}, {'remap', 'textures/mc13.dds'}, }, [436] = { {'*remap*', 'textures/y5/1.dds'}, {'*remap*', 'textures/y5/2.dds'}, {'*remap*', 'textures/y5/3.dds'}, {'*remap*', 'textures/y5/4.dds'}, {'*remap*', 'textures/y5/5.dds'}, }, [604] = { {'*remap*', 'textures/1.dds'}, {'*remap*', 'textures/2.dds'}, {'*remap*', 'textures/3.dds'}, {'*remap*', 'textures/4.dds'}, {'*remap*', 'textures/5.dds'}, {'*remap*', 'textures/6.dds'}, {'*remap*', 'textures/7.dds'}, }, }; local shaders = {} local elementShaders = {} local textureCache = {} local textureCount = {} local textureSize = 768 function applyShader(texture, img, distance, element) if element then destroyShaderCache(element) end local this = #shaders + 1 shaders[this] = {} shaders[this][1] = dxCreateShader("texturechanger.fx",0,distance,layered) if not textureCount[img] then textureCount[img] = 0 end if textureCount[img] == 0 then textureCache[img] = dxCreateTexture(img) end textureCount[img] = textureCount[img] + 1 shaders[this][2] = textureCache[img] shaders[this][3] = texture if element then if not elementShaders[element] then elementShaders[element] = {shaders[this], img} end end if shaders[this][1] and shaders[this][2] then dxSetShaderValue(shaders[this][1], "TEXTURE", shaders[this][2]) engineApplyShaderToWorldTexture(shaders[this][1], texture, element) end end function destroyShaderCache(element) if elementShaders[element] then destroyElement(elementShaders[element][1][1]) local old_img = elementShaders[element][2] textureCount[old_img] = textureCount[old_img] - 1 if textureCount[old_img] == 0 then destroyElement(elementShaders[element][1][2]) end elementShaders[element] = nil end end addEvent("destroyShaderCache", true) addEventHandler("destroyShaderCache", root, destroyShaderCache) addEventHandler("onClientResourceStart", resourceRoot, function() for k,v in ipairs(getElementsByType("vehicle", root, true)) do local pj = tonumber(getElementData(v, "tuning.paintjob")) or 0 if pj > 0 then addVehiclePaintJob(v, getElementModel(v), pj) end end end) function getVehiclePaintJobs(model) if availablePaintJobs[model] then return #availablePaintJobs[model] else return 0 end end function addVehiclePaintJob(veh, model, id) local pj = availablePaintJobs[model] if pj then local pj = pj[id] if pj then applyShader(pj[1], pj[2], 100, veh) end end end addEvent("addVehiclePaintJob", true) addEventHandler("addVehiclePaintJob", root, addVehiclePaintJob) function removeVehiclePaintJob(veh) if veh then destroyShaderCache(veh) end end addEvent("removeVehiclePaintJob", true) addEventHandler("removeVehiclePaintJob", root, removeVehiclePaintJob) addEventHandler("onClientElementDestroy", getRootElement(), function() if getElementType(source) == "vehicle" then destroyShaderCache(source) end end) addEventHandler("onClientElementStreamIn", getRootElement(), function() if getElementType(source) == "vehicle" then local pj = tonumber(getElementData(source, "tuning.paintjob")) or 0 if pj > 0 then addVehiclePaintJob(source, getElementModel(source), pj) end end end) addEventHandler("onClientElementStreamOut", getRootElement(), function() if getElementType(source) == "vehicle" then destroyShaderCache(source) end end) or 2.: local modelTextures = { [582] = "template", [445] = "remap", [402] = "remap", [400] = "remapsrt8", [555] = "remap", [416] = "remap", [503] = "remap", [580] = "remap", [558] = "remap", [598] = "remap", [458] = "remap", [589] = "remap", [420] = "face", [566] = "bodykit", [518] = "remap", [561] = "remap", [597] = "r_map", [596] = "remap_taurus", [599] = "remap", } local availableTextures = { [1] = "sprinter/1.dds", [2] = "sprinter/2.dds", [3] = "sprinter/3.dds", [4] = "sprinter/4.dds", [5] = "sprinter/5.dds", [6] = "sprinter/6.dds", [7] = "sprinter/7.dds", [8] = "sprinter/8.dds", [9] = "sprinter/9.dds", [12] = "e60/4.dds", [13] = "e60/5.dds", [14] = "e60/m51.dds", [15] = "e60/m52.dds", [16] = "e60/m53.dds", [17] = "e60/m61.dds", [18] = "e60/m63.dds", [19] = "e60/m62.dds", [20] = "250gto/1.dds", [21] = "250gto/2.dds", [22] = "250gto/3.dds", [23] = "250gto/4.dds", [24] = "ambulance/1.dds", [25] = "amggt/1.dds", [26] = "amggt/2.dds", [27] = "amggt/3.dds", [28] = "amggt/4.dds", [29] = "amggt/5.dds", [30] = "amggt/6.dds", [31] = "audirs4/1.dds", [32] = "audirs4/2.dds", [33] = "audirs4/3.dds", [34] = "audirs4/4.dds", [35] = "audirs4/5.dds", [36] = "audirs4/6.dds", [37] = "audirs4/7.dds", [38] = "bmwm3/1.dds", [39] = "bmwm3/2.dds", [40] = "bmwm3/3.dds", [41] = "copcarpj/1.dds", [42] = "copcarpj/2.dds", [43] = "copcarpj/3.dds", [44] = "e250/1.dds", [45] = "e250/2.dds", [46] = "golf/1.dds", [47] = "golf/2.dds", [48] = "golf/3.dds", [49] = "golf/4.dds", [50] = "golf/5.dds", [51] = "viper/1.dds", [52] = "viper/2.dds", [53] = "viper/3.dds", [54] = "viper/4.dds", [55] = "viper/5.dds", [56] = "viper/6.dds", [57] = "viper/7.dds", [58] = "viper/8.dds", [59] = "nissan240/1.dds", [60] = "nissan240/2.dds", [61] = "nissan240/3.dds", [62] = "nissan240/4.dds", [63] = "nissan240/5.dds", [64] = "nissan240/6.dds", [65] = "nissan240/7.dds", [66] = "nissan240/8.dds", [67] = "grandsrt8/1.dds", [68] = "grandsrt8/2.dds", [69] = "grandsrt8/3.dds", [70] = "tahoe/1.dds", [71] = "tahoe/2.dds", [72] = "tahoe/3.dds", [73] = "tahoe/4.dds", [74] = "taurus/1.dds", [75] = "taurus/2.dds", [76] = "taurus/3.dds", [77] = "suburban/1.dds", [78] = "suburban/2.dds", [79] = "suburban/3.dds", [80] = "polmav/1.dds", [81] = "polmav/2.dds", [82] = "polmav/3.dds", [83] = "polmav/4.dds", [84] = "polmav/5.dds", [85] = "f10/1.dds", [86] = "f10/2.dds", [87] = "e34/1.dds", [88] = "e34/2.dds", [89] = "e34/3.dds", } local paintjobs = { ["template"] = { [582] = {1, 2, 3, 4, 5, 6, 7, 8, 9} }, ["bodykit"] = { [566] = {85, 86} }, ["face"] = { [420] = {87, 88, 89} }, ["remap"] = { [402] = {10}, [445] = {12, 13, 14, 15, 16, 17, 18, 19}, [555] = {20, 21, 22, 23}, [416] = {24}, [503] = {25,26,27,28,29,30}, [580] = {31,32,33,34,35,36,37}, [558] = {38,39,40}, [598] = {41,42,43}, [458] = {44,45}, [589] = {46,47,48,49,50}, [518] = {51,52,53,54,55,56,57,58}, [561] = {59,60,61,62,63,64,65,66}, }, ["remapsrt8"] = { [400] = {67,68,69}, }, ["remap_taurus"] = { [596] = {70,71,72,73}, }, ["r_map"] = { [596] = {80, 81, 82, 83, 84}, [597] = {77,78,79}, }, } local textures = {} local shaders = {} addEventHandler("onClientResourceStart", getResourceRootElement(), function () for k, v in pairs(availableTextures) do textures[k] = dxCreateTexture("textures/" .. v, "dxt1") end for k, v in pairs(getElementsByType("vehicle"), getRootElement(), true) do applyTexture(v) end end) addEventHandler("onClientElementStreamIn", getRootElement(), function () if getElementType(source) == "vehicle" then applyTexture(source) end end) addEventHandler("onClientResourceStop", getResourceRootElement(), function () for k, v in pairs(shaders) do if isElement(v) then destroyElement(v) end end for k, v in pairs(textures) do destroyElement(v) end end) addEventHandler("onClientElementDataChange", getRootElement(), function (dataName) if dataName == "vehicle.tuning.Paintjob" then applyTexture(source) end end) addEventHandler("onClientElementDestroy", getRootElement(), function () if shaders[source] then if isElement(shaders[source]) then destroyElement(shaders[source]) end shaders[source] = nil end end) function applyTexture(vehicle) local paintjobId = getElementData(vehicle, "vehicle.tuning.Paintjob") or 0 if paintjobId then if paintjobId == 0 then if isElement(shaders[vehicle]) then destroyElement(shaders[vehicle]) end shaders[vehicle] = nil elseif paintjobId > 0 then local model = getElementModel(vehicle) if not isElement(shaders[vehicle]) then shaders[vehicle] = dxCreateShader("texturechanger.fx") end local modelTexture = modelTextures[model] if modelTexture then local paintjob = paintjobs[modelTexture] if paintjob and paintjob[model] then local textureId = paintjob[model][paintjobId] if textureId and shaders[vehicle] then if textures[textureId] then dxSetShaderValue(shaders[vehicle], "gTexture", textures[textureId]) engineApplyShaderToWorldTexture(shaders[vehicle], modelTexture, vehicle) end end end end end end end function isTextureIdValid(model, textureId) local modelTexture = modelTextures[model] if modelTexture then local paintjob = paintjobs[modelTexture] if paintjob then if paintjob[model] then if paintjob[model][textureId] then return true else return false end else return false end else return false end else return false end end function applyPaintJob(paintjobId, sync) local pedveh = getPedOccupiedVehicle(localPlayer) paintjobId = tonumber(paintjobId) if paintjobId then if isElement(pedveh) then local model = getElementModel(pedveh) if isTextureIdValid(model, paintjobId) or paintjobId == 0 then setElementData(pedveh, "vehicle.tuning.Paintjob", paintjobId, sync) end end end end function getPaintJobCount(model) model = tonumber(model) local modelTexture = modelTextures[model] local paintjob = paintjobs[modelTexture] if model == 467 then return #paintjob[model] - 2 end if model == 500 or model == 438 then return #paintjob[model] - 1 end if model and modelTexture and paintjob and paintjob[model] then return #paintjob[model] end return false end Link to comment
Dzsozi (h03) Posted October 27 Share Posted October 27 2nd seems more sophisticated and more likely to be performance friendly since it somewhat has logic for ElementStreamIn event, however it'd need more checks to avoid unnecessarily calling the applyTexture function (like check if the vehicle already has it applied, handle ElementStreamOut as well, etc etc.) Link to comment
pixel77 Posted October 28 Author Share Posted October 28 16 hours ago, Dzsozi (h03) said: 2nd seems more sophisticated and more likely to be performance friendly since it somewhat has logic for ElementStreamIn event, however it'd need more checks to avoid unnecessarily calling the applyTexture function (like check if the vehicle already has it applied, handle ElementStreamOut as well, etc etc.) 1st one also has logic for streamIn and streamOut, creating/destroying and applying textures. But the 2nd option creates all of the textures(dxCreateTexture) on the resource start, and I don't know that is a good or a bad idea. The 1st one is only creating textures when an element streamed in, and it is need the texture. Link to comment
Dzsozi (h03) Posted October 28 Share Posted October 28 1 hour ago, pixel77 said: 1st one also has logic for streamIn and streamOut, creating/destroying and applying textures. But the 2nd option creates all of the textures(dxCreateTexture) on the resource start, and I don't know that is a good or a bad idea. The 1st one is only creating textures when an element streamed in, and it is need the texture. Yeah you are right, I didn't pay close enough attention to the first code. However in my opinion, constantly creating and destroying textures per element as they stream in/out makes a larger performance impact, rather than keeping all of the textures in memory and applying a texture replace shader when needed. As far as I know when you create or destroy a texture it makes an impact on the CPU, but when you apply or remove or use a shader it rather uses the resources of the GPU, which could handle more. In my opinion it would be better to just create every texture needed on the start of the resource. If you'd have 100+ vehicle paintjob textures to create and use later, it may freeze for a few seconds when starting the resource/script, but after that you will have access to all of them. Then you could handle the shader creating and destroying on streaming events, other than the texture creating and destroying. If you have 5 vehicle paintjob textures in high quality, and suddenly 5 HQ model/texture vehicle requires the creation of the HQ paintjob textures, a client might more likely to stutter when the 5 vehicles stream in/out at the same time. I think it is more efficient to store what you need in RAM and take a toll on the GPU, rather than constantly burdening the CPU. Correct me if I am wrong, but so far this is my understanding of how these kinds of things work. Link to comment
Nico834 Posted October 30 Share Posted October 30 The necessary textures should be created at the start of the resource, because although they are not necessarily needed immediately, in the case of intermediate creation, lag spikes may occur, which may degrade the gameplay. Link to comment
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now