├── .github └── workflows │ └── dotnet-desktop.yml ├── .gitignore ├── .paket ├── Paket.Restore.targets └── paket.bootstrapper.exe ├── BatGuano ├── CopyBlenderToWC.bat ├── delspin.bat └── makespin.bat ├── BlenderScripts ├── install.py └── io_scene_mmobj │ ├── __init__.py │ ├── export_mmobj.py │ └── import_mmobj.py ├── DEVNOTES.md ├── Docs ├── binpackage │ └── README.md ├── devguide │ └── README.md └── userguide │ └── README.md ├── LICENSE.txt ├── MMAppDomain ├── MMAppDomain.cs ├── MMAppDomain.tlb ├── ModelModCLRAppDomain.csproj └── Properties │ └── AssemblyInfo.cs ├── MMDotNet.sln ├── MMLaunch ├── App.config ├── App.fs ├── App.xaml ├── AssemblyInfo.fs ├── BlenderUtil.fs ├── BlenderWindow.xaml ├── BlenderWindow.xaml.fs ├── ConfirmDialog.xaml ├── ConfirmDialog.xaml.fs ├── CreateModWindow.xaml ├── CreateModWindow.xaml.fs ├── GameProfileWindow.xaml ├── GameProfileWindow.xaml.fs ├── KnownGames.fs ├── MMLaunch.fsproj ├── MainWindow.xaml ├── MainWindow.xaml.fs ├── ModUtil.fs ├── ModelMod.ico ├── PreferencesWindow.xaml ├── PreferencesWindow.xaml.fs ├── PreviewHost.fs ├── ProcessUtil.fs ├── Resource.rc ├── Resource.res ├── TestFSI.fsx ├── ViewModelUtil.fs ├── WpfInteropSample │ ├── App.xaml │ ├── App.xaml.cs │ ├── D3D11Host.cs │ ├── D3D11Image.cs │ ├── D3D9.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings │ ├── ReadMe.txt │ ├── WpfInteropSample.csproj │ └── WpfInteropSample.sln └── paket.references ├── MMManaged ├── AssemblyInfo.fs ├── CoreTypes.fs ├── DataEncoding.fs ├── Dn5-MMManaged.fsproj ├── Interop.fs ├── InteropTypes.fs ├── LoadInFSI.fsx ├── Logging.fs ├── MMManaged.fsproj ├── MemoryCache.fs ├── MeshRelation.fs ├── MeshTransform.fs ├── MeshUtil.fs ├── ModDB.fs ├── ModDBInterop.fs ├── Program.fs ├── RegConfig.fs ├── Snapshot.fs ├── SnapshotProfile.fs ├── StartConf.fs ├── State.fs ├── Util.fs ├── Yaml.fs └── paket.references ├── MMView ├── App.config ├── Camera.fs ├── MMView.fsproj ├── Main.fs ├── Program.fs ├── XnaRender.fs └── paket.references ├── ModelMod.ico ├── Native ├── Cargo.lock ├── Cargo.toml ├── MMCore.code-workspace ├── build_pgo.sh ├── constant_tracking │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── constant_tracking.rs │ │ └── lib.rs ├── d3dx │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── d3dx.rs │ │ └── lib.rs ├── device_state │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── device_state.rs │ │ └── lib.rs ├── dnclr │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── dnclr.rs │ │ └── lib.rs ├── global_state │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── global_state.rs │ │ └── lib.rs ├── hook_core │ ├── .gitignore │ ├── .ionide │ │ └── symbolCache.db │ ├── .vscode │ │ └── settings.json │ ├── Cargo.lock │ ├── Cargo.toml │ ├── bresh.sh │ ├── build.rs │ ├── buildrel.sh │ ├── d332-sh.sh │ ├── r2025g1.sh │ ├── rb.sh │ ├── rb32.sh │ ├── rgw1.sh │ ├── rme2.sh │ ├── rt.sh │ ├── rtl.sh │ ├── shutil.sh │ └── src │ │ ├── debugmode.rs │ │ ├── hook_constants.rs │ │ ├── hook_device.rs │ │ ├── hook_device_d3d11.rs │ │ ├── hook_render.rs │ │ ├── hook_render_d3d11.rs │ │ ├── input_commands.rs │ │ ├── lib.rs │ │ └── mod_render.rs ├── hook_snapshot │ ├── Cargo.toml │ └── src │ │ ├── hook_snapshot.rs │ │ ├── lib.rs │ │ └── snap_extdll.rs ├── input │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── input.rs │ │ └── lib.rs ├── interop │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── interop.rs │ │ └── lib.rs ├── mod_load │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── data_encoding.rs │ │ ├── lib.rs │ │ ├── mod_load.rs │ │ └── mod_vector.rs ├── mod_stats │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── mod_stats.rs ├── profiler │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── profile.rs ├── resetlinks.sh ├── setjunc.sh ├── shader_capture │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── shader_capture.rs ├── shared_dx │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── defs_dx11.rs │ │ ├── defs_dx9.rs │ │ ├── dx11rs.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── state.rs │ │ ├── types.rs │ │ ├── types_dx11.rs │ │ ├── types_dx9.rs │ │ └── util.rs ├── snap_plugin │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── snap_plugin.rs ├── snaplib │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── anim_frame.rs │ │ ├── anim_snap_state.rs │ │ ├── lib.rs │ │ └── snap_config.rs ├── test_e2e │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── test_e2e.rs ├── types │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ ├── d3ddata.rs │ │ ├── d3dx.rs │ │ ├── interop.rs │ │ ├── lib.rs │ │ └── native_mod.rs └── util │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ ├── lib.rs │ └── util.rs ├── README.md ├── SnapshotProfiles └── SnapshotProfiles.yaml ├── StartupApp ├── App.config ├── AssemblyInfo.fs ├── ModelMod.ico ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── StartupApp.csproj └── StartupApp.sln ├── Test.MMManaged ├── App.config ├── Program.fs ├── Test.MMManaged.fsproj ├── TestMesh.fs ├── TestMeshTransform.fs ├── TestModDB.fs ├── TestModDBInterop.fs ├── TestWriters.fs ├── TestYaml.fs ├── Util.fs └── paket.references ├── Test.ManagedLaunch ├── Program.fs └── Test.ManagedLaunch.fsproj ├── Test.NativeLaunch ├── Cargo.lock ├── Cargo.toml ├── basic_format.txt ├── basic_vshader.cso ├── basic_vshader.hlsl ├── dxgiformats.txt ├── runmm.sh ├── simple_vertex_shader.dat └── src │ ├── interop_mmobj.rs │ ├── load_mmobj.rs │ ├── main.rs │ └── shadercomp.rs ├── TestData ├── DelMod.yaml ├── ModIndex.yaml ├── Mods │ ├── FT_Barrel │ │ ├── FT_BarrelMod.mmobj │ │ ├── FT_BarrelMod.yaml │ │ ├── FT_BarrelRef.dds │ │ ├── FT_BarrelRef.mmobj │ │ ├── FT_BarrelRef.mtl │ │ ├── FT_BarrelRef.yaml │ │ └── FT_BarrelRef_VBDecl.dat │ ├── FT_Crate │ │ ├── FT_CrateMod.mmobj │ │ ├── FT_CrateMod.yaml │ │ ├── FT_CrateRef.dds │ │ ├── FT_CrateRef.mmobj │ │ ├── FT_CrateRef.mtl │ │ ├── FT_CrateRef.yaml │ │ └── FT_CrateRef_VBDecl.dat │ ├── FT_LargeRock │ │ ├── FT_LargeRockMod.mmobj │ │ ├── FT_LargeRockMod.yaml │ │ ├── FT_LargeRockRef.dds │ │ ├── FT_LargeRockRef.mmobj │ │ ├── FT_LargeRockRef.mtl │ │ ├── FT_LargeRockRef.yaml │ │ └── FT_LargeRockRef_VBDecl.dat │ └── ModIndex.yaml ├── MonolithMod.mmobj ├── MonolithMod.yaml ├── MonolithRef.mmobj ├── MonolithRef.yaml ├── MonolithRef_VBDecl.dat ├── Test.yaml ├── monolithmod.blend └── monolithref.blend ├── appveyor.yml ├── build.bat ├── build.fsx ├── installdeps.bat ├── paket.dependencies ├── paket.lock ├── sign.bat └── updateversions.bat /.gitignore: -------------------------------------------------------------------------------- 1 | MMWiz/packages/ 2 | MMWiz/obj/ 3 | ModelMod.sdf 4 | ModelMod.v12.suo 5 | MMWiz/bin/ 6 | ModelMod/Debug/ 7 | MMWiz/WizUI/obj/ 8 | MMView/obj/ 9 | MMView/bin/ 10 | ModelMod/Release/ 11 | MMManaged/obj/ 12 | Release/ 13 | MMLoader/Debug/ 14 | MMAppDomain/obj/ 15 | MMAppDomain/bin/ 16 | Debug/ 17 | packages/ 18 | MMDotNet.v12.suo 19 | MMWiz/MMWiz.v12.suo 20 | ModelMod.opensdf 21 | MMData 22 | TestData/monolith.OUT.mmobj 23 | TestData/monolith.TestWrite.mmobj 24 | TestData/monolith.TestWrite.mtl 25 | ipch/ 26 | MMManaged/TestFSI.fsx 27 | Logs/ 28 | Data/ 29 | .fake/ 30 | test/ 31 | build/ 32 | deploy/ 33 | .vs/MMDotNet/v14/.suo 34 | .paket/paket.exe 35 | .vs/ 36 | certpath.txt 37 | sign/ 38 | ModelMod.VC.opendb 39 | MMDotNet.sdf 40 | E2ETestData/ 41 | ModelMod.VC.VC.opendb 42 | ModelMod.VC.db 43 | ModelMod/Exports.cpp 44 | ModelMod/exports.h 45 | Native/rb.sh 46 | Native/rb32.sh 47 | Native/testlog.txt 48 | Native/d332-sh.sh 49 | ModelMod/ModelMod.vcxproj.user 50 | TPLib/D3DX9_43_x86_64.dll 51 | TPLib/D3DX9_43_x86.dll 52 | Test.MMManaged/obj 53 | Native/.vscode/settings.json 54 | Native/.ionide/symbolCache.db 55 | MMLaunch/obj 56 | .ionide/symbolCache.db 57 | .rustc_info.json 58 | Native/snaplib/target 59 | 60 | **/target/rls 61 | **/target 62 | Test.ManagedLaunch/obj 63 | Test.ManagedLaunch/bin 64 | 65 | DebugMode.txt 66 | dDebugMode.txt 67 | Native/profiler/__testprofiler__test_prof_summary_log.txt 68 | Native/shared_dx/__testutil__test_log_limit.txt 69 | Native/hook_core/__testhd3d11__test_query_interface.txt 70 | Native/hook_core/__testhd3d11__test_create_device.txt 71 | Native/hook_core/__testhd3d11__test_create_device_threads.txt 72 | Native/hook_core/__testhd3d11__create_and_draw.txt 73 | Test.NativeLaunch/mmobjlist.txt 74 | Native/mod_stats/__test_mod_stats_update.txt 75 | Native/mod_stats/__test_mod_stats.txt 76 | Native/mod_stats/__test_process_messages_1.txt 77 | Native/mod_stats/__test_process_messages_2.txt 78 | Native/mod_stats/__test_process_messages_3.txt 79 | g2025g1_pre.sh 80 | G2025g1 81 | SnapshotProfiles/TestProfile1.yaml 82 | SnapshotProfiles/TexFormat.txt 83 | -------------------------------------------------------------------------------- /.paket/paket.bootstrapper.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmquigs/ModelMod/2e743d54f8086afb723bc4eaac7dc59d690d2608/.paket/paket.bootstrapper.exe -------------------------------------------------------------------------------- /BatGuano/CopyBlenderToWC.bat: -------------------------------------------------------------------------------- 1 | cd %~dp0 2 | 3 | copy "%appdata%\Blender Foundation\Blender\2.73\scripts\addons\io_scene_mmobj\*.*" ..\BlenderScripts\io_scene_mmobj 4 | 5 | rem pause 6 | -------------------------------------------------------------------------------- /BatGuano/delspin.bat: -------------------------------------------------------------------------------- 1 | cd %~dp0 2 | del ..\debug\spin.txt -------------------------------------------------------------------------------- /BatGuano/makespin.bat: -------------------------------------------------------------------------------- 1 | cd %~dp0 2 | dir > ..\debug\spin.txt 3 | -------------------------------------------------------------------------------- /BlenderScripts/install.py: -------------------------------------------------------------------------------- 1 | # this script is executed by mmlaunch to enable the mmobj plugin 2 | 3 | import addon_utils 4 | import bpy 5 | import sys 6 | 7 | # note, the UI checks for this string because we can't return error codes (see below) 8 | SUCCESS="MMINFO: Plugin enabled" 9 | 10 | outHandle = None 11 | 12 | def wline(line): 13 | if outHandle != None: 14 | print(line, end="\n", file=outHandle) 15 | else: 16 | print(line) 17 | 18 | def install_mmobj(): 19 | enabled,_ = addon_utils.check('io_scene_mmobj') 20 | if not enabled: 21 | try: 22 | # this interface changed in 2.77+, try old way first 23 | addon_utils.enable('io_scene_mmobj', True) 24 | except TypeError: 25 | addon_utils.enable('io_scene_mmobj', default_set=True, persistent=True) 26 | enabled,_ = addon_utils.check('io_scene_mmobj') 27 | if enabled: 28 | bpy.ops.wm.save_userpref() 29 | wline(SUCCESS) 30 | else: 31 | # sys.exit(1) # this causes blender to die messily without setting the return code. so we'll just have to print the error 32 | wline("MMERROR: Plugin failed to enable; install path may not be correct, or it is incompatible with this version of blender") 33 | else: 34 | wline(SUCCESS) 35 | 36 | def show_paths(): 37 | for p in addon_utils.paths(): 38 | wline("MMPATH:" + p) 39 | 40 | defaultCommand = "paths" 41 | command = "" 42 | 43 | commands = { 44 | "paths": show_paths, 45 | "install": install_mmobj 46 | } 47 | 48 | ddIdx = sys.argv.index('--') if '--' in sys.argv else None 49 | if ddIdx == None: 50 | command = defaultCommand 51 | else: 52 | args = sys.argv[(ddIdx+1):] 53 | argc = len(args) 54 | if argc == 0: 55 | command = defaultCommand 56 | elif argc == 1: 57 | command = args[0] 58 | elif argc >= 2: 59 | command = args[0] 60 | outHandle = open(args[1], "w") 61 | 62 | if not command in commands: 63 | print("unknown command") 64 | else: 65 | commands[command]() 66 | if outHandle != None: 67 | outHandle.close() -------------------------------------------------------------------------------- /DEVNOTES.md: -------------------------------------------------------------------------------- 1 | ## Dev notes 2 | 3 | Its been a while since I wrote most of this code and I don't really 4 | remember all the details of what is going on everywhere. So I made this 5 | document to record any new investigations into changing things 6 | and what issues I found. 7 | 8 | Could use github issues/wiki for this kind of note-taking but I tend to 9 | lose track of information in there. However I will cite any issues that 10 | are relevant here. 11 | 12 | ### 2/21/2022: Deferred managed mod loading 13 | 14 | As of this writing, native code will only create D3D resources for mods 15 | that are actually used in the scene. However once loaded, this memory 16 | is not freed (there is no garbage collection for mods that subsequently 17 | become unused). Ctrl-F1 will clear all of this memory, though mods in 18 | use by the current scene will immediately be reloaded. 19 | 20 | Managed code still loads mesh data for all mods on startup. I looked at 21 | converting some of this to use Lazy loading (via F#'s `lazy` function), 22 | but its a non-trivial change. First issue is that primitive and vertex 23 | counts generally come from the meshes. This means the full mesh needs 24 | to be loaded to get these values, which is slow. It would have been nice 25 | if the mmobj exporter wrote out the vert/prim counts in a comment line 26 | at the top of the file, but I didn't do that back in the day. 27 | 28 | I could probably get away with not knowing the mod vert/prim counts 29 | (they are used for variant processing but maybe not needed early). 30 | However the ref vert/prim counts are needed for native code to 31 | determine when a given rendered primitive actually has some mod available 32 | to replace it. So at a minimum all the ref meshes need to be loaded. 33 | 34 | An option would be to implement additional metadata cache files for each 35 | `.mmobj` on disk. So `.mmobj.cache` or something like that. This could 36 | store the vert/prim counts and anything else needed. There is already 37 | code for detecting whether mmobj files have changed on disk, so this 38 | could just be an extension of that. However the cache file would 39 | probably need to write in an mtime or something so that I don't need to checksum the whole mmobj file, which would be slow. And mtime-based 40 | caching can be a little unreliable, esp when using other software that 41 | "preserves" mtimes. So I'm not going to do this now. 42 | 43 | A related item is the `MeshRelation` objects. These aren't needed until 44 | the mod is actually being prepped for render. I did a test where I 45 | replaced the internal `VertRel` used by this class with a `Lazy`, and it 46 | did help reduce load times especially for unused mods. However, 47 | because of the way the code is structured, we won't actually create the 48 | value until fillModData is called, and then it will block the native thread for potentially 100s of ms, just waiting on managed code, which I don't like to do. So I'd need some scheme where I can start an async 49 | process for that and return "in-progress" to the native code. This would 50 | likely require a new interop API because fillModData needs fully allocated D3D data structures, which would then go unused if 51 | the managed code didn't have any data to fill them with. So, native code 52 | would need to use a new api function to actually start the deferred load and check on its progress for an individual mod. Right now there is a global mod loading API, but not one for individual mods. 53 | 54 | For now I not going to implement deferred/incremental mod loading in the managed code. 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Docs/binpackage/README.md: -------------------------------------------------------------------------------- 1 | ### ModelMod 2 | 3 | Get the latest version and source code here: https://github.com/jmquigs/ModelMod 4 | 5 | ### Docs 6 | 7 | Required third party software: https://github.com/jmquigs/ModelMod/blob/master/Docs/userguide/README.md#Requirements 8 | 9 | User guide: https://github.com/jmquigs/ModelMod/blob/master/Docs/userguide/README.md 10 | -------------------------------------------------------------------------------- /MMAppDomain/MMAppDomain.tlb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmquigs/ModelMod/2e743d54f8086afb723bc4eaac7dc59d690d2608/MMAppDomain/MMAppDomain.tlb -------------------------------------------------------------------------------- /MMAppDomain/ModelModCLRAppDomain.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {0AB82497-9F3B-46A5-8FA2-D95E2A79E33B} 8 | Library 9 | Properties 10 | MMAppDomain 11 | ModelModCLRAppDomain 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | ..\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | ..\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 57 | -------------------------------------------------------------------------------- /MMAppDomain/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitleAttribute("ModelMod CLR app domain host")] 6 | [assembly: AssemblyDescriptionAttribute("")] 7 | [assembly: GuidAttribute("7b59b7f1-5876-4dd3-abc5-ee380144983d")] 8 | [assembly: AssemblyProductAttribute("ModelMod")] 9 | [assembly: AssemblyVersionAttribute("1.2.0.0")] 10 | [assembly: AssemblyFileVersionAttribute("1.2.0.0")] 11 | namespace System { 12 | internal static class AssemblyVersionInformation { 13 | internal const string Version = "1.2.0.0"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /MMLaunch/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /MMLaunch/App.fs: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | open System 4 | open FsXaml 5 | 6 | type App = XAML<"App.xaml"> 7 | 8 | [] 9 | [] 10 | let main argv = 11 | App().Root.Run() -------------------------------------------------------------------------------- /MMLaunch/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /MMLaunch/AssemblyInfo.fs: -------------------------------------------------------------------------------- 1 | namespace System 2 | open System.Reflection 3 | open System.Runtime.InteropServices 4 | 5 | [] 6 | [] 7 | [] 8 | [] 9 | [] 10 | [] 11 | do () 12 | 13 | module internal AssemblyVersionInformation = 14 | let [] Version = "1.2.0.0" 15 | -------------------------------------------------------------------------------- /MMLaunch/BlenderWindow.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 |