14. Load and save scenesThe file format I have chosen is XML.
Let's get straight in the heart of the matter with a sample file:
+ Code Snippet<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<LV3D>
<Settings MediaPath="media\"/>
<Resource Type="Mesh" FilePath="" FileName="Object1.x"/>
<Resource Type="Mesh" FilePath="Folder1\" FileName="Object2.x"/>
<Resource Type="Mesh" FilePath="Folder1\" FileName="Object3.x"/>
<Resource Type="Texture" FilePath="" FileName="Texture1.bmp"/>
<Material Procedure="StdLighting" Name="Std Lighting 1">
<StdLighting Diffuse="FFFF00FF" Specular="FFFFFF" Emissive="0" Ambient="FFFFFF"/>
</Material>
<Material Procedure="SingleTexture" Name="Single Texture 1">
<SingleTexture TextureResource="4"/>
</Material>
<Material Procedure="StdLighting" Name="Std Lighting 2">
<StdLighting Diffuse="FFFF00FF" Specular="0" Emissive="0" Ambient="0"/>
</Material>
<Camera Yaw="18" Pitch="39" FOVLevel="0" XC="0.512499928474" YC="0.471866965294" ZC="0.230144843459"/>
<Light Yaw="0" Pitch="90"/>
<EntityList>
<Entity Name="Cube 0" Enabled="1">
<ModuleTransform Enabled="1" PosX="-1" PosY="0" PosZ="0" RotX="0" RotY="0" RotZ="0" SclX="1" SclY="1" SclZ="1"/>
<ModuleCube Enabled="1"/>
<ModuleObjRender Enabled="1" Material="1" EnObjLight="1"/>
</Entity>
<Entity Name="Sphere 0" Enabled="1">
<ModuleTransform Enabled="1" PosX="1" PosY="0" PosZ="0.5" RotX="0" RotY="0" RotZ="0" SclX="1" SclY="1" SclZ="1"/>
<ModuleSphere Enabled="1" RowsIndex="9" ColumnsIndex="9"/>
<ModuleObjRender Enabled="1" Material="2" EnObjLight="1"/>
</Entity>
<Entity Name="Object 1" Enabled="1">
<ModuleTransform Enabled="1" PosX="1" PosY="0" PosZ="2" RotX="0" RotY="0" RotZ="0" SclX="1" SclY="1" SclZ="1"/>
<ModuleMesh Enabled="1" MeshResource="3"/>
<ModuleObjRender Enabled="1" Material="1" EnObjLight="1"/>
</Entity>
</EntityList>
</LV3D>
Basically it is a text file, so saving it has no particular difficulty.
But loading it is a different kettle of fish. Because it needs a complete parsing.
For that task, we'll use the Madbit's XML plugin, it will let us spare a lot of time!!
Saving a sceneIf you've looked to the source, you may have noticed these mysterious
..._TranslateToXML void functions.
You've have likely guessed what they do: converting data values of the component into a XML formatted String...
+ Code SnippetFunction ModuleTransform_TranslateToXML(Module)
Local ModuleData As DWORD
s$ = "<ModuleTransform"+XMLAttr("Enabled", Str$(Module_GetEnabled(Module)))
ModuleData = Modules(Module).ModuleData
s$ = s$+XMLAttr("PosX", Str$(Peek Float(ModuleData+MODULETRANSFORM_POSITIONX)))
s$ = s$+XMLAttr("PosY", Str$(Peek Float(ModuleData+MODULETRANSFORM_POSITIONY)))
s$ = s$+XMLAttr("PosZ", Str$(Peek Float(ModuleData+MODULETRANSFORM_POSITIONZ)))
s$ = s$+XMLAttr("RotX", Str$(Peek Float(ModuleData+MODULETRANSFORM_ROTATIONX)))
s$ = s$+XMLAttr("RotY", Str$(Peek Float(ModuleData+MODULETRANSFORM_ROTATIONY)))
s$ = s$+XMLAttr("RotZ", Str$(Peek Float(ModuleData+MODULETRANSFORM_ROTATIONZ)))
s$ = s$+XMLAttr("SclX", Str$(Peek Float(ModuleData+MODULETRANSFORM_SCALEX)))
s$ = s$+XMLAttr("SclY", Str$(Peek Float(ModuleData+MODULETRANSFORM_SCALEY)))
s$ = s$+XMLAttr("SclZ", Str$(Peek Float(ModuleData+MODULETRANSFORM_SCALEZ)))+"/>"
EndFunction s$
...
Function XMLAttr(AttrName$, AttrString$)
s$ = " "+AttrName$+"="+Quote$(AttrString$)
EndFunction s$
Saving a scene consists mainly to scan the project components in a defined order and call the translate function
to write the XML nodes (also named XML Elements).
-Settings
-Resources
-Materials
-Camera
-Light
-Entity List, for each Entity, the modules
Loading a sceneThe
Project_Load(FileName$) function seems quite short (A file validity check is done before in the load command):
+ Code SnippetFunction Project_Load(FileName$)
ProjectFileName$ = ""
XML Open Document FileName$, 1
XML Load Document 1
XML Get Root Element 1, 1
ProjectMediaPath$ = "": Rem - Settings -
ProjectMediaPathLen = 0
_Project_LoadXMLChildElements(1, "LV3D")
XML Release Element 1
XML Close Document 1
ProjectFileName$ = FileName$
EndFunction true
Loading a scene consists of
-Scan the root node
-Scan the child nodes
-For each node, scan the attributes
-If here, scan the child nodes...
The hard part is done in the
_Project_LoadXMLChildElements(), that is a recursive function to traverse the node hierarchy!
Loading a nodeSo we have _TranslateToXML functions to save components. Of course we have a load counterpart!
And these functions are given a
SetProperty_ prefix (they are not a part of the structure, unlike _TranslateToXML):
+ Code SnippetFunction SetProperty_ModuleSphere(PropertyName$, Value$)
PropertyName$ = Fast Upper$(PropertyName$)
If PropertyName$ = "#BEGIN"
Entity = TreeView_GetItemData(EntityListTreeView, GetGadgetData(EntityListTreeView))
SphereEditorModule = CreateModule(GetModuleClass("Sphere (Mesh)"))
Entity_AddModule(Entity, SphereEditorModule)
ExitFunction true
EndIf
If PropertyName$ = "#END" Then SphereEditorModule = 0: ExitFunction true
If PropertyName$ = "ENABLED"
ExitFunction true
EndIf
If PropertyName$ = "ROWSINDEX"
Entity = TreeView_GetItemData(EntityListTreeView, GetGadgetData(EntityListTreeView))
Poke Integer Modules(SphereEditorModule).ModuleData+MODULESPHERE_ROWSINDEX, IntVal(Value$)
ExitFunction true
EndIf
If PropertyName$ = "COLUMNSINDEX"
Entity = TreeView_GetItemData(EntityListTreeView, GetGadgetData(EntityListTreeView))
Poke Integer Modules(SphereEditorModule).ModuleData+MODULESPHERE_COLUMNSINDEX, IntVal(Value$)
ExitFunction true
EndIf
EndFunction true
For each attribute scanned, the corresponding
SetProperty_ function is called.
The secret lies in the function name. It is the same name of the XML Node.
<ModuleSphere Enabled="1" RowsIndex="9" ColumnsIndex="9"/>
|Node name | |Attribute| ...
So the function pointer can be solved using
Get Ptr To Function():
Get Ptr To Function("SetProperty_"+XML Get Element Name(XMLElement))The
SetProperty_ function is also called at beginning and the end of the node with dummy attributes #BEGIN and #END.
Load & New commandsThere is no particular difficulty with Save and a Save As command.. But Load is different...
Load involves drastic changes with the structures, all project data are erased.
It is better not to do this in the main loop but to set this a part of app flowchart:
So there is several ways to init a project:
- Create a new blank project (App start, New Project)
- Load from a file (Load project!)
Now, when the app is being written, It can be very time saving to automatically start with a test project.
this is included as a part of the project init..
- Create a debug scene
- Load a debug scene
Deffered scene buildingWhen a project is loading, the data is created as the XML are scanned.
The visual part of creating the object is done after the file is loaded with the
SceneEditor_BuildSceneAfterLoad() function
Merging All Together (Download)