Skip to content

Commit a5bbe8d

Browse files
committed
Centralize and standardize KH2 and BBS map rendering logic
1 parent 2dee1d3 commit a5bbe8d

File tree

8 files changed

+217
-182
lines changed

8 files changed

+217
-182
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using Microsoft.Xna.Framework.Graphics;
2+
3+
namespace OpenKh.Engine.MonoGame
4+
{
5+
public static class GraphicsDeviceExtensions
6+
{
7+
public static void RenderMeshNew(this GraphicsDevice graphics, KingdomShader shader,
8+
EffectPass pass, IMonoGameModel model, bool passRenderOpaque)
9+
{
10+
if (model?.MeshDescriptors == null)
11+
return;
12+
13+
foreach (var meshDescriptor in model.MeshDescriptors)
14+
{
15+
if (meshDescriptor.Indices.Length == 0 || meshDescriptor.IsOpaque != passRenderOpaque)
16+
continue;
17+
18+
var textureIndex = meshDescriptor.TextureIndex & 0xffff;
19+
if (textureIndex < model.Textures.Length)
20+
shader.SetRenderTexture(pass, model.Textures[textureIndex]);
21+
22+
graphics.DrawUserIndexedPrimitives(
23+
PrimitiveType.TriangleList,
24+
meshDescriptor.Vertices,
25+
0,
26+
meshDescriptor.Vertices.Length,
27+
meshDescriptor.Indices,
28+
0,
29+
meshDescriptor.Indices.Length / 3,
30+
MeshLoader.PositionColoredTexturedVertexDeclaration);
31+
}
32+
}
33+
}
34+
}

OpenKh.Game/Field/BbsMap.cs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using Microsoft.Xna.Framework.Graphics;
2+
using OpenKh.Bbs;
3+
using OpenKh.Common;
4+
using OpenKh.Engine;
5+
using OpenKh.Engine.MonoGame;
6+
using OpenKh.Engine.Parsers;
7+
using OpenKh.Game.Entities;
8+
using System;
9+
using System.Collections.Generic;
10+
using System.IO;
11+
using System.Numerics;
12+
13+
namespace OpenKh.Game.Field
14+
{
15+
public class BbsMap : IMap, IDisposable
16+
{
17+
private readonly GraphicsDevice _graphics;
18+
private List<PmpEntity> _pmpEntities = new List<PmpEntity>();
19+
private List<MeshGroup> _pmpModels = new List<MeshGroup>();
20+
21+
public BbsMap(GraphicsDevice graphics, string filePath)
22+
{
23+
_graphics = graphics;
24+
25+
var pmp = File.OpenRead(filePath).Using(Pmp.Read);
26+
var group = new List<MeshGroup>();
27+
28+
int pmoIndex = 0;
29+
for (int i = 0; i < pmp.objectInfo.Count; i++)
30+
{
31+
Pmp.ObjectInfo currentInfo = pmp.objectInfo[i];
32+
33+
if (currentInfo.PMO_Offset != 0)
34+
{
35+
var pmpEntity = new PmpEntity(pmoIndex,
36+
new Vector3(currentInfo.PositionX, currentInfo.PositionY, currentInfo.PositionZ),
37+
new Vector3(currentInfo.RotationX, currentInfo.RotationY, currentInfo.RotationZ),
38+
new Vector3(currentInfo.ScaleX, currentInfo.ScaleY, currentInfo.ScaleZ));
39+
pmpEntity.DifferentMatrix = pmp.hasDifferentMatrix[pmoIndex];
40+
41+
var pParser = new PmoParser(pmp.PmoList[pmoIndex], 100.0f);
42+
var textures = new List<Tim2KingdomTexture>();
43+
44+
var meshGroup = new MeshGroup();
45+
meshGroup.MeshDescriptors = pParser.MeshDescriptors;
46+
meshGroup.Textures = new IKingdomTexture[pmp.PmoList[pmoIndex].header.TextureCount];
47+
48+
for (int j = 0; j < pmp.PmoList[pmoIndex].header.TextureCount; j++)
49+
{
50+
textures.Add(new Tim2KingdomTexture(pmp.PmoList[pmoIndex].texturesData[j], graphics));
51+
meshGroup.Textures[j] = textures[j];
52+
}
53+
54+
_pmpEntities.Add(pmpEntity);
55+
_pmpModels.Add(meshGroup);
56+
pmoIndex++;
57+
}
58+
}
59+
}
60+
61+
public void Dispose()
62+
{
63+
foreach (var model in _pmpModels)
64+
{
65+
foreach (var texture in model.Textures)
66+
texture.Dispose();
67+
}
68+
}
69+
70+
public void Render(Camera camera, KingdomShader shader, EffectPass pass, bool passRenderOpaque)
71+
{
72+
var worldView = camera.World;
73+
var specialWorldView = worldView;
74+
specialWorldView.M14 = 0;
75+
specialWorldView.M24 = 0;
76+
specialWorldView.M34 = 0;
77+
specialWorldView.M41 = 0;
78+
specialWorldView.M42 = 0;
79+
specialWorldView.M43 = 0;
80+
specialWorldView.M44 = 1;
81+
82+
var originalDepthStencilState = _graphics.DepthStencilState;
83+
var originalResterizerState = _graphics.RasterizerState;
84+
foreach (var entity in _pmpEntities)
85+
{
86+
if (entity.DifferentMatrix)
87+
{
88+
shader.SetWorldView(ref specialWorldView);
89+
_graphics.DepthStencilState = DepthStencilState.DepthRead;
90+
}
91+
else
92+
{
93+
shader.SetWorldView(ref worldView);
94+
_graphics.DepthStencilState = DepthStencilState.Default;
95+
}
96+
97+
int AxisNumberChanged = 0;
98+
AxisNumberChanged += Convert.ToInt32(entity.Scaling.X < 0);
99+
AxisNumberChanged += Convert.ToInt32(entity.Scaling.Y < 0);
100+
AxisNumberChanged += Convert.ToInt32(entity.Scaling.Z < 0);
101+
102+
if (AxisNumberChanged == 1 || AxisNumberChanged == 3)
103+
_graphics.RasterizerState = RasterizerState.CullCounterClockwise;
104+
else
105+
_graphics.RasterizerState = RasterizerState.CullClockwise;
106+
107+
108+
shader.SetModelView(entity.GetMatrix());
109+
shader.UseAlphaMask = true;
110+
pass.Apply();
111+
112+
_graphics.RenderMeshNew(shader, pass, _pmpModels[entity.Index], passRenderOpaque);
113+
}
114+
115+
_graphics.DepthStencilState = originalDepthStencilState;
116+
_graphics.RasterizerState = originalResterizerState;
117+
}
118+
}
119+
}

OpenKh.Game/Field/IField.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
using OpenKh.Engine;
1+
using Microsoft.Xna.Framework.Graphics;
22
using OpenKh.Engine.MonoGame;
3-
using System;
43
using System.Collections.Generic;
54
using System.Numerics;
65

@@ -15,8 +14,7 @@ public interface IField
1514

1615
void PlayEvent(string eventName);
1716

18-
void ForEveryStaticModel(Action<IMonoGameModel> action);
19-
void ForEveryModel(Action<IEntity, IMonoGameModel> action);
17+
void Render(Camera camera, KingdomShader shader, EffectPass pass, bool passRenderOpaque);
2018
void AddActor(int actorId, int objectId);
2119
void SetActorPosition(int actorId, float x, float y, float z, float rotation);
2220
void SetActorAnimation(int actorId, string path);

OpenKh.Game/Field/IMap.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Microsoft.Xna.Framework.Graphics;
2+
using OpenKh.Engine.MonoGame;
3+
using System;
4+
5+
namespace OpenKh.Game.Field
6+
{
7+
public interface IMap : IDisposable
8+
{
9+
void Render(Camera camera, KingdomShader shader, EffectPass pass, bool passRenderOpaque);
10+
}
11+
}

OpenKh.Game/Field/Kh2Field.cs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class Kh2Field : IField
3434
private readonly Dictionary<int, byte[]> _subtitleData = new Dictionary<int, byte[]>();
3535
private readonly MonoSpriteDrawing _drawing;
3636
private Bar _binarcAreaData;
37-
private Kh2Map _map;
37+
private IMap _map;
3838
private EventPlayer _eventPlayer;
3939
private int _spawnScriptMap;
4040
private int _spawnScriptBtl;
@@ -113,7 +113,7 @@ public void LoadArea(int world, int area)
113113
// TODO load voices (eg. voice/us/battle/nm0_jack.vsb)
114114
// TODO load field2d (eg. field2d/jp/nm0field.2dd)
115115
// TODO load command (eg. field2d/jp/nm1command.2dd)
116-
_map = new Kh2Map(_graphicsDevice, _kernel, world, area);
116+
_map = new Kh2Map(_graphicsDevice, _kernel, world, area); // new BbsMap(_graphicsDevice, @"PATH_TO_PMP");
117117
LoadMsg(world);
118118
// TODO load libretto (eg. libretto-nm.bar)
119119
// TODO load effect
@@ -204,16 +204,34 @@ public void Draw()
204204
DrawFade();
205205
}
206206

207-
public void ForEveryStaticModel(Action<IMonoGameModel> action)
207+
public void Render(Camera camera, KingdomShader shader, EffectPass pass, bool passRenderOpaque)
208208
{
209-
_map.ForEveryStaticModel(action);
210-
}
209+
_map.Render(camera, shader, pass, passRenderOpaque);
211210

212-
public void ForEveryModel(Action<IEntity, IMonoGameModel> action)
213-
{
214-
_map.ForEveryModel(action);
215211
foreach (var actor in _actors.Where(x => x.IsVisible))
216-
action(actor, actor);
212+
{
213+
shader.SetModelView(actor.GetMatrix());
214+
pass.Apply();
215+
216+
_graphicsDevice.RenderMeshNew(shader, pass, actor, passRenderOpaque);
217+
218+
if (_kernel.DebugMode)
219+
{
220+
var matrixArray = actor.Model?.CurrentPose;
221+
if (matrixArray != null)
222+
{
223+
foreach (var bone in actor.Model.Bones)
224+
{
225+
if (bone.Parent > 0)
226+
{
227+
var bonePos = matrixArray[bone.Index].Translation;
228+
var parentPos = matrixArray[bone.Parent].Translation;
229+
Debugging.DebugDraw.DrawLine(_graphicsDevice, bonePos, parentPos, Color.Red);
230+
}
231+
}
232+
}
233+
}
234+
}
217235
}
218236

219237
public void AddActor(int actorId, int objectId)

OpenKh.Game/Field/Kh2Map.cs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313

1414
namespace OpenKh.Game.Field
1515
{
16-
public class Kh2Map : IDisposable
16+
public class Kh2Map : IMap, IDisposable
1717
{
1818
private static readonly MeshGroup Empty = new MeshGroup
1919
{
2020
MeshDescriptors = new List<Engine.Parsers.MeshDescriptor>(0),
2121
Textures = new IKingdomTexture[0]
2222
};
23+
24+
private readonly GraphicsDevice _graphics;
2325
private readonly MeshGroup _mapMeshGroup;
2426
private readonly MeshGroup _skybox0MeshGroup;
2527
private readonly MeshGroup _skybox1MeshGroup;
@@ -32,6 +34,8 @@ public Kh2Map(GraphicsDevice graphics, Kernel kernel, int world, int area) :
3234

3335
public Kh2Map(GraphicsDevice graphics, IDataContent content, string path)
3436
{
37+
_graphics = graphics;
38+
3539
var binarc = content.FileOpen(path).Using(Bar.Read);
3640
_skybox0MeshGroup = FromMdlx(graphics, binarc, "SK0") ?? Empty;
3741
_skybox1MeshGroup = FromMdlx(graphics, binarc, "SK1") ?? Empty;
@@ -54,17 +58,22 @@ public Kh2Map(GraphicsDevice graphics, IDataContent content, string path)
5458
}
5559
}
5660

57-
public void ForEveryStaticModel(Action<IMonoGameModel> action)
61+
public void Render(Camera camera, KingdomShader shader, EffectPass pass, bool passRenderOpaque)
5862
{
59-
action(_skybox0MeshGroup);
60-
action(_skybox1MeshGroup);
61-
action(_mapMeshGroup);
62-
}
63+
shader.SetModelViewIdentity();
64+
pass.Apply();
65+
66+
_graphics.RenderMeshNew(shader, pass, _skybox0MeshGroup, passRenderOpaque);
67+
_graphics.RenderMeshNew(shader, pass, _skybox1MeshGroup, passRenderOpaque);
68+
_graphics.RenderMeshNew(shader, pass, _mapMeshGroup, passRenderOpaque);
6369

64-
public void ForEveryModel(Action<IEntity, IMonoGameModel> action)
65-
{
6670
foreach (var bob in _bobEntities)
67-
action(bob, _bobModels[bob.BobIndex]);
71+
{
72+
shader.SetModelView(bob.GetMatrix());
73+
pass.Apply();
74+
75+
_graphics.RenderMeshNew(shader, pass, _bobModels[bob.BobIndex], passRenderOpaque);
76+
}
6877
}
6978

7079
public void Dispose()

OpenKh.Game/Infrastructure/Kernel.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public string Language
5555
public List<Objentry> ObjEntries { get; }
5656
public Dictionary<string, List<Place>> Places { get; }
5757

58+
public bool DebugMode { get; set; }
59+
5860
public int World
5961
{
6062
get => SaveData.WorldId;

0 commit comments

Comments
 (0)