mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-26 13:51:40 +00:00
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:
parent
d6e1b815c3
commit
4213be1c7a
9 changed files with 259 additions and 28 deletions
BIN
Build/SlimDX.dll
BIN
Build/SlimDX.dll
Binary file not shown.
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue