-
Notifications
You must be signed in to change notification settings - Fork 83
Document loaders
In Sledge, a document is what appears inside the main tabbed view in the middle of the window. Currently the only available document type is the MapDocument
, which is the document class for the BSP editor.
Required reading: Exporting and importing classes • Map primitive objects
By default, most common formats are built into Sledge, including:
- RMF (Hammer 3)
- VMF (Hammer 4)
- MAP (Quake)
- SMF (Sledge native format)
Other formats are planned, including JMF (Jackhammer editor) and OBJ (3D models).
Adding a custom format isn't difficult if you already know the format. The example below is from the OBJ loader.
Start by implementing the shell of the IBspSourceProvider
interface. The interface is documented below.
[Export(typeof(IBspSourceProvider))]
public class ObjBspSourceProvider : IBspSourceProvider
{
private static readonly IEnumerable<Type> SupportedTypes = new List<Type>
{
// Sledge only supports solids in the OBJ format
typeof(Solid),
};
// A list of data types supported by this loader.
public IEnumerable<Type> SupportedDataTypes => SupportedTypes;
// A list of file extensions supported by this loader.
public IEnumerable<FileExtensionInfo> SupportedFileExtensions { get; } = new[]
{
new FileExtensionInfo("Wavefront model format", ".obj")
};
// Code to load the map from a file goes here
public Task<BspFileLoadResult> Load(Stream stream, IEnvironment environment)
{
throw new NotImplementedException();
}
// Code to save the map to a file goes here
public Task Save(Stream stream, Map map)
{
throw new NotImplementedException();
}
}
The BspFileLoadResult
is a simple class which contains the loaded map, a list of invalid objects, and a list of messages to show the user. If there are no messages or invalid objects for the format, these can be ignored, you just have to set the map.
Both the Load
and Save
methods are asynchronous, so it's a good idea to take advantage of that fact whenever possible. But loading data from a single file is usually synchronous code, so most loaders will probably just launch a new task to do the work.
public Task<BspFileLoadResult> Load(Stream stream, IEnvironment environment)
{
return Task.Run(() =>
{
using (var reader = new StreamReader(stream, Encoding.ASCII, true, 1024, false))
{
var result = new BspFileLoadResult();
var map = new Map();
// Read map from file
result.Map = map;
return result;
}
});
}
public Task Save(Stream stream, Map map)
{
return Task.Run(() =>
{
using (var writer = new StreamWriter(stream, Encoding.ASCII, 1024, true))
{
// Write map data here
}
});
}
The actual loading code will vary depending on the format you are loading. Refer to the code for the current loaders for an idea on how the code should look.
The most important rule is that DescendantsChanged
should be called on each object when the code has finished building an object. This will generate the bounding box for the object and update its parent bounding boxes as well. This is required for interaction to work correctly.
If you're doing some advanced stuff by adding a whole new document type, then you will need to implement the IDocumentLoader
interface for your document type. The BspSourceDocumentLoader
is the only class that currently implements this interface.
This interface is well documented and shouldn't be too difficult to implement, so refer to the code comments and the BspSourceDocumentLoader
code when implementing this interface. Be sure that you have exported your class with [Export(typeof(IDocumentLoader))]
.