Added simple Unreal model support; Fixed numerous issues with normal handling; Fixed outright invalid handling of AngleOffset/PitchOffset/RollOffset in MODELDEF

This commit is contained in:
ZZYZX 2018-05-27 08:53:54 +03:00
parent d6e1b815c3
commit 4213be1c7a
9 changed files with 259 additions and 28 deletions

Binary file not shown.

View file

@ -1,4 +1,4 @@
URL http://devbuilds.drdteam.org/gzdbbf/
FileName Builder.exe
UpdateName GZDoom_Builder_Bugfix-r[REVNUM]-x64.7z
UpdaterName GZDB_Updater-x64.7z
UpdateName GZDoom_Builder_Bugfix-r[REVNUM].7z
UpdaterName GZDB_Updater-x86.7z

View file

@ -23,6 +23,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
private ModelLoadState loadstate;
private Vector3 scale;
private Matrix transform;
private Matrix transformrotation;
private Matrix transformstretched;
#endregion
@ -39,6 +40,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
internal Vector3 Scale { get { return scale; } }
internal Matrix Transform { get { /* return (General.Settings.GZStretchView ? transformstretched : transform); */ return transformstretched; } }
internal Matrix TransformRotation { get { return transformrotation; } }
internal bool OverridePalette; // Used for voxel models only
internal float AngleOffset; // Used for voxel models only
internal bool InheritActorPitch;
@ -93,6 +95,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
internal void SetTransform(Matrix rotation, Matrix offset, Vector3 scale)
{
this.scale = scale;
transformrotation = rotation * Matrix.Scaling(scale);
transform = rotation * Matrix.Scaling(scale) * offset;
transformstretched = Matrix.Scaling(1.0f, 1.0f, General.Map.Data.InvertedVerticalViewStretch) * transform;
}

View file

@ -125,15 +125,20 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
case ".md2":
result = ReadMD2Model(ref bbs, ms, device, mde.FrameIndices[i], mde.FrameNames[i]);
break;
case ".3d":
result = Read3DModel(ref bbs, skins, ms, device, mde.FrameIndices[i], mde.ModelNames[i], containers);
break;
default:
result.Errors = "model format is not supported";
break;
}
ms.Close();
if (result == null)
continue;
//got errors?
if(!String.IsNullOrEmpty(result.Errors))
//got errors?
if (!String.IsNullOrEmpty(result.Errors))
{
General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + mde.ModelNames[i] + "\": " + result.Errors);
}
@ -232,11 +237,208 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
mde.Model.Radius = Math.Max(Math.Max(Math.Abs(bbs.MinY), Math.Abs(bbs.MaxY)), Math.Max(Math.Abs(bbs.MinX), Math.Abs(bbs.MaxX)));
}
#endregion
#endregion
#region ================== MD3
#region ================== 3D (unreal)
internal static MD3LoadResult ReadMD3Model(ref BoundingBoxSizes bbs, Dictionary<int, string> skins, Stream s, Device device, int frame)
// there is probably better way to emulate 16-bit cast, but this was easiest for me at 3am
private static int PadInt16(int n)
{
if (n > 32767)
return -(65536 - n);
return n;
}
private static float UnpackUVertex(int n, int c)
{
switch (c)
{
case 2:
return PadInt16((n & 0x7ff) << 5) / 127f;
case 1:
return PadInt16((((int)n >> 11) & 0x7ff) << 5) / 127f;
case 0:
return PadInt16((((int)n >> 22) & 0x3ff) << 6) / 127f;
default:
return 0f;
}
}
private struct UE1Poly
{
public int[] V;
public float[] S;
public float[] T;
public int TexNum;
}
internal static MD3LoadResult Read3DModel(ref BoundingBoxSizes bbs, Dictionary<int, string> skins, Stream s, Device device, int frame, string filename, List<DataReader> containers)
{
Stream stream_d;
Stream stream_a;
if (filename.IndexOf("_d.3d") == filename.Length-5)
{
string filename_a = filename.Replace("_d.3d", "_a.3d");
stream_d = s;
stream_a = LoadFile(containers, filename_a, true);
if (stream_a == null)
{
General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + filename + "\": unable to find corresponding \"_a.3d\" file.");
return null;
}
}
else
{
string filename_d = filename.Replace("_a.3d", "_d.3d");
stream_a = s;
stream_d = LoadFile(containers, filename_d, true);
if (stream_d == null)
{
General.ErrorLogger.Add(ErrorType.Error, "Error while loading \"" + filename + "\": unable to find corresponding \"_d.3d\" file.");
return null;
}
}
MD3LoadResult result = new MD3LoadResult();
BinaryReader br_d = new BinaryReader(stream_d);
BinaryReader br_a = new BinaryReader(stream_a);
// read d3d header
uint d3d_numpolys = br_d.ReadUInt16();
uint d3d_numverts = br_d.ReadUInt16();
stream_d.Position += 16; // bogusrot, bogusframe, bogusnorm[3]
uint d3d_fixscale = br_d.ReadUInt32();
stream_d.Position += 12; // unused[3]
stream_d.Position += 12; // padding[12]
long start_d = stream_d.Position;
uint numGroups = 0;
// some black magic
bool[] used = new bool[256];
for (int i = 0; i < d3d_numpolys; i++)
{
stream_d.Position = start_d + 16 * i;
stream_d.Position += 14;
byte texnum = br_d.ReadByte();
used[texnum] = true;
}
for (int i = 0; i < 256; i++)
if (used[i]) numGroups++;
// read a3d header
uint a3d_numframes = br_a.ReadUInt16();
uint a3d_framesize = br_a.ReadUInt16();
long start_a = stream_a.Position;
// Sanity check
if (frame < 0 || frame >= a3d_numframes)
{
result.Errors = "frame " + frame + " is outside of model's frame range [0.." + (a3d_numframes - 1) + "]";
return result;
}
// read vertices
WorldVertex[] vertices = new WorldVertex[d3d_numverts];
for (uint i = 0; i < d3d_numverts; i++)
{
WorldVertex Vert = new WorldVertex();
stream_a.Position = start_a + (i + frame * d3d_numverts) * 4;
int v_uint = br_a.ReadInt32();
Vert.x = UnpackUVertex(v_uint, 0);
Vert.y = UnpackUVertex(v_uint, 1);
Vert.z = UnpackUVertex(v_uint, 2);
vertices[i] = Vert;
}
// read polygons
UE1Poly[] polys = new UE1Poly[d3d_numpolys];
int[] polyindexlist = new int[d3d_numpolys*3];
for (uint i = 0; i < d3d_numpolys; i++)
{
//
stream_d.Position = start_d + 16 * i;
polys[i].V = new int[3];
polys[i].S = new float[3];
polys[i].T = new float[3];
for (int j = 0; j < 3; j++)
polyindexlist[i*3+j] = polys[i].V[j] = br_d.ReadInt16();
stream_d.Position += 2;
for (int j = 0; j < 3; j++)
{
byte u = br_d.ReadByte();
byte v = br_d.ReadByte();
polys[i].S[j] = u / 255f;
polys[i].T[j] = v / 255f;
}
polys[i].TexNum = br_d.ReadByte();
}
for (uint i = 0; i < d3d_numverts; i++)
{
Vector3D nsum = new Vector3D(0, 0, 0);
int total = 0;
for (uint j = 0; j < d3d_numpolys; j++)
{
if ((polys[j].V[0] != i) && (polys[j].V[1] != i) && (polys[j].V[2] != i)) continue;
Vector3D[] vert = new Vector3D[3];
Vector3D[] dir = new Vector3D[2];
Vector3D norm;
//
for (int k = 0; k < 3; k++)
vert[k] = new Vector3D(vertices[polys[j].V[k]].x, vertices[polys[j].V[k]].y, vertices[polys[j].V[k]].z);
dir[0].x = vert[1].x - vert[0].x;
dir[0].y = vert[1].y - vert[0].y;
dir[0].z = vert[1].z - vert[0].z;
dir[1].x = vert[2].x - vert[0].x;
dir[1].y = vert[2].y - vert[0].y;
dir[1].z = vert[2].z - vert[0].z;
norm.x = dir[0].y * dir[1].z - dir[0].z * dir[1].y;
norm.y = dir[0].z * dir[1].x - dir[0].x * dir[1].z;
norm.z = dir[0].x * dir[1].y - dir[0].y * dir[1].x;
norm = norm.GetNormal();
nsum.x += norm.x;
nsum.y += norm.y;
nsum.z += norm.z;
total++;
}
vertices[i].nx = -nsum.x / total;
vertices[i].ny = -nsum.y / total;
vertices[i].nz = -nsum.z / total;
}
// wtf is skin groups?
// don't do this for now
List<WorldVertex> out_verts = new List<WorldVertex>();
List<int> out_polys = new List<int>();
for (int i = 0; i < polys.Length; i++)
{
for (int j = 0; j < 3; j++)
{
WorldVertex vx = vertices[polys[i].V[j]];
vx.u = polys[i].S[j];
vx.v = polys[i].T[j];
out_polys.Add(out_verts.Count);
out_verts.Add(vx);
}
}
CreateMesh(device, ref result, out_verts, out_polys);
result.Skins.Add("");
return result;
}
#endregion
#region ================== MD3
internal static MD3LoadResult ReadMD3Model(ref BoundingBoxSizes bbs, Dictionary<int, string> skins, Stream s, Device device, int frame)
{
long start = s.Position;
MD3LoadResult result = new MD3LoadResult();

View file

@ -838,7 +838,8 @@ namespace CodeImp.DoomBuilder.Rendering
if(wantedshaderpass > 7)
{
graphics.Shaders.World3D.World = world;
}
graphics.Shaders.World3D.ModelNormal = Matrix.Identity;
}
}
//mxd. Set variables for fog rendering?
@ -948,7 +949,8 @@ namespace CodeImp.DoomBuilder.Rendering
if(wantedshaderpass > 7)
{
graphics.Shaders.World3D.World = world;
graphics.Shaders.World3D.CameraPosition = new Vector4(cameraposition.x, cameraposition.y, cameraposition.z, t.FogFactor);
graphics.Shaders.World3D.ModelNormal = Matrix.Identity;
graphics.Shaders.World3D.CameraPosition = new Vector4(cameraposition.x, cameraposition.y, cameraposition.z, t.FogFactor);
}
// Set the colors to use
@ -1121,6 +1123,7 @@ namespace CodeImp.DoomBuilder.Rendering
if (wantedshaderpass > 7)
{
graphics.Shaders.World3D.World = world;
graphics.Shaders.World3D.ModelNormal = Matrix.Identity;
}
}
@ -1264,7 +1267,8 @@ namespace CodeImp.DoomBuilder.Rendering
if(wantedshaderpass > 7)
{
graphics.Shaders.World3D.World = world;
if(t.FogFactor != fogfactor)
graphics.Shaders.World3D.ModelNormal = Matrix.Identity;
if (t.FogFactor != fogfactor)
{
graphics.Shaders.World3D.CameraPosition = new Vector4(cameraposition.x, cameraposition.y, cameraposition.z, t.FogFactor);
fogfactor = t.FogFactor;
@ -1508,6 +1512,7 @@ namespace CodeImp.DoomBuilder.Rendering
if (geometrytolit.Count == 0) return;
graphics.Shaders.World3D.World = Matrix.Identity;
graphics.Shaders.World3D.ModelNormal = Matrix.Identity;
graphics.Shaders.World3D.BeginPass(SHADERPASS_LIGHT);
VisualSector sector = null;
@ -1530,6 +1535,7 @@ namespace CodeImp.DoomBuilder.Rendering
if (geometrytolit.Count == 0) return;
graphics.Shaders.World3D.World = Matrix.Identity;
graphics.Shaders.World3D.ModelNormal = Matrix.Identity;
graphics.Shaders.World3D.BeginPass(SHADERPASS_LIGHT);
VisualSector sector = null;
@ -1639,7 +1645,7 @@ namespace CodeImp.DoomBuilder.Rendering
// Create the matrix for positioning / rotation
float sx = t.Thing.ScaleX * t.Thing.ActorScale.Width;
float sy = t.Thing.ScaleY * t.Thing.ActorScale.Height;
Matrix modelscale = Matrix.Scaling(sx, sx, sy);
Matrix modelrotation = Matrix.RotationY(-t.Thing.RollRad) * Matrix.RotationX(-t.Thing.PitchRad) * Matrix.RotationZ(t.Thing.Angle);
@ -1650,10 +1656,12 @@ namespace CodeImp.DoomBuilder.Rendering
if(wantedshaderpass > 7)
{
graphics.Shaders.World3D.World = world;
if(t.Thing.Sector != null) graphics.Shaders.World3D.LightColor = t.Thing.Sector.FogColor;
// this is not right...
graphics.Shaders.World3D.ModelNormal = General.Map.Data.ModeldefEntries[t.Thing.Type].TransformRotation * modelrotation;
if (t.Thing.Sector != null) graphics.Shaders.World3D.LightColor = t.Thing.Sector.FogColor;
graphics.Shaders.World3D.CameraPosition = new Vector4(cameraposition.x, cameraposition.y, cameraposition.z, t.FogFactor);
}
graphics.Shaders.World3D.Desaturation = t.Thing.Sector.Desaturation;
GZModel model = General.Map.Data.ModeldefEntries[t.Thing.Type].Model;

View file

@ -47,7 +47,8 @@ namespace CodeImp.DoomBuilder.Rendering
private readonly EffectHandle ignoreNormalsHandle;
private readonly EffectHandle spotLightHandle;
private readonly EffectHandle world;
private readonly EffectHandle camPosHandle; //used for fog rendering
private readonly EffectHandle modelnormal;
private readonly EffectHandle camPosHandle; //used for fog rendering
// [ZZ]
private readonly EffectHandle stencilColorHandle;
@ -230,10 +231,24 @@ namespace CodeImp.DoomBuilder.Rendering
settingschanged = true;
}
}
}
}
//mxd. This sets the highlight color
private Color4 hicolor;
private Matrix mmodelnormal;
public Matrix ModelNormal
{
set
{
if (mmodelnormal != value)
{
effect.SetValue(modelnormal, value);
mmodelnormal = value;
settingschanged = true;
}
}
}
//mxd. This sets the highlight color
private Color4 hicolor;
public Color4 HighlightColor
{
set
@ -285,7 +300,8 @@ namespace CodeImp.DoomBuilder.Rendering
stencilColorHandle = effect.GetParameter(null, "stencilColor");
world = effect.GetParameter(null, "world");
}
modelnormal = effect.GetParameter(null, "modelnormal");
}
// Initialize world vertex declaration
VertexElement[] ve = {
@ -328,9 +344,10 @@ namespace CodeImp.DoomBuilder.Rendering
if(camPosHandle != null) camPosHandle.Dispose();
if(stencilColorHandle != null) stencilColorHandle.Dispose();
if(world != null) world.Dispose();
if(modelnormal != null) modelnormal.Dispose();
// Done
base.Dispose();
// Done
base.Dispose();
}
}

View file

@ -50,6 +50,7 @@ const float4x4 worldviewproj;
//mxd
float4x4 world;
float4x4 modelnormal;
float4 vertexColor;
// [ZZ]
float4 stencilColor;
@ -137,7 +138,7 @@ LitPixelData vs_customvertexcolor_fog(VertexData vd)
pd.pos_w = mul(float4(vd.pos, 1.0f), world).xyz;
pd.color = vertexColor;
pd.uv = vd.uv;
pd.normal = vd.normal;
pd.normal = normalize(mul(float4(vd.normal, 1.0f), modelnormal).xyz);
// Return result
return pd;
@ -151,7 +152,7 @@ LitPixelData vs_lightpass(VertexData vd)
pd.pos_w = mul(float4(vd.pos, 1.0f), world).xyz;
pd.color = vd.color;
pd.uv = vd.uv;
pd.normal = vd.normal;
pd.normal = normalize(mul(float4(vd.normal, 1.0f), modelnormal).xyz);
// Return result
return pd;
@ -259,7 +260,7 @@ float4 ps_lightpass(LitPixelData pd) : COLOR
//is face facing away from light source?
// [ZZ] oddly enough pd.normal is not a proper normal, so using dot on it returns rather unexpected results. wrapped in normalize().
// update 01.02.2017: offset the equation by 3px back to try to emulate GZDoom's broken visibility check.
float diffuseContribution = dot(normalize(pd.normal), normalize(lightPosAndRadius.xyz - pd.pos_w + normalize(pd.normal)*3));
float diffuseContribution = dot(pd.normal, normalize(lightPosAndRadius.xyz - pd.pos_w + pd.normal*3));
if (diffuseContribution < 0 && ignoreNormals < 0.5)
clip(-1);
diffuseContribution = max(diffuseContribution, 0); // to make sure

View file

@ -103,8 +103,8 @@ namespace CodeImp.DoomBuilder.ZDoom
// Things are complicated in GZDoom...
Matrix moffset = Matrix.Translation(mds.Offset.Y, -mds.Offset.X, mds.Offset.Z);
Matrix mrotation = Matrix.RotationY(-Angle2D.DegToRad(mds.RollOffset)) * Matrix.RotationX(-Angle2D.DegToRad(mds.PitchOffset)) * Matrix.RotationZ(Angle2D.DegToRad(mds.AngleOffset));
md.SetTransform(mrotation, moffset, mds.Scale);
Matrix mrotation = Matrix.RotationZ(Angle2D.DegToRad(mds.AngleOffset)) * Matrix.RotationY(-Angle2D.DegToRad(mds.RollOffset)) * Matrix.RotationX(-Angle2D.DegToRad(mds.PitchOffset));
md.SetTransform(mrotation, moffset, mds.Scale);
// Add models
int disabledframescount = 0;
@ -113,7 +113,7 @@ namespace CodeImp.DoomBuilder.ZDoom
// Sanity checks
if(string.IsNullOrEmpty(mds.ModelNames[fs.ModelIndex]))
{
LogWarning("Model definition \"" + classname + "\", frame \"" + fs.SpriteName + " " + fs.FrameName + "\" references undefiend model index " + fs.ModelIndex);
LogWarning("Model definition \"" + classname + "\", frame \"" + fs.SpriteName + " " + fs.FrameName + "\" references undefined model index " + fs.ModelIndex);
continue;
}

View file

@ -150,9 +150,9 @@ namespace CodeImp.DoomBuilder.ZDoom
return false;
}
if(modelext != ".md3" && modelext != ".md2")
if(modelext != ".md3" && modelext != ".md2" && modelext != ".3d")
{
parser.ReportError("Model \"" + token + "\" won't be loaded. Only MD2 and MD3 models are supported");
parser.ReportError("Model \"" + token + "\" won't be loaded. Only Unreal 3D, MD2 and MD3 models are supported");
return false;
}