mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2025-01-31 12:50:52 +00:00
- Added support for Wavefront OBJ models
- Fixed a problem where model textures were not loaded from the textures directory or from TEXTUREx definitions. Fixes #173 and #226
This commit is contained in:
parent
44745bdbc8
commit
dde8742ff2
4 changed files with 439 additions and 43 deletions
|
@ -481,9 +481,6 @@ namespace CodeImp.DoomBuilder.Data
|
||||||
LoadSndSeq();
|
LoadSndSeq();
|
||||||
LoadSndInfo();
|
LoadSndInfo();
|
||||||
LoadVoxels();
|
LoadVoxels();
|
||||||
Dictionary<string, int> actorsbyclass = CreateActorsByClassList();
|
|
||||||
LoadModeldefs(actorsbyclass);
|
|
||||||
foreach(Thing t in General.Map.Map.Things) t.UpdateCache();
|
|
||||||
General.MainWindow.DisplayReady();
|
General.MainWindow.DisplayReady();
|
||||||
|
|
||||||
// Process colormaps (we just put them in as textures)
|
// Process colormaps (we just put them in as textures)
|
||||||
|
@ -568,7 +565,6 @@ namespace CodeImp.DoomBuilder.Data
|
||||||
|
|
||||||
//mxd. Should be done after loading textures...
|
//mxd. Should be done after loading textures...
|
||||||
int hirestexcount = LoadHiResTextures();
|
int hirestexcount = LoadHiResTextures();
|
||||||
LoadGldefs(actorsbyclass);
|
|
||||||
|
|
||||||
//mxd. Create camera textures. Should be done after loading textures.
|
//mxd. Create camera textures. Should be done after loading textures.
|
||||||
LoadAnimdefs();
|
LoadAnimdefs();
|
||||||
|
@ -580,6 +576,13 @@ namespace CodeImp.DoomBuilder.Data
|
||||||
texturenames.Sort();
|
texturenames.Sort();
|
||||||
flatnames.Sort();
|
flatnames.Sort();
|
||||||
|
|
||||||
|
// biwa. Moved model processing after texture processing, since the model might need one of those textures
|
||||||
|
Dictionary<string, int> actorsbyclass = CreateActorsByClassList();
|
||||||
|
LoadModeldefs(actorsbyclass);
|
||||||
|
foreach (Thing t in General.Map.Map.Things) t.UpdateCache();
|
||||||
|
|
||||||
|
LoadGldefs(actorsbyclass);
|
||||||
|
|
||||||
// Sort things
|
// Sort things
|
||||||
foreach (ThingCategory tc in thingcategories) tc.SortIfNeeded();
|
foreach (ThingCategory tc in thingcategories) tc.SortIfNeeded();
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data
|
||||||
{
|
{
|
||||||
#region ================== Constants
|
#region ================== Constants
|
||||||
|
|
||||||
public static readonly string[] SUPPORTED_TEXTURE_EXTENSIONS = { ".jpg", ".tga", ".png", ".dds", ".pcx" };
|
// Keep sage order of extensions as in GZDoom's r_data\models\models.cpp FindGFXFile function. That doesn't
|
||||||
|
// list .dds, but just keep it in here
|
||||||
|
public static readonly string[] SUPPORTED_TEXTURE_EXTENSIONS = { ".png", ".jpg", ".tga", ".pcx", ".dds" };
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
|
using System.Globalization; // biwa
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using CodeImp.DoomBuilder.IO;
|
using CodeImp.DoomBuilder.IO;
|
||||||
|
@ -127,6 +128,15 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
||||||
break;
|
break;
|
||||||
case ".3d":
|
case ".3d":
|
||||||
result = Read3DModel(ref bbs, skins, ms, device, mde.FrameIndices[i], mde.ModelNames[i], containers);
|
result = Read3DModel(ref bbs, skins, ms, device, mde.FrameIndices[i], mde.ModelNames[i], containers);
|
||||||
|
break;
|
||||||
|
case ".obj":
|
||||||
|
// OBJ doesn't support frames, so print out an error
|
||||||
|
if (mde.FrameIndices[i] > 0)
|
||||||
|
{
|
||||||
|
General.ErrorLogger.Add(ErrorType.Error, "Trying to load frame " + mde.FrameIndices[i] + " of model \"" + mde.ModelNames[i] + "\", but OBJ doesn't support frames!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result = ReadOBJModel(ref bbs, skins, ms, device, mde.ModelNames[i]);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
result.Errors = "model format is not supported";
|
result.Errors = "model format is not supported";
|
||||||
|
@ -160,6 +170,14 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
||||||
//try to use model's own skins
|
//try to use model's own skins
|
||||||
for(int m = 0; m < result.Meshes.Count; m++)
|
for(int m = 0; m < result.Meshes.Count; m++)
|
||||||
{
|
{
|
||||||
|
// biwa. Makes sure to add a dummy texture if the MODELDEF skin definition is erroneous
|
||||||
|
if(m >= result.Skins.Count)
|
||||||
|
{
|
||||||
|
errors.Add("no skin defined for mesh " + m + ".");
|
||||||
|
mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(string.IsNullOrEmpty(result.Skins[m]))
|
if(string.IsNullOrEmpty(result.Skins[m]))
|
||||||
{
|
{
|
||||||
mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
|
mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
|
||||||
|
@ -167,21 +185,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
string path = result.Skins[m].Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
Texture t = GetTexture(containers, result.Skins[m], device);
|
||||||
ext = Path.GetExtension(path);
|
|
||||||
|
|
||||||
if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, ext) == -1)
|
|
||||||
{
|
|
||||||
mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
|
|
||||||
errors.Add("image format \"" + ext + "\" is not supported!");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//relative path?
|
|
||||||
if(path.IndexOf(Path.DirectorySeparatorChar) == -1)
|
|
||||||
path = Path.Combine(Path.GetDirectoryName(mde.ModelNames[i]), path);
|
|
||||||
|
|
||||||
Texture t = LoadTexture(containers, path, device);
|
|
||||||
|
|
||||||
if(t == null)
|
if(t == null)
|
||||||
{
|
{
|
||||||
|
@ -196,11 +200,12 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
||||||
//Try to use texture loaded from MODELDEFS
|
//Try to use texture loaded from MODELDEFS
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Texture t = LoadTexture(containers, mde.SkinNames[i], device);
|
Texture t = GetTexture(containers, mde.SkinNames[i], device);
|
||||||
|
|
||||||
if(t == null)
|
if(t == null)
|
||||||
{
|
{
|
||||||
mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
|
mde.Model.Textures.Add(General.Map.Data.UnknownTexture3D.Texture);
|
||||||
errors.Add("unable to load texture \"" + mde.SkinNames[i] + "\"");
|
errors.Add("unable to load skin \"" + mde.SkinNames[i] + "\"");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -237,6 +242,92 @@ 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)));
|
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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Texture GetTexture(List<DataReader> containers, string texturename, Device device)
|
||||||
|
{
|
||||||
|
Texture t = null;
|
||||||
|
string[] extensions = new string[ModelData.SUPPORTED_TEXTURE_EXTENSIONS.Length + 1];
|
||||||
|
|
||||||
|
Array.Copy(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, 0, extensions, 1, ModelData.SUPPORTED_TEXTURE_EXTENSIONS.Length);
|
||||||
|
extensions[0] = "";
|
||||||
|
|
||||||
|
// Try to load the texture as defined by its path. GZDoom doesn't care about extensions
|
||||||
|
if (t == null)
|
||||||
|
{
|
||||||
|
foreach (string extension in extensions)
|
||||||
|
{
|
||||||
|
string name = Path.ChangeExtension(texturename, null) + extension;
|
||||||
|
name = name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||||
|
|
||||||
|
t = LoadTexture(containers, name, device);
|
||||||
|
|
||||||
|
if (t != null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to use an already defined texture. Again, just try out all extensions
|
||||||
|
foreach (string extension in extensions)
|
||||||
|
{
|
||||||
|
string name = Path.ChangeExtension(texturename, null) + extension;
|
||||||
|
|
||||||
|
if (General.Map.Data.GetTextureExists(name))
|
||||||
|
{
|
||||||
|
ImageData image = General.Map.Data.GetTextureImage(name);
|
||||||
|
|
||||||
|
if (!image.IsImageLoaded)
|
||||||
|
image.LoadImage();
|
||||||
|
|
||||||
|
if (image.Texture == null)
|
||||||
|
image.CreateTexture();
|
||||||
|
|
||||||
|
t = image.Texture;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GZDoom can also ignore the path completely (because why not), so let's see if there's a texture with
|
||||||
|
// just the skin name
|
||||||
|
if (t == null)
|
||||||
|
{
|
||||||
|
string name = Path.ChangeExtension(Path.GetFileName(texturename), null);
|
||||||
|
|
||||||
|
if (General.Map.Data.GetTextureExists(name))
|
||||||
|
{
|
||||||
|
ImageData image = General.Map.Data.GetTextureImage(name);
|
||||||
|
|
||||||
|
if (!image.IsImageLoaded)
|
||||||
|
image.LoadImage();
|
||||||
|
|
||||||
|
if (image.Texture == null)
|
||||||
|
image.CreateTexture();
|
||||||
|
|
||||||
|
t = image.Texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or maybe it's a sprite
|
||||||
|
if(t == null)
|
||||||
|
{
|
||||||
|
string name = Path.ChangeExtension(texturename, null);
|
||||||
|
|
||||||
|
if (General.Map.Data.GetSpriteExists(name))
|
||||||
|
{
|
||||||
|
ImageData image = General.Map.Data.GetSpriteImage(name);
|
||||||
|
|
||||||
|
if (!image.IsImageLoaded)
|
||||||
|
image.LoadImage();
|
||||||
|
|
||||||
|
if (image.Texture == null)
|
||||||
|
image.CreateTexture();
|
||||||
|
|
||||||
|
t = image.Texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ================== 3D (unreal)
|
#region ================== 3D (unreal)
|
||||||
|
@ -1225,6 +1316,322 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region ================== OBJ
|
||||||
|
|
||||||
|
private static MD3LoadResult ReadOBJModel(ref BoundingBoxSizes bbs, Dictionary<int, string> skins, Stream s, Device device, string name)
|
||||||
|
{
|
||||||
|
MD3LoadResult result = new MD3LoadResult();
|
||||||
|
|
||||||
|
using (var reader = new StreamReader(s, Encoding.ASCII))
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
int linenum = 1;
|
||||||
|
string message;
|
||||||
|
int surfaceskinid = 0;
|
||||||
|
List<Vector3D> vertices = new List<Vector3D>();
|
||||||
|
List<int> faces = new List<int>();
|
||||||
|
List<Vector3D> normals = new List<Vector3D>();
|
||||||
|
List<Vector2D> texcoords = new List<Vector2D>();
|
||||||
|
List<WorldVertex> worldvertices = new List<WorldVertex>();
|
||||||
|
List<int> polyindiceslist = new List<int>();
|
||||||
|
|
||||||
|
while ((line = reader.ReadLine()) != null) {
|
||||||
|
string[] fields = line.Trim().Split(new[] { ' ', '\t' }, 2, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if (fields.Length == 0) continue; // Empty line
|
||||||
|
if (fields[0].Trim() == "#") continue; // Comment
|
||||||
|
|
||||||
|
string keyword = fields[0].Trim();
|
||||||
|
string payload = fields[1].Trim();
|
||||||
|
|
||||||
|
switch(keyword)
|
||||||
|
{
|
||||||
|
case "v":
|
||||||
|
Vector3D v = new Vector3D(0, 0, 0);
|
||||||
|
|
||||||
|
if (OBJParseVertex(payload, ref v, out message))
|
||||||
|
vertices.Add(v);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Errors = String.Format("Error in line {0}: {1}", linenum, message);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "vt":
|
||||||
|
Vector2D t = new Vector2D(0, 0);
|
||||||
|
|
||||||
|
if (OBJParseTextureCoords(payload, ref t, out message))
|
||||||
|
texcoords.Add(t);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Errors = String.Format("Error in line {0}: {1}", linenum, message);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "vn":
|
||||||
|
Vector3D n = new Vector3D(0, 0, 0);
|
||||||
|
|
||||||
|
if (OBJParseNormal(payload, ref n, out message))
|
||||||
|
normals.Add(n);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Errors = String.Format("Error in line {0}: {1}", linenum, message);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "f":
|
||||||
|
List<int> fv = new List<int>();
|
||||||
|
List<int> vt = new List<int>();
|
||||||
|
List<int> vn = new List<int>();
|
||||||
|
|
||||||
|
if (OBJParseFace(payload, ref fv, ref vt, ref vn, out message))
|
||||||
|
{
|
||||||
|
// Sanity check for vertices
|
||||||
|
for (int i=0; i < fv.Count; i++)
|
||||||
|
if(fv[i] != -1 && fv[i] > vertices.Count)
|
||||||
|
{
|
||||||
|
result.Errors = String.Format("Error in line {0}: vertex {1} does not exist", linenum, i + 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check for texture coordinates
|
||||||
|
for (int i=0; i < vt.Count; i++)
|
||||||
|
if(vt[i] != -1 && vt[i] > texcoords.Count)
|
||||||
|
{
|
||||||
|
result.Errors = String.Format("Error in line {0}: texture coordinate {1} does not exist", linenum, i + 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check for normals
|
||||||
|
for (int i = 0; i < vn.Count; i++)
|
||||||
|
if (vn[i] != -1 && vn[i] > normals.Count)
|
||||||
|
{
|
||||||
|
result.Errors = String.Format("Error in line {0}: vertex {1} does not exist", linenum, i + 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] seq;
|
||||||
|
|
||||||
|
// If the face is a quad split it into two triangles
|
||||||
|
if (fv.Count == 3)
|
||||||
|
seq = new int[] { 0, 1, 2 };
|
||||||
|
else
|
||||||
|
seq = new int[] { 0, 1, 2, 0, 2, 3 };
|
||||||
|
|
||||||
|
for (int i = 0; i < seq.Length; i++) {
|
||||||
|
WorldVertex wc = new WorldVertex(vertices[fv[seq[i]]]);
|
||||||
|
|
||||||
|
if(vt[seq[i]] != -1)
|
||||||
|
{
|
||||||
|
wc.u = texcoords[vt[seq[i]]].x;
|
||||||
|
wc.v = texcoords[vt[seq[i]]].y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vn[seq[i]] != -1)
|
||||||
|
{
|
||||||
|
wc.nx = normals[vn[seq[i]]].x;
|
||||||
|
wc.ny = normals[vn[seq[i]]].y;
|
||||||
|
wc.nz = normals[vn[seq[i]]].z;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoundingBoxTools.UpdateBoundingBoxSizes(ref bbs, wc);
|
||||||
|
|
||||||
|
worldvertices.Add(wc);
|
||||||
|
polyindiceslist.Add(polyindiceslist.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Errors = String.Format("Error in line {0}: {1}", linenum, message);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "usemtl":
|
||||||
|
// If there's a new texture defined create a mesh from the current faces and
|
||||||
|
// start a gather new faces for the next mesh
|
||||||
|
if(worldvertices.Count > 0)
|
||||||
|
{
|
||||||
|
CreateMesh(device, ref result, worldvertices, polyindiceslist);
|
||||||
|
worldvertices.Clear();
|
||||||
|
polyindiceslist.Clear();
|
||||||
|
}
|
||||||
|
if (fields.Length >= 2)
|
||||||
|
result.Skins.Add(fields[1]);
|
||||||
|
|
||||||
|
surfaceskinid++;
|
||||||
|
break;
|
||||||
|
case "": // Empty line
|
||||||
|
case "#": // Line is a comment
|
||||||
|
case "s": // Smooth
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
linenum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateMesh(device, ref result, worldvertices, polyindiceslist);
|
||||||
|
|
||||||
|
// Overwrite internal textures with SurfaceSkin definitions if necessary
|
||||||
|
if (skins != null)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<int, string> group in skins)
|
||||||
|
{
|
||||||
|
// Add dummy skins if necessary
|
||||||
|
while (result.Skins.Count <= group.Key)
|
||||||
|
result.Skins.Add(String.Empty);
|
||||||
|
|
||||||
|
result.Skins[group.Key] = group.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool OBJParseVertex(string payload, ref Vector3D v, out string message)
|
||||||
|
{
|
||||||
|
string[] fields = payload.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if(fields.Length < 3)
|
||||||
|
{
|
||||||
|
message = "too few arguments";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
v.x = float.Parse(fields[0], CultureInfo.InvariantCulture);
|
||||||
|
v.z = float.Parse(fields[1], CultureInfo.InvariantCulture);
|
||||||
|
v.y = float.Parse(fields[2], CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
|
||||||
|
// Prepare to fix rotation angle
|
||||||
|
float angleOfsetCos = (float)Math.Cos(Angle2D.PIHALF);
|
||||||
|
float angleOfsetSin = (float)Math.Sin(Angle2D.PIHALF);
|
||||||
|
|
||||||
|
// Fix rotation angle
|
||||||
|
float rx = angleOfsetCos * v.x - angleOfsetSin * v.y;
|
||||||
|
float ry = angleOfsetSin * v.x + angleOfsetCos * v.y;
|
||||||
|
v.x = rx;
|
||||||
|
v.y = ry;
|
||||||
|
}
|
||||||
|
catch (FormatException e)
|
||||||
|
{
|
||||||
|
message = "field is not a float";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool OBJParseTextureCoords(string payload, ref Vector2D t, out string message)
|
||||||
|
{
|
||||||
|
string[] fields = payload.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if (fields.Length < 2)
|
||||||
|
{
|
||||||
|
message = "too few arguments";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
t.x = float.Parse(fields[0], CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
if (fields.Length == 2)
|
||||||
|
t.y = float.Parse(fields[1], CultureInfo.InvariantCulture);
|
||||||
|
else
|
||||||
|
t.y = 0.0f;
|
||||||
|
}
|
||||||
|
catch (FormatException e)
|
||||||
|
{
|
||||||
|
message = "field is not a float";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool OBJParseNormal(string payload, ref Vector3D normal, out string message)
|
||||||
|
{
|
||||||
|
string[] fields = payload.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if (fields.Length < 3)
|
||||||
|
{
|
||||||
|
message = "too few arguments";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
normal.x = float.Parse(fields[0], CultureInfo.InvariantCulture);
|
||||||
|
normal.y = float.Parse(fields[1], CultureInfo.InvariantCulture);
|
||||||
|
normal.z = float.Parse(fields[2], CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
catch (FormatException e)
|
||||||
|
{
|
||||||
|
message = "field is not a float";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool OBJParseFace(string payload, ref List<int> face, ref List<int> texcoords, ref List<int> normals, out string message)
|
||||||
|
{
|
||||||
|
string[] fields = payload.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if (fields.Length < 3)
|
||||||
|
{
|
||||||
|
message = "too few arguments";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fields.Length > 4)
|
||||||
|
{
|
||||||
|
message = "faces with more than 4 sides are not supported";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (int i = 0; i < fields.Length; i++)
|
||||||
|
{
|
||||||
|
string[] vertexdata = fields[i].Split('/');
|
||||||
|
|
||||||
|
face.Add(int.Parse(vertexdata[0], CultureInfo.InvariantCulture) - 1);
|
||||||
|
|
||||||
|
if (vertexdata.Length >= 1 && vertexdata[1] != "")
|
||||||
|
texcoords.Add(int.Parse(vertexdata[1], CultureInfo.InvariantCulture) - 1);
|
||||||
|
else
|
||||||
|
texcoords.Add(-1);
|
||||||
|
|
||||||
|
if (vertexdata.Length >= 2 && vertexdata[2] != "")
|
||||||
|
normals.Add(int.Parse(vertexdata[2], CultureInfo.InvariantCulture) - 1);
|
||||||
|
else
|
||||||
|
normals.Add(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(FormatException e)
|
||||||
|
{
|
||||||
|
message = "field is not an integer";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region ================== Utility
|
#region ================== Utility
|
||||||
|
|
||||||
private static MemoryStream LoadFile(List<DataReader> containers, string path, bool isModel)
|
private static MemoryStream LoadFile(List<DataReader> containers, string path, bool isModel)
|
||||||
|
@ -1243,7 +1650,7 @@ namespace CodeImp.DoomBuilder.GZBuilder.MD3
|
||||||
{
|
{
|
||||||
if(string.IsNullOrEmpty(path)) return null;
|
if(string.IsNullOrEmpty(path)) return null;
|
||||||
|
|
||||||
MemoryStream ms = LoadFile(containers, path, false);
|
MemoryStream ms = LoadFile(containers, path, true);
|
||||||
if(ms == null) return null;
|
if(ms == null) return null;
|
||||||
|
|
||||||
Texture texture = null;
|
Texture texture = null;
|
||||||
|
|
|
@ -150,9 +150,9 @@ namespace CodeImp.DoomBuilder.ZDoom
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(modelext != ".md3" && modelext != ".md2" && modelext != ".3d")
|
if(modelext != ".md3" && modelext != ".md2" && modelext != ".3d" && modelext != ".obj")
|
||||||
{
|
{
|
||||||
parser.ReportError("Model \"" + token + "\" won't be loaded. Only Unreal 3D, MD2 and MD3 models are supported");
|
parser.ReportError("Model \"" + token + "\" won't be loaded. Only Unreal 3D, MD2, MD3, and OBJ models are supported");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,16 +193,8 @@ namespace CodeImp.DoomBuilder.ZDoom
|
||||||
// Check invalid path chars
|
// Check invalid path chars
|
||||||
if(!parser.CheckInvalidPathChars(token)) return false;
|
if(!parser.CheckInvalidPathChars(token)) return false;
|
||||||
|
|
||||||
// Check extension
|
|
||||||
string texext = Path.GetExtension(token);
|
|
||||||
if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, texext) == -1)
|
|
||||||
{
|
|
||||||
parser.ReportError("Image format \"" + texext + "\" is not supported");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GZDoom allows skins with identical index, it uses the last one encountered
|
// GZDoom allows skins with identical index, it uses the last one encountered
|
||||||
skinnames[skinindex] = Path.Combine(path, token).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
skinnames[skinindex] = Path.Combine(path, token);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// SurfaceSkin <int modelindex> <int surfaceindex> <string skinfile>
|
// SurfaceSkin <int modelindex> <int surfaceindex> <string skinfile>
|
||||||
|
@ -258,14 +250,6 @@ namespace CodeImp.DoomBuilder.ZDoom
|
||||||
// Check invalid path chars
|
// Check invalid path chars
|
||||||
if(!parser.CheckInvalidPathChars(token)) return false;
|
if(!parser.CheckInvalidPathChars(token)) return false;
|
||||||
|
|
||||||
// Check extension
|
|
||||||
string skinext = Path.GetExtension(token);
|
|
||||||
if(Array.IndexOf(ModelData.SUPPORTED_TEXTURE_EXTENSIONS, skinext) == -1)
|
|
||||||
{
|
|
||||||
parser.ReportError("Image format \"" + skinext + "\" is not supported");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
surfaceskinenames[modelindex][surfaceindex] = Path.Combine(path, token).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
surfaceskinenames[modelindex][surfaceindex] = Path.Combine(path, token).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue