├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── Reclaimer.Architect.sln └── Reclaimer.Architect ├── Blam ├── Common │ ├── CacheSegmenter.cs │ └── PlacementType.cs ├── Halo3 │ ├── model.cs │ ├── object.cs │ └── scenario_structure_bsp.cs ├── Halo4 │ ├── model.cs │ └── object.cs └── HaloReach │ ├── model.cs │ └── object.cs ├── Components ├── AiComponentManager.cs ├── ComponentManager.cs ├── MissionComponentManager.cs ├── PaletteComponentManager.cs ├── StartPositionComponentManager.cs ├── TerrainComponentManager.cs └── TriggerVolumeComponentManager.cs ├── Controls ├── BoxManipulator3D.cs ├── BspEditor.xaml ├── BspEditor.xaml.cs ├── DXEditor.xaml ├── DXEditor.xaml.cs ├── DXRenderer.cs ├── DXViewer.xaml ├── DXViewer.xaml.cs ├── ElementHighlighter3D.cs ├── HierarchyView.xaml ├── HierarchyView.xaml.cs ├── IManipulatable.cs ├── IMeshNode.cs ├── IMetaViewerHost.cs ├── IRendererHost.cs ├── InstancePropertyView.xaml ├── InstancePropertyView.xaml.cs ├── ManipulationFlags.cs ├── Markers │ ├── DecalMarker3D.cs │ ├── LightMarker3D.cs │ ├── MarkerGeometry3D.cs │ ├── PositionMarker3D.cs │ ├── SpawnPointMarker3D.cs │ └── StartPositionMarker3D.cs ├── ObjectModel3D.cs ├── PaletteEditorWindow.xaml ├── PaletteEditorWindow.xaml.cs ├── PropertyView.xaml ├── PropertyView.xaml.cs ├── RenderModel3D.cs ├── Spinner.xaml ├── Spinner.xaml.cs └── TransformManipulatorEx3D.cs ├── Geometry ├── BspManager.cs ├── MeshTemplate.cs ├── ModelFactory.cs ├── ModelProperties.cs └── ObjectHolder.cs ├── Models ├── Ai │ ├── AiArea.cs │ ├── AiFiringPosition.cs │ ├── AiNamedBlock.cs │ ├── AiSquadHierarchy.cs │ └── AiStartingLocation.cs ├── BlockPropertiesLocator.cs ├── IBspGeometryInstanceBlock.cs ├── IDisplayName.cs ├── IMetaUpdateReceiver.cs ├── IScenarioHandler.cs ├── IStructureBspHandler.cs ├── InstancePlacement.cs ├── ModelConfig.cs ├── NodeType.cs ├── ObjectName.cs ├── ObjectPlacement.cs ├── ScenarioListItem.cs ├── ScenarioModel.cs ├── ScenarioObject.cs ├── SceneNodeModel.cs ├── StartPosition.cs ├── StructureBspModel.cs └── TriggerVolume.cs ├── Plugins ├── ArchitectEditorPlugin.cs ├── ArchitectSettingsPlugin.cs └── ArchitectViewerPlugin.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── Reclaimer.Architect.csproj ├── Resources ├── AiSection.cs ├── FieldId.cs ├── MetaViewerPaletteEditor.xaml ├── MetaViewerSmall.xaml ├── MetadataXML │ ├── Halo3Metadata.xml │ ├── Halo3ODSTMetadata.xml │ ├── Halo4Metadata.xml │ ├── HaloReachMetadata.xml │ ├── MccHalo2XMetadata.xml │ ├── MccHalo3Metadata.xml │ ├── MccHalo3ODSTMetadata.xml │ ├── MccHalo4Metadata.xml │ ├── MccHaloReachMetadata.xml │ └── Minimize.xslt ├── NodeHierarchy.xml ├── PaletteType.cs ├── ScenarioXML │ ├── Halo3ODSTScenario.xml │ ├── Halo3Scenario.xml │ ├── Halo4Scenario.xml │ ├── HaloReachScenario.xml │ ├── MccHalo2XScenario.xml │ ├── MccHalo3ODSTScenario.xml │ ├── MccHalo3Scenario.xml │ ├── MccHalo4Scenario.xml │ ├── MccHaloReachScenario.xml │ └── TemplateScenario.xml ├── Section.cs └── Templates.xaml ├── Themes └── Generic.xaml ├── Utilities ├── EulerTransformConverter.cs ├── Extensions.cs ├── IO │ ├── IBlockEditor.cs │ ├── IMetadataStream.cs │ ├── InMemoryBlockCollection.cs │ ├── InMemoryMetadataStream.cs │ └── MemoryTracker.cs ├── IsPaletteNodeConverter.cs ├── MatrixTransformConverter.cs ├── NativeMethods.cs ├── QuaternionTransformConverter.cs ├── SharpDXVectorConverter.cs ├── TranslationTransformConverter.cs └── ViewDistanceGroupNode.cs └── packages.config /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About Architect 2 | Architect is a plugin for the Reclaimer tool. It adds a visual 3d scenario editor where you can move objects around, change their properties and switch them to different kinds of objects. It allows you to edit map objects such as vehicles, weapons, machines, bipeds, scenery crates etc as well as mission details like trigger volumes, spawn locations and starting profiles. It also adds a DirectX based model/bsp viewer that has far better performance than Reclaimer's standard one. 3 | 4 | ## Compatibility 5 | Architect currently supports editing map files from Halo 3 and up on both Xbox 360 and MCC versions of the game. Beta versions of the games are not supported. 6 | Architect can open the following tag types: 7 | - **render_model, scenario_structure_bsp** 8 | - These will be opened in a model viewer similar to the one built in to Reclaimer, however the Architect one uses DirectX and is much more performant. 9 | - **model, vehicle, weapon, equipment, biped, scenery, crate, machine, control** 10 | - These tags will be opened in a model viewer that has the ability to preview model variants including any attachments the model has. These models cannot be extracted. 11 | - **scenario** 12 | - This tag will open the Architect scenario editor. This gives a 3d view of the entire map including all scenery and other objects. The editor is similar to the Sapien tool from Halo Custom Edition where you can view and edit details of what objects are on the map. 13 | - **IMPORTANT**: it is highly recommended to back up your map file before editing the scenario 14 | 15 | ## Controls 16 | - Click and hold `R mouse button` anywhere in the viewport to take control of the camera 17 | - While controlling the camera the use following controls to move around 18 | - Move the mouse to rotate the camera 19 | - Press and hold `WASD` for movement 20 | - Use `R` and `F` to move the camera up and down 21 | - While moving hold `Shift` to move faster and `Ctrl` to move slower 22 | - Use `mouse scroll` to change the camera speed 23 | - Viewport FOV can be changed in settings 24 | - Does not affect any viewports that are already open 25 | 26 | ## Installation 27 | Put the **Architect** folder into the **Plugins** folder of your Reclaimer installation. You can find this in Reclaimer by going to `View > Application Directory` from the menu. 28 | 29 | If the tag types mentioned above do not automatically open using Architect when you double-click them, you may need to set Architect as the default viewer by right-clicking on the tag, select `Open With` then select **Architect Viewer/Editor**. 30 | 31 | If you do not see **Architect Viewer/Editor** option when using the `Open With` menu, make sure you have the newest version of both Architect and Reclaimer itself. This may also mean Architect has been blocked by Windows. 32 | 33 | Use the following steps to unblock it: 34 | - Delete the **Architect** folder in **Plugins** 35 | - Right-click the downloaded Architect zip file, select Properties, then click `Unblock` 36 | - Re-extract the zip file and place the extracted folder into the **Plugins** folder 37 | - If the above doesn't work you may need to unblock each file individually after extracting them 38 | -------------------------------------------------------------------------------- /Reclaimer.Architect.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reclaimer.Architect", "Reclaimer.Architect\Reclaimer.Architect.csproj", "{D785CA27-13CA-4A16-8268-DBAB4330B123}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reclaimer", "..\Reclaimer\Reclaimer\Reclaimer.csproj", "{F05A3EB2-3336-47BC-8F5A-0DBB4E2EDABB}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adjutant", "..\Adjutant\Adjutant\Adjutant.csproj", "{A46DD713-D7F0-4D6A-BD0F-348D890E135A}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Drawing.Dds", "..\System.Drawing.Dds\System.Drawing.Dds\System.Drawing.Dds.csproj", "{A31A58A8-2BC6-43AF-AD81-692B2E37F01A}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Studio", "..\Studio\Studio\Studio.csproj", "{5F4C3814-8097-4869-B5E2-4C21EF45CEB2}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.IO.Endian", "..\System.IO.Endian\System.IO.Endian\System.IO.Endian.csproj", "{06A312C3-C3EC-447D-B7B7-F50E47A886D5}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {D785CA27-13CA-4A16-8268-DBAB4330B123}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {D785CA27-13CA-4A16-8268-DBAB4330B123}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {D785CA27-13CA-4A16-8268-DBAB4330B123}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {D785CA27-13CA-4A16-8268-DBAB4330B123}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {F05A3EB2-3336-47BC-8F5A-0DBB4E2EDABB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {F05A3EB2-3336-47BC-8F5A-0DBB4E2EDABB}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {F05A3EB2-3336-47BC-8F5A-0DBB4E2EDABB}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {F05A3EB2-3336-47BC-8F5A-0DBB4E2EDABB}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {A46DD713-D7F0-4D6A-BD0F-348D890E135A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {A46DD713-D7F0-4D6A-BD0F-348D890E135A}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {A46DD713-D7F0-4D6A-BD0F-348D890E135A}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {A46DD713-D7F0-4D6A-BD0F-348D890E135A}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {A31A58A8-2BC6-43AF-AD81-692B2E37F01A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {A31A58A8-2BC6-43AF-AD81-692B2E37F01A}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {A31A58A8-2BC6-43AF-AD81-692B2E37F01A}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {A31A58A8-2BC6-43AF-AD81-692B2E37F01A}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {5F4C3814-8097-4869-B5E2-4C21EF45CEB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {5F4C3814-8097-4869-B5E2-4C21EF45CEB2}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {5F4C3814-8097-4869-B5E2-4C21EF45CEB2}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {5F4C3814-8097-4869-B5E2-4C21EF45CEB2}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {06A312C3-C3EC-447D-B7B7-F50E47A886D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {06A312C3-C3EC-447D-B7B7-F50E47A886D5}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {06A312C3-C3EC-447D-B7B7-F50E47A886D5}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {06A312C3-C3EC-447D-B7B7-F50E47A886D5}.Release|Any CPU.Build.0 = Release|Any CPU 48 | EndGlobalSection 49 | GlobalSection(SolutionProperties) = preSolution 50 | HideSolutionNode = FALSE 51 | EndGlobalSection 52 | EndGlobal 53 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Blam/Common/PlacementType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Blam.Common 8 | { 9 | public enum PlacementTypeHalo3 : short 10 | { 11 | Null = -1, 12 | Biped = 0, 13 | Vehicle = 1, 14 | Weapon = 2, 15 | Equipment = 3, 16 | Terminal = 4, 17 | Projectile = 5, 18 | Scenery = 6, 19 | Machine = 7, 20 | Control = 8, 21 | SoundScenery = 9, 22 | Crate = 10, 23 | Creature = 11, 24 | Giant = 12, 25 | EffectScenery = 13, 26 | } 27 | 28 | public enum PlacementTypeHalo4 : short 29 | { 30 | Null = -1, 31 | Biped = 0, 32 | Vehicle = 1, 33 | Weapon = 2, 34 | Equipment = 3, 35 | Terminal = 4, 36 | Projectile = 5, 37 | Scenery = 6, 38 | Machine = 7, 39 | Control = 8, 40 | Dispenser = 9, 41 | SoundScenery = 10, 42 | Crate = 11, 43 | Creature = 12, 44 | Giant = 13, 45 | EffectScenery = 14, 46 | Spawner = 15 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Blam/Halo3/object.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Adjutant.Utilities; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO.Endian; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Reclaimer.Blam.Halo3 11 | { 12 | public class @object 13 | { 14 | [Offset(0)] 15 | public short ObjectType { get; set; } 16 | 17 | [Offset(48)] 18 | public StringId DefaultVariant { get; set; } 19 | 20 | [Offset(52)] 21 | public TagReference Model { get; set; } 22 | 23 | [Offset(68)] 24 | public TagReference CrateObject { get; set; } 25 | 26 | [Offset(84)] 27 | public TagReference CollisionDamage { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Blam/Halo3/scenario_structure_bsp.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Adjutant.Geometry; 3 | using Adjutant.Spatial; 4 | using Adjutant.Utilities; 5 | using Reclaimer.Models; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Globalization; 9 | using System.IO; 10 | using System.IO.Endian; 11 | using System.Linq; 12 | using System.Numerics; 13 | using System.Text; 14 | using System.Threading.Tasks; 15 | 16 | namespace Reclaimer.Blam.Halo3 17 | { 18 | public class scenario_structure_bsp 19 | { 20 | private readonly ICacheFile cache; 21 | private readonly IIndexItem item; 22 | 23 | public scenario_structure_bsp(ICacheFile cache, IIndexItem item) 24 | { 25 | this.cache = cache; 26 | this.item = item; 27 | } 28 | 29 | [Offset(432, MaxVersion = (int)CacheType.Halo3ODST)] 30 | [Offset(436, MinVersion = (int)CacheType.Halo3ODST)] 31 | public BlockCollection GeometryInstances { get; set; } 32 | 33 | } 34 | 35 | [FixedSize(120)] 36 | public class BspGeometryInstanceBlock : IBspGeometryInstanceBlock 37 | { 38 | [Offset(0)] 39 | public float TransformScale { get; set; } 40 | 41 | [Offset(4)] 42 | public Matrix4x4 Transform { get; set; } 43 | 44 | [Offset(52)] 45 | public short SectionIndex { get; set; } 46 | 47 | [Offset(64)] 48 | public RealVector3D BoundingSpherePosition { get; set; } 49 | 50 | [Offset(76)] 51 | public float BoundingSphereRadius { get; set; } 52 | 53 | [Offset(84)] 54 | public StringId Name { get; set; } 55 | 56 | public override string ToString() => Name; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Blam/Halo4/object.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Adjutant.Utilities; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO.Endian; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Reclaimer.Blam.Halo4 11 | { 12 | public class @object 13 | { 14 | [Offset(0)] 15 | public short ObjectType { get; set; } 16 | 17 | [Offset(132)] 18 | public StringId DefaultVariant { get; set; } 19 | 20 | [Offset(136)] 21 | public TagReference Model { get; set; } 22 | 23 | [Offset(152)] 24 | public TagReference CrateObject { get; set; } 25 | 26 | [Offset(168)] 27 | public TagReference CollisionDamage { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Blam/HaloReach/object.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Adjutant.Utilities; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO.Endian; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Reclaimer.Blam.HaloReach 11 | { 12 | public class @object 13 | { 14 | [Offset(0)] 15 | public short ObjectType { get; set; } 16 | 17 | [Offset(96)] 18 | public StringId DefaultVariant { get; set; } 19 | 20 | [Offset(100)] 21 | public TagReference Model { get; set; } 22 | 23 | [Offset(116)] 24 | public TagReference CrateObject { get; set; } 25 | 26 | [Offset(132)] 27 | public TagReference CollisionDamage { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Components/ComponentManager.cs: -------------------------------------------------------------------------------- 1 | using Reclaimer.Geometry; 2 | using Reclaimer.Models; 3 | using Reclaimer.Plugins.MetaViewer; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Collections.ObjectModel; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Data; 11 | using System.Xml; 12 | 13 | using Media = System.Windows.Media; 14 | using Media3D = System.Windows.Media.Media3D; 15 | using Helix = HelixToolkit.Wpf.SharpDX; 16 | 17 | namespace Reclaimer.Components 18 | { 19 | public abstract class ComponentManager 20 | { 21 | protected readonly ScenarioModel scenario; 22 | 23 | public ComponentManager(ScenarioModel scenario) 24 | { 25 | this.scenario = scenario; 26 | } 27 | 28 | protected int OffsetById(XmlNode node, string fieldId) 29 | { 30 | return node.SelectSingleNode($"*[@id='{fieldId}']")?.GetIntAttribute("offset") ?? -1; 31 | } 32 | 33 | //true if this component deal with this type of scenario node 34 | public virtual bool HandlesNodeType(NodeType nodeType) => false; 35 | 36 | //if it can be loaded asynchronously do it here 37 | public virtual Task InitializeResourcesAsync(ModelFactory factory) => Task.CompletedTask; 38 | 39 | //anything that must be synchronous such as building elements can be done here 40 | public virtual void InitializeElements(ModelFactory factory) { } 41 | 42 | //provide any elements that shoule be added to the scene 43 | public virtual IEnumerable GetSceneElements() => Enumerable.Empty(); 44 | 45 | //provide any nodes that should be added to the scene tree 46 | public virtual IEnumerable GetSceneNodes() => Enumerable.Empty(); 47 | 48 | //called for any type of node, even null node, when the selection changes 49 | public virtual void OnSelectedTreeNodeChanged(SceneNodeModel newNode) { } 50 | 51 | //called when everything has been added to the viewport and it is ready for interaction 52 | public virtual void OnViewportReady() { } 53 | 54 | //only called if HandlesNodeType, can return null if item is not geometry 55 | //use to select the returned element when user clicks on an item in the listbox 56 | public virtual Helix.Element3D GetElement(SceneNodeModel treeNode, int itemIndex) 57 | { 58 | return null; 59 | } 60 | 61 | //only called if HandlesNodeType, called when requested navigation to a particular object 62 | public virtual SharpDX.BoundingBox GetObjectBounds(SceneNodeModel treeNode, int itemIndex) 63 | { 64 | throw new NotImplementedException(); 65 | } 66 | 67 | //only called if HandlesNodeType, called when an element is clicked in the viewport 68 | public virtual int GetElementIndex(SceneNodeModel treeNode, Helix.Element3D element) 69 | { 70 | throw new NotImplementedException(); 71 | } 72 | 73 | //only called if HandlesNodeType, called to fill the listbox when a tree item is clicked 74 | public virtual IEnumerable GetListItems(SceneNodeModel treeNode) 75 | { 76 | yield break; 77 | } 78 | 79 | //only called if HandlesNodeType, called to populate properties list 80 | internal virtual BlockPropertiesLocator GetPropertiesLocator(SceneNodeModel treeNode, int itemIndex) 81 | { 82 | throw new NotImplementedException(); 83 | } 84 | 85 | //true if the add/remove buttons should be enabled for this node type 86 | public virtual bool SupportsObjectOperation(ObjectOperation operation, NodeType nodeType) => false; 87 | 88 | //only called if SupportsObjectOperation 89 | public virtual bool ExecuteObjectOperation(SceneNodeModel treeNode, ObjectOperation operation, int itemIndex) 90 | { 91 | throw new NotImplementedException(); 92 | } 93 | 94 | public virtual void DisposeSceneElements() { } 95 | } 96 | 97 | public enum ObjectOperation 98 | { 99 | Add, 100 | Remove, 101 | Copy 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Components/MissionComponentManager.cs: -------------------------------------------------------------------------------- 1 | using Reclaimer.Models; 2 | using Reclaimer.Resources; 3 | using Reclaimer.Utilities; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Xml; 11 | 12 | namespace Reclaimer.Components 13 | { 14 | public class MissionComponentManager : ComponentManager 15 | { 16 | private IEnumerable HandledNodeTypes 17 | { 18 | get 19 | { 20 | yield return NodeType.Mission; 21 | yield return NodeType.StartProfiles; 22 | } 23 | } 24 | 25 | public MissionComponentManager(ScenarioModel scenario) 26 | : base(scenario) 27 | { } 28 | 29 | public override bool HandlesNodeType(NodeType nodeType) => HandledNodeTypes.Any(t => t == nodeType); 30 | 31 | public override bool SupportsObjectOperation(ObjectOperation operation, NodeType nodeType) => nodeType == NodeType.StartProfiles; 32 | 33 | public override IEnumerable GetListItems(SceneNodeModel treeNode) 34 | { 35 | if (treeNode.NodeType == NodeType.StartProfiles) 36 | { 37 | var items = new List(); 38 | 39 | var section = scenario.Sections[Section.StartProfiles]; 40 | var nameOffset = OffsetById(section.Node, FieldId.Name); 41 | 42 | using (var reader = scenario.CreateReader()) 43 | { 44 | for (int i = 0; i < section.TagBlock.Count; i++) 45 | { 46 | var baseAddress = section.TagBlock.Pointer.Address + section.BlockSize * i; 47 | reader.Seek(baseAddress + nameOffset, SeekOrigin.Begin); 48 | 49 | var name = reader.ReadNullTerminatedString(); 50 | if (string.IsNullOrEmpty(name)) 51 | name = ""; 52 | 53 | items.Add(new ScenarioListItem(name)); 54 | } 55 | } 56 | 57 | return items; 58 | } 59 | 60 | return Enumerable.Empty(); 61 | } 62 | 63 | internal override BlockPropertiesLocator GetPropertiesLocator(SceneNodeModel treeNode, int itemIndex) 64 | { 65 | XmlNode rootNode; 66 | long baseAddress; 67 | IMetaUpdateReceiver target; 68 | 69 | if (treeNode.NodeType == NodeType.Mission) 70 | { 71 | rootNode = scenario.Sections[Section.Mission].Node; 72 | baseAddress = scenario.RootAddress; 73 | target = null; 74 | } 75 | else if (treeNode.NodeType == NodeType.StartProfiles && itemIndex >= 0) 76 | { 77 | var section = scenario.Sections[Section.StartProfiles]; 78 | 79 | rootNode = section.Node; 80 | baseAddress = section.TagBlock.Pointer.Address 81 | + itemIndex * section.BlockSize; 82 | 83 | target = scenario.Items[itemIndex]; 84 | } 85 | else return null; 86 | 87 | return new BlockPropertiesLocator 88 | { 89 | RootNode = rootNode, 90 | BaseAddress = baseAddress, 91 | TargetObject = target 92 | }; 93 | } 94 | 95 | public override bool ExecuteObjectOperation(SceneNodeModel treeNode, ObjectOperation operation, int itemIndex) 96 | { 97 | var blockRef = scenario.Sections[Section.StartProfiles]; 98 | var blockEditor = scenario.MetadataStream.GetBlockEditor(blockRef.TagBlock.Pointer.Address); 99 | 100 | switch (operation) 101 | { 102 | case ObjectOperation.Add: 103 | blockEditor.Add(); 104 | break; 105 | 106 | case ObjectOperation.Remove: 107 | if (itemIndex < 0 || itemIndex >= blockRef.TagBlock.Count) 108 | return false; 109 | 110 | blockEditor.Remove(itemIndex); 111 | break; 112 | 113 | case ObjectOperation.Copy: 114 | if (itemIndex < 0 || itemIndex >= blockRef.TagBlock.Count) 115 | return false; 116 | 117 | blockEditor.Copy(itemIndex, itemIndex + 1); 118 | break; 119 | } 120 | 121 | blockEditor.UpdateBlockReference(blockRef); 122 | return true; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Components/StartPositionComponentManager.cs: -------------------------------------------------------------------------------- 1 | using Reclaimer.Controls.Markers; 2 | using Reclaimer.Geometry; 3 | using Reclaimer.Models; 4 | using Reclaimer.Resources; 5 | using Reclaimer.Utilities; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Collections.ObjectModel; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows.Data; 13 | 14 | using Helix = HelixToolkit.Wpf.SharpDX; 15 | 16 | namespace Reclaimer.Components 17 | { 18 | public class StartPositionComponentManager : ComponentManager 19 | { 20 | private readonly ObservableCollection StartPositions; 21 | 22 | private Helix.GroupElement3D StartPositionGroup; 23 | 24 | public StartPositionComponentManager(ScenarioModel scenario) 25 | : base(scenario) 26 | { 27 | StartPositions = new ObservableCollection(); 28 | } 29 | 30 | public override bool HandlesNodeType(NodeType nodeType) => nodeType == NodeType.StartPositions; 31 | 32 | public override bool SupportsObjectOperation(ObjectOperation operation, NodeType nodeType) => nodeType == NodeType.StartPositions; 33 | 34 | public override void InitializeElements(ModelFactory factory) 35 | { 36 | StartPositionGroup = new Helix.GroupModel3D(); 37 | foreach (var pos in scenario.StartingPositions) 38 | AddStartPositionElement(pos); 39 | } 40 | 41 | public override IEnumerable GetSceneElements() 42 | { 43 | if (StartPositionGroup.Children.Any()) 44 | yield return StartPositionGroup; 45 | } 46 | 47 | public override void OnSelectedTreeNodeChanged(SceneNodeModel newNode) 48 | { 49 | StartPositionGroup.IsRendering = newNode?.NodeType == NodeType.StartPositions; 50 | } 51 | 52 | public override Helix.Element3D GetElement(SceneNodeModel treeNode, int itemIndex) 53 | { 54 | return StartPositions[itemIndex]; 55 | } 56 | 57 | public override int GetElementIndex(SceneNodeModel treeNode, Helix.Element3D element) 58 | { 59 | return scenario.StartingPositions.IndexOf(element.DataContext as StartPosition); 60 | } 61 | 62 | public override SharpDX.BoundingBox GetObjectBounds(SceneNodeModel treeNode, int itemIndex) 63 | { 64 | return GetElement(treeNode, itemIndex).GetTotalBounds(); 65 | } 66 | 67 | public override IEnumerable GetListItems(SceneNodeModel treeNode) 68 | { 69 | return scenario.StartingPositions.Select(pos => new ScenarioListItem(pos)); 70 | } 71 | 72 | internal override BlockPropertiesLocator GetPropertiesLocator(SceneNodeModel treeNode, int itemIndex) 73 | { 74 | if (itemIndex < 0) 75 | return null; 76 | 77 | var section = scenario.Sections[Section.StartPositions]; 78 | return new BlockPropertiesLocator 79 | { 80 | RootNode = section.Node, 81 | BaseAddress = section.TagBlock.Pointer.Address 82 | + itemIndex * section.BlockSize, 83 | TargetObject = scenario.Items[itemIndex] 84 | }; 85 | } 86 | 87 | public override bool ExecuteObjectOperation(SceneNodeModel treeNode, ObjectOperation operation, int itemIndex) 88 | { 89 | var blockRef = scenario.Sections[Section.StartPositions]; 90 | var blockEditor = scenario.MetadataStream.GetBlockEditor(blockRef.TagBlock.Pointer.Address); 91 | 92 | switch (operation) 93 | { 94 | case ObjectOperation.Add: 95 | blockEditor.Add(); 96 | var pos = new StartPosition(scenario); 97 | scenario.StartingPositions.Add(pos); 98 | AddStartPositionElement(pos); 99 | break; 100 | 101 | case ObjectOperation.Remove: 102 | if (itemIndex < 0 || itemIndex >= blockRef.TagBlock.Count) 103 | return false; 104 | 105 | blockEditor.Remove(itemIndex); 106 | scenario.StartingPositions.RemoveAt(itemIndex); 107 | RemoveStartPositionElement(itemIndex); 108 | break; 109 | 110 | case ObjectOperation.Copy: 111 | if (itemIndex < 0 || itemIndex >= blockRef.TagBlock.Count) 112 | return false; 113 | 114 | var destIndex = itemIndex + 1; 115 | blockEditor.Copy(itemIndex, destIndex); 116 | pos = new StartPosition(scenario); 117 | scenario.StartingPositions.Insert(destIndex, pos); 118 | InsertStartPositionElement(pos, destIndex); 119 | pos.CopyFrom(scenario.StartingPositions[itemIndex]); 120 | break; 121 | } 122 | 123 | blockEditor.UpdateBlockReference(blockRef); 124 | return true; 125 | } 126 | 127 | private void AddStartPositionElement(StartPosition pos) => InsertStartPositionElement(pos, StartPositions.Count); 128 | 129 | private void InsertStartPositionElement(StartPosition pos, int index) 130 | { 131 | var element = new StartPositionMarker3D(); 132 | BindStartPosition(pos, element); 133 | StartPositions.Insert(index, element); 134 | StartPositionGroup.Children.Add(element); 135 | } 136 | 137 | private void RemoveStartPositionElement(int index) 138 | { 139 | var element = StartPositions[index]; 140 | StartPositions.Remove(element); 141 | StartPositionGroup.Children.Remove(element); 142 | element.Dispose(); 143 | } 144 | 145 | private void BindStartPosition(StartPosition pos, Helix.Element3D model) 146 | { 147 | var binding = new MultiBinding { Converter = EulerTransformConverter.Instance, Mode = BindingMode.TwoWay }; 148 | binding.Bindings.Add(new Binding(nameof(StartPosition.Position)) { Mode = BindingMode.TwoWay }); 149 | binding.Bindings.Add(new Binding(nameof(StartPosition.Orientation)) { Mode = BindingMode.TwoWay }); 150 | 151 | model.DataContext = pos; 152 | BindingOperations.SetBinding(model, Helix.Element3D.TransformProperty, binding); 153 | } 154 | 155 | public override void DisposeSceneElements() 156 | { 157 | StartPositionGroup?.Dispose(); 158 | StartPositions.Clear(); 159 | StartPositionGroup = null; 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Components/TerrainComponentManager.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Reclaimer.Geometry; 3 | using Reclaimer.Models; 4 | using Reclaimer.Utilities; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | using Helix = HelixToolkit.Wpf.SharpDX; 12 | 13 | namespace Reclaimer.Components 14 | { 15 | public class TerrainComponentManager : ComponentManager 16 | { 17 | private const string NullTagName = ""; 18 | 19 | private readonly ObjectHolder BspHolder; 20 | private readonly ObjectHolder SkyHolder; 21 | 22 | public IEnumerable BspElements => BspHolder.Elements.WhereNotNull(); 23 | 24 | public TerrainComponentManager(ScenarioModel scenario) 25 | : base(scenario) 26 | { 27 | BspHolder = new ObjectHolder("sbsp"); 28 | SkyHolder = new ObjectHolder("sky"); 29 | } 30 | 31 | public override Task InitializeResourcesAsync(ModelFactory factory) 32 | { 33 | var tasks = GetResourceInitializers(factory).ToList(); 34 | return Task.WhenAll(tasks); 35 | } 36 | 37 | private IEnumerable GetResourceInitializers(ModelFactory factory) 38 | { 39 | yield return Task.Run(() => 40 | { 41 | for (int i = 0; i < scenario.Bsps.Count; i++) 42 | factory.LoadTag(scenario.Bsps[i].Tag, false); 43 | }); 44 | 45 | yield return Task.Run(() => 46 | { 47 | for (int i = 0; i < scenario.Skies.Count; i++) 48 | factory.LoadTag(scenario.Skies[i].Tag, false); 49 | }); 50 | } 51 | 52 | public override void InitializeElements(ModelFactory factory) 53 | { 54 | foreach (var bsp in scenario.Bsps) 55 | BspHolder.Elements.Add(bsp.Tag == null ? null : factory.CreateRenderModel(bsp.Tag.Id)); 56 | 57 | foreach (var sky in scenario.Skies) 58 | SkyHolder.Elements.Add(sky.Tag == null ? null : factory.CreateObjectModel(sky.Tag.Id)); 59 | 60 | foreach (var element in GetSceneElements()) 61 | element.IsHitTestVisible = false; 62 | } 63 | 64 | public override IEnumerable GetSceneElements() 65 | { 66 | return BspHolder.Elements.Concat(SkyHolder.Elements).WhereNotNull(); 67 | } 68 | 69 | public override IEnumerable GetSceneNodes() 70 | { 71 | var bspNode = new TreeItemModel { Header = BspHolder.Name, IsChecked = true }; 72 | for (int i = 0; i < BspHolder.Elements.Count; i++) 73 | { 74 | var bsp = BspHolder.Elements[i]; 75 | if (bsp == null) 76 | continue; 77 | 78 | var tag = scenario.Bsps[i].Tag; 79 | var permNode = new TreeItemModel { Header = tag?.FileName() ?? NullTagName, IsChecked = true, Tag = bsp }; 80 | bspNode.Items.Add(permNode); 81 | } 82 | 83 | var skyNode = new TreeItemModel { Header = SkyHolder.Name, IsChecked = true }; 84 | for (int i = 0; i < SkyHolder.Elements.Count; i++) 85 | { 86 | var sky = SkyHolder.Elements[i]; 87 | if (sky == null) 88 | continue; 89 | 90 | var tag = scenario.Skies[i].Tag; 91 | var permNode = new TreeItemModel { Header = tag?.FileName() ?? NullTagName, IsChecked = true, Tag = sky }; 92 | skyNode.Items.Add(permNode); 93 | } 94 | 95 | if (bspNode.HasItems) 96 | yield return bspNode; 97 | 98 | if (skyNode.HasItems) 99 | yield return skyNode; 100 | } 101 | 102 | public override void DisposeSceneElements() 103 | { 104 | BspHolder.Dispose(); 105 | SkyHolder.Dispose(); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/ElementHighlighter3D.cs: -------------------------------------------------------------------------------- 1 | using HelixToolkit.Wpf.SharpDX; 2 | using HelixToolkit.Wpf.SharpDX.Model.Scene; 3 | using SharpDX; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows; 10 | using Reclaimer.Utilities; 11 | 12 | using Media3D = System.Windows.Media.Media3D; 13 | using System.Windows.Data; 14 | 15 | namespace Reclaimer.Controls 16 | { 17 | public class ElementHighlighter3D : GroupElement3D 18 | { 19 | #region Dependency Properties 20 | 21 | public static readonly DependencyProperty TargetProperty = 22 | DependencyProperty.Register(nameof(Target), typeof(Element3D), typeof(ElementHighlighter3D), new PropertyMetadata(null, (d, e) => 23 | { 24 | (d as ElementHighlighter3D).OnTargetChanged(e.NewValue as Element3D); 25 | })); 26 | 27 | public static readonly DependencyProperty HighlightColorProperty = 28 | DependencyProperty.Register(nameof(HighlightColor), typeof(Color), typeof(ElementHighlighter3D), new PropertyMetadata(Color.Yellow, (d, e) => 29 | { 30 | (d as ElementHighlighter3D).material.DiffuseColor = (Color)e.NewValue; 31 | })); 32 | 33 | public static readonly DependencyProperty EnableXRayGridProperty = 34 | DependencyProperty.Register(nameof(EnableXRayGrid), typeof(bool), typeof(ElementHighlighter3D), new PropertyMetadata(true, (d, e) => 35 | { 36 | (d as ElementHighlighter3D).xrayEffect.IsRendering = (bool)e.NewValue; 37 | })); 38 | 39 | public Element3D Target 40 | { 41 | get { return (Element3D)GetValue(TargetProperty); } 42 | set { SetValue(TargetProperty, value); } 43 | } 44 | 45 | public Color HighlightColor 46 | { 47 | get { return (Color)GetValue(HighlightColorProperty); } 48 | set { SetValue(HighlightColorProperty, value); } 49 | } 50 | 51 | public bool EnableXRayGrid 52 | { 53 | get { return (bool)GetValue(EnableXRayGridProperty); } 54 | set { SetValue(EnableXRayGridProperty, value); } 55 | } 56 | 57 | #endregion 58 | 59 | private readonly GroupModel3D meshGroup; 60 | private readonly DiffuseMaterial material; 61 | private readonly Element3D xrayEffect; 62 | 63 | public ElementHighlighter3D() 64 | { 65 | meshGroup = new GroupModel3D(); 66 | material = new DiffuseMaterial { DiffuseColor = HighlightColor }; 67 | xrayEffect = new PostEffectMeshXRayGrid() 68 | { 69 | EffectName = "HighlighterXRayGrid", 70 | DimmingFactor = 0.5, 71 | BlendingFactor = 0.8, 72 | GridDensity = 4, 73 | GridColor = System.Windows.Media.Colors.Gray 74 | }; 75 | (xrayEffect.SceneNode as NodePostEffectXRayGrid).XRayDrawingPassName = DefaultPassNames.EffectMeshDiffuseXRayGridP3; 76 | 77 | Children.Add(meshGroup); 78 | Children.Add(xrayEffect); 79 | 80 | var binding = new Binding("Target.Transform") { RelativeSource = RelativeSource.Self }; 81 | BindingOperations.SetBinding(this, TransformProperty, binding); 82 | } 83 | 84 | private void OnTargetChanged(Element3D target) 85 | { 86 | foreach (var element in meshGroup.Children) 87 | element.Dispose(); 88 | 89 | meshGroup.Children.Clear(); 90 | //(Parent as GroupElement3D)?.Children.Remove(this); 91 | 92 | if (target == null) 93 | return; 94 | 95 | var allMeshes = ((target as GroupElement3D)?.EnumerateDescendents() ?? Enumerable.Repeat(target, 1)).OfType().ToList(); 96 | if (!allMeshes.Any()) 97 | return; 98 | 99 | foreach (var m in allMeshes) 100 | { 101 | var transform = m.Transform.Value; 102 | var ancestors = m.EnumerateAncestors() 103 | .TakeWhile(e => e != target) 104 | .ToList(); 105 | 106 | if (ancestors.Count > 0) 107 | { 108 | transform *= ancestors 109 | .Select(e => e.Transform.Value) 110 | .Aggregate((a, b) => a * b); 111 | } 112 | 113 | if (HighlightColor.A == byte.MinValue) 114 | return; 115 | 116 | meshGroup.Children.Add(new MeshGeometryModel3D 117 | { 118 | CullMode = SharpDX.Direct3D11.CullMode.Back, 119 | DepthBias = -100, 120 | Geometry = m.Geometry, 121 | Material = material, 122 | IsHitTestVisible = false, 123 | Transform = new Media3D.MatrixTransform3D(transform), 124 | PostEffects = "HighlighterXRayGrid" 125 | }); 126 | } 127 | 128 | //(target as GroupElement3D)?.Children.Add(this); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/HierarchyView.xaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 36 | 37 | 42 | 47 | 52 | 53 | 57 | 58 | 61 | 62 | 63 | 64 | 66 | 67 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 114 | 115 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/HierarchyView.xaml.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Reclaimer.Components; 3 | using Reclaimer.Models; 4 | using Reclaimer.Plugins; 5 | using Reclaimer.Resources; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows; 12 | using System.Windows.Controls; 13 | using System.Windows.Data; 14 | using System.Windows.Documents; 15 | using System.Windows.Input; 16 | using System.Windows.Media; 17 | using System.Windows.Media.Imaging; 18 | using System.Windows.Navigation; 19 | using System.Windows.Shapes; 20 | 21 | namespace Reclaimer.Controls 22 | { 23 | /// 24 | /// Interaction logic for HierarchyView.xaml 25 | /// 26 | public partial class HierarchyView : IScenarioHierarchyView 27 | { 28 | public TabModel TabModel { get; } 29 | 30 | private ScenarioModel scenario; 31 | 32 | public HierarchyView() 33 | { 34 | InitializeComponent(); 35 | TabModel = new TabModel(this, Studio.Controls.TabItemType.Tool) { Header = "Hierarchy", ToolTip = "Hierarchy View" }; 36 | } 37 | 38 | public void ClearScenario() 39 | { 40 | DataContext = scenario = null; 41 | } 42 | 43 | public void SetScenario(ScenarioModel scenario) 44 | { 45 | DataContext = this.scenario = scenario; 46 | } 47 | 48 | public void ShowCurrentSelection() 49 | { 50 | //dont use indexing in case the list is empty 51 | if (scenario.SelectedItemIndex < 0) 52 | list.ScrollIntoView(scenario.Items.FirstOrDefault()); 53 | else list.ScrollIntoView(scenario.Items.Skip(scenario.SelectedItemIndex).FirstOrDefault()); 54 | } 55 | 56 | private void tv_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) 57 | { 58 | scenario.SelectedNode = tv.SelectedItem as SceneNodeModel; 59 | var nodeType = scenario.SelectedNodeType; 60 | var handler = scenario.GetNodeTypeHandler(nodeType); 61 | btnAddItem.IsEnabled = handler?.SupportsObjectOperation(ObjectOperation.Add, nodeType) ?? false; 62 | btnDeleteItem.IsEnabled = handler?.SupportsObjectOperation(ObjectOperation.Remove, nodeType) ?? false; 63 | btnCopyItem.IsEnabled = handler?.SupportsObjectOperation(ObjectOperation.Copy, nodeType) ?? false; 64 | } 65 | 66 | private void ListItemMouseDoubleClick(object sender, MouseButtonEventArgs e) 67 | { 68 | scenario.RenderView?.NavigateToObject(scenario.SelectedNode, scenario.SelectedItemIndex); 69 | } 70 | 71 | private void RecursiveToggle(IEnumerable collection, bool value) 72 | { 73 | foreach (var item in collection) 74 | { 75 | item.IsExpanded = value; 76 | RecursiveToggle(item.Items, value); 77 | } 78 | } 79 | 80 | #region Toolbar Events 81 | 82 | private void btnCollapseAll_Click(object sender, RoutedEventArgs e) 83 | { 84 | RecursiveToggle(scenario.Hierarchy, false); 85 | } 86 | 87 | private void btnExpandAll_Click(object sender, RoutedEventArgs e) 88 | { 89 | RecursiveToggle(scenario.Hierarchy, true); 90 | } 91 | 92 | private void btnAddItem_Click(object sender, RoutedEventArgs e) 93 | { 94 | var treeNode = scenario.SelectedNode; 95 | var handler = scenario.GetNodeTypeHandler(treeNode.NodeType); 96 | if (handler.ExecuteObjectOperation(treeNode, ObjectOperation.Add, scenario.SelectedItemIndex)) 97 | { 98 | scenario.RefreshItemList(); 99 | list.ScrollIntoView(scenario.Items.LastOrDefault()); 100 | } 101 | } 102 | 103 | private void btnDeleteItem_Click(object sender, RoutedEventArgs e) 104 | { 105 | var treeNode = scenario.SelectedNode; 106 | var handler = scenario.GetNodeTypeHandler(treeNode.NodeType); 107 | if (handler.ExecuteObjectOperation(treeNode, ObjectOperation.Remove, scenario.SelectedItemIndex)) 108 | scenario.RefreshItemList(); 109 | } 110 | 111 | private void btnCopyItem_Click(object sender, RoutedEventArgs e) 112 | { 113 | var treeNode = scenario.SelectedNode; 114 | var handler = scenario.GetNodeTypeHandler(treeNode.NodeType); 115 | if (handler.ExecuteObjectOperation(treeNode, ObjectOperation.Copy, scenario.SelectedItemIndex)) 116 | scenario.RefreshItemList(); 117 | } 118 | 119 | private void btnEditPalette_Click(object sender, RoutedEventArgs e) 120 | { 121 | var paletteKey = PaletteType.FromNodeType(scenario.SelectedNodeType); 122 | if (paletteKey != null) 123 | new PaletteEditorWindow(scenario, paletteKey) { Owner = Window.GetWindow(this) }.ShowDialog(); 124 | } 125 | 126 | private void btnSave_Click(object sender, RoutedEventArgs e) 127 | { 128 | try 129 | { 130 | scenario.MetadataStream.Commit(); 131 | } 132 | catch (Exception ex) 133 | { 134 | scenario.LogError($"Unable to save changes to {scenario.ScenarioTag.FileName()}", ex, true); 135 | Substrate.ShowErrorMessage(ex.Message); 136 | } 137 | } 138 | 139 | #endregion 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/IManipulatable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Controls 8 | { 9 | public interface IManipulatable 10 | { 11 | ManipulationFlags ManipulationFlags { get; } 12 | float ScaleMultiplier { get; } 13 | bool UseLocalOrigin { get; } 14 | bool UniformScaling { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/IMeshNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Controls 8 | { 9 | public interface IMeshNode 10 | { 11 | string Name { get; } 12 | bool IsVisible { get; set; } 13 | SharpDX.BoundingBox GetNodeBounds(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/IMetaViewerHost.cs: -------------------------------------------------------------------------------- 1 | using Reclaimer.Plugins.MetaViewer; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Reclaimer.Controls 10 | { 11 | public interface IMetaViewerHost 12 | { 13 | bool ShowInvisibles { get; } 14 | ObservableCollection Metadata { get; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/IRendererHost.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using Helix = HelixToolkit.Wpf.SharpDX; 8 | 9 | namespace Reclaimer.Controls 10 | { 11 | public interface IRendererHost 12 | { 13 | void OnElementSelected(Helix.Element3D element); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/InstancePropertyView.xaml.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Geometry; 2 | using Adjutant.Spatial; 3 | using Reclaimer.Models; 4 | using Reclaimer.Plugins.MetaViewer; 5 | using Reclaimer.Plugins.MetaViewer.Halo3; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Collections.ObjectModel; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows; 13 | using System.Windows.Controls; 14 | using System.Windows.Data; 15 | using System.Windows.Documents; 16 | using System.Windows.Input; 17 | using System.Windows.Media; 18 | using System.Windows.Media.Imaging; 19 | using System.Windows.Navigation; 20 | using System.Windows.Shapes; 21 | using System.Xml; 22 | 23 | namespace Reclaimer.Controls 24 | { 25 | /// 26 | /// Interaction logic for InstancePropertyView.xaml 27 | /// 28 | public partial class InstancePropertyView : IStructureBspPropertyView 29 | { 30 | public static readonly DependencyProperty CurrentItemProperty = 31 | DependencyProperty.Register(nameof(CurrentItem), typeof(InstancePlacement), typeof(InstancePropertyView), new PropertyMetadata(null, (d, e) => 32 | { 33 | (d as FrameworkElement).Visibility = e.NewValue == null ? Visibility.Hidden : Visibility.Visible; 34 | })); 35 | 36 | public InstancePlacement CurrentItem 37 | { 38 | get { return (InstancePlacement)GetValue(CurrentItemProperty); } 39 | set { SetValue(CurrentItemProperty, value); } 40 | } 41 | 42 | private StructureBspModel bspModel; 43 | 44 | public TabModel TabModel { get; } 45 | 46 | public InstancePropertyView() 47 | { 48 | InitializeComponent(); 49 | TabModel = new TabModel(this, Studio.Controls.TabItemType.Tool) { Header = "Properties", ToolTip = "Property View" }; 50 | } 51 | 52 | public void ClearScenario() 53 | { 54 | CurrentItem = null; 55 | Visibility = Visibility.Hidden; 56 | bspModel = null; 57 | } 58 | 59 | public void SetScenario(StructureBspModel bspModel) 60 | { 61 | CurrentItem = null; 62 | Visibility = Visibility.Hidden; 63 | this.bspModel = bspModel; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/ManipulationFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Controls 8 | { 9 | [Flags] 10 | public enum ManipulationFlags 11 | { 12 | None = 0, 13 | 14 | TranslateX = 1, 15 | TranslateY = 2, 16 | TranslateZ = 4, 17 | RotateX = 8, 18 | RotateY = 16, 19 | RotateZ = 32, 20 | ScaleX = 64, 21 | ScaleY = 128, 22 | ScaleZ = 256, 23 | 24 | TranslateXY = TranslateX | TranslateY, 25 | TranslateXZ = TranslateX | TranslateZ, 26 | TranslateYZ = TranslateY | TranslateZ, 27 | Translate = TranslateX | TranslateY | TranslateZ, 28 | 29 | RotateXY = RotateX | RotateY, 30 | RotateXZ = RotateX | RotateZ, 31 | RotateYZ = RotateY | RotateZ, 32 | Rotate = RotateX | RotateY | RotateZ, 33 | 34 | ScaleXY = ScaleX | ScaleY, 35 | ScaleXZ = ScaleX | ScaleZ, 36 | ScaleYZ = ScaleY | ScaleZ, 37 | Scale = ScaleX | ScaleY | ScaleZ, 38 | 39 | Default = Translate | Rotate | Scale 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/Markers/DecalMarker3D.cs: -------------------------------------------------------------------------------- 1 | using HelixToolkit.Wpf.SharpDX; 2 | using SharpDX; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System; 6 | using System.Diagnostics; 7 | 8 | using System.Windows; 9 | using Media3D = System.Windows.Media.Media3D; 10 | using Media = System.Windows.Media; 11 | 12 | namespace Reclaimer.Controls.Markers 13 | { 14 | public sealed class DecalMarker3D : MarkerGeometry3D 15 | { 16 | private static readonly Material MarkerMaterial = DiffuseMaterials.LightBlue; 17 | private static readonly Geometry3D MarkerGeometry; 18 | 19 | static DecalMarker3D() 20 | { 21 | var builder = new MeshBuilder(); 22 | 23 | var baseHeight = 0.05f; 24 | var halfHeight = baseHeight / 2f; 25 | var sideLength = 0.4f; 26 | 27 | builder.AddBox(Vector3.UnitZ * halfHeight, Vector3.UnitX, Vector3.UnitY, sideLength, sideLength, baseHeight); 28 | builder.AddCylinder(Vector3.Zero, new Vector3(0, 0, sideLength * 0.6f), halfHeight / 2f, 18, true, true); 29 | builder.AddArrow(new Vector3(0, 0, halfHeight), new Vector3(sideLength * 0.9f, 0, halfHeight), halfHeight, 3, 18); 30 | 31 | MarkerGeometry = builder.ToMesh(); 32 | } 33 | 34 | protected override MeshGeometryModel3D GetMeshGeometry() 35 | { 36 | return new MeshGeometryModel3D 37 | { 38 | Geometry = MarkerGeometry, 39 | Material = MarkerMaterial 40 | }; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/Markers/LightMarker3D.cs: -------------------------------------------------------------------------------- 1 | using HelixToolkit.Wpf.SharpDX; 2 | using SharpDX; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System; 6 | using System.Diagnostics; 7 | 8 | using System.Windows; 9 | using Media3D = System.Windows.Media.Media3D; 10 | using Media = System.Windows.Media; 11 | 12 | namespace Reclaimer.Controls.Markers 13 | { 14 | public sealed class LightMarker3D : MarkerGeometry3D 15 | { 16 | private static readonly Material MarkerMaterial = DiffuseMaterials.LightBlue; 17 | private static readonly Geometry3D MarkerGeometry; 18 | 19 | static LightMarker3D() 20 | { 21 | var builder = new MeshBuilder(); 22 | 23 | var baseHeight = 0.075f; 24 | var halfHeight = baseHeight / 2f; 25 | var baseRadius = 0.2f; 26 | 27 | builder.AddCylinder(Vector3.Zero, new Vector3(0, 0, baseHeight), baseRadius, 18, true, true); 28 | builder.AddArrow(new Vector3(0, 0, halfHeight), new Vector3(baseRadius * 1.6f, 0, halfHeight), halfHeight, 3, 18); 29 | 30 | MarkerGeometry = builder.ToMesh(); 31 | } 32 | 33 | protected override MeshGeometryModel3D GetMeshGeometry() 34 | { 35 | return new MeshGeometryModel3D 36 | { 37 | Geometry = MarkerGeometry, 38 | Material = MarkerMaterial 39 | }; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/Markers/MarkerGeometry3D.cs: -------------------------------------------------------------------------------- 1 | using HelixToolkit.Wpf.SharpDX; 2 | using Reclaimer.Utilities; 3 | using SharpDX; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System; 7 | using System.Diagnostics; 8 | 9 | using System.Windows; 10 | using Media3D = System.Windows.Media.Media3D; 11 | using Media = System.Windows.Media; 12 | 13 | namespace Reclaimer.Controls.Markers 14 | { 15 | public abstract class MarkerGeometry3D : GroupElement3D, IMeshNode, IManipulatable 16 | { 17 | public MarkerGeometry3D() 18 | { 19 | Children.Add(GetMeshGeometry()); 20 | } 21 | 22 | public virtual ManipulationFlags ManipulationFlags => ManipulationFlags.Default; 23 | 24 | public virtual float ScaleMultiplier => 1f; 25 | 26 | public virtual bool UseLocalOrigin => true; 27 | 28 | public virtual bool UniformScaling => true; 29 | 30 | protected abstract MeshGeometryModel3D GetMeshGeometry(); 31 | 32 | #region IMeshNode 33 | 34 | string IMeshNode.Name => GetType().Name; 35 | 36 | bool IMeshNode.IsVisible 37 | { 38 | get { return IsRendering; } 39 | set { IsRendering = value; } 40 | } 41 | 42 | BoundingBox IMeshNode.GetNodeBounds() 43 | { 44 | return this.GetTotalBounds(); 45 | } 46 | 47 | #endregion 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/Markers/PositionMarker3D.cs: -------------------------------------------------------------------------------- 1 | using HelixToolkit.Wpf.SharpDX; 2 | using SharpDX; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System; 6 | using System.Diagnostics; 7 | 8 | using System.Windows; 9 | using Media3D = System.Windows.Media.Media3D; 10 | using Media = System.Windows.Media; 11 | 12 | namespace Reclaimer.Controls.Markers 13 | { 14 | public sealed class PositionMarker3D : MarkerGeometry3D 15 | { 16 | private static readonly Material MarkerMaterial = DiffuseMaterials.Red; 17 | private static readonly Geometry3D MarkerGeometry; 18 | 19 | static PositionMarker3D() 20 | { 21 | var builder = new MeshBuilder(); 22 | 23 | var baseHeight = 0.075f; 24 | var halfHeight = baseHeight / 2; 25 | var sideLength = 0.75f; 26 | 27 | builder.AddBox(Vector3.UnitZ * halfHeight, Vector3.UnitX, Vector3.UnitY, sideLength, sideLength, baseHeight); 28 | 29 | MarkerGeometry = builder.ToMesh(); 30 | } 31 | 32 | public override ManipulationFlags ManipulationFlags => ManipulationFlags.Translate; 33 | 34 | public override float ScaleMultiplier => 1.5f; 35 | 36 | protected override MeshGeometryModel3D GetMeshGeometry() 37 | { 38 | return new MeshGeometryModel3D 39 | { 40 | Geometry = MarkerGeometry, 41 | Material = MarkerMaterial 42 | }; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/Markers/SpawnPointMarker3D.cs: -------------------------------------------------------------------------------- 1 | using HelixToolkit.Wpf.SharpDX; 2 | using SharpDX; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System; 6 | using System.Diagnostics; 7 | 8 | using System.Windows; 9 | using Media3D = System.Windows.Media.Media3D; 10 | using Media = System.Windows.Media; 11 | 12 | namespace Reclaimer.Controls.Markers 13 | { 14 | public sealed class SpawnPointMarker3D : MarkerGeometry3D 15 | { 16 | private static readonly Material MarkerMaterial = DiffuseMaterials.LightBlue; 17 | private static readonly Geometry3D MarkerGeometry; 18 | 19 | static SpawnPointMarker3D() 20 | { 21 | var builder = new MeshBuilder(); 22 | 23 | var baseHeight = 0.075f; 24 | var halfHeight = baseHeight / 2f; 25 | var baseRadius = 0.2f; 26 | 27 | builder.AddCylinder(Vector3.Zero, new Vector3(0, 0, baseHeight), baseRadius, 18, true, true); 28 | builder.AddArrow(new Vector3(0, 0, halfHeight), new Vector3(baseRadius * 1.6f, 0, halfHeight), halfHeight, 3, 18); 29 | 30 | var diamondSize = 0.3f; 31 | builder.AddOctahedron(new Vector3(0f, 0f, diamondSize * 1.5f), Vector3.UnitX, Vector3.UnitZ, diamondSize, diamondSize); 32 | builder.AddOctahedron(new Vector3(0f, 0f, diamondSize * 0.5f), Vector3.UnitX, -Vector3.UnitZ, diamondSize, diamondSize); 33 | 34 | MarkerGeometry = builder.ToMesh(); 35 | } 36 | 37 | public override ManipulationFlags ManipulationFlags => ManipulationFlags.Translate | ManipulationFlags.Rotate; 38 | 39 | protected override MeshGeometryModel3D GetMeshGeometry() 40 | { 41 | return new MeshGeometryModel3D 42 | { 43 | Geometry = MarkerGeometry, 44 | Material = MarkerMaterial 45 | }; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/Markers/StartPositionMarker3D.cs: -------------------------------------------------------------------------------- 1 | using HelixToolkit.Wpf.SharpDX; 2 | using SharpDX; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System; 6 | using System.Diagnostics; 7 | 8 | using System.Windows; 9 | using Media3D = System.Windows.Media.Media3D; 10 | using Media = System.Windows.Media; 11 | 12 | namespace Reclaimer.Controls.Markers 13 | { 14 | public sealed class StartPositionMarker3D : MarkerGeometry3D 15 | { 16 | private static readonly Material MarkerMaterial = DiffuseMaterials.LightBlue; 17 | private static readonly Geometry3D MarkerGeometry; 18 | 19 | static StartPositionMarker3D() 20 | { 21 | var builder = new MeshBuilder(); 22 | 23 | var baseHeight = 0.075f; 24 | var halfHeight = baseHeight / 2f; 25 | var baseRadius = 0.2f; 26 | 27 | builder.AddCylinder(Vector3.Zero, new Vector3(0, 0, baseHeight), baseRadius, 18, true, true); 28 | builder.AddArrow(new Vector3(0, 0, halfHeight), new Vector3(baseRadius * 1.6f, 0, halfHeight), halfHeight, 3, 18); 29 | 30 | MarkerGeometry = builder.ToMesh(); 31 | } 32 | 33 | public override ManipulationFlags ManipulationFlags => ManipulationFlags.Translate | ManipulationFlags.RotateYZ; 34 | 35 | protected override MeshGeometryModel3D GetMeshGeometry() 36 | { 37 | return new MeshGeometryModel3D 38 | { 39 | Geometry = MarkerGeometry, 40 | Material = MarkerMaterial 41 | }; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/ObjectModel3D.cs: -------------------------------------------------------------------------------- 1 | using HelixToolkit.Wpf.SharpDX; 2 | using HelixToolkit.Wpf.SharpDX.Model.Scene; 3 | using SharpDX; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows; 10 | using Reclaimer.Utilities; 11 | 12 | using Media3D = System.Windows.Media.Media3D; 13 | using System.Windows.Data; 14 | using Prism.Mvvm; 15 | using Reclaimer.Geometry; 16 | using Reclaimer.Models; 17 | 18 | namespace Reclaimer.Controls 19 | { 20 | public sealed class ObjectModel3D : GroupElement3D, IMeshNode 21 | { 22 | private readonly ModelConfig config; 23 | private readonly RenderModel3D baseModel; 24 | private readonly VariantConfig defaultVariant; 25 | private readonly Dictionary> attachments; 26 | 27 | public string ModelName { get; } 28 | public string DefaultVariant => defaultVariant?.Name; 29 | public IEnumerable Variants => config.Variants.Select(v => v.Name); 30 | 31 | public ObjectModel3D(ModelFactory factory, ModelConfig config, string name, string defaultVariant) 32 | { 33 | this.config = config; 34 | ModelName = name; 35 | this.defaultVariant = config.Variants.FirstOrDefault(v => v.Name == defaultVariant) ?? config.Variants.FirstOrDefault(); 36 | attachments = new Dictionary>(); 37 | 38 | if (config.RenderModelTag == null) 39 | baseModel = RenderModel3D.Error("null"); 40 | else 41 | baseModel = factory.CreateRenderModel(config.RenderModelTag.Id); 42 | 43 | foreach (var variant in config.Variants) 44 | { 45 | var children = new List(); 46 | foreach (var attachment in variant.Attachments.Where(att => att.ChildTag != null)) 47 | { 48 | var child = factory.CreateObjectModel(attachment.ChildTag.Id); 49 | if (child.config.RenderModelTag == null) 50 | continue; 51 | 52 | var parentProps = factory.GetProperties(config.RenderModelTag.Id); 53 | var childProps = factory.GetProperties(child.config.RenderModelTag.Id); 54 | child.Transform = GetAttachmentTransform(parentProps, attachment.ParentMarker, childProps, attachment.ChildMarker); 55 | 56 | child.SetVariant(attachment.ChildVariant); 57 | children.Add(child); 58 | } 59 | 60 | attachments.Add(variant.Name, children); 61 | } 62 | 63 | SetVariant(DefaultVariant); 64 | } 65 | 66 | private Media3D.MatrixTransform3D GetAttachmentTransform(ModelProperties p1, string m1, ModelProperties p2, string m2) 67 | { 68 | var t1 = GetMarkerTransform(p1, m1); 69 | var t2 = GetMarkerTransform(p2, m2); 70 | 71 | if (t2.IsIdentity) 72 | return new Media3D.MatrixTransform3D(t1.ToMatrix3D()); 73 | 74 | t2.Invert(); 75 | return new Media3D.MatrixTransform3D((t1 * t2).ToMatrix3D()); 76 | } 77 | 78 | private Matrix GetMarkerTransform(ModelProperties model, string markerName) 79 | { 80 | var marker = model.MarkerGroups.FirstOrDefault(g => g.Name == markerName)?.Markers.FirstOrDefault(); 81 | if (marker == null) 82 | return Matrix.Identity; 83 | 84 | var transfoms = new List(); 85 | 86 | if (marker.NodeIndex != byte.MaxValue) 87 | { 88 | int parentIndex = marker.NodeIndex; 89 | while (parentIndex >= 0) 90 | { 91 | var node = model.Nodes[parentIndex]; 92 | var t = Matrix.RotationQuaternion(node.Rotation.ToQuaternion()); 93 | t.TranslationVector = node.Position.ToVector3(); 94 | transfoms.Insert(0, t); 95 | parentIndex = node.ParentIndex; 96 | } 97 | } 98 | 99 | var mat = Matrix.RotationQuaternion(marker.Rotation.ToQuaternion()); 100 | mat.TranslationVector = marker.Position.ToVector3(); 101 | transfoms.Add(mat); 102 | 103 | return transfoms.Aggregate((m1, m2) => m1 * m2); 104 | } 105 | 106 | public void SetVariant(string variantName) 107 | { 108 | variantName = variantName ?? DefaultVariant; 109 | 110 | var variant = config.Variants.FirstOrDefault(v => v.Name.Equals(variantName, StringComparison.OrdinalIgnoreCase)) ?? defaultVariant; 111 | if (variant == null) 112 | { 113 | Children.Clear(); 114 | baseModel.ShowAll(); 115 | Children.Add(baseModel); 116 | return; 117 | } 118 | 119 | Children.Clear(); 120 | baseModel.ApplyVariant(variant); 121 | Children.Add(baseModel); 122 | 123 | foreach (var att in attachments[variant.Name]) 124 | Children.Add(att); 125 | } 126 | 127 | protected override SceneNode OnCreateSceneNode() 128 | { 129 | return new ViewDistanceGroupNode(this); 130 | } 131 | 132 | #region IMeshNode 133 | 134 | string IMeshNode.Name => ModelName; 135 | 136 | bool IMeshNode.IsVisible 137 | { 138 | get { return IsRendering; } 139 | set { IsRendering = value; } 140 | } 141 | 142 | BoundingBox IMeshNode.GetNodeBounds() 143 | { 144 | return ((IMeshNode)baseModel).GetNodeBounds(); 145 | } 146 | 147 | #endregion 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/PaletteEditorWindow.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 30 | 31 | 32 | 34 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/PaletteEditorWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Reclaimer.Models; 3 | using Reclaimer.Plugins.MetaViewer; 4 | using Reclaimer.Plugins.MetaViewer.Halo3; 5 | using Reclaimer.Resources; 6 | using Reclaimer.Utilities; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Collections.ObjectModel; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | using System.Windows; 14 | using System.Windows.Controls; 15 | using System.Windows.Data; 16 | using System.Windows.Documents; 17 | using System.Windows.Input; 18 | using System.Windows.Media; 19 | using System.Windows.Media.Imaging; 20 | using System.Windows.Navigation; 21 | using System.Windows.Shapes; 22 | 23 | namespace Reclaimer.Controls 24 | { 25 | /// 26 | /// Interaction logic for PaletteEditor.xaml 27 | /// 28 | public partial class PaletteEditorWindow : Window, IMetaViewerHost 29 | { 30 | private readonly string paletteKey; 31 | private readonly ScenarioModel scenario; 32 | private readonly MetaContext context; 33 | 34 | public ObservableCollection Metadata { get; } 35 | 36 | public bool ShowInvisibles => true; 37 | 38 | internal PaletteEditorWindow(ScenarioModel scenario, string paletteKey) 39 | : this() 40 | { 41 | this.scenario = scenario; 42 | this.paletteKey = paletteKey; 43 | context = new MetaContext(scenario.Xml, scenario.ScenarioTag.CacheFile, scenario.ScenarioTag, scenario.MetadataStream); 44 | 45 | Reload(); 46 | 47 | DataContext = this; 48 | } 49 | 50 | public PaletteEditorWindow() 51 | { 52 | InitializeComponent(); 53 | Metadata = new ObservableCollection(); 54 | } 55 | 56 | private void Reload() 57 | { 58 | var palette = scenario.Palettes[paletteKey]; 59 | var blockRef = palette.PaletteBlockRef; 60 | var blockAddress = blockRef.TagBlock.Pointer.Address; 61 | var tagRefNode = palette.PaletteNode.SelectSingleNode($"*[@id='{FieldId.TagReference}']"); 62 | var tagRefOffset = tagRefNode.GetIntAttribute("offset") ?? 0; 63 | 64 | Metadata.Clear(); 65 | for (int i = 0; i < blockRef.TagBlock.Count; i++) 66 | { 67 | var baseAddress = blockAddress + blockRef.BlockSize * i + tagRefOffset; 68 | var meta = (TagReferenceValue)MetaValueBase.GetMetaValue(tagRefNode, context, baseAddress); 69 | 70 | if ((meta.SelectedItem?.Context?.Id ?? 0) == 0) 71 | meta.SelectedClass = meta.ClassOptions.FirstOrDefault(ci => ci.Label.ToLower() == paletteKey) ?? meta.ClassOptions.FirstOrDefault(); 72 | 73 | meta.PropertyChanged += Meta_PropertyChanged; 74 | Metadata.Add(meta); 75 | } 76 | } 77 | 78 | private void Meta_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 79 | { 80 | var item = (sender as TagReferenceValue).SelectedItem?.Context; 81 | var tagRef = item == null ? TagReference.NullReference : new TagReference(item.CacheFile, item.ClassId, item.Id); 82 | 83 | var index = Metadata.IndexOf(sender as MetaValueBase); 84 | scenario.Palettes[paletteKey].Palette[index] = tagRef; 85 | scenario.RenderView?.RefreshPalette(paletteKey, index); 86 | scenario.PropertyView?.Reload(); 87 | } 88 | 89 | #region Toolbar Events 90 | 91 | private void btnAddItem_Click(object sender, RoutedEventArgs e) 92 | { 93 | var palette = scenario.Palettes[paletteKey]; 94 | var blockRef = palette.PaletteBlockRef; 95 | var nextIndex = blockRef.TagBlock.Count; 96 | 97 | var blockEditor = scenario.MetadataStream.GetBlockEditor(blockRef.TagBlock.Pointer.Address); 98 | blockEditor.Add(); 99 | blockEditor.UpdateBlockReference(blockRef); 100 | 101 | scenario.Palettes[paletteKey].Palette.Add(TagReference.NullReference); 102 | scenario.RenderView.RefreshPalette(paletteKey, nextIndex); 103 | 104 | scenario.PropertyView?.Reload(); 105 | Reload(); 106 | } 107 | 108 | private void btnDeleteItem_Click(object sender, RoutedEventArgs e) 109 | { 110 | var palette = scenario.Palettes[paletteKey]; 111 | var blockRef = palette.PaletteBlockRef; 112 | var nextIndex = blockRef.TagBlock.Count - 1; 113 | 114 | var blockEditor = scenario.MetadataStream.GetBlockEditor(blockRef.TagBlock.Pointer.Address); 115 | blockEditor.Remove(nextIndex); 116 | blockEditor.UpdateBlockReference(blockRef); 117 | 118 | scenario.Palettes[paletteKey].Palette.RemoveAt(nextIndex); 119 | scenario.RenderView.RefreshPalette(paletteKey, nextIndex); 120 | 121 | scenario.PropertyView?.Reload(); 122 | Reload(); 123 | } 124 | 125 | #endregion 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/PropertyView.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 34 | 35 | 36 | 38 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/PropertyView.xaml.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Geometry; 2 | using Reclaimer.Models; 3 | using Reclaimer.Models.Ai; 4 | using Reclaimer.Plugins.MetaViewer; 5 | using Reclaimer.Plugins.MetaViewer.Halo3; 6 | using Reclaimer.Resources; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Collections.ObjectModel; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | using System.Windows; 14 | using System.Windows.Controls; 15 | using System.Xml; 16 | 17 | namespace Reclaimer.Controls 18 | { 19 | /// 20 | /// Interaction logic for PropertyView.xaml 21 | /// 22 | public partial class PropertyView : IScenarioPropertyView, IMetaViewerHost 23 | { 24 | #region Dependency Properties 25 | public static readonly DependencyProperty ShowInvisiblesProperty = 26 | DependencyProperty.Register(nameof(ShowInvisibles), typeof(bool), typeof(PropertyView), new PropertyMetadata(false, ShowInvisiblesChanged)); 27 | 28 | public bool ShowInvisibles 29 | { 30 | get { return (bool)GetValue(ShowInvisiblesProperty); } 31 | set { SetValue(ShowInvisiblesProperty, value); } 32 | } 33 | 34 | public static void ShowInvisiblesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 35 | { 36 | //MetaViewerPlugin.Settings.ShowInvisibles = e.NewValue as bool? ?? false; 37 | } 38 | #endregion 39 | 40 | private readonly Dictionary valuesById; 41 | private readonly List> altNodes; 42 | 43 | private ScenarioModel scenario; 44 | private MetaContext context; //dont dispose this because it will dispose our MetadataStream 45 | private XmlNode rootNode; 46 | private long baseAddress; 47 | 48 | public TabModel TabModel { get; } 49 | public object CurrentItem { get; private set; } 50 | public ObservableCollection Metadata { get; } 51 | 52 | public PropertyView() 53 | { 54 | InitializeComponent(); 55 | valuesById = new Dictionary(); 56 | altNodes = new List>(); 57 | Metadata = new ObservableCollection(); 58 | TabModel = new TabModel(this, Studio.Controls.TabItemType.Tool) { Header = "Properties", ToolTip = "Property View" }; 59 | DataContext = this; 60 | } 61 | 62 | public void ClearScenario() 63 | { 64 | ClearProperties(); 65 | scenario = null; 66 | } 67 | 68 | public void SetScenario(ScenarioModel scenario) 69 | { 70 | this.scenario = scenario; 71 | } 72 | 73 | public void ClearProperties() 74 | { 75 | Metadata.Clear(); 76 | Visibility = Visibility.Hidden; 77 | } 78 | 79 | public void SetValue(string id, object value) 80 | { 81 | if (!valuesById.ContainsKey(id)) 82 | return; 83 | 84 | var meta = valuesById[id]; 85 | 86 | var single = meta as SimpleValue; 87 | if (single != null) 88 | { 89 | single.SetValue(value); 90 | return; 91 | } 92 | 93 | var multi = meta as MultiValue; 94 | if (multi != null) 95 | { 96 | multi.SetValue((IXMVector)value); 97 | return; 98 | } 99 | } 100 | 101 | public void ShowProperties(SceneNodeModel node, int itemIndex) 102 | { 103 | context = new MetaContext(scenario.Xml, scenario.ScenarioTag.CacheFile, scenario.ScenarioTag, scenario.MetadataStream); 104 | Visibility = Visibility.Hidden; 105 | rootNode = null; 106 | altNodes.Clear(); 107 | 108 | var nodeType = node?.NodeType ?? NodeType.None; 109 | var handler = scenario.GetNodeTypeHandler(nodeType); 110 | var details = handler?.GetPropertiesLocator(node, itemIndex); 111 | if (details == null) 112 | { 113 | CurrentItem = null; 114 | return; 115 | } 116 | 117 | rootNode = details.RootNode; 118 | baseAddress = details.BaseAddress; 119 | if (details.AdditionalNodes != null) 120 | altNodes.AddRange(details.AdditionalNodes); 121 | CurrentItem = details.TargetObject; 122 | 123 | Reload(); 124 | 125 | Visibility = Visibility.Visible; 126 | } 127 | 128 | public void Reload() 129 | { 130 | if (rootNode == null) 131 | return; 132 | 133 | foreach (var meta in valuesById.Values) 134 | meta.PropertyChanged -= Meta_PropertyChanged; 135 | Metadata.Clear(); 136 | valuesById.Clear(); 137 | 138 | //load altnodes beforehand so any root nodes will overwrite altnodes if they are duplicates 139 | //this brings in additional xml nodes to use their block names 140 | foreach (var t in altNodes) 141 | { 142 | if (t.Item1.Name.ToLower() == "section") 143 | new StructureValue(t.Item1, context, context.CreateReader(), t.Item2); 144 | else 145 | MetaValueBase.GetMetaValue(t.Item1, context, t.Item2); 146 | } 147 | 148 | foreach (XmlNode n in rootNode.ChildNodes) 149 | { 150 | try 151 | { 152 | var meta = MetaValueBase.GetMetaValue(n, context, baseAddress); 153 | Metadata.Add(meta); 154 | if (n.Attributes["id"] != null) 155 | { 156 | valuesById.Add(n.Attributes["id"].Value, meta); 157 | meta.PropertyChanged += Meta_PropertyChanged; 158 | } 159 | } 160 | catch { } 161 | } 162 | 163 | context.UpdateBlockIndices(); 164 | } 165 | 166 | private void Meta_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 167 | { 168 | var id = valuesById.FirstOrDefault(p => p.Value == sender).Key; 169 | if (id == null) 170 | return; 171 | 172 | (CurrentItem as IMetaUpdateReceiver)?.UpdateFromMetaValue(sender as MetaValueBase, id); 173 | } 174 | 175 | private void RecursiveToggle(IEnumerable collection, bool value) 176 | { 177 | foreach (var s in collection.OfType()) 178 | { 179 | s.IsExpanded = value; 180 | RecursiveToggle(s.Children, value); 181 | } 182 | } 183 | 184 | private void btnReload_Click(object sender, RoutedEventArgs e) 185 | { 186 | Reload(); 187 | } 188 | 189 | private void btnCollapseAll_Click(object sender, RoutedEventArgs e) 190 | { 191 | RecursiveToggle(Metadata, false); 192 | } 193 | 194 | private void btnExpandAll_Click(object sender, RoutedEventArgs e) 195 | { 196 | RecursiveToggle(Metadata, true); 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Controls/Spinner.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace Reclaimer.Controls 17 | { 18 | /// 19 | /// Interaction logic for Spinner.xaml 20 | /// https://stackoverflow.com/a/45941268/12034691 21 | /// 22 | public partial class Spinner : UserControl 23 | { 24 | public static readonly DependencyProperty EllipseSizeProperty = 25 | DependencyProperty.Register(nameof(EllipseSize), typeof(int), typeof(Spinner), new PropertyMetadata(8)); 26 | 27 | public static readonly DependencyProperty SpinnerHeightProperty = 28 | DependencyProperty.Register(nameof(SpinnerHeight), typeof(int), typeof(Spinner), new PropertyMetadata(64)); 29 | 30 | public static readonly DependencyProperty SpinnerWidthProperty = 31 | DependencyProperty.Register(nameof(SpinnerWidth), typeof(int), typeof(Spinner), new PropertyMetadata(64)); 32 | 33 | public int EllipseSize 34 | { 35 | get { return (int)GetValue(EllipseSizeProperty); } 36 | set { SetValue(EllipseSizeProperty, value); } 37 | } 38 | 39 | public int SpinnerHeight 40 | { 41 | get { return (int)GetValue(SpinnerHeightProperty); } 42 | set { SetValue(SpinnerHeightProperty, value); } 43 | } 44 | 45 | public int SpinnerWidth 46 | { 47 | get { return (int)GetValue(SpinnerWidthProperty); } 48 | set { SetValue(SpinnerWidthProperty, value); } 49 | } 50 | 51 | // start positions 52 | public EllipseStartPosition EllipseN { get; private set; } 53 | public EllipseStartPosition EllipseNE { get; private set; } 54 | public EllipseStartPosition EllipseE { get; private set; } 55 | public EllipseStartPosition EllipseSE { get; private set; } 56 | public EllipseStartPosition EllipseS { get; private set; } 57 | public EllipseStartPosition EllipseSW { get; private set; } 58 | public EllipseStartPosition EllipseW { get; private set; } 59 | public EllipseStartPosition EllipseNW { get; private set; } 60 | 61 | public Spinner() 62 | { 63 | InitializeComponent(); 64 | } 65 | 66 | private void InitialSetup() 67 | { 68 | float horizontalCenter = SpinnerWidth / 2f; 69 | float verticalCenter = SpinnerHeight / 2f; 70 | float distance = (float)Math.Min(SpinnerHeight, SpinnerWidth) / 2; 71 | 72 | double angleInRadians = 44.8d; 73 | float cosine = (float)Math.Cos(angleInRadians); 74 | float sine = (float)Math.Sin(angleInRadians); 75 | 76 | EllipseN = GetNewPosition(left: horizontalCenter, top: verticalCenter - distance); 77 | EllipseNE = GetNewPosition(left: horizontalCenter + (distance * cosine), top: verticalCenter - (distance * sine)); 78 | EllipseE = GetNewPosition(left: horizontalCenter + distance, top: verticalCenter); 79 | EllipseSE = GetNewPosition(left: horizontalCenter + (distance * cosine), top: verticalCenter + (distance * sine)); 80 | EllipseS = GetNewPosition(left: horizontalCenter, top: verticalCenter + distance); 81 | EllipseSW = GetNewPosition(left: horizontalCenter - (distance * cosine), top: verticalCenter + (distance * sine)); 82 | EllipseW = GetNewPosition(left: horizontalCenter - distance, top: verticalCenter); 83 | EllipseNW = GetNewPosition(left: horizontalCenter - (distance * cosine), top: verticalCenter - (distance * sine)); 84 | } 85 | 86 | private EllipseStartPosition GetNewPosition(float left, float top) 87 | { 88 | return new EllipseStartPosition { Left = left, Top = top }; 89 | } 90 | 91 | protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) 92 | { 93 | if (e.Property.Name == nameof(Height)) 94 | SpinnerHeight = Convert.ToInt32(e.NewValue); 95 | 96 | if (e.Property.Name == nameof(Width)) 97 | SpinnerWidth = Convert.ToInt32(e.NewValue); 98 | 99 | if (SpinnerHeight > 0 && SpinnerWidth > 0) 100 | InitialSetup(); 101 | 102 | base.OnPropertyChanged(e); 103 | } 104 | } 105 | 106 | public struct EllipseStartPosition 107 | { 108 | public float Left { get; set; } 109 | public float Top { get; set; } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Geometry/BspManager.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Reclaimer.Models; 3 | using Reclaimer.Utilities; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Data; 10 | 11 | using static HelixToolkit.Wpf.SharpDX.Media3DExtension; 12 | 13 | using Media = System.Windows.Media; 14 | using Media3D = System.Windows.Media.Media3D; 15 | using Helix = HelixToolkit.Wpf.SharpDX; 16 | 17 | namespace Reclaimer.Geometry 18 | { 19 | public class BspManager : IDisposable 20 | { 21 | private readonly ModelFactory factory = new ModelFactory(); 22 | 23 | private StructureBspModel bspModel; 24 | private ModelProperties modelProps; 25 | 26 | private int tagId => bspModel.StructureBspTag.Id; 27 | 28 | public ObjectHolder ClusterHolder { get; private set; } 29 | public Dictionary InstanceHolders { get; private set; } 30 | 31 | public IEnumerable ReadStructureBsp(StructureBspModel bspModel) 32 | { 33 | this.bspModel = bspModel; 34 | ClusterHolder = new ObjectHolder("Clusters"); 35 | InstanceHolders = new Dictionary(); 36 | 37 | yield return Task.Run(() => 38 | { 39 | factory.LoadTag(bspModel.StructureBspTag, false); 40 | modelProps = factory.GetProperties(tagId); 41 | 42 | var instanceData = ReadInstanceData(); 43 | foreach (var i in modelProps.InstanceMeshes) 44 | InstanceHolders.Add(i, new InstanceHolder(i, modelProps, instanceData)); 45 | }); 46 | } 47 | 48 | public void RenderStructureBsp() 49 | { 50 | if (bspModel == null) 51 | throw new InvalidOperationException(); 52 | 53 | foreach (var index in modelProps.StandardMeshes) 54 | ClusterHolder.Elements.Add(factory.CreateModelSection(tagId, 0, index, 1)); 55 | 56 | foreach (var holder in InstanceHolders.Values) 57 | { 58 | holder.GroupElement = new Helix.GroupModel3D(); 59 | holder.SetCapacity(holder.Placements.Count); 60 | 61 | for (int i = 0; i < holder.Placements.Count; i++) 62 | ConfigurePlacement(holder, i); 63 | } 64 | } 65 | 66 | private List ReadInstanceData() 67 | { 68 | switch (bspModel.StructureBspTag.CacheFile.CacheType) 69 | { 70 | case CacheType.Halo3Beta: 71 | case CacheType.Halo3Retail: 72 | case CacheType.Halo3ODST: 73 | case CacheType.MccHalo3: 74 | case CacheType.MccHalo3ODST: 75 | return ReadInstanceDataHalo3(); 76 | default: 77 | throw new NotSupportedException(); 78 | } 79 | } 80 | 81 | private List ReadInstanceDataHalo3() 82 | { 83 | var meta = bspModel.StructureBspTag.ReadMetadata(); 84 | var result = new List(meta.GeometryInstances.Count); 85 | result.AddRange(meta.GeometryInstances); 86 | return result; 87 | } 88 | 89 | private void RemovePlacement(InstanceHolder holder, int index) 90 | { 91 | var element = holder.Elements[index]; 92 | if (element == null) 93 | return; 94 | 95 | holder.GroupElement.Children.Remove(element); 96 | element.Dispose(); 97 | holder.Elements[index] = null; 98 | } 99 | 100 | private void ConfigurePlacement(InstanceHolder holder, int index) 101 | { 102 | RemovePlacement(holder, index); 103 | 104 | var placement = holder.Placements[index]; 105 | 106 | var inst = factory.CreateModelSection(tagId, 0, placement.MeshIndex, 1); 107 | if (inst == null) 108 | { 109 | holder.Elements[index] = null; 110 | return; 111 | } 112 | 113 | BindPlacement(placement, inst); 114 | 115 | holder.Elements[index] = inst; 116 | holder.GroupElement.Children.Add(inst); 117 | } 118 | 119 | //public void RefreshObject(string paletteKey, ObjectPlacement placement, string fieldId) 120 | //{ 121 | // var holder = InstanceHolders[paletteKey]; 122 | // var index = holder.Definition.Placements.IndexOf(placement); 123 | 124 | // if (fieldId == FieldId.Variant) 125 | // (holder.Elements[index] as ObjectModel3D)?.SetVariant(placement.Variant); 126 | // else if (fieldId == FieldId.PaletteIndex) 127 | // { 128 | // ConfigurePlacement(holder, index); 129 | 130 | // var info = holder.GetInfoForIndex(index); 131 | // info.TreeItem.Header = info.Placement.GetDisplayName(); 132 | // info.TreeItem.Tag = info.Element; 133 | 134 | // var listItem = bspModel.Items.FirstOrDefault(i => i.Tag == info.Placement); 135 | // if (listItem != null) 136 | // listItem.Content = info.TreeItem.Header; 137 | // } 138 | //} 139 | 140 | private void BindPlacement(InstancePlacement placement, Helix.Element3D model) 141 | { 142 | var binding = new MultiBinding { Converter = MatrixTransformConverter.Instance, Mode = BindingMode.TwoWay }; 143 | binding.Bindings.Add(new Binding(nameof(InstancePlacement.TransformScale)) { Mode = BindingMode.TwoWay }); 144 | binding.Bindings.Add(new Binding(nameof(InstancePlacement.Transform)) { Mode = BindingMode.TwoWay }); 145 | 146 | model.DataContext = placement; 147 | BindingOperations.SetBinding(model, Helix.Element3D.TransformProperty, binding); 148 | } 149 | 150 | public void Dispose() 151 | { 152 | ClusterHolder?.Dispose(); 153 | 154 | if (InstanceHolders != null) 155 | { 156 | foreach (var holder in InstanceHolders.Values) 157 | holder.Dispose(); 158 | } 159 | 160 | factory.Dispose(); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Geometry/ModelProperties.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Geometry; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Reclaimer.Geometry 9 | { 10 | public class ModelProperties 11 | { 12 | public string Name { get; } 13 | public IReadOnlyList Regions { get; } 14 | public IReadOnlyList MarkerGroups { get; } 15 | public IReadOnlyList Nodes { get; } 16 | public IReadOnlyList Materials { get; } 17 | 18 | public int MeshCount { get; } 19 | public IReadOnlyList StandardMeshes { get; } 20 | public IReadOnlyList InstanceMeshes { get; } 21 | 22 | public ModelProperties(IGeometryModel source) 23 | { 24 | Name = source.Name; 25 | Regions = source.Regions.ToList(); 26 | MarkerGroups = source.MarkerGroups.ToList(); 27 | Nodes = source.Nodes.ToList(); 28 | Materials = source.Materials.ToList(); 29 | 30 | var standard = new List(); 31 | var instances = new List(); 32 | 33 | for (int i = 0; i < source.Meshes.Count; i++) 34 | { 35 | if (source.Meshes[i].IsInstancing) 36 | instances.Add(i); 37 | else standard.Add(i); 38 | } 39 | 40 | MeshCount = source.Meshes.Count; 41 | StandardMeshes = standard; 42 | InstanceMeshes = instances; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Geometry/ObjectHolder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Reclaimer.Models; 8 | 9 | using Helix = HelixToolkit.Wpf.SharpDX; 10 | using Reclaimer.Utilities; 11 | 12 | namespace Reclaimer.Geometry 13 | { 14 | public class ObjectHolder 15 | { 16 | public virtual string Name { get; } 17 | 18 | public List Elements { get; } 19 | 20 | public ObjectHolder(string name) 21 | : this() 22 | { 23 | Name = name; 24 | } 25 | 26 | public ObjectHolder() 27 | { 28 | Elements = new List(); 29 | } 30 | 31 | public virtual void Dispose() 32 | { 33 | foreach (var e in Elements) 34 | e?.Dispose(); 35 | 36 | Elements.Clear(); 37 | } 38 | } 39 | 40 | public class PaletteHolder : ObjectHolder 41 | { 42 | public override string Name => Definition.Name; 43 | public PaletteDefinition Definition { get; } 44 | 45 | public List TreeItems { get; } 46 | 47 | public Helix.GroupModel3D GroupElement { get; internal set; } 48 | 49 | public PaletteHolder(PaletteDefinition definition) 50 | { 51 | TreeItems = new List(); 52 | Definition = definition; 53 | } 54 | 55 | public ObjectInfo GetInfoForIndex(int index) => new ObjectInfo(this, index); 56 | 57 | public void SetCapacity(int newSize) 58 | { 59 | Elements.Resize(newSize); 60 | TreeItems.Resize(newSize); 61 | } 62 | 63 | public ObjectPlacement InsertPlacement(int index, ScenarioModel scenario, string paletteKey) 64 | { 65 | var placement = new ObjectPlacement(scenario, paletteKey); 66 | Definition.Placements.Insert(index, placement); 67 | TreeItems.Insert(index, new TreeItemModel { Header = placement.GetDisplayName(), IsChecked = true, Tag = null }); 68 | Elements.Insert(index, null); 69 | 70 | return placement; 71 | } 72 | 73 | public void RemovePlacement(int index) 74 | { 75 | Definition.Placements.RemoveAt(index); 76 | TreeItems.RemoveAt(index); 77 | 78 | var element = Elements[index]; 79 | GroupElement.Children.Remove(element); 80 | Elements.Remove(element); 81 | element.Dispose(); 82 | } 83 | 84 | public override void Dispose() 85 | { 86 | base.Dispose(); 87 | GroupElement.Dispose(); 88 | GroupElement = null; 89 | } 90 | 91 | public struct ObjectInfo 92 | { 93 | private readonly PaletteHolder holder; 94 | private readonly int index; 95 | 96 | public Helix.Element3D Element 97 | { 98 | get { return holder.Elements[index]; } 99 | set { holder.Elements[index] = value; } 100 | } 101 | 102 | public ObjectPlacement Placement 103 | { 104 | get { return holder.Definition.Placements[index]; } 105 | set { holder.Definition.Placements[index] = value; } 106 | } 107 | 108 | public TreeItemModel TreeItem 109 | { 110 | get { return holder.TreeItems[index]; } 111 | set { holder.TreeItems[index] = value; } 112 | } 113 | 114 | public ObjectInfo(PaletteHolder holder, int index) 115 | { 116 | this.holder = holder; 117 | this.index = index; 118 | } 119 | } 120 | } 121 | 122 | public class InstanceHolder : ObjectHolder 123 | { 124 | private readonly int sectionIndex; 125 | 126 | public override string Name => $"Instances {sectionIndex:D3}"; 127 | public List TreeItems { get; } 128 | public List Placements { get; } 129 | 130 | public Helix.GroupModel3D GroupElement { get; internal set; } 131 | 132 | public InstanceHolder(int sectionIndex, ModelProperties props, IList instanceData) 133 | { 134 | this.sectionIndex = sectionIndex; 135 | TreeItems = new List(); 136 | Placements = new List(); 137 | 138 | var perms = props.Regions.SelectMany(r => r.Permutations) 139 | .Where(p => p.MeshIndex == sectionIndex); 140 | 141 | foreach (var p in perms) 142 | { 143 | var source = instanceData[p.SourceIndex]; 144 | Placements.Add(new InstancePlacement(p.SourceIndex) 145 | { 146 | Name = p.Name, 147 | MeshIndex = p.MeshIndex, 148 | TransformScale = p.TransformScale, 149 | Transform = p.Transform, 150 | SphereX = source.BoundingSpherePosition.X, 151 | SphereY = source.BoundingSpherePosition.Y, 152 | SphereZ = source.BoundingSpherePosition.Z, 153 | SphereRadius = source.BoundingSphereRadius 154 | }); 155 | } 156 | 157 | SetCapacity(Placements.Count); 158 | } 159 | 160 | public void SetCapacity(int newSize) 161 | { 162 | Elements.Resize(newSize); 163 | TreeItems.Resize(newSize); 164 | Placements.Resize(newSize); 165 | } 166 | 167 | public override void Dispose() 168 | { 169 | base.Dispose(); 170 | GroupElement.Dispose(); 171 | GroupElement = null; 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/Ai/AiArea.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Spatial; 2 | using Reclaimer.Plugins.MetaViewer; 3 | using Reclaimer.Plugins.MetaViewer.Halo3; 4 | using Reclaimer.Resources; 5 | using Reclaimer.Utilities; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace Reclaimer.Models.Ai 13 | { 14 | public class AiArea : ScenarioObject 15 | { 16 | internal AiZone Zone { get; } 17 | internal BlockReference BlockReference { get; } 18 | internal override int BlockIndex { get; } 19 | 20 | private string name; 21 | public string Name 22 | { 23 | get { return name; } 24 | set { SetProperty(ref name, value, FieldId.Name); } 25 | } 26 | 27 | public AiArea(ScenarioModel parent, AiZone zone, BlockReference blockRef, int index) 28 | : base(parent) 29 | { 30 | Zone = zone; 31 | BlockReference = blockRef; 32 | BlockIndex = index; 33 | } 34 | 35 | protected override long GetFieldAddress(string fieldId) 36 | { 37 | var fieldOffset = Parent.SquadHierarchy.AiNodes[AiSection.Areas].SelectSingleNode($"*[@id='{fieldId}']").GetIntAttribute("offset") ?? 0; 38 | return BlockReference.TagBlock.Pointer.Address + BlockReference.BlockSize * BlockIndex + fieldOffset; 39 | } 40 | 41 | public override string GetDisplayName() 42 | { 43 | var displayName = Name.AsDisplayName(); 44 | return BlockIndex >= 0 ? $"[{BlockIndex:D3}] {displayName}" : displayName; 45 | } 46 | 47 | public override void UpdateFromMetaValue(MetaValueBase meta, string fieldId) 48 | { 49 | switch (fieldId) 50 | { 51 | case FieldId.Name: 52 | var str = meta as StringValue; 53 | Name = str.Value; 54 | break; 55 | case FieldId.Position: 56 | var multi = meta as MultiValue; 57 | Position = new RealVector3D(multi.Value1, multi.Value2, multi.Value3); 58 | break; 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/Ai/AiFiringPosition.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Spatial; 2 | using Reclaimer.Plugins.MetaViewer; 3 | using Reclaimer.Plugins.MetaViewer.Halo3; 4 | using Reclaimer.Resources; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Reclaimer.Models.Ai 12 | { 13 | public class AiFiringPosition : ScenarioObject 14 | { 15 | internal AiZone Zone { get; } 16 | internal BlockReference BlockReference { get; } 17 | internal override int BlockIndex { get; } 18 | 19 | public AiFiringPosition(ScenarioModel parent, AiZone zone, BlockReference blockRef, int index) 20 | : base(parent) 21 | { 22 | Zone = zone; 23 | BlockReference = blockRef; 24 | BlockIndex = index; 25 | } 26 | 27 | protected override long GetFieldAddress(string fieldId) 28 | { 29 | var fieldOffset = Parent.SquadHierarchy.AiNodes[AiSection.FiringPositions].SelectSingleNode($"*[@id='{fieldId}']").GetIntAttribute("offset") ?? 0; 30 | return BlockReference.TagBlock.Pointer.Address + BlockReference.BlockSize * BlockIndex + fieldOffset; 31 | } 32 | 33 | public override string GetDisplayName() 34 | { 35 | return $"firing position {Zone.FiringPositions.IndexOf(this)}"; 36 | } 37 | 38 | public override void UpdateFromMetaValue(MetaValueBase meta, string fieldId) 39 | { 40 | switch (fieldId) 41 | { 42 | case FieldId.Position: 43 | var multi = meta as MultiValue; 44 | Position = new RealVector3D(multi.Value1, multi.Value2, multi.Value3); 45 | break; 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/Ai/AiNamedBlock.cs: -------------------------------------------------------------------------------- 1 | using Prism.Mvvm; 2 | using Reclaimer.Plugins.MetaViewer; 3 | using Reclaimer.Resources; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Reclaimer.Models.Ai 11 | { 12 | public class AiNamedBlock : BindableBase, IMetaUpdateReceiver 13 | { 14 | internal BlockReference BlockReference { get; } 15 | internal int BlockIndex { get; } 16 | 17 | internal long BlockStartAddress => BlockIndex < 0 ? -1 : BlockReference.TagBlock.Pointer.Address + BlockIndex * BlockReference.BlockSize; 18 | 19 | private string name; 20 | public string Name 21 | { 22 | get { return name; } 23 | set { SetProperty(ref name, value); } 24 | } 25 | 26 | public AiNamedBlock(BlockReference blockRef, int index) 27 | { 28 | BlockReference = blockRef; 29 | BlockIndex = index; 30 | } 31 | 32 | public virtual void UpdateFromMetaValue(MetaValueBase meta, string fieldId) 33 | { 34 | if (fieldId == FieldId.Name) 35 | Name = ((Plugins.MetaViewer.Halo3.StringValue)meta).Value; 36 | } 37 | 38 | public virtual string GetDisplayName() => BlockIndex >= 0 ? $"[{BlockIndex:D3}] {Name}" : Name; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/Ai/AiSquadHierarchy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Collections.ObjectModel; 7 | using Reclaimer.Resources; 8 | using System.Xml; 9 | 10 | namespace Reclaimer.Models.Ai 11 | { 12 | public class AiSquadHierarchy 13 | { 14 | internal Dictionary AiNodes { get; } 15 | public ObservableCollection SquadGroups { get; } 16 | public ObservableCollection Zones { get; } 17 | public AiZone DefaultZone { get; } 18 | 19 | public AiSquadHierarchy(ScenarioModel scenario) 20 | { 21 | if (!scenario.Sections.ContainsKey(Section.Squads)) 22 | AiNodes = new Dictionary(); 23 | else 24 | { 25 | AiNodes = scenario.Sections[Section.Squads].Node.SelectNodes("./tagblock[@id]") 26 | .OfType() 27 | .ToDictionary(n => n.Attributes["id"].Value); 28 | } 29 | 30 | SquadGroups = new ObservableCollection(); 31 | Zones = new ObservableCollection(); 32 | DefaultZone = new AiZone(null, -1) { Name = "" }; 33 | } 34 | 35 | public IEnumerable EnumerateZones() => Enumerable.Repeat(DefaultZone, 1).Concat(Zones); 36 | } 37 | 38 | public class AiZone : AiNamedBlock 39 | { 40 | public ObservableCollection FiringPositions { get; } 41 | public ObservableCollection Areas { get; } 42 | public ObservableCollection Squads { get; } 43 | 44 | public AiZone(BlockReference blockRef, int index) 45 | : base(blockRef, index) 46 | { 47 | FiringPositions = new ObservableCollection(); 48 | Areas = new ObservableCollection(); 49 | Squads = new ObservableCollection(); 50 | } 51 | } 52 | 53 | public class AiSquad : AiNamedBlock 54 | { 55 | private int zoneIndex; 56 | public int ZoneIndex 57 | { 58 | get { return zoneIndex; } 59 | set { SetProperty(ref zoneIndex, value); } 60 | } 61 | 62 | public ObservableCollection Encounters { get; } 63 | public ObservableCollection GroupStartLocations { get; } 64 | public ObservableCollection SoloStartLocations { get; } 65 | 66 | public AiSquad(BlockReference blockRef, int index) 67 | : base(blockRef, index) 68 | { 69 | Encounters = new ObservableCollection(); 70 | GroupStartLocations = new ObservableCollection(); 71 | SoloStartLocations = new ObservableCollection(); 72 | } 73 | } 74 | 75 | public class AiEncounter : AiNamedBlock 76 | { 77 | public ObservableCollection StartingLocations { get; } 78 | 79 | public AiEncounter(BlockReference blockRef, int index) 80 | : base(blockRef, index) 81 | { 82 | StartingLocations = new ObservableCollection(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/Ai/AiStartingLocation.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Adjutant.Spatial; 3 | using Reclaimer.Plugins.MetaViewer; 4 | using Reclaimer.Plugins.MetaViewer.Halo3; 5 | using Reclaimer.Resources; 6 | using Reclaimer.Utilities; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace Reclaimer.Models.Ai 14 | { 15 | public class AiStartingLocation : ScenarioObject 16 | { 17 | internal readonly string SectionKey; 18 | 19 | internal BlockReference BlockReference { get; } 20 | internal override int BlockIndex { get; } 21 | 22 | private StringId name; 23 | public StringId Name 24 | { 25 | get { return name; } 26 | set { SetProperty(ref name, value, FieldId.Name); } 27 | } 28 | 29 | private RealVector3D rotation; 30 | public RealVector3D Rotation 31 | { 32 | get { return rotation; } 33 | set { SetProperty(ref rotation, value, FieldId.Rotation); } 34 | } 35 | 36 | public AiStartingLocation(ScenarioModel parent, BlockReference blockRef, int index, string sectionKey) 37 | : base(parent) 38 | { 39 | SectionKey = sectionKey; 40 | BlockReference = blockRef; 41 | BlockIndex = index; 42 | } 43 | 44 | protected override long GetFieldAddress(string fieldId) 45 | { 46 | var fieldOffset = Parent.SquadHierarchy.AiNodes[SectionKey].SelectSingleNode($"*[@id='{fieldId}']").GetIntAttribute("offset") ?? 0; 47 | return BlockReference.TagBlock.Pointer.Address + BlockReference.BlockSize * BlockIndex + fieldOffset; 48 | } 49 | 50 | public override string GetDisplayName() 51 | { 52 | var displayName = Name.AsDisplayName(); 53 | return BlockIndex >= 0 ? $"[{BlockIndex:D3}] {displayName}" : displayName; 54 | } 55 | 56 | public override void UpdateFromMetaValue(MetaValueBase meta, string fieldId) 57 | { 58 | switch (fieldId) 59 | { 60 | case FieldId.Name: 61 | SetStringId(meta, ref name, nameof(Name)); 62 | break; 63 | case FieldId.Position: 64 | case FieldId.Rotation: 65 | var multi = meta as MultiValue; 66 | var vector = new RealVector3D(multi.Value1, multi.Value2, multi.Value3); 67 | if (fieldId == FieldId.Position) 68 | Position = vector; 69 | else Rotation = vector; 70 | break; 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/BlockPropertiesLocator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Xml; 7 | 8 | namespace Reclaimer.Models 9 | { 10 | internal class BlockPropertiesLocator 11 | { 12 | public XmlNode RootNode { get; set; } 13 | public long BaseAddress { get; set; } 14 | public IList> AdditionalNodes { get; set; } 15 | public IMetaUpdateReceiver TargetObject { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/IBspGeometryInstanceBlock.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Spatial; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Numerics; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Reclaimer.Models 10 | { 11 | public interface IBspGeometryInstanceBlock 12 | { 13 | float TransformScale { get; } 14 | Matrix4x4 Transform { get; } 15 | short SectionIndex { get; } 16 | RealVector3D BoundingSpherePosition { get; } 17 | float BoundingSphereRadius { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/IDisplayName.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Models 8 | { 9 | public interface IDisplayName 10 | { 11 | string GetDisplayName(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/IMetaUpdateReceiver.cs: -------------------------------------------------------------------------------- 1 | using Reclaimer.Plugins.MetaViewer; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Reclaimer.Models 9 | { 10 | internal interface IMetaUpdateReceiver : IDisplayName 11 | { 12 | void UpdateFromMetaValue(MetaValueBase meta, string fieldId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/IScenarioHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Models 8 | { 9 | public interface IScenarioHandler 10 | { 11 | void SetScenario(ScenarioModel scenario); 12 | void ClearScenario(); 13 | } 14 | 15 | public interface IScenarioHierarchyView : IScenarioHandler 16 | { 17 | void ShowCurrentSelection(); 18 | } 19 | 20 | public interface IScenarioPropertyView : IScenarioHandler 21 | { 22 | object CurrentItem { get; } 23 | void ShowProperties(SceneNodeModel node, int itemIndex); 24 | void ClearProperties(); 25 | void SetValue(string id, object value); 26 | void Reload(); 27 | } 28 | 29 | public interface IScenarioRenderView : IScenarioHandler 30 | { 31 | void SelectPalette(SceneNodeModel node); 32 | void SelectObject(SceneNodeModel node, int itemIndex); 33 | void NavigateToObject(SceneNodeModel node, int itemIndex); 34 | void RefreshPalette(string paletteKey, int index); 35 | void RefreshObject(string paletteKey, ObjectPlacement placement, string fieldId); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/IStructureBspHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Models 8 | { 9 | public interface IStructureBspHandler 10 | { 11 | void SetScenario(StructureBspModel bspModel); 12 | void ClearScenario(); 13 | } 14 | 15 | public interface IStructureBspHierarchyView : IStructureBspHandler 16 | { 17 | 18 | } 19 | 20 | public interface IStructureBspPropertyView : IStructureBspHandler 21 | { 22 | InstancePlacement CurrentItem { get; set; } 23 | } 24 | 25 | public interface IStructureBspRenderView : IStructureBspHandler 26 | { 27 | IEnumerable GetPlacements(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/InstancePlacement.cs: -------------------------------------------------------------------------------- 1 | using Prism.Mvvm; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Numerics; 6 | using System.Runtime.CompilerServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Media.Media3D; 10 | 11 | namespace Reclaimer.Models 12 | { 13 | public class InstancePlacement : BindableBase 14 | { 15 | public int SourceIndex { get; } 16 | 17 | private float transformScale; 18 | public float TransformScale 19 | { 20 | get { return transformScale; } 21 | set { SetProperty(ref transformScale, value); } 22 | } 23 | 24 | #region Matrix Properties 25 | public Matrix4x4 Transform 26 | { 27 | get { return GetTransform(); } 28 | set { SetTransform(value); } 29 | } 30 | 31 | //these need to be individual properties 32 | //so the ui can bind to them and also 33 | //trigger/receive change events for them 34 | 35 | private float m11; 36 | public float M11 37 | { 38 | get { return m11; } 39 | set { SetTransformProperty(ref m11, value); } 40 | } 41 | 42 | private float m12; 43 | public float M12 44 | { 45 | get { return m12; } 46 | set { SetTransformProperty(ref m12, value); } 47 | } 48 | 49 | private float m13; 50 | public float M13 51 | { 52 | get { return m13; } 53 | set { SetTransformProperty(ref m13, value); } 54 | } 55 | 56 | private float m21; 57 | public float M21 58 | { 59 | get { return m21; } 60 | set { SetTransformProperty(ref m21, value); } 61 | } 62 | 63 | private float m22; 64 | public float M22 65 | { 66 | get { return m22; } 67 | set { SetTransformProperty(ref m22, value); } 68 | } 69 | 70 | private float m23; 71 | public float M23 72 | { 73 | get { return m23; } 74 | set { SetTransformProperty(ref m23, value); } 75 | } 76 | 77 | private float m31; 78 | public float M31 79 | { 80 | get { return m31; } 81 | set { SetTransformProperty(ref m31, value); } 82 | } 83 | 84 | private float m32; 85 | public float M32 86 | { 87 | get { return m32; } 88 | set { SetTransformProperty(ref m32, value); } 89 | } 90 | 91 | private float m33; 92 | public float M33 93 | { 94 | get { return m33; } 95 | set { SetTransformProperty(ref m33, value); } 96 | } 97 | 98 | private float m41; 99 | public float M41 100 | { 101 | get { return m41; } 102 | set { SetTransformProperty(ref m41, value); } 103 | } 104 | 105 | private float m42; 106 | public float M42 107 | { 108 | get { return m42; } 109 | set { SetTransformProperty(ref m42, value); } 110 | } 111 | 112 | private float m43; 113 | public float M43 114 | { 115 | get { return m43; } 116 | set { SetTransformProperty(ref m43, value); } 117 | } 118 | #endregion 119 | 120 | private int meshIndex; 121 | public int MeshIndex 122 | { 123 | get { return meshIndex; } 124 | set { SetProperty(ref meshIndex, value); } 125 | } 126 | 127 | #region Bounding Sphere Properties 128 | 129 | private float sphereX; 130 | public float SphereX 131 | { 132 | get { return sphereX; } 133 | set { SetProperty(ref sphereX, value); } 134 | } 135 | 136 | private float sphereY; 137 | public float SphereY 138 | { 139 | get { return sphereY; } 140 | set { SetProperty(ref sphereY, value); } 141 | } 142 | 143 | private float sphereZ; 144 | public float SphereZ 145 | { 146 | get { return sphereZ; } 147 | set { SetProperty(ref sphereZ, value); } 148 | } 149 | 150 | private float sphereRadius; 151 | public float SphereRadius 152 | { 153 | get { return sphereRadius; } 154 | set { SetProperty(ref sphereRadius, value); } 155 | } 156 | 157 | #endregion 158 | 159 | private string name; 160 | public string Name 161 | { 162 | get { return name; } 163 | set { SetProperty(ref name, value); } 164 | } 165 | 166 | public InstancePlacement(int sourceIndex) 167 | { 168 | SourceIndex = sourceIndex; 169 | } 170 | 171 | private void SetTransformProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) 172 | { 173 | if (SetProperty(ref storage, value, propertyName)) 174 | RaisePropertyChanged(nameof(Transform)); 175 | } 176 | 177 | public Matrix4x4 GetTransform() 178 | { 179 | return new Matrix4x4 180 | { 181 | M11 = M11, 182 | M12 = M12, 183 | M13 = M13, 184 | M21 = M21, 185 | M22 = M22, 186 | M23 = M23, 187 | M31 = M31, 188 | M32 = M32, 189 | M33 = M33, 190 | M41 = M41, 191 | M42 = M42, 192 | M43 = M43, 193 | M44 = 1f 194 | }; 195 | } 196 | 197 | public void SetTransform(Matrix4x4 transform) 198 | { 199 | M11 = transform.M11; 200 | M12 = transform.M12; 201 | M13 = transform.M13; 202 | M21 = transform.M21; 203 | M22 = transform.M22; 204 | M23 = transform.M23; 205 | M31 = transform.M31; 206 | M32 = transform.M32; 207 | M33 = transform.M33; 208 | M41 = transform.M41; 209 | M42 = transform.M42; 210 | M43 = transform.M43; 211 | } 212 | 213 | public override string ToString() => Name; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/ModelConfig.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Reclaimer.Models 9 | { 10 | public class ModelConfig 11 | { 12 | public static readonly ModelConfig Empty = new ModelConfig(); 13 | 14 | public IIndexItem ModelTag { get; set; } 15 | public IIndexItem RenderModelTag { get; set; } 16 | public List Variants { get; set; } = new List(); 17 | 18 | public override string ToString() => ModelTag?.ToString(); 19 | 20 | public static ModelConfig FromIndexItem(IIndexItem item) 21 | { 22 | ModelConfig result; 23 | switch (item.CacheFile.CacheType) 24 | { 25 | case CacheType.Halo3Retail: 26 | case CacheType.MccHalo3: 27 | case CacheType.MccHalo3U4: 28 | case CacheType.Halo3ODST: 29 | case CacheType.MccHalo3ODST: 30 | result = item.ReadMetadata().ToModelConfig(); 31 | break; 32 | 33 | case CacheType.HaloReachRetail: 34 | case CacheType.MccHaloReach: 35 | case CacheType.MccHaloReachU3: 36 | result = item.ReadMetadata().ToModelConfig(); 37 | break; 38 | 39 | case CacheType.Halo4Retail: 40 | case CacheType.MccHalo4: 41 | case CacheType.MccHalo2X: 42 | result = item.ReadMetadata().ToModelConfig(); 43 | break; 44 | 45 | default: return null; 46 | } 47 | 48 | result.ModelTag = item; 49 | return result; 50 | } 51 | } 52 | 53 | public class VariantConfig 54 | { 55 | public string Name { get; set; } 56 | public byte[] RegionLookup { get; set; } 57 | public List Regions { get; set; } = new List(); 58 | public List Attachments { get; set; } = new List(); 59 | 60 | public override string ToString() => Name; 61 | } 62 | 63 | public class VariantRegionConfig 64 | { 65 | public string Name { get; set; } 66 | public int BaseRegionIndex { get; set; } 67 | public int ParentVariantIndex { get; set; } 68 | public List Permutations { get; set; } = new List(); 69 | 70 | public override string ToString() => Name; 71 | } 72 | 73 | public class VariantPermutationConfig 74 | { 75 | public string Name { get; set; } 76 | public int BasePermutationIndex { get; set; } 77 | 78 | public override string ToString() => Name; 79 | } 80 | 81 | public class AttachmentConfig 82 | { 83 | public string ParentMarker { get; set; } 84 | public string ChildMarker { get; set; } 85 | public string ChildVariant { get; set; } 86 | public IIndexItem ChildTag { get; set; } 87 | 88 | public override string ToString() => ChildTag?.ToString(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/NodeType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Models 8 | { 9 | public enum NodeType 10 | { 11 | None, 12 | Mission, 13 | 14 | // Devices 15 | Machines, 16 | Controls, 17 | LightFixtures, 18 | DeviceGroups, 19 | 20 | // Items 21 | Equipment, 22 | Weapons, 23 | 24 | // Units 25 | Bipeds, 26 | Vehicles, 27 | 28 | // Objects 29 | Crates, 30 | Scenery, 31 | SoundScenery, 32 | 33 | StartPositions, 34 | Comments, 35 | 36 | // AI 37 | AiSquadGroups, 38 | AiZones, 39 | AiDefaultZoneItem, 40 | AiZoneItem, 41 | AiFiringPositions, 42 | AiZoneAreas, 43 | AiSquads, 44 | AiSquadItem, 45 | 46 | //h3 47 | AiEncounters, //base squads 48 | AiEncounterItem, 49 | AiStartingLocations, 50 | 51 | //odst 52 | AiGroupStartingLocations, 53 | AiSoloStartingLocations, 54 | 55 | // Sandbox 56 | ForgeVehicles, 57 | ForgeWeapons, 58 | ForgeEquipment, 59 | ForgeScenery, 60 | ForgeTeleporters, 61 | ForgeGoalObjects, 62 | ForgeSpawning, 63 | 64 | // Game Data 65 | TriggerVolumes, 66 | CameraPoints, 67 | StartProfiles, 68 | Decals 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/ObjectName.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Prism.Mvvm; 3 | using Reclaimer.Blam.Common; 4 | using Reclaimer.Plugins.MetaViewer; 5 | using Reclaimer.Resources; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Xml; 13 | 14 | namespace Reclaimer.Models 15 | { 16 | public class ObjectName : BindableBase 17 | { 18 | protected ScenarioModel Parent { get; } 19 | 20 | internal int BlockIndex { get; } 21 | internal ScenarioSection BlockReference => Parent.Sections[Section.ObjectNames]; 22 | 23 | internal long BlockStartAddress => BlockIndex < 0 ? -1 : BlockReference.TagBlock.Pointer.Address + BlockIndex * BlockReference.BlockSize; 24 | 25 | public string Name { get; set; } 26 | 27 | private short type; 28 | public short Type 29 | { 30 | get { return type; } 31 | set { SetProperty(ref type, value, OnValueChanged); } 32 | } 33 | 34 | private int placementIndex; 35 | public int PlacementIndex 36 | { 37 | get { return placementIndex; } 38 | set { SetProperty(ref placementIndex, value, OnValueChanged); } 39 | } 40 | 41 | public ObjectName(ScenarioModel parent, int index) 42 | { 43 | Parent = parent; 44 | BlockIndex = index; 45 | } 46 | 47 | public bool ComparePalette(string paletteType) 48 | { 49 | if (Parent.ScenarioTag.CacheFile.CacheType < CacheType.Halo4Beta) 50 | return string.Equals(paletteType, ((PlacementTypeHalo3)Type).ToString(), StringComparison.OrdinalIgnoreCase); 51 | else 52 | return string.Equals(paletteType, ((PlacementTypeHalo4)Type).ToString(), StringComparison.OrdinalIgnoreCase); 53 | } 54 | 55 | private void OnValueChanged() 56 | { 57 | if (Parent.IsBusy) 58 | return; 59 | 60 | //var nameOffset = OffsetById(BlockReference.Node, FieldId.Name); 61 | var typeOffset = OffsetById(BlockReference.Node, FieldId.PlacementType); 62 | var indexOffset = OffsetById(BlockReference.Node, FieldId.PlacementIndex); 63 | 64 | using (var writer = Parent.CreateWriter()) 65 | { 66 | writer.Seek(BlockStartAddress + typeOffset, SeekOrigin.Begin); 67 | writer.Write(Type); 68 | 69 | writer.Seek(BlockStartAddress + indexOffset, SeekOrigin.Begin); 70 | writer.Write((short)PlacementIndex); 71 | } 72 | } 73 | 74 | private int OffsetById(XmlNode node, string fieldId) 75 | { 76 | return node.SelectSingleNode($"*[@id='{fieldId}']")?.GetIntAttribute("offset") ?? -1; 77 | } 78 | 79 | public override string ToString() => Name; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/ObjectPlacement.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Adjutant.Spatial; 3 | using Reclaimer.Plugins.MetaViewer; 4 | using Reclaimer.Plugins.MetaViewer.Halo3; 5 | using Reclaimer.Resources; 6 | using Reclaimer.Utilities; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace Reclaimer.Models 14 | { 15 | public class ObjectPlacement : ScenarioObject 16 | { 17 | internal readonly string PaletteKey; 18 | 19 | internal override int BlockIndex => Parent.Palettes[PaletteKey].Placements.IndexOf(this); 20 | 21 | private short paletteIndex; 22 | public short PaletteIndex 23 | { 24 | get { return paletteIndex; } 25 | set 26 | { 27 | var oldValue = paletteIndex; 28 | if (SetProperty(ref paletteIndex, value, FieldId.PaletteIndex)) 29 | { 30 | Parent.RenderView?.RefreshObject(PaletteKey, this, FieldId.PaletteIndex); 31 | if (oldValue == -1) //reselect because theres actually something to select now 32 | Parent.RenderView?.SelectObject(Parent.SelectedNode, Parent.SelectedItemIndex); 33 | } 34 | } 35 | } 36 | 37 | private short nameIndex; 38 | public short NameIndex 39 | { 40 | get { return nameIndex; } 41 | set { SetProperty(ref nameIndex, value, FieldId.NameIndex); } 42 | } 43 | 44 | private RealVector3D rotation; 45 | public RealVector3D Rotation 46 | { 47 | get { return rotation; } 48 | set { SetProperty(ref rotation, value, FieldId.Rotation); } 49 | } 50 | 51 | private RealVector4D qrotation; 52 | public RealVector4D QRotation 53 | { 54 | get { return qrotation; } 55 | set { SetProperty(ref qrotation, value, FieldId.QRotation); } 56 | } 57 | 58 | private float scale; 59 | public float Scale 60 | { 61 | get { return scale; } 62 | set { SetProperty(ref scale, value, FieldId.Scale); } 63 | } 64 | 65 | private StringId variant; 66 | public StringId Variant 67 | { 68 | get { return variant; } 69 | set 70 | { 71 | if (SetProperty(ref variant, value, FieldId.Variant)) 72 | Parent.RenderView?.RefreshObject(PaletteKey, this, FieldId.Variant); 73 | } 74 | } 75 | 76 | public ObjectPlacement(ScenarioModel parent, string paletteKey) 77 | : base(parent) 78 | { 79 | PaletteKey = paletteKey; 80 | paletteIndex = -1; 81 | } 82 | 83 | protected override long GetFieldAddress(string fieldId) 84 | { 85 | var palette = Parent.Palettes[PaletteKey]; 86 | var block = palette.PlacementBlockRef; 87 | var fieldOffset = palette.PlacementsNode.SelectSingleNode($"*[@id='{fieldId}']")?.GetIntAttribute("offset") ?? -1; 88 | 89 | return fieldOffset < 0 ? fieldOffset : block.TagBlock.Pointer.Address + block.BlockSize * BlockIndex + fieldOffset; 90 | } 91 | 92 | public override string GetDisplayName() 93 | { 94 | var palette = Parent.Palettes[PaletteKey]; 95 | 96 | if (PaletteIndex < 0 || PaletteIndex >= palette.Palette.Count) 97 | return ""; 98 | 99 | string name; 100 | if (NameIndex >= 0 && NameIndex < Parent.ObjectNames.Count) 101 | name = Parent.ObjectNames[NameIndex].Name.AsDisplayName(); 102 | else name = palette.Palette[PaletteIndex].Tag?.FileName() ?? ""; 103 | 104 | return $"[{BlockIndex:D3}] {name}"; 105 | } 106 | 107 | public override void UpdateFromMetaValue(MetaValueBase meta, string fieldId) 108 | { 109 | switch (fieldId) 110 | { 111 | case FieldId.Position: 112 | case FieldId.Rotation: 113 | var multi = meta as MultiValue; 114 | var vector = new RealVector3D(multi.Value1, multi.Value2, multi.Value3); 115 | if (fieldId == FieldId.Position) 116 | Position = vector; 117 | else Rotation = vector; 118 | break; 119 | case FieldId.QRotation: 120 | multi = meta as MultiValue; 121 | QRotation = new RealVector4D(multi.Value1, multi.Value2, multi.Value3, multi.Value4); 122 | break; 123 | case FieldId.Scale: 124 | var simple = meta as SimpleValue; 125 | Scale = float.Parse(simple.Value.ToString()); 126 | break; 127 | case FieldId.PaletteIndex: 128 | var blockIndex = meta as BlockIndexValue; 129 | PaletteIndex = short.Parse(blockIndex.Value.ToString()); 130 | break; 131 | case FieldId.NameIndex: 132 | blockIndex = meta as BlockIndexValue; 133 | NameIndex = short.Parse(blockIndex.Value.ToString()); 134 | break; 135 | case FieldId.Variant: 136 | SetStringId(meta, ref variant, nameof(Variant)); 137 | break; 138 | } 139 | } 140 | 141 | public void CopyFrom(ObjectPlacement other) 142 | { 143 | Variant = other.Variant; 144 | Scale = other.Scale; 145 | QRotation = other.QRotation; 146 | Rotation = other.Rotation; 147 | Position = other.Position; 148 | NameIndex = other.NameIndex; 149 | PaletteIndex = other.PaletteIndex; 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/ScenarioListItem.cs: -------------------------------------------------------------------------------- 1 | using Prism.Mvvm; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Reclaimer.Plugins.MetaViewer; 8 | using Reclaimer.Resources; 9 | using Reclaimer.Plugins.MetaViewer.Halo3; 10 | using System.Windows.Controls; 11 | 12 | namespace Reclaimer.Models 13 | { 14 | public class ScenarioListItem : ListBoxItem, IMetaUpdateReceiver 15 | { 16 | public ScenarioListItem(string content) 17 | : this(content, null) 18 | { } 19 | 20 | public ScenarioListItem(IDisplayName nameProvider) 21 | : this(nameProvider?.GetDisplayName(), nameProvider) 22 | { } 23 | 24 | private ScenarioListItem(string content, object tag) 25 | { 26 | Content = content; 27 | Tag = tag; 28 | } 29 | 30 | public void Refresh() 31 | { 32 | var provider = Tag as IDisplayName; 33 | if (provider != null) 34 | Content = provider.GetDisplayName(); 35 | } 36 | 37 | void IMetaUpdateReceiver.UpdateFromMetaValue(MetaValueBase meta, string fieldId) 38 | { 39 | var receiver = Tag as IMetaUpdateReceiver; 40 | if (receiver == null) 41 | return; 42 | 43 | receiver.UpdateFromMetaValue(meta, fieldId); 44 | 45 | if (fieldId == FieldId.Name || fieldId == FieldId.NameIndex || fieldId == FieldId.TagReference) 46 | Content = receiver.GetDisplayName(); 47 | } 48 | 49 | string IDisplayName.GetDisplayName() => (Tag as IMetaUpdateReceiver)?.GetDisplayName(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/ScenarioObject.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Adjutant.Spatial; 3 | using Prism.Mvvm; 4 | using Reclaimer.Plugins.MetaViewer; 5 | using Reclaimer.Plugins.MetaViewer.Halo3; 6 | using Reclaimer.Resources; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Linq; 11 | using System.Runtime.CompilerServices; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace Reclaimer.Models 16 | { 17 | public abstract class ScenarioObject : BindableBase, IMetaUpdateReceiver 18 | { 19 | private volatile bool isBusy; 20 | 21 | protected ScenarioModel Parent { get; } 22 | 23 | internal abstract int BlockIndex { get; } 24 | 25 | private RealVector3D position; 26 | public RealVector3D Position 27 | { 28 | get { return position; } 29 | set { SetProperty(ref position, value, FieldId.Position); } 30 | } 31 | 32 | public ScenarioObject(ScenarioModel parent) 33 | { 34 | Parent = parent; 35 | } 36 | 37 | protected bool SetProperty(ref T storage, T value, string fieldId, [CallerMemberName] string propertyName = null) 38 | { 39 | if (Parent.IsBusy) //if the scenario is still loading just set the value and return 40 | return base.SetProperty(ref storage, value, propertyName); 41 | 42 | //if this object is busy dont set the value to avoid cyclic property setting 43 | if (isBusy || !base.SetProperty(ref storage, value, propertyName)) 44 | return false; 45 | 46 | var fieldAddress = GetFieldAddress(fieldId); 47 | if (fieldAddress < 0) 48 | return false; 49 | 50 | isBusy = true; 51 | 52 | using (var writer = Parent.CreateWriter()) 53 | { 54 | writer.Seek(fieldAddress, SeekOrigin.Begin); 55 | 56 | if (typeof(T) == typeof(StringId)) 57 | writer.Write(((StringId)(object)value).Id); 58 | else writer.WriteObject(value); 59 | } 60 | 61 | if (Parent.PropertyView?.CurrentItem == this) 62 | Parent.PropertyView.SetValue(fieldId, value); 63 | 64 | isBusy = false; 65 | 66 | return true; 67 | } 68 | 69 | protected abstract long GetFieldAddress(string fieldId); 70 | 71 | protected void SetStringId(MetaValueBase meta, ref StringId stringId, string propertyName) 72 | { 73 | var editor = meta as StringIdValue; 74 | var cache = Parent.ScenarioTag.CacheFile; 75 | var intValue = cache.StringIndex.GetStringId(editor.Value); 76 | 77 | if (intValue >= 0 && stringId.Id != intValue) 78 | { 79 | stringId = new StringId(intValue, cache); 80 | RaisePropertyChanged(propertyName); 81 | } 82 | } 83 | 84 | public abstract string GetDisplayName(); 85 | 86 | public abstract void UpdateFromMetaValue(MetaValueBase meta, string fieldId); 87 | 88 | public void RaiseBlockIndexChanged() 89 | { 90 | RaisePropertyChanged(nameof(BlockIndex)); 91 | } 92 | 93 | public override string ToString() => GetDisplayName(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/SceneNodeModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Models 8 | { 9 | public class SceneNodeModel : TreeItemModel 10 | { 11 | private NodeType nodeType; 12 | public NodeType NodeType 13 | { 14 | get { return nodeType; } 15 | set { SetProperty(ref nodeType, value); } 16 | } 17 | 18 | private int iconType; 19 | public int IconType 20 | { 21 | get { return iconType; } 22 | set { SetProperty(ref iconType, value); } 23 | } 24 | 25 | public SceneNodeModel() 26 | : base() 27 | { 28 | NodeType = NodeType.None; 29 | } 30 | 31 | public SceneNodeModel(string header, NodeType type) 32 | : base(header) 33 | { 34 | NodeType = type; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/StartPosition.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Spatial; 2 | using Reclaimer.Plugins.MetaViewer; 3 | using Reclaimer.Plugins.MetaViewer.Halo3; 4 | using Reclaimer.Resources; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Reclaimer.Models 12 | { 13 | public class StartPosition : ScenarioObject 14 | { 15 | internal override int BlockIndex => Parent.StartingPositions.IndexOf(this); 16 | 17 | private RealVector2D orientation; 18 | public RealVector2D Orientation 19 | { 20 | get { return orientation; } 21 | set { SetProperty(ref orientation, value, FieldId.Orientation); } 22 | } 23 | 24 | public StartPosition(ScenarioModel parent) 25 | : base(parent) 26 | { 27 | 28 | } 29 | 30 | protected override long GetFieldAddress(string fieldId) 31 | { 32 | var section = Parent.Sections[Section.StartPositions]; 33 | var fieldOffset = section.Node.SelectSingleNode($"*[@id='{fieldId}']").GetIntAttribute("offset") ?? 0; 34 | 35 | return section.TagBlock.Pointer.Address + section.BlockSize * BlockIndex + fieldOffset; 36 | } 37 | 38 | public override string GetDisplayName() 39 | { 40 | return $"starting position {Parent.StartingPositions.IndexOf(this)}"; 41 | } 42 | 43 | public override void UpdateFromMetaValue(MetaValueBase meta, string fieldId) 44 | { 45 | switch (fieldId) 46 | { 47 | case FieldId.Position: 48 | case FieldId.Orientation: 49 | var multi = meta as MultiValue; 50 | if (fieldId == FieldId.Position) 51 | Position = new RealVector3D(multi.Value1, multi.Value2, multi.Value3); 52 | else Orientation = new RealVector2D(multi.Value1, multi.Value2); 53 | break; 54 | } 55 | } 56 | 57 | public void CopyFrom(StartPosition other) 58 | { 59 | Orientation = other.Orientation; 60 | Position = other.Position; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/StructureBspModel.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Prism.Mvvm; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.IO.Endian; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using System.Windows.Controls; 11 | 12 | namespace Reclaimer.Models 13 | { 14 | public class StructureBspModel : BindableBase 15 | { 16 | internal bool IsBusy { get; private set; } 17 | internal Action LogError { get; set; } 18 | 19 | public IIndexItem StructureBspTag { get; } 20 | 21 | private IStructureBspHierarchyView hierarchyView; 22 | public IStructureBspHierarchyView HierarchyView 23 | { 24 | get { return hierarchyView; } 25 | set 26 | { 27 | var prev = hierarchyView; 28 | if (SetProperty(ref hierarchyView, value)) 29 | { 30 | prev?.ClearScenario(); 31 | hierarchyView?.SetScenario(this); 32 | } 33 | } 34 | } 35 | 36 | private IStructureBspPropertyView propertyView; 37 | public IStructureBspPropertyView PropertyView 38 | { 39 | get { return propertyView; } 40 | set 41 | { 42 | var prev = propertyView; 43 | if (SetProperty(ref propertyView, value)) 44 | { 45 | prev?.ClearScenario(); 46 | propertyView?.SetScenario(this); 47 | } 48 | } 49 | } 50 | 51 | private IStructureBspRenderView renderView; 52 | public IStructureBspRenderView RenderView 53 | { 54 | get { return renderView; } 55 | set 56 | { 57 | var prev = renderView; 58 | if (SetProperty(ref renderView, value)) 59 | { 60 | prev?.ClearScenario(); 61 | renderView?.SetScenario(this); 62 | } 63 | } 64 | } 65 | 66 | public StructureBspModel(IIndexItem item) 67 | { 68 | IsBusy = true; 69 | 70 | StructureBspTag = item; 71 | 72 | IsBusy = false; 73 | } 74 | 75 | public void SaveChanges() 76 | { 77 | switch (StructureBspTag.CacheFile.CacheType) 78 | { 79 | case CacheType.Halo3Retail: 80 | case CacheType.MccHalo3: 81 | SaveChangesHalo3(); 82 | break; 83 | } 84 | } 85 | 86 | private void SaveChangesHalo3() 87 | { 88 | var meta = StructureBspTag.ReadMetadata(); 89 | var baseAddress = meta.GeometryInstances.Pointer.Address; 90 | 91 | using (var fs = new FileStream(StructureBspTag.CacheFile.FileName, FileMode.Open, FileAccess.Write)) 92 | using (var writer = new EndianWriter(fs, StructureBspTag.CacheFile.ByteOrder)) 93 | { 94 | foreach (var placement in RenderView.GetPlacements()) 95 | { 96 | writer.Seek(baseAddress + 120 * placement.SourceIndex, SeekOrigin.Begin); 97 | 98 | writer.Write(placement.TransformScale); 99 | writer.Write(placement.M11); 100 | writer.Write(placement.M12); 101 | writer.Write(placement.M13); 102 | writer.Write(placement.M21); 103 | writer.Write(placement.M22); 104 | writer.Write(placement.M23); 105 | writer.Write(placement.M31); 106 | writer.Write(placement.M32); 107 | writer.Write(placement.M33); 108 | writer.Write(placement.M41); 109 | writer.Write(placement.M42); 110 | writer.Write(placement.M43); 111 | writer.Write((short)placement.MeshIndex); 112 | 113 | writer.Seek(baseAddress + 120 * placement.SourceIndex + 64, SeekOrigin.Begin); 114 | writer.Write(placement.SphereX); 115 | writer.Write(placement.SphereY); 116 | writer.Write(placement.SphereZ); 117 | writer.Write(placement.SphereRadius); 118 | } 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Models/TriggerVolume.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Adjutant.Spatial; 3 | using Reclaimer.Plugins.MetaViewer; 4 | using Reclaimer.Plugins.MetaViewer.Halo3; 5 | using Reclaimer.Resources; 6 | using Reclaimer.Utilities; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace Reclaimer.Models 14 | { 15 | public class TriggerVolume : ScenarioObject 16 | { 17 | public static readonly SharpDX.Color DefaultColour = new SharpDX.Color(0, 1, 0, 0.5f); 18 | public static readonly SharpDX.Color SelectedColour = new SharpDX.Color(0, 0.5f, 1, 0.5f); 19 | 20 | internal override int BlockIndex => Parent.TriggerVolumes.IndexOf(this); 21 | 22 | private StringId name; 23 | public StringId Name 24 | { 25 | get { return name; } 26 | set { SetProperty(ref name, value, FieldId.Name); } 27 | } 28 | 29 | private RealVector3D forwardVector; 30 | public RealVector3D ForwardVector 31 | { 32 | get { return forwardVector; } 33 | set { SetProperty(ref forwardVector, value, FieldId.ForwardVector); } 34 | } 35 | 36 | private RealVector3D upVector; 37 | public RealVector3D UpVector 38 | { 39 | get { return upVector; } 40 | set { SetProperty(ref upVector, value, FieldId.UpVector); } 41 | } 42 | 43 | private RealVector3D size; 44 | public RealVector3D Size 45 | { 46 | get { return size; } 47 | set { SetProperty(ref size, value, FieldId.Size); } 48 | } 49 | 50 | public TriggerVolume(ScenarioModel parent) 51 | : base(parent) 52 | { } 53 | 54 | protected override long GetFieldAddress(string fieldId) 55 | { 56 | var section = Parent.Sections[Section.TriggerVolumes]; 57 | var fieldOffset = section.Node.SelectSingleNode($"*[@id='{fieldId}']").GetIntAttribute("offset") ?? 0; 58 | 59 | return section.TagBlock.Pointer.Address + section.BlockSize * BlockIndex + fieldOffset; 60 | } 61 | 62 | public override string GetDisplayName() => $"[{BlockIndex:D3}] " + Name.AsDisplayName(); 63 | 64 | public override void UpdateFromMetaValue(MetaValueBase meta, string fieldId) 65 | { 66 | if (fieldId == FieldId.Name) 67 | { 68 | SetStringId(meta, ref name, nameof(Name)); 69 | return; 70 | } 71 | if (fieldId == FieldId.NameIndex) 72 | return; 73 | 74 | var multi = meta as MultiValue; 75 | var vector = new RealVector3D(multi.Value1, multi.Value2, multi.Value3); 76 | 77 | switch (fieldId) 78 | { 79 | case FieldId.ForwardVector: 80 | ForwardVector = vector; 81 | break; 82 | case FieldId.UpVector: 83 | UpVector = vector; 84 | break; 85 | case FieldId.Position: 86 | Position = vector; 87 | break; 88 | case FieldId.Size: 89 | Size = vector; 90 | break; 91 | } 92 | } 93 | 94 | public void CopyFrom(TriggerVolume other) 95 | { 96 | Name = other.Name; 97 | Position = other.Position; 98 | ForwardVector = other.ForwardVector; 99 | UpVector = other.UpVector; 100 | Size = other.Size; 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /Reclaimer.Architect/Plugins/ArchitectEditorPlugin.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Adjutant.Utilities; 3 | using Reclaimer.Annotations; 4 | using Reclaimer.Models; 5 | using Reclaimer.Windows; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Reflection; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace Reclaimer.Plugins 15 | { 16 | public class ArchitectEditorPlugin : Plugin 17 | { 18 | public override string Name => "Architect Editor"; 19 | 20 | public override bool CanOpenFile(OpenFileArgs args) 21 | { 22 | #if DEBUG 23 | return args.File.OfType().Any(i => Controls.DXEditor.CanOpenTag(i)) 24 | || args.File.OfType().Any(i => Controls.BspEditor.CanOpenTag(i)); 25 | #else 26 | return args.File.OfType().Any(i => Controls.DXEditor.CanOpenTag(i)); 27 | #endif 28 | } 29 | 30 | public override void OpenFile(OpenFileArgs args) 31 | { 32 | var modelTag = args.File.OfType().FirstOrDefault(); 33 | DisplayModel(args.TargetWindow, modelTag, args.FileName); 34 | } 35 | 36 | private IEnumerable EnumerateChildren(TabOwnerModelBase obj) 37 | { 38 | var container = obj as DockContainerModel; 39 | if (container != null) 40 | return EnumerateChildren(container.Content); 41 | 42 | var panel = obj as SplitPanelModel; 43 | if (panel != null) 44 | return panel.Items.Union(panel.Items.SelectMany(i => EnumerateChildren(i))); 45 | 46 | return Enumerable.Repeat(obj, 1); 47 | } 48 | 49 | [SharedFunction] 50 | public void DisplayModel(ITabContentHost targetWindow, IIndexItem modelTag, string fileName) 51 | { 52 | var container = targetWindow.DocumentPanel; 53 | 54 | LogOutput($"Loading tag: {fileName}"); 55 | 56 | try 57 | { 58 | if (Controls.DXEditor.CanOpenTag(modelTag)) 59 | { 60 | var hierarchyView = new Controls.HierarchyView(); 61 | var propertyView = new Controls.PropertyView(); 62 | var renderView = new Controls.DXEditor(); 63 | 64 | var model = new ScenarioModel(modelTag) 65 | { 66 | HierarchyView = hierarchyView, 67 | PropertyView = propertyView, 68 | RenderView = renderView, 69 | LogOutput = LogOutput, 70 | LogError = LogError 71 | }; 72 | 73 | var existingToolWells = EnumerateChildren(targetWindow.DockContainer) 74 | .OfType() 75 | .ToList(); 76 | 77 | targetWindow.DocumentPanel.AddItem(renderView.TabModel); 78 | targetWindow.DockContainer.AddTool(hierarchyView.TabModel, null, System.Windows.Controls.Dock.Right); 79 | targetWindow.DockContainer.AddTool(propertyView.TabModel, hierarchyView.TabModel.Parent, System.Windows.Controls.Dock.Bottom); 80 | 81 | //the panels attempt to size to the contents but since the controls havent loaded yet 82 | //they will have no size - we need to set a size for them. 83 | hierarchyView.TabModel.Parent.PanelSize = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star); 84 | propertyView.TabModel.Parent.PanelSize = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star); 85 | hierarchyView.TabModel.Parent.Parent.PanelSize = new System.Windows.GridLength(500); 86 | 87 | foreach (var well in existingToolWells) 88 | well.TogglePinStatusCommand.Execute(null); 89 | 90 | renderView.LoadScenario(); 91 | } 92 | else 93 | { 94 | var propertyView = new Controls.InstancePropertyView(); 95 | var renderView = new Controls.BspEditor(); 96 | 97 | var model = new StructureBspModel(modelTag) 98 | { 99 | PropertyView = propertyView, 100 | RenderView = renderView, 101 | LogError = LogError 102 | }; 103 | 104 | var existingToolWells = EnumerateChildren(targetWindow.DockContainer) 105 | .OfType() 106 | .ToList(); 107 | 108 | targetWindow.DocumentPanel.AddItem(renderView.TabModel); 109 | targetWindow.DockContainer.AddTool(propertyView.TabModel, null, System.Windows.Controls.Dock.Right); 110 | 111 | //the panels attempt to size to the contents but since the controls havent loaded yet 112 | //they will have no size - we need to set a size for them. 113 | propertyView.TabModel.Parent.PanelSize = new System.Windows.GridLength(500); 114 | 115 | foreach (var well in existingToolWells) 116 | well.TogglePinStatusCommand.Execute(null); 117 | 118 | renderView.LoadStructureBsp(); 119 | } 120 | 121 | LogOutput($"Loaded tag: {fileName}"); 122 | } 123 | catch (Exception e) 124 | { 125 | LogError($"Error loading tag: {fileName}", e, true); 126 | } 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Plugins/ArchitectSettingsPlugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Reclaimer.Plugins 11 | { 12 | public class ArchitectSettingsPlugin : Plugin 13 | { 14 | public override string Name => "Architect"; 15 | 16 | //this needs to have a value at design time because the editor control uses it 17 | internal static ArchitectSettings Settings = new ArchitectSettings(); 18 | 19 | public override void Initialise() 20 | { 21 | Settings = LoadSettings(); 22 | 23 | foreach (var f in Directory.GetFiles($"{Substrate.PluginsDirectory}\\Architect", "*.dll")) 24 | Assembly.LoadFile(f); 25 | } 26 | 27 | public override void Suspend() 28 | { 29 | SaveSettings(Settings); 30 | } 31 | } 32 | 33 | internal class ArchitectSettings 34 | { 35 | [DisplayName("Default Save Format")] 36 | [Description("The initially selected file type when saving a model")] 37 | public string DefaultSaveFormat { get; set; } 38 | 39 | [DisplayName("Default FOV")] 40 | [Description("The starting camera field of view in the editor")] 41 | public double DefaultFieldOfView { get; set; } 42 | 43 | [DisplayName("Highlight Selection")] 44 | [Description("Highlight the currently selected object in the editor")] 45 | public bool HighlightSelection { get; set; } 46 | 47 | [DisplayName("Editor Translation")] 48 | [Description("Show the translation handles on the editor gizmo")] 49 | public bool EditorTranslation { get; set; } 50 | 51 | [DisplayName("Editor Rotation")] 52 | [Description("Show the rotation handles on the editor gizmo")] 53 | public bool EditorRotation { get; set; } 54 | 55 | [DisplayName("Editor Scaling")] 56 | [Description("Show the scale handles on the editor gizmo")] 57 | public bool EditorScaling { get; set; } 58 | 59 | [DisplayName("Editor Global Axes")] 60 | [Description("Toggle between global and local axis mode on the editor gizmo")] 61 | public bool EditorGlobalAxes { get; set; } 62 | 63 | public ArchitectSettings() 64 | { 65 | DefaultSaveFormat = "amf"; 66 | DefaultFieldOfView = 45; 67 | 68 | HighlightSelection = true; 69 | EditorTranslation = EditorRotation = EditorScaling = true; 70 | EditorGlobalAxes = true; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Plugins/ArchitectViewerPlugin.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Adjutant.Utilities; 3 | using Reclaimer.Annotations; 4 | using Reclaimer.Models; 5 | using Reclaimer.Windows; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Reflection; 11 | using System.Text; 12 | using System.Threading.Tasks; 13 | 14 | namespace Reclaimer.Plugins 15 | { 16 | public class ArchitectViewerPlugin : Plugin 17 | { 18 | public override string Name => "Architect Viewer"; 19 | 20 | public override bool CanOpenFile(OpenFileArgs args) 21 | { 22 | return args.File.Any(i => i is IRenderGeometry) 23 | || args.File.OfType().Any(i => Controls.DXViewer.CanOpenTag(i)); 24 | } 25 | 26 | public override void OpenFile(OpenFileArgs args) 27 | { 28 | var model = args.File.OfType().FirstOrDefault(); 29 | if (model != null) 30 | { 31 | DisplayModel(args.TargetWindow, model, args.FileName); 32 | return; 33 | } 34 | 35 | var modelTag = args.File.OfType().FirstOrDefault(); 36 | DisplayModel(args.TargetWindow, modelTag, args.FileName); 37 | } 38 | 39 | [SharedFunction] 40 | public void DisplayModel(ITabContentHost targetWindow, IRenderGeometry model, string fileName) 41 | { 42 | var container = targetWindow.DocumentPanel; 43 | 44 | LogOutput($"Loading model: {fileName}"); 45 | 46 | try 47 | { 48 | var viewer = new Controls.DXViewer 49 | { 50 | LogOutput = LogOutput, 51 | LogError = LogError, 52 | SetStatus = SetWorkingStatus, 53 | ClearStatus = ClearWorkingStatus 54 | }; 55 | 56 | viewer.LoadGeometry(model, $"{fileName}"); 57 | 58 | container.AddItem(viewer.TabModel); 59 | 60 | LogOutput($"Loaded model: {fileName}"); 61 | } 62 | catch (Exception e) 63 | { 64 | LogError($"Error loading model: {fileName}", e); 65 | } 66 | } 67 | 68 | [SharedFunction] 69 | public void DisplayModel(ITabContentHost targetWindow, IIndexItem modelTag, string fileName) 70 | { 71 | var container = targetWindow.DocumentPanel; 72 | 73 | LogOutput($"Loading model: {fileName}"); 74 | 75 | try 76 | { 77 | var viewer = new Controls.DXViewer 78 | { 79 | LogOutput = LogOutput, 80 | LogError = LogError, 81 | SetStatus = SetWorkingStatus, 82 | ClearStatus = ClearWorkingStatus 83 | }; 84 | 85 | viewer.LoadGeometry(modelTag, $"{fileName}"); 86 | 87 | container.AddItem(viewer.TabModel); 88 | 89 | LogOutput($"Loaded model: {fileName}"); 90 | } 91 | catch (Exception e) 92 | { 93 | LogError($"Error loading model: {fileName}", e); 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("Architect")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("Reclaimer.Architect")] 15 | [assembly: AssemblyCopyright("Copyright © Gravemind2401 2020 - 2021")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly:ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.4.201.0")] 55 | [assembly: AssemblyFileVersion("1.4.201.0")] 56 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Reclaimer.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Resources/AiSection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Resources 8 | { 9 | public static class AiSection 10 | { 11 | public const string SquadGroups = "squadgroups"; 12 | public const string Zones = "zones"; 13 | public const string FiringPositions = "firingpositions"; 14 | public const string Areas = "areas"; 15 | public const string Squads = "squads"; 16 | 17 | //h3 only 18 | public const string Encounters = "encounters"; 19 | public const string StartLocations = "startinglocations"; 20 | 21 | //odst+ 22 | public const string GroupSTartLocations = "groupstartlocations"; 23 | public const string SoloStartLocations = "singlestartlocations"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Resources/FieldId.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Resources 8 | { 9 | public static class FieldId 10 | { 11 | public const string Name = "name"; 12 | public const string NameIndex = "nameindex"; 13 | public const string Orientation = "orientation"; 14 | public const string PaletteIndex = "paletteindex"; 15 | public const string ParentIndex = "parentindex"; 16 | public const string PlacementIndex = "placementindex"; 17 | public const string PlacementType = "placementtype"; 18 | public const string Position = "position"; 19 | public const string Rotation = "rotation"; 20 | public const string QRotation = "qrotation"; 21 | public const string ForwardVector = "forwardvector"; 22 | public const string UpVector = "upvector"; 23 | public const string Scale = "scale"; 24 | public const string Size = "size"; 25 | public const string TagReference = "tagreference"; 26 | public const string Variant = "variant"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Resources/MetadataXML/Minimize.xslt: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Resources/NodeHierarchy.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Resources/PaletteType.cs: -------------------------------------------------------------------------------- 1 | using Reclaimer.Models; 2 | using Reclaimer.Utilities; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Reclaimer.Resources 10 | { 11 | public static class PaletteType 12 | { 13 | public const string Biped = "biped"; 14 | public const string Vehicle = "vehicle"; 15 | public const string Weapon = "weapon"; 16 | public const string Equipment = "equipment"; 17 | public const string Scenery = "scenery"; 18 | public const string Machine = "machine"; 19 | public const string Control = "control"; 20 | public const string Crate = "crate"; 21 | public const string LightFixture = "lightfixture"; 22 | public const string Decal = "decal"; 23 | 24 | private static Dictionary ByNodeType = new Dictionary 25 | { 26 | { NodeType.Scenery, PaletteType.Scenery }, 27 | { NodeType.Bipeds, PaletteType.Biped }, 28 | { NodeType.Vehicles, PaletteType.Vehicle }, 29 | { NodeType.Equipment, PaletteType.Equipment }, 30 | { NodeType.Weapons, PaletteType.Weapon }, 31 | { NodeType.Machines, PaletteType.Machine }, 32 | { NodeType.Controls, PaletteType.Control }, 33 | { NodeType.LightFixtures, PaletteType.LightFixture }, 34 | { NodeType.Crates, PaletteType.Crate }, 35 | { NodeType.Decals, PaletteType.Decal } 36 | }; 37 | 38 | public static string FromNodeType(NodeType type) => ByNodeType.ValueOrDefault(type); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Resources/ScenarioXML/TemplateScenario.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 |
79 |
80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 |
95 |
-------------------------------------------------------------------------------- /Reclaimer.Architect/Resources/Section.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Resources 8 | { 9 | public static class Section 10 | { 11 | public const string Mission = "mission"; 12 | public const string StructureBsps = "structurebsps"; 13 | public const string Skies = "skies"; 14 | public const string ObjectNames = "objectnames"; 15 | public const string Palettes = "palettes"; 16 | public const string Placements = "placements"; 17 | public const string StartPositions = "startpositions"; 18 | public const string StartProfiles = "startprofiles"; 19 | public const string TriggerVolumes = "triggervolumes"; 20 | public const string Sandbox = "sandbox"; 21 | public const string Squads = "squads"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Themes/Generic.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 40 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Utilities/EulerTransformConverter.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Spatial; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Data; 9 | 10 | using static HelixToolkit.Wpf.SharpDX.Media3DExtension; 11 | 12 | using Media3D = System.Windows.Media.Media3D; 13 | using Helix = HelixToolkit.Wpf.SharpDX; 14 | using Adjutant.Geometry; 15 | 16 | namespace Reclaimer.Utilities 17 | { 18 | public class EulerTransformConverter : IMultiValueConverter 19 | { 20 | public static EulerTransformConverter Instance { get; } = new EulerTransformConverter(); 21 | 22 | private EulerTransformConverter() { } 23 | 24 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | var position = (IXMVector)values[0]; 27 | var rotation = (IXMVector)values[1]; 28 | var scale = values.Length > 2 ? (float)values[2] : 1f; 29 | 30 | var euler = new SharpDX.Vector3(rotation.X, rotation.Y, float.IsNaN(rotation.Z) ? 0f : rotation.Z); 31 | 32 | var matrix = SharpDX.Matrix.Scaling(scale == 0 ? 1 : scale) 33 | * SharpDX.Matrix.RotationQuaternion(euler.EulerToQuaternion()) 34 | * SharpDX.Matrix.Translation(position.ToVector3()); 35 | 36 | return new Media3D.MatrixTransform3D(matrix.ToMatrix3D()); 37 | } 38 | 39 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 40 | { 41 | var matrix = ((Media3D.MatrixTransform3D)value).Value.ToMatrix(); 42 | 43 | SharpDX.Vector3 position; 44 | SharpDX.Quaternion rotation; 45 | float scale; 46 | 47 | matrix.DecomposeUniformScale(out scale, out rotation, out position); 48 | var euler = rotation.ToEulerAngles(); 49 | 50 | //floating point error in rotation conversion makes it hard to keep the scale still 51 | scale = (float)Math.Round(scale, 4); 52 | 53 | var result = new object[3]; 54 | result[0] = position.ToRealVector3D(); 55 | result[1] = targetTypes[1] == typeof(RealVector2D) 56 | ? (object)new RealVector2D(euler.X, euler.Y) 57 | : (object)new RealVector3D(euler.X, euler.Y, euler.Z); 58 | result[2] = scale; 59 | return result; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Utilities/IO/IBlockEditor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Utilities.IO 8 | { 9 | public interface IBlockEditor 10 | { 11 | void Add(); 12 | void Remove(int index); 13 | void Insert(int index); 14 | void Copy(int sourceIndex, int destIndex); 15 | void Resize(int newCount); 16 | int EntryCount { get; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Utilities/IO/IMetadataStream.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Blam.Common; 2 | using Adjutant.Utilities; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO.Endian; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Reclaimer.Utilities.IO 11 | { 12 | internal interface IMetadataStream 13 | { 14 | bool IsInitialised { get; } 15 | IIndexItem SourceTag { get; } 16 | ICacheFile SourceCache { get; } 17 | ByteOrder ByteOrder { get; } 18 | IAddressTranslator AddressTranslator { get; } 19 | IPointerExpander PointerExpander { get; } 20 | void ResizeTagBlock(EndianWriterEx writer, ref TagBlock block, int entrySize, int newCount); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Utilities/IO/MemoryTracker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Reclaimer.Utilities.IO 8 | { 9 | internal class MemoryTracker 10 | { 11 | private readonly List freeSpace = new List(); 12 | 13 | public void Allocate(int start, int size) 14 | { 15 | var request = new Span(start, size); 16 | var free = freeSpace.FirstOrDefault(s => s.Contains(request)); 17 | if (free == null) 18 | throw new ArgumentException("Requested span cannot be allocated because it is in use."); 19 | 20 | freeSpace.Remove(free); 21 | if (free.Start == request.Start && free.End == request.End) 22 | return; //nothing left 23 | 24 | if (request.Start > free.Start) //leftover space at the beginning 25 | freeSpace.Add(new Span(free.Start, request.Start - free.Start)); 26 | 27 | if (request.End < free.End) //leftover space at the end 28 | freeSpace.Add(new Span(request.End, free.End - request.End)); 29 | } 30 | 31 | public void Release(int start, int size) 32 | { 33 | freeSpace.Add(new Span(start, size)); 34 | Defragment(); 35 | } 36 | 37 | public bool Find(int size, out int start) 38 | { 39 | //try to fill the smallest gap available 40 | var span = freeSpace 41 | .OrderBy(s => s.Size) 42 | .FirstOrDefault(s => s.Size >= size); 43 | start = span?.End - size ?? 0; 44 | return span != null; 45 | } 46 | 47 | //insert new free space 48 | public void Insert(int position, int count) 49 | { 50 | var containing = freeSpace.FirstOrDefault(s => s.Contains(position)); 51 | var shifted = freeSpace.Where(s => s.Start > position).ToList(); 52 | 53 | //offset everything after the insert 54 | foreach (var span in shifted) 55 | span.Start += count; 56 | 57 | //if the space was inserted in the middle of a span then expand it 58 | if (containing != null) 59 | containing.Size += count; 60 | else //else release it as a new span 61 | Release(position, count); 62 | } 63 | 64 | private void Defragment() 65 | { 66 | freeSpace.Sort((a, b) => a.Start.CompareTo(b.Start)); 67 | for (int i = 0; i < freeSpace.Count - 1;) 68 | { 69 | var a = freeSpace[i]; 70 | var b = freeSpace[i + 1]; 71 | 72 | if (a.End >= b.Start) //the spans are sequential or overlapping 73 | { 74 | //dont increment - keep merging as long as possible 75 | freeSpace.RemoveAt(i + 1); 76 | a.Size = b.End - a.Start; 77 | 78 | //(but break if we just merged the last span) 79 | if (i == freeSpace.Count - 1) 80 | break; 81 | } 82 | else i++; //increment and try the next one 83 | } 84 | } 85 | 86 | private class Span 87 | { 88 | public int Start { get; set; } 89 | public int Size { get; set; } 90 | public int End => Start + Size; 91 | 92 | public Span(int start, int size) 93 | { 94 | Start = start; 95 | Size = size; 96 | } 97 | 98 | public bool Contains(int position) => position >= Start && position < End; 99 | public bool Contains(Span other) => Contains(other.Start) && Contains(other.End - 1); 100 | 101 | public override string ToString() => $"{Start} - {End} ({Size})"; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Utilities/IsPaletteNodeConverter.cs: -------------------------------------------------------------------------------- 1 | using Reclaimer.Models; 2 | using Reclaimer.Resources; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows; 10 | using System.Windows.Data; 11 | 12 | namespace Reclaimer.Utilities 13 | { 14 | public class IsPaletteNodeConverter : IValueConverter 15 | { 16 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | var nodeType = value as NodeType? ?? NodeType.None; 19 | var action = parameter?.ToString(); 20 | 21 | var paletteKey = PaletteType.FromNodeType(nodeType); 22 | if (paletteKey != null) 23 | return true; 24 | 25 | return false; 26 | } 27 | 28 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 29 | { 30 | throw new NotImplementedException(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Utilities/MatrixTransformConverter.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Spatial; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Data; 9 | 10 | using static HelixToolkit.Wpf.SharpDX.Media3DExtension; 11 | 12 | using Numerics = System.Numerics; 13 | using Media3D = System.Windows.Media.Media3D; 14 | using Helix = HelixToolkit.Wpf.SharpDX; 15 | 16 | namespace Reclaimer.Utilities 17 | { 18 | public class MatrixTransformConverter : IMultiValueConverter 19 | { 20 | public static MatrixTransformConverter Instance { get; } = new MatrixTransformConverter(); 21 | 22 | private MatrixTransformConverter() { } 23 | 24 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | var scale = (float)values[0]; 27 | var transform = (Numerics.Matrix4x4)values[1]; 28 | 29 | var tGroup = new Media3D.Transform3DGroup(); 30 | 31 | if (scale != 1) 32 | { 33 | var tform = new Media3D.ScaleTransform3D(scale, scale, scale); 34 | 35 | tform.Freeze(); 36 | tGroup.Children.Add(tform); 37 | } 38 | 39 | if (!transform.IsIdentity) 40 | { 41 | var tform = new Media3D.MatrixTransform3D(transform.ToMatrix3D()); 42 | 43 | tform.Freeze(); 44 | tGroup.Children.Add(tform); 45 | } 46 | 47 | tGroup.Freeze(); 48 | return tGroup; 49 | } 50 | 51 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 52 | { 53 | var matrix = ((Media3D.MatrixTransform3D)value).Value.ToMatrix(); 54 | 55 | float scale; 56 | SharpDX.Quaternion rotation; 57 | SharpDX.Vector3 position; 58 | 59 | matrix.DecomposeUniformScale(out scale, out rotation, out position); 60 | 61 | var result = new object[2]; 62 | result[0] = scale; 63 | result[1] = (SharpDX.Matrix.RotationQuaternion(rotation) * SharpDX.Matrix.Translation(position)).ToNumericsMatrix4x4(); 64 | 65 | return result; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Utilities/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Reclaimer.Utilities 10 | { 11 | internal static class NativeMethods 12 | { 13 | [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] 14 | public static extern short GetAsyncKeyState(int KeyID); 15 | 16 | [DllImport("user32.dll")] 17 | public static extern bool GetCursorPos(out Point Point); 18 | 19 | [DllImport("user32.dll")] 20 | public static extern int SetCursorPos(int X, int Y); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Utilities/QuaternionTransformConverter.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Spatial; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Data; 9 | 10 | using static HelixToolkit.Wpf.SharpDX.Media3DExtension; 11 | 12 | using Media3D = System.Windows.Media.Media3D; 13 | using Helix = HelixToolkit.Wpf.SharpDX; 14 | using Adjutant.Geometry; 15 | 16 | namespace Reclaimer.Utilities 17 | { 18 | public class QuaternionTransformConverter : IMultiValueConverter 19 | { 20 | public static QuaternionTransformConverter Instance { get; } = new QuaternionTransformConverter(); 21 | 22 | private QuaternionTransformConverter() { } 23 | 24 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | var position = (IXMVector)values[0]; 27 | var rotation = (IXMVector)values[1]; 28 | var scale = values.Length > 2 ? (float)values[2] : 1f; 29 | 30 | var matrix = SharpDX.Matrix.Scaling(scale == 0 ? 1 : scale) 31 | * SharpDX.Matrix.RotationQuaternion(rotation.ToQuaternion()) 32 | * SharpDX.Matrix.Translation(position.ToVector3()); 33 | 34 | return new Media3D.MatrixTransform3D(matrix.ToMatrix3D()); 35 | } 36 | 37 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 38 | { 39 | var matrix = ((Media3D.MatrixTransform3D)value).Value.ToMatrix(); 40 | 41 | SharpDX.Vector3 position; 42 | SharpDX.Quaternion rotation; 43 | float scale; 44 | 45 | matrix.DecomposeUniformScale(out scale, out rotation, out position); 46 | 47 | //floating point error in rotation conversion makes it hard to keep the scale still 48 | scale = (float)Math.Round(scale, 4); 49 | 50 | var result = new object[3]; 51 | result[0] = position.ToRealVector3D(); 52 | result[1] = rotation.ToRealVector4D(); 53 | result[2] = scale; 54 | return result; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Utilities/SharpDXVectorConverter.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Spatial; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Data; 9 | 10 | using static HelixToolkit.Wpf.SharpDX.Media3DExtension; 11 | 12 | using Media3D = System.Windows.Media.Media3D; 13 | using Helix = HelixToolkit.Wpf.SharpDX; 14 | 15 | namespace Reclaimer.Utilities 16 | { 17 | public class SharpDXVectorConverter : IValueConverter 18 | { 19 | public static SharpDXVectorConverter Instance { get; } = new SharpDXVectorConverter(); 20 | 21 | private SharpDXVectorConverter() { } 22 | 23 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 24 | { 25 | if (value is IRealVector3D) 26 | return ((IRealVector3D)value).ToVector3(); 27 | 28 | return null; 29 | } 30 | 31 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 32 | { 33 | if (value is SharpDX.Vector3) 34 | return ((SharpDX.Vector3)value).ToRealVector3D(); 35 | 36 | return null; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Utilities/TranslationTransformConverter.cs: -------------------------------------------------------------------------------- 1 | using Adjutant.Spatial; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Data; 9 | 10 | using static HelixToolkit.Wpf.SharpDX.Media3DExtension; 11 | 12 | using Media3D = System.Windows.Media.Media3D; 13 | using Helix = HelixToolkit.Wpf.SharpDX; 14 | using Adjutant.Geometry; 15 | 16 | namespace Reclaimer.Utilities 17 | { 18 | public class TranslationTransformConverter : IValueConverter 19 | { 20 | public static TranslationTransformConverter Instance { get; } = new TranslationTransformConverter(); 21 | 22 | private TranslationTransformConverter() { } 23 | 24 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | var position = (IXMVector)value; 27 | var matrix = SharpDX.Matrix.Translation(position.ToVector3()); 28 | return new Media3D.MatrixTransform3D(matrix.ToMatrix3D()); 29 | } 30 | 31 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 32 | { 33 | var matrix = ((Media3D.MatrixTransform3D)value).Value.ToMatrix(); 34 | return matrix.TranslationVector.ToRealVector3D(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Reclaimer.Architect/Utilities/ViewDistanceGroupNode.cs: -------------------------------------------------------------------------------- 1 | using HelixToolkit.Wpf.SharpDX; 2 | using HelixToolkit.Wpf.SharpDX.Model.Scene; 3 | using SharpDX; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Reclaimer.Utilities 11 | { 12 | public sealed class ViewDistanceGroupNode : GroupNode 13 | { 14 | private readonly Element3D element; 15 | 16 | private bool isDormant; 17 | private BoundingBox originalBounds; 18 | 19 | public ViewDistanceGroupNode(Element3D element) 20 | { 21 | this.element = element; 22 | } 23 | 24 | private void ScanAncestors() 25 | { 26 | //if one of the ancestors is already a ViewDistanceGroupNode 27 | //then this node should act like a regular GroupNode 28 | //so it does not interfere with the ancestor 29 | 30 | isDormant = false; 31 | var ancestor = Parent; 32 | while (ancestor != null) 33 | { 34 | if (ancestor is ViewDistanceGroupNode) 35 | { 36 | isDormant = true; 37 | return; 38 | } 39 | else ancestor = ancestor.Parent; 40 | } 41 | } 42 | 43 | protected override void OnAttached() 44 | { 45 | base.OnAttached(); 46 | 47 | isDormant = true; 48 | //ScanAncestors(); 49 | //originalBounds = element.GetTotalBounds(true); 50 | } 51 | 52 | protected override bool CanRender(RenderContext context) 53 | { 54 | var baseValue = base.CanRender(context); 55 | if (isDormant || !baseValue) 56 | return baseValue; 57 | 58 | var bounds = originalBounds.Transform(element.Transform.ToMatrix()); 59 | var camPos = context.Camera.Position; 60 | if (bounds.Contains(ref camPos) == ContainmentType.Contains) 61 | return true; 62 | 63 | //var length = (bounds.Center - camPos).Length(); 64 | //if (length < 5) 65 | // return true; 66 | 67 | var screenBounds = bounds.Project(context); 68 | return screenBounds.Width > 3 && screenBounds.Height > 3; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Reclaimer.Architect/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------