I am having an issue where triangles in a model aren't being shown.
The left is wireframe, the right is the finished image. For some reason a bunch of the triangles aren't being shown.
The project is in C#, using OpenGL (OpenTK). I'll provide more information if neccessary (such as code), but there's a lot of code, so i'd rather check if anyone has a simple solution first... Please comment if you need more info though.
EDIT: Here's the buffer code if it helps...
public void LoadBuffers(MeshData m, out uint indexBuffer, out uint dataBuffer, out int vertOffset, out int normOffset, out int texcoordOffset)
{
float[] verts, norms, texcoords;
uint[] indices;
m.OpenGLArrays(out verts, out norms, out texcoords, out indices);
GL.GenBuffers(1, out dataBuffer);
GL.GenBuffers(1, out indexBuffer);
int buffersize = (verts.Length + norms.Length + texcoords.Length);
float[] bufferdata = new float[buffersize];
vertOffset = 0;
normOffset = verts.Length;
texcoordOffset = (verts.Length + norms.Length);
verts.CopyTo(bufferdata, vertOffset);
norms.CopyTo(bufferdata, normOffset);
texcoords.CopyTo(bufferdata, texcoordOffset);
bool v = false;
for (int i = texcoordOffset; i < bufferdata.Length; i++)
{
if (v)
{
bufferdata[i] = 1 - bufferdata[i];
v = false;
}
else
{
v = true;
}
}
GL.BindBuffer(BufferTarget.ArrayBuffer, dataBuffer);
GL.BufferData<float>(BufferTarget.ArrayBuffer, (IntPtr)(buffersize * sizeof(float)), bufferdata,
BufferUsageHint.StaticDraw);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);
GL.BufferData<uint>(BufferTarget.ElementArrayBuffer,
(IntPtr)(indices.Length * sizeof(uint)), indices, BufferUsageHint.StaticDraw);
}
public bool DrawBuffer(MeshData m, uint tex, uint indexBuffer, uint dataBuffer, int vertOffset, int normOffset, int texcoordOffset)
{
tex = tempTexture;
GL.CullFace(CullFaceMode.Back);
GL.PushClientAttrib(ClientAttribMask.ClientVertexArrayBit);
GL.ClientActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, tex);
GL.BindBuffer(BufferTarget.ArrayBuffer, dataBuffer);
GL.NormalPointer(NormalPointerType.Float, 0, (IntPtr)(normOffset * sizeof(float)));
GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, (IntPtr)(texcoordOffset * sizeof(float)));
GL.VertexPointer(3, VertexPointerType.Float, 0, (IntPtr)(vertOffset * sizeof(float)));
GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);
GL.DrawElements(BeginMode.Triangles, m.Tris.Length * 3, DrawElementsType.UnsignedInt, IntPtr.Zero);
GL.PopClientAttrib();
return true;
}
EDIT 2: And here is my OBJ loader... There's quite a chance this is the problem.
public class ObjLoader
{
public MeshData LoadStream(Stream stream)
{
StreamReader reader = new StreamReader(stream);
List<Vector3> points = new List<Vector3>();
List<Vector3> normals = new List<Vector3>();
List<Vector2> texCoords = new List<Vector2>();
List<Tri> tris = new List<Tri>();
string line;
char[] splitChars = { ' ' };
while ((line = reader.ReadLine()) != null)
{
line = line.Trim(splitChars);
line = line.Replace(" ", " ");
string[] parameters = line.Split(splitChars);
switch (parameters[0])
{
case "p":
// Point
break;
case "v":
// Vertex
float x = float.Parse(parameters[1], CultureInfo.InvariantCulture.NumberFormat);
float y = float.Parse(parameters[2], CultureInfo.InvariantCulture.NumberFormat);
float z = float.Parse(parameters[3], CultureInfo.InvariantCulture.NumberFormat);
points.Add(new Vector3(x, y, z));
break;
case "vt":
// TexCoord
float u = float.Parse(parameters[1], CultureInfo.InvariantCulture.NumberFormat);
float v = float.Parse(parameters[2], CultureInfo.InvariantCulture.NumberFormat);
texCoords.Add(new Vector2(u, v));
break;
case "vn":
// Normal
float nx = float.Parse(parameters[1], CultureInfo.InvariantCulture.NumberFormat);
float ny = float.Parse(parameters[2], CultureInfo.InvariantCulture.NumberFormat);
float nz = float.Parse(parameters[3], CultureInfo.InvariantCulture.NumberFormat);
normals.Add(new Vector3(nx, ny, nz));
break;
case "f":
// Face
tris.AddRange(parseFace(parameters));
break;
}
}
Vector3[] p = points.ToArray();
Vector2[] tc = texCoords.ToArray();
Vector3[] n = normals.ToArray();
Tri[] f = tris.ToArray();
// If there are no specified texcoords or normals, we add a dummy one.
// That way the Points will have something to refer to.
if (tc.Length == 0)
{
tc = new Vector2[1];
tc[0] = new Vector2(0, 0);
}
if (n.Length == 0)
{
n = new Vector3[1];
n[0] = new Vector3(1, 0, 0);
}
return new MeshData(p, n, tc, f);
}
public MeshData LoadFile(string file)
{
// Silly me, using() closes the file automatically.
using (FileStream s = File.Open(file, FileMode.Open))
{
return LoadStream(s);
}
}
private static Tri[] parseFace(string[] indices)
{
Point[] p = new Point[indices.Length - 1];
for (int i = 0; i < p.Length; i++)
{
p[i] = parsePoint(indices[i + 1]);
}
return Triangulate(p);
//return new Face(p);
}
// Takes an array of points and returns an array of triangles.
// The points form an arbitrary polygon.
private static Tri[] Triangulate(Point[] ps)
{
List<Tri> ts = new List<Tri>();
if (ps.Length < 3)
{
throw new Exception("Invalid shape! Must have >2 points");
}
Point lastButOne = ps[1];
Point lastButTwo = ps[0];
for (int i = 2; i < ps.Length; i++)
{
Tri t = new Tri(lastButTwo, lastButOne, ps[i]);
lastButOne = ps[i];
lastButTwo = ps[i - 1];
ts.Add(t);
}
return ts.ToArray();
}
private static Point parsePoint(string s)
{
s += "//";
char[] splitChars = { '/' };
string[] parameters = s.Split(splitChars);
int vert, tex, norm;
vert = tex = norm = 0;
vert = int.Parse(parameters[0]) - 1;
// Texcoords and normals are optional in .obj files
if (parameters[1] != "")
{
tex = int.Parse(parameters[1]) - 1;
}
if (parameters[2] != "")
{
norm = int.Parse(parameters[2]) - 1;
}
return new Point(vert, norm, tex);
}
}