Skip to content

Add linux support & various bug fixes.#78

Open
JCalebBR wants to merge 5 commits into
AlwarrenSidh:masterfrom
JCalebBR:improvements
Open

Add linux support & various bug fixes.#78
JCalebBR wants to merge 5 commits into
AlwarrenSidh:masterfrom
JCalebBR:improvements

Conversation

@JCalebBR

@JCalebBR JCalebBR commented Mar 17, 2026

Copy link
Copy Markdown

Linux Support

Will fix #74
More comprehensive alternative to #77

Added linux support via blender_manifest.toml and in ArmaTools.py -> RunO2Script(), it should be checking now which OS the user is on and handles how o2script is run.

In my system, CachyOS, I made a wrapper for o2script.exe with the following code, this could be noted in the README.md under a new Linux compatibility (experimental) tab?

#!/bin/bash
# Point Wine to the Arma 3 Tools Steam prefix
export WINEPREFIX="$HOME/.local/share/Steam/steamapps/compatdata/233800/pfx"
# Suppress wine debug spam in console
export WINEDEBUG=-all
# Disable wine's crash dialogs
export b=1
# Run the real exe through Wine
wine "$HOME/.local/share/Steam/steamapps/common/Arma 3 Tools/ObjectBuilder/O2Script.exe" "$@"

The user would add this o2script_wrapper.sh to the o2script path in the Blender Addon Settings.

"Various bug fixes"

Before, if the user had an error, e.g n-gons in any meshes, the addon would error out and lock the .p3d file, until blender was restarted. This could also happen for other exported file types like .rtm and .asc.
Now, the various bug fixes were mainly about adding try/except blocks to filePtr lines throughout the entire code base.

filePtr = open(filename, "wb")
# Some code
filePtr.close()

After:

try:
  with open(filename, "wb") as filePtr:
    # Some code
except:
  # Some exception handling

I realize the code is super old and with blocks may have not existed back when it was first written, alas python is not C and we can use with as a context manager to open and close files gracefully.

@JCalebBR
JCalebBR marked this pull request as ready for review March 17, 2026 19:42
@JCalebBR

Copy link
Copy Markdown
Author

Tested this personally on Blender 4.2.X (Windows) and Blender 5.1.0 (CachyOS), no issues noticed on my end.

@JCalebBR

JCalebBR commented Mar 18, 2026

Copy link
Copy Markdown
Author

I have also done few more improvements/rewrites to long running functions during export.
Namely, writeFaces, writeUVSets, writeNormals and such.
I won't go in too much detail in this comment about what specific changes were made, since these are outside the scope of this PR/comment. Let me know if these would be ok to include in this PR, or a new PR!.

However, I will include a short comparison example. I used cProfile to profile the before and after for these changes.
The before is v4.2.3 of this addon, the after is my improved version (v4.3.0?)

Before:

         2150595 function calls in 26.733 seconds

   Ordered by: cumulative time
   List reduced from 234 to 40 due to restriction <40>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000   26.733   26.733 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:963(_execute_core)
        1    0.267    0.267   23.725   23.725 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:728(exportObjectListAsMDL)
        5    0.054    0.011   17.261    3.452 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:466(export_lod)
        5    5.601    1.120    8.280    1.656 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:180(writeFaces)
        4    5.029    1.257    5.553    1.388 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:436(writeUVSet)
       38    3.080    0.081    3.084    0.081 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:689(applyModifiersOnObject)
        1    0.000    0.000    2.999    2.999 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/ArmaTools.py:859(RunO2Script)
       38    0.014    0.000    2.708    0.071 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:589(duplicateObject)
       38    1.878    0.049    2.686    0.071 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:620(instantiateMeshCollector)
   353565    1.431    0.000    2.431    0.000 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:134(writeFloat)
        2    0.000    0.000    1.846    0.923 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:423(check_output)
        2    0.000    0.000    1.846    0.923 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:512(run)
        2    0.169    0.085    1.843    0.922 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:642(exportLodLevelWithModifiers)
        2    0.000    0.000    1.824    0.912 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:1178(communicate)
        2    1.824    0.912    1.824    0.912 {method 'read' of '_io.BufferedReader' objects}
        5    0.303    0.061    1.276    0.255 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:157(writeNormals)
   166664    0.678    0.000    1.153    0.000 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:131(writeULong)
        1    0.000    0.000    1.152    1.152 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:387(call)
        6    0.000    0.000    1.152    0.192 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:1275(wait)
        6    0.000    0.000    1.152    0.192 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:2034(_wait)
        3    0.000    0.000    1.152    0.384 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:2021(_try_wait)
        3    1.152    0.384    1.152    0.384 {built-in method posix.waitpid}
   547612    0.793    0.000    0.793    0.000 {method 'write' of '_io.BufferedWriter' objects}
        5    0.203    0.041    0.773    0.155 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/ArmaTools.py:762(optimize_export_lod)
   547549    0.769    0.000    0.769    0.000 {built-in method _struct.pack}
        5    0.471    0.094    0.673    0.135 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:288(writeNamedSelections)
       38    0.617    0.016    0.646    0.017 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/ArmaTools.py:1111(deleteVertexGroupList)
        5    0.518    0.104    0.519    0.104 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/ArmaTools.py:621(optimizeSectionCountObj)
    13546    0.122    0.000    0.459    0.000 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:75(getMaterialInfo)
       41    0.390    0.010    0.393    0.010 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/ArmaTools.py:660(joinObjectToObject)
        5    0.083    0.017    0.390    0.078 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:171(writeVertices)
    27169    0.198    0.000    0.360    0.000 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:137(writeString)
    20644    0.062    0.000    0.318    0.000 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:63(stripAddonPath)
    20644    0.114    0.000    0.256    0.000 <frozen posixpath>:61(isabs)
   166902    0.230    0.000    0.230    0.000 {built-in method builtins.len}
        5    0.086    0.017    0.221    0.044 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:345(writeSharpEdges)
    20674    0.056    0.000    0.085    0.000 <frozen posixpath>:42(_get_sep)
    19296    0.052    0.000    0.079    0.000 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:113(convertWeight)
    37414    0.068    0.000    0.068    0.000 ~/.local/share/Steam/steamapps/common/Blender/5.1/scripts/modules/_bpy_types.py:857(loop_indices)
    47129    0.067    0.000    0.067    0.000 {method 'append' of 'list' objects}

After, same .p3d being exported:

         308698 function calls in 10.196 seconds

   Ordered by: cumulative time
   List reduced from 246 to 40 due to restriction <40>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000   10.196   10.196 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:1057(_execute_core)
        1    0.269    0.269    8.182    8.182 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:822(exportObjectListAsMDL)
       38    3.054    0.080    3.057    0.080 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:783(applyModifiersOnObject)
       38    0.014    0.000    2.635    0.069 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:683(duplicateObject)
       38    1.827    0.048    2.613    0.069 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:714(instantiateMeshCollector)
        1    0.000    0.000    1.998    1.998 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/ArmaTools.py:859(RunO2Script)
        5    0.056    0.011    1.799    0.360 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:559(export_lod)
        1    0.000    0.000    1.129    1.129 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:387(call)
        6    0.000    0.000    1.128    0.188 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:1275(wait)
        6    0.000    0.000    1.128    0.188 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:2034(_wait)
        3    0.000    0.000    1.128    0.376 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:2021(_try_wait)
        3    1.128    0.376    1.128    0.376 {built-in method posix.waitpid}
        2    0.000    0.000    0.868    0.434 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:423(check_output)
        2    0.000    0.000    0.868    0.434 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:512(run)
        2    0.000    0.000    0.864    0.432 ~/.local/share/Steam/steamapps/common/Blender/5.1/python/lib/python3.13/subprocess.py:1178(communicate)
        2    0.864    0.432    0.864    0.432 {method 'read' of '_io.BufferedReader' objects}
        2    0.184    0.092    0.857    0.429 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:736(exportLodLevelWithModifiers)
        5    0.196    0.039    0.777    0.155 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/ArmaTools.py:762(optimize_export_lod)
       38    0.604    0.016    0.632    0.017 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/ArmaTools.py:1111(deleteVertexGroupList)
        5    0.530    0.106    0.532    0.106 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/ArmaTools.py:621(optimizeSectionCountObj)
        5    0.268    0.054    0.508    0.102 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:378(writeNamedSelections)
       41    0.385    0.009    0.388    0.009 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/ArmaTools.py:660(joinObjectToObject)
        5    0.069    0.014    0.218    0.044 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:443(writeSharpEdges)
        5    0.075    0.015    0.152    0.030 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:197(writeFaces)
    20007    0.079    0.000    0.135    0.000 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:131(writeULong)
    70861    0.101    0.000    0.101    0.000 {method 'add' of 'set' objects}
    19296    0.053    0.000    0.079    0.000 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:113(convertWeight)
    41016    0.056    0.000    0.056    0.000 {built-in method builtins.len}
    32643    0.051    0.000    0.051    0.000 {method 'intersection_update' of 'set' objects}
    31437    0.045    0.000    0.045    0.000 {built-in method _struct.pack}
       40    0.042    0.001    0.045    0.001 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/ArmaTools.py:747(selectTransparencyObj)
    28044    0.038    0.000    0.038    0.000 {method 'append' of 'list' objects}
        5    0.019    0.004    0.038    0.008 ~/.config/blender/5.1/extensions/user_default/ArmaToolbox/MDLExporter.py:171(writeVertices)
    20321    0.032    0.000    0.032    0.000 {method 'write' of '_io.BufferedWriter' objects}
       86    0.002    0.000    0.028    0.000 ~/.local/share/Steam/steamapps/common/Blender/5.1/scripts/startup/bl_operators/wm.py:2348(execute)
    19296    0.026    0.000    0.026    0.000 {built-in method builtins.round}
    13546    0.020    0.000    0.020    0.000 {method 'copy' of 'set' objects}
       33    0.019    0.001    0.019    0.001 {method 'foreach_get' of 'bpy_prop_collection' objects}
       86    0.001    0.000    0.017    0.000 ~/.local/share/Steam/steamapps/common/Blender/5.1/scripts/startup/bl_ui/space_toolsystem_common.py:1094(activate_by_id)
      528    0.005    0.000    0.012    0.000 ~/.local/share/Steam/steamapps/common/Blender/5.1/scripts/modules/_bpy_types.py:1023(__getattribute__)

The results speak for themselves XD.
Some things to note, major reduction in function calls, down from ~2.1M to ~300K. Functions that took many seconds to execute, now only take ~200ms at most.

This test was run on a not too complex model, first visual LOD ~10k triangles on CachyOS.
However, I have also tested it on W11 with a more complex model, first LOD ~100k triangles and the difference was ~18.37s in overall execution down to ~2.89s.

This code is available on my master branch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow for Linux as a version

1 participant