Skip to content

Commit aeaa5c8

Browse files
committed
Add low lod terrain importer
Still requires changes to be fully supported by undo/redo + need loading for the baked lighting and minimap textures. Going to add texture loading soon since it'll be needed for high lod terrain and buildings to look pretty.
1 parent 526a84d commit aeaa5c8

File tree

3 files changed

+192
-28
lines changed

3 files changed

+192
-28
lines changed

Diff for: src/Gui/Documents/MapEditorDocument.bf

+82-26
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#pragma warning disable 168
12
using System.Threading;
23
using Nanoforge.App;
34
using Nanoforge.Rfg;
@@ -8,6 +9,8 @@ using Nanoforge.Rfg.Import;
89
using Nanoforge.Misc;
910
using Nanoforge.Render;
1011
using Common.Math;
12+
using Nanoforge.Render.Resources;
13+
using System.Collections;
1114

1215
namespace Nanoforge.Gui.Documents
1316
{
@@ -33,6 +36,10 @@ namespace Nanoforge.Gui.Documents
3336
Loading = true;
3437
defer { Loading = false; }
3538

39+
//Create scene for rendering
40+
Renderer renderer = app.GetResource<Renderer>();
41+
_scene = renderer.CreateScene();
42+
3643
//Check if the map was already imported
3744
Territory findResult = ProjectDB.Find<Territory>(MapName);
3845
if (findResult != null)
@@ -56,9 +63,50 @@ namespace Nanoforge.Gui.Documents
5663
return;
5764
}
5865

59-
//Create scene for rendering
60-
Renderer renderer = app.GetResource<Renderer>();
61-
_scene = renderer.CreateScene();
66+
//Create render objects for meshes
67+
for (Zone zone in Map.Zones)
68+
{
69+
for (int i in 0 ... 8)
70+
{
71+
List<u8> indexBuffer = null;
72+
List<u8> vertexBuffer = null;
73+
defer { DeleteIfSet!(indexBuffer); }
74+
defer { DeleteIfSet!(vertexBuffer); }
75+
if (zone.LowLodTerrainIndexBuffers[i].Load() case .Ok(let val))
76+
{
77+
indexBuffer = val;
78+
}
79+
else
80+
{
81+
Logger.Error("Failed to load index buffer for low lod terrain submesh {} of {}.", i, zone.Name);
82+
continue;
83+
}
84+
if (zone.LowLodTerrainVertexBuffers[i].Load() case .Ok(let val))
85+
{
86+
vertexBuffer = val;
87+
}
88+
else
89+
{
90+
Logger.Error("Failed to load vertex buffer for low lod terrain submesh {} of {}.", i, zone.Name);
91+
continue;
92+
}
93+
94+
Mesh mesh = new .();
95+
if (mesh.Init(renderer.Device, zone.LowLodTerrainMeshConfig[i], indexBuffer, vertexBuffer) case .Ok)
96+
{
97+
if (_scene.CreateRenderObject("TerrainLowLod", mesh, zone.TerrainPosition, .Identity) case .Err)
98+
{
99+
Logger.Error("Failed to create render object for low lod terrain submesh {} of {}", i, zone.Name);
100+
continue;
101+
}
102+
}
103+
else
104+
{
105+
Logger.Error("Failed to create render object for low lod terrain submesh {} of {}", i, zone.Name);
106+
delete mesh;
107+
}
108+
}
109+
}
62110
}
63111

64112
public override void Update(App app, Gui gui)
@@ -68,6 +116,31 @@ namespace Nanoforge.Gui.Documents
68116
ThreadPool.QueueUserWorkItem(new () => { this.Load(app); });
69117
}
70118

119+
if (_scene != null)
120+
{
121+
//Update scene viewport size
122+
ImGui.Vec2 contentAreaSize;
123+
contentAreaSize.x = ImGui.GetWindowContentRegionMax().x - ImGui.GetWindowContentRegionMin().x;
124+
contentAreaSize.y = ImGui.GetWindowContentRegionMax().y - ImGui.GetWindowContentRegionMin().y;
125+
if (contentAreaSize.x > 0.0f && contentAreaSize.y > 0.0f)
126+
{
127+
_scene.Resize((u32)contentAreaSize.x, (u32)contentAreaSize.y);
128+
}
129+
130+
//Store initial position so we can draw buttons over the scene texture after drawing it
131+
ImGui.Vec2 initialPos = ImGui.GetCursorPos();
132+
133+
//Render scene texture
134+
ImGui.PushStyleColor(.WindowBg, .(_scene.ClearColor.x, _scene.ClearColor.y, _scene.ClearColor.z, _scene.ClearColor.w));
135+
ImGui.Image(_scene.View, .(_scene.ViewWidth, _scene.ViewHeight));
136+
ImGui.PopStyleColor();
137+
138+
//Set cursor pos to top left corner to draw buttons over scene texture
139+
ImGui.Vec2 adjustedPos = initialPos;
140+
adjustedPos.x += 10.0f;
141+
adjustedPos.y += 10.0f;
142+
ImGui.SetCursorPos(adjustedPos);
143+
}
71144
if (Loading)
72145
return;
73146

@@ -82,29 +155,6 @@ namespace Nanoforge.Gui.Documents
82155
_scene.DrawBox(obj.BBox.Min, obj.BBox.Max, color);
83156
}
84157
}
85-
86-
//Update scene viewport size
87-
ImGui.Vec2 contentAreaSize;
88-
contentAreaSize.x = ImGui.GetWindowContentRegionMax().x - ImGui.GetWindowContentRegionMin().x;
89-
contentAreaSize.y = ImGui.GetWindowContentRegionMax().y - ImGui.GetWindowContentRegionMin().y;
90-
if (contentAreaSize.x > 0.0f && contentAreaSize.y > 0.0f)
91-
{
92-
_scene.Resize((u32)contentAreaSize.x, (u32)contentAreaSize.y);
93-
}
94-
95-
//Store initial position so we can draw buttons over the scene texture after drawing it
96-
ImGui.Vec2 initialPos = ImGui.GetCursorPos();
97-
98-
//Render scene texture
99-
ImGui.PushStyleColor(.WindowBg, .(_scene.ClearColor.x, _scene.ClearColor.y, _scene.ClearColor.z, _scene.ClearColor.w));
100-
ImGui.Image(_scene.View, .(_scene.ViewWidth, _scene.ViewHeight));
101-
ImGui.PopStyleColor();
102-
103-
//Set cursor pos to top left corner to draw buttons over scene texture
104-
ImGui.Vec2 adjustedPos = initialPos;
105-
adjustedPos.x += 10.0f;
106-
adjustedPos.y += 10.0f;
107-
ImGui.SetCursorPos(adjustedPos);
108158
}
109159

110160
public override void Save(App app, Gui gui)
@@ -114,6 +164,12 @@ namespace Nanoforge.Gui.Documents
114164

115165
public override void OnClose(App app, Gui gui)
116166
{
167+
if (_scene != null)
168+
{
169+
Renderer renderer = app.GetResource<Renderer>();
170+
renderer.DestroyScene(_scene);
171+
}
172+
117173
return;
118174
}
119175

Diff for: src/Rfg/Import/MapImporter.bf

+95-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ using Nanoforge.App;
66
using System.IO;
77
using Common;
88
using System;
9+
using Common.IO;
10+
using RfgTools.Formats.Meshes;
11+
using Nanoforge.Render.Resources;
12+
using Nanoforge.Render;
913

1014
namespace Nanoforge.Rfg.Import
1115
{
@@ -36,8 +40,7 @@ namespace Nanoforge.Rfg.Import
3640
if (Path.GetExtension(entryName, .. scope .()) != ".rfgzone_pc" || entryName.StartsWith("p_", .OrdinalIgnoreCase))
3741
continue;
3842

39-
Result<Zone, StringView> zoneImportResult = ImportZone(packfile, entryName, changes);
40-
switch (zoneImportResult)
43+
switch (ImportZone(packfile, entryName, changes))
4144
{
4245
case .Ok(let zone):
4346
map.Zones.Add(zone);
@@ -48,6 +51,18 @@ namespace Nanoforge.Rfg.Import
4851
}
4952
}
5053

54+
//Import terrain, roads, and rocks
55+
for (Zone zone in map.Zones)
56+
{
57+
if (LoadTerrain(packfile, zone, changes, name) case .Err)
58+
{
59+
Logger.Error("Failed to import terrain for zone '{}'", zone.Name);
60+
//TODO: RE-ENABLE AND MAKE SURE IT WORKS BEFORE COMMIT
61+
//changes.Rollback();
62+
return .Err("Failed to import terrain. Check the log.");
63+
}
64+
}
65+
5166
return .Ok(map);
5267
}
5368
}
@@ -78,5 +93,83 @@ namespace Nanoforge.Rfg.Import
7893
return .Err(err);
7994
}
8095
}
96+
97+
public static Result<void> LoadTerrain(PackfileV3 packfile, Zone zone, DiffUtil changes, StringView name)
98+
{
99+
//Determine terrain position from obj_zone center
100+
for (ZoneObject obj in zone.Objects)
101+
{
102+
if (obj.Classname == "obj_zone")
103+
{
104+
zone.TerrainPosition = obj.BBox.Center();
105+
break;
106+
}
107+
}
108+
109+
//Load ns_base.str2_pc. Contains the terrain meshes. This will need to be changed when SP support is added since SP vpp structure is more complex
110+
var str2ReadResult = packfile.ReadSingleFile("ns_base.str2_pc");
111+
if (str2ReadResult case .Err(StringView err))
112+
{
113+
Logger.Error("Failed to load ns_base.str2_pc for terrain import. Error: {}", err);
114+
return .Err;
115+
}
116+
117+
//Parse ns_base.str2_pc
118+
defer delete str2ReadResult.Value;
119+
PackfileV3 nsBase = scope .(new ByteSpanStream(str2ReadResult.Value), "ns_base.str2_pc");
120+
if (nsBase.ReadMetadata() case .Err(StringView err))
121+
{
122+
Logger.Info("Failed to parse ns_base.str2_pc for terrain import. Error: {}", err);
123+
return .Err;
124+
}
125+
126+
//Get cterrain_pc and gterrain_pc files
127+
u8[] cpuFile = null;
128+
u8[] gpuFile = null;
129+
defer { DeleteIfSet!(cpuFile); }
130+
defer { DeleteIfSet!(gpuFile); }
131+
132+
switch (nsBase.ReadSingleFile(scope $"{name}.cterrain_pc"))
133+
{
134+
case .Ok(u8[] bytes):
135+
cpuFile = bytes;
136+
case .Err(StringView err):
137+
Logger.Error("Failed to extract cterrain_pc file. Error: {}", err);
138+
return .Err;
139+
}
140+
switch (nsBase.ReadSingleFile(scope $"{name}.gterrain_pc"))
141+
{
142+
case .Ok(u8[] bytes):
143+
gpuFile = bytes;
144+
case .Err(StringView err):
145+
Logger.Error("Failed to extract gterrain_pc file. Error: {}", err);
146+
return .Err;
147+
}
148+
149+
TerrainLowLod terrain = scope .();
150+
if (terrain.Load(cpuFile, gpuFile, false) case .Err(StringView err))
151+
{
152+
Logger.Error("Failed to parse cterrain_pc file. Error: {}", err);
153+
return .Err;
154+
}
155+
156+
for (int i in 0 ... 8)
157+
{
158+
switch (terrain.GetMeshData(i))
159+
{
160+
case .Ok(MeshInstanceData meshData):
161+
ProjectBuffer indexBuffer = ProjectDB.CreateBuffer(meshData.IndexBuffer, scope $"{name}_low_lod_{i}");
162+
ProjectBuffer vertexBuffer = ProjectDB.CreateBuffer(meshData.VertexBuffer, scope $"{name}_low_lod_{i}");
163+
zone.LowLodTerrainMeshConfig[i] = meshData.Config.Clone(.. new .());
164+
zone.LowLodTerrainIndexBuffers[i] = indexBuffer;
165+
zone.LowLodTerrainVertexBuffers[i] = vertexBuffer;
166+
case .Err(StringView err):
167+
Logger.Error("Failed to get mesh data from gterrain_pc file. Error: {}", err);
168+
return .Err;
169+
}
170+
}
171+
172+
return .Ok;
173+
}
81174
}
82175
}

Diff for: src/Rfg/Territory.bf

+15
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ using Nanoforge.App;
33
using Common.Math;
44
using Common;
55
using System;
6+
using Nanoforge.Render.Resources;
7+
using RfgTools.Formats.Meshes;
68

79
namespace Nanoforge.Rfg
810
{
@@ -35,5 +37,18 @@ namespace Nanoforge.Rfg
3537
public bool MissionLayer = false;
3638
[EditorProperty]
3739
public append List<ZoneObject> Objects = .();
40+
41+
[EditorProperty]
42+
public Vec3<f32> TerrainPosition = .Zero;
43+
44+
public ProjectBuffer[9] LowLodTerrainIndexBuffers;
45+
public ProjectBuffer[9] LowLodTerrainVertexBuffers;
46+
public MeshDataBlock[9] LowLodTerrainMeshConfig;
47+
48+
public ~this()
49+
{
50+
for (int i in 0 ... 8)
51+
delete LowLodTerrainMeshConfig[i];
52+
}
3853
}
3954
}

0 commit comments

Comments
 (0)